diff options
Diffstat (limited to 'contrib/llvm/tools/clang/lib/StaticAnalyzer')
161 files changed, 76240 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AllocationState.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AllocationState.h new file mode 100644 index 000000000000..c8193f77f928 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AllocationState.h @@ -0,0 +1,39 @@ +//===--- AllocationState.h ------------------------------------- *- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONSTATE_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONSTATE_H + +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" + +namespace clang { +namespace ento { + +namespace allocation_state { + +ProgramStateRef markReleased(ProgramStateRef State, SymbolRef Sym, + const Expr *Origin); + +/// This function provides an additional visitor that augments the bug report +/// with information relevant to memory errors caused by the misuse of +/// AF_InnerBuffer symbols. +std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym); + +/// 'Sym' represents a pointer to the inner buffer of a container object. +/// This function looks up the memory region of that object in +/// DanglingInternalBufferChecker's program state map. +const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym); + +} // end namespace allocation_state + +} // end namespace ento +} // end namespace clang + +#endif diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp new file mode 100644 index 000000000000..b5d0f6620a1d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp @@ -0,0 +1,178 @@ +//===- AnalysisOrderChecker - Print callbacks called ------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker prints callbacks that are called during analysis. +// This is required to ensure that callbacks are fired in order +// and do not duplicate or get lost. +// Feel free to extend this checker with any callback you need to check. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ExprCXX.h" +#include "clang/Analysis/CFGStmtMap.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { + +class AnalysisOrderChecker + : public Checker<check::PreStmt<CastExpr>, + check::PostStmt<CastExpr>, + check::PreStmt<ArraySubscriptExpr>, + check::PostStmt<ArraySubscriptExpr>, + check::PreStmt<CXXNewExpr>, + check::PostStmt<CXXNewExpr>, + check::PreStmt<OffsetOfExpr>, + check::PostStmt<OffsetOfExpr>, + check::PreCall, + check::PostCall, + check::EndFunction, + check::NewAllocator, + check::Bind, + check::RegionChanges, + check::LiveSymbols> { + + bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const { + return Opts.getCheckerBooleanOption("*", false, this) || + Opts.getCheckerBooleanOption(CallbackName, false, this); + } + + bool isCallbackEnabled(CheckerContext &C, StringRef CallbackName) const { + AnalyzerOptions &Opts = C.getAnalysisManager().getAnalyzerOptions(); + return isCallbackEnabled(Opts, CallbackName); + } + + bool isCallbackEnabled(ProgramStateRef State, StringRef CallbackName) const { + AnalyzerOptions &Opts = State->getStateManager().getOwningEngine() + .getAnalysisManager().getAnalyzerOptions(); + return isCallbackEnabled(Opts, CallbackName); + } + +public: + void checkPreStmt(const CastExpr *CE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PreStmtCastExpr")) + llvm::errs() << "PreStmt<CastExpr> (Kind : " << CE->getCastKindName() + << ")\n"; + } + + void checkPostStmt(const CastExpr *CE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PostStmtCastExpr")) + llvm::errs() << "PostStmt<CastExpr> (Kind : " << CE->getCastKindName() + << ")\n"; + } + + void checkPreStmt(const ArraySubscriptExpr *SubExpr, + CheckerContext &C) const { + if (isCallbackEnabled(C, "PreStmtArraySubscriptExpr")) + llvm::errs() << "PreStmt<ArraySubscriptExpr>\n"; + } + + void checkPostStmt(const ArraySubscriptExpr *SubExpr, + CheckerContext &C) const { + if (isCallbackEnabled(C, "PostStmtArraySubscriptExpr")) + llvm::errs() << "PostStmt<ArraySubscriptExpr>\n"; + } + + void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PreStmtCXXNewExpr")) + llvm::errs() << "PreStmt<CXXNewExpr>\n"; + } + + void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PostStmtCXXNewExpr")) + llvm::errs() << "PostStmt<CXXNewExpr>\n"; + } + + void checkPreStmt(const OffsetOfExpr *OOE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PreStmtOffsetOfExpr")) + llvm::errs() << "PreStmt<OffsetOfExpr>\n"; + } + + void checkPostStmt(const OffsetOfExpr *OOE, CheckerContext &C) const { + if (isCallbackEnabled(C, "PostStmtOffsetOfExpr")) + llvm::errs() << "PostStmt<OffsetOfExpr>\n"; + } + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const { + if (isCallbackEnabled(C, "PreCall")) { + llvm::errs() << "PreCall"; + if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl())) + llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')'; + llvm::errs() << '\n'; + } + } + + void checkPostCall(const CallEvent &Call, CheckerContext &C) const { + if (isCallbackEnabled(C, "PostCall")) { + llvm::errs() << "PostCall"; + if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl())) + llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')'; + llvm::errs() << '\n'; + } + } + + void checkEndFunction(const ReturnStmt *S, CheckerContext &C) const { + if (isCallbackEnabled(C, "EndFunction")) { + llvm::errs() << "EndFunction\nReturnStmt: " << (S ? "yes" : "no") << "\n"; + if (!S) + return; + + llvm::errs() << "CFGElement: "; + CFGStmtMap *Map = C.getCurrentAnalysisDeclContext()->getCFGStmtMap(); + CFGElement LastElement = Map->getBlock(S)->back(); + + if (LastElement.getAs<CFGStmt>()) + llvm::errs() << "CFGStmt\n"; + else if (LastElement.getAs<CFGAutomaticObjDtor>()) + llvm::errs() << "CFGAutomaticObjDtor\n"; + } + } + + void checkNewAllocator(const CXXNewExpr *CNE, SVal Target, + CheckerContext &C) const { + if (isCallbackEnabled(C, "NewAllocator")) + llvm::errs() << "NewAllocator\n"; + } + + void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const { + if (isCallbackEnabled(C, "Bind")) + llvm::errs() << "Bind\n"; + } + + void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SymReaper) const { + if (isCallbackEnabled(State, "LiveSymbols")) + llvm::errs() << "LiveSymbols\n"; + } + + ProgramStateRef + checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, const CallEvent *Call) const { + if (isCallbackEnabled(State, "RegionChanges")) + llvm::errs() << "RegionChanges\n"; + return State; + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +void ento::registerAnalysisOrderChecker(CheckerManager &mgr) { + mgr.registerChecker<AnalysisOrderChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp new file mode 100644 index 000000000000..5e01012401b2 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp @@ -0,0 +1,142 @@ +//==--AnalyzerStatsChecker.cpp - Analyzer visitation statistics --*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This file reports various statistics about analyzer visitation. +//===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +#define DEBUG_TYPE "StatsChecker" + +STATISTIC(NumBlocks, + "The # of blocks in top level functions"); +STATISTIC(NumBlocksUnreachable, + "The # of unreachable blocks in analyzing top level functions"); + +namespace { +class AnalyzerStatsChecker : public Checker<check::EndAnalysis> { +public: + void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const; +}; +} + +void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G, + BugReporter &B, + ExprEngine &Eng) const { + const CFG *C = nullptr; + const SourceManager &SM = B.getSourceManager(); + llvm::SmallPtrSet<const CFGBlock*, 32> reachable; + + // Root node should have the location context of the top most function. + const ExplodedNode *GraphRoot = *G.roots_begin(); + const LocationContext *LC = GraphRoot->getLocation().getLocationContext(); + + const Decl *D = LC->getDecl(); + + // Iterate over the exploded graph. + for (ExplodedGraph::node_iterator I = G.nodes_begin(); + I != G.nodes_end(); ++I) { + const ProgramPoint &P = I->getLocation(); + + // Only check the coverage in the top level function (optimization). + if (D != P.getLocationContext()->getDecl()) + continue; + + if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { + const CFGBlock *CB = BE->getBlock(); + reachable.insert(CB); + } + } + + // Get the CFG and the Decl of this block. + C = LC->getCFG(); + + unsigned total = 0, unreachable = 0; + + // Find CFGBlocks that were not covered by any node + for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) { + const CFGBlock *CB = *I; + ++total; + // Check if the block is unreachable + if (!reachable.count(CB)) { + ++unreachable; + } + } + + // We never 'reach' the entry block, so correct the unreachable count + unreachable--; + // There is no BlockEntrance corresponding to the exit block as well, so + // assume it is reached as well. + unreachable--; + + // Generate the warning string + SmallString<128> buf; + llvm::raw_svector_ostream output(buf); + PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); + if (!Loc.isValid()) + return; + + if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) { + const NamedDecl *ND = cast<NamedDecl>(D); + output << *ND; + } + else if (isa<BlockDecl>(D)) { + output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn(); + } + + NumBlocksUnreachable += unreachable; + NumBlocks += total; + std::string NameOfRootFunction = output.str(); + + output << " -> Total CFGBlocks: " << total << " | Unreachable CFGBlocks: " + << unreachable << " | Exhausted Block: " + << (Eng.wasBlocksExhausted() ? "yes" : "no") + << " | Empty WorkList: " + << (Eng.hasEmptyWorkList() ? "yes" : "no"); + + B.EmitBasicReport(D, this, "Analyzer Statistics", "Internal Statistics", + output.str(), PathDiagnosticLocation(D, SM)); + + // Emit warning for each block we bailed out on. + typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator; + const CoreEngine &CE = Eng.getCoreEngine(); + for (ExhaustedIterator I = CE.blocks_exhausted_begin(), + E = CE.blocks_exhausted_end(); I != E; ++I) { + const BlockEdge &BE = I->first; + const CFGBlock *Exit = BE.getDst(); + if (Exit->empty()) + continue; + const CFGElement &CE = Exit->front(); + if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { + SmallString<128> bufI; + llvm::raw_svector_ostream outputI(bufI); + outputI << "(" << NameOfRootFunction << ")" << + ": The analyzer generated a sink at this point"; + B.EmitBasicReport( + D, this, "Sink Point", "Internal Statistics", outputI.str(), + PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC)); + } + } +} + +void ento::registerAnalyzerStatsChecker(CheckerManager &mgr) { + mgr.registerChecker<AnalyzerStatsChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp new file mode 100644 index 000000000000..20f3092fdba4 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -0,0 +1,93 @@ +//== ArrayBoundChecker.cpp ------------------------------*- 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 ArrayBoundChecker, which is a path-sensitive check +// which looks for an out-of-bound array element access. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class ArrayBoundChecker : + public Checker<check::Location> { + mutable std::unique_ptr<BuiltinBug> BT; + +public: + void checkLocation(SVal l, bool isLoad, const Stmt* S, + CheckerContext &C) const; +}; +} + +void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS, + CheckerContext &C) const { + // Check for out of bound array element access. + const MemRegion *R = l.getAsRegion(); + if (!R) + return; + + const ElementRegion *ER = dyn_cast<ElementRegion>(R); + if (!ER) + return; + + // Get the index of the accessed element. + DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); + + // Zero index is always in bound, this also passes ElementRegions created for + // pointer casts. + if (Idx.isZeroConstant()) + return; + + ProgramStateRef state = C.getState(); + + // Get the size of the array. + DefinedOrUnknownSVal NumElements + = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), + ER->getValueType()); + + ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); + ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.generateErrorNode(StOutBound); + if (!N) + return; + + if (!BT) + BT.reset(new BuiltinBug( + this, "Out-of-bound array access", + "Access out-of-bound array element (buffer overflow)")); + + // FIXME: It would be nice to eventually make this diagnostic more clear, + // e.g., by referencing the original declaration or by saying *why* this + // reference is outside the range. + + // Generate a report for this bug. + auto report = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); + + report->addRange(LoadS->getSourceRange()); + C.emitReport(std::move(report)); + return; + } + + // Array bound check succeeded. From this point forward the array bound + // should always succeed. + C.addTransition(StInBound); +} + +void ento::registerArrayBoundChecker(CheckerManager &mgr) { + mgr.registerChecker<ArrayBoundChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp new file mode 100644 index 000000000000..26887be9f258 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -0,0 +1,356 @@ +//== ArrayBoundCheckerV2.cpp ------------------------------------*- 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 ArrayBoundCheckerV2, which is a path-sensitive check +// which looks for an out-of-bound array element access. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/CharUnits.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class ArrayBoundCheckerV2 : + public Checker<check::Location> { + mutable std::unique_ptr<BuiltinBug> BT; + + enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted }; + + void reportOOB(CheckerContext &C, ProgramStateRef errorState, OOB_Kind kind, + std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const; + +public: + void checkLocation(SVal l, bool isLoad, const Stmt*S, + CheckerContext &C) const; +}; + +// FIXME: Eventually replace RegionRawOffset with this class. +class RegionRawOffsetV2 { +private: + const SubRegion *baseRegion; + SVal byteOffset; + + RegionRawOffsetV2() + : baseRegion(nullptr), byteOffset(UnknownVal()) {} + +public: + RegionRawOffsetV2(const SubRegion* base, SVal offset) + : baseRegion(base), byteOffset(offset) {} + + NonLoc getByteOffset() const { return byteOffset.castAs<NonLoc>(); } + const SubRegion *getRegion() const { return baseRegion; } + + static RegionRawOffsetV2 computeOffset(ProgramStateRef state, + SValBuilder &svalBuilder, + SVal location); + + void dump() const; + void dumpToStream(raw_ostream &os) const; +}; +} + +static SVal computeExtentBegin(SValBuilder &svalBuilder, + const MemRegion *region) { + const MemSpaceRegion *SR = region->getMemorySpace(); + if (SR->getKind() == MemRegion::UnknownSpaceRegionKind) + return UnknownVal(); + else + return svalBuilder.makeZeroArrayIndex(); +} + +// TODO: once the constraint manager is smart enough to handle non simplified +// symbolic expressions remove this function. Note that this can not be used in +// the constraint manager as is, since this does not handle overflows. It is +// safe to assume, however, that memory offsets will not overflow. +static std::pair<NonLoc, nonloc::ConcreteInt> +getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent, + SValBuilder &svalBuilder) { + Optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>(); + if (SymVal && SymVal->isExpression()) { + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SymVal->getSymbol())) { + llvm::APSInt constant = + APSIntType(extent.getValue()).convert(SIE->getRHS()); + switch (SIE->getOpcode()) { + case BO_Mul: + // The constant should never be 0 here, since it the result of scaling + // based on the size of a type which is never 0. + if ((extent.getValue() % constant) != 0) + return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent); + else + return getSimplifiedOffsets( + nonloc::SymbolVal(SIE->getLHS()), + svalBuilder.makeIntVal(extent.getValue() / constant), + svalBuilder); + case BO_Add: + return getSimplifiedOffsets( + nonloc::SymbolVal(SIE->getLHS()), + svalBuilder.makeIntVal(extent.getValue() - constant), svalBuilder); + default: + break; + } + } + } + + return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent); +} + +void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad, + const Stmt* LoadS, + CheckerContext &checkerContext) const { + + // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping + // some new logic here that reasons directly about memory region extents. + // Once that logic is more mature, we can bring it back to assumeInBound() + // for all clients to use. + // + // The algorithm we are using here for bounds checking is to see if the + // memory access is within the extent of the base region. Since we + // have some flexibility in defining the base region, we can achieve + // various levels of conservatism in our buffer overflow checking. + ProgramStateRef state = checkerContext.getState(); + + SValBuilder &svalBuilder = checkerContext.getSValBuilder(); + const RegionRawOffsetV2 &rawOffset = + RegionRawOffsetV2::computeOffset(state, svalBuilder, location); + + if (!rawOffset.getRegion()) + return; + + NonLoc rawOffsetVal = rawOffset.getByteOffset(); + + // CHECK LOWER BOUND: Is byteOffset < extent begin? + // If so, we are doing a load/store + // before the first valid offset in the memory region. + + SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion()); + + if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) { + if (NV->getAs<nonloc::ConcreteInt>()) { + std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets = + getSimplifiedOffsets(rawOffset.getByteOffset(), + NV->castAs<nonloc::ConcreteInt>(), + svalBuilder); + rawOffsetVal = simplifiedOffsets.first; + *NV = simplifiedOffsets.second; + } + + SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffsetVal, *NV, + svalBuilder.getConditionType()); + + Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>(); + if (!lowerBoundToCheck) + return; + + ProgramStateRef state_precedesLowerBound, state_withinLowerBound; + std::tie(state_precedesLowerBound, state_withinLowerBound) = + state->assume(*lowerBoundToCheck); + + // Are we constrained enough to definitely precede the lower bound? + if (state_precedesLowerBound && !state_withinLowerBound) { + reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes); + return; + } + + // Otherwise, assume the constraint of the lower bound. + assert(state_withinLowerBound); + state = state_withinLowerBound; + } + + do { + // CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so, + // we are doing a load/store after the last valid offset. + DefinedOrUnknownSVal extentVal = + rawOffset.getRegion()->getExtent(svalBuilder); + if (!extentVal.getAs<NonLoc>()) + break; + + if (extentVal.getAs<nonloc::ConcreteInt>()) { + std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets = + getSimplifiedOffsets(rawOffset.getByteOffset(), + extentVal.castAs<nonloc::ConcreteInt>(), + svalBuilder); + rawOffsetVal = simplifiedOffsets.first; + extentVal = simplifiedOffsets.second; + } + + SVal upperbound = svalBuilder.evalBinOpNN(state, BO_GE, rawOffsetVal, + extentVal.castAs<NonLoc>(), + svalBuilder.getConditionType()); + + Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>(); + if (!upperboundToCheck) + break; + + ProgramStateRef state_exceedsUpperBound, state_withinUpperBound; + std::tie(state_exceedsUpperBound, state_withinUpperBound) = + state->assume(*upperboundToCheck); + + // If we are under constrained and the index variables are tainted, report. + if (state_exceedsUpperBound && state_withinUpperBound) { + SVal ByteOffset = rawOffset.getByteOffset(); + if (state->isTainted(ByteOffset)) { + reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted, + llvm::make_unique<TaintBugVisitor>(ByteOffset)); + return; + } + } else if (state_exceedsUpperBound) { + // If we are constrained enough to definitely exceed the upper bound, + // report. + assert(!state_withinUpperBound); + reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes); + return; + } + + assert(state_withinUpperBound); + state = state_withinUpperBound; + } + while (false); + + checkerContext.addTransition(state); +} + +void ArrayBoundCheckerV2::reportOOB( + CheckerContext &checkerContext, ProgramStateRef errorState, OOB_Kind kind, + std::unique_ptr<BugReporterVisitor> Visitor) const { + + ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState); + if (!errorNode) + return; + + if (!BT) + BT.reset(new BuiltinBug(this, "Out-of-bound access")); + + // FIXME: This diagnostics are preliminary. We should get far better + // diagnostics for explaining buffer overruns. + + SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + os << "Out of bound memory access "; + switch (kind) { + case OOB_Precedes: + os << "(accessed memory precedes memory block)"; + break; + case OOB_Excedes: + os << "(access exceeds upper limit of memory block)"; + break; + case OOB_Tainted: + os << "(index is tainted)"; + break; + } + + auto BR = llvm::make_unique<BugReport>(*BT, os.str(), errorNode); + BR->addVisitor(std::move(Visitor)); + checkerContext.emitReport(std::move(BR)); +} + +#ifndef NDEBUG +LLVM_DUMP_METHOD void RegionRawOffsetV2::dump() const { + dumpToStream(llvm::errs()); +} + +void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const { + os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}'; +} +#endif + +// Lazily computes a value to be used by 'computeOffset'. If 'val' +// is unknown or undefined, we lazily substitute '0'. Otherwise, +// return 'val'. +static inline SVal getValue(SVal val, SValBuilder &svalBuilder) { + return val.getAs<UndefinedVal>() ? svalBuilder.makeArrayIndex(0) : val; +} + +// Scale a base value by a scaling factor, and return the scaled +// value as an SVal. Used by 'computeOffset'. +static inline SVal scaleValue(ProgramStateRef state, + NonLoc baseVal, CharUnits scaling, + SValBuilder &sb) { + return sb.evalBinOpNN(state, BO_Mul, baseVal, + sb.makeArrayIndex(scaling.getQuantity()), + sb.getArrayIndexType()); +} + +// Add an SVal to another, treating unknown and undefined values as +// summing to UnknownVal. Used by 'computeOffset'. +static SVal addValue(ProgramStateRef state, SVal x, SVal y, + SValBuilder &svalBuilder) { + // We treat UnknownVals and UndefinedVals the same here because we + // only care about computing offsets. + if (x.isUnknownOrUndef() || y.isUnknownOrUndef()) + return UnknownVal(); + + return svalBuilder.evalBinOpNN(state, BO_Add, x.castAs<NonLoc>(), + y.castAs<NonLoc>(), + svalBuilder.getArrayIndexType()); +} + +/// Compute a raw byte offset from a base region. Used for array bounds +/// checking. +RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state, + SValBuilder &svalBuilder, + SVal location) +{ + const MemRegion *region = location.getAsRegion(); + SVal offset = UndefinedVal(); + + while (region) { + switch (region->getKind()) { + default: { + if (const SubRegion *subReg = dyn_cast<SubRegion>(region)) { + offset = getValue(offset, svalBuilder); + if (!offset.isUnknownOrUndef()) + return RegionRawOffsetV2(subReg, offset); + } + return RegionRawOffsetV2(); + } + case MemRegion::ElementRegionKind: { + const ElementRegion *elemReg = cast<ElementRegion>(region); + SVal index = elemReg->getIndex(); + if (!index.getAs<NonLoc>()) + return RegionRawOffsetV2(); + QualType elemType = elemReg->getElementType(); + // If the element is an incomplete type, go no further. + ASTContext &astContext = svalBuilder.getContext(); + if (elemType->isIncompleteType()) + return RegionRawOffsetV2(); + + // Update the offset. + offset = addValue(state, + getValue(offset, svalBuilder), + scaleValue(state, + index.castAs<NonLoc>(), + astContext.getTypeSizeInChars(elemType), + svalBuilder), + svalBuilder); + + if (offset.isUnknownOrUndef()) + return RegionRawOffsetV2(); + + region = elemReg->getSuperRegion(); + continue; + } + } + } + return RegionRawOffsetV2(); +} + +void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) { + mgr.registerChecker<ArrayBoundCheckerV2>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp new file mode 100644 index 000000000000..577b5349f62e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -0,0 +1,1269 @@ +//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- 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 BasicObjCFoundationChecks, a class that encapsulates +// a set of simple checks to run on Objective-C code using Apple's Foundation +// classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/StmtObjC.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/Analysis/SelectorExtras.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; +using namespace llvm; + +namespace { +class APIMisuse : public BugType { +public: + APIMisuse(const CheckerBase *checker, const char *name) + : BugType(checker, name, "API Misuse (Apple)") {} +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) { + if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface()) + return ID->getIdentifier()->getName(); + return StringRef(); +} + +enum FoundationClass { + FC_None, + FC_NSArray, + FC_NSDictionary, + FC_NSEnumerator, + FC_NSNull, + FC_NSOrderedSet, + FC_NSSet, + FC_NSString +}; + +static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID, + bool IncludeSuperclasses = true) { + static llvm::StringMap<FoundationClass> Classes; + if (Classes.empty()) { + Classes["NSArray"] = FC_NSArray; + Classes["NSDictionary"] = FC_NSDictionary; + Classes["NSEnumerator"] = FC_NSEnumerator; + Classes["NSNull"] = FC_NSNull; + Classes["NSOrderedSet"] = FC_NSOrderedSet; + Classes["NSSet"] = FC_NSSet; + Classes["NSString"] = FC_NSString; + } + + // FIXME: Should we cache this at all? + FoundationClass result = Classes.lookup(ID->getIdentifier()->getName()); + if (result == FC_None && IncludeSuperclasses) + if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) + return findKnownClass(Super); + + return result; +} + +//===----------------------------------------------------------------------===// +// NilArgChecker - Check for prohibited nil arguments to ObjC method calls. +//===----------------------------------------------------------------------===// + +namespace { + class NilArgChecker : public Checker<check::PreObjCMessage, + check::PostStmt<ObjCDictionaryLiteral>, + check::PostStmt<ObjCArrayLiteral> > { + mutable std::unique_ptr<APIMisuse> BT; + + mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors; + mutable Selector ArrayWithObjectSel; + mutable Selector AddObjectSel; + mutable Selector InsertObjectAtIndexSel; + mutable Selector ReplaceObjectAtIndexWithObjectSel; + mutable Selector SetObjectAtIndexedSubscriptSel; + mutable Selector ArrayByAddingObjectSel; + mutable Selector DictionaryWithObjectForKeySel; + mutable Selector SetObjectForKeySel; + mutable Selector SetObjectForKeyedSubscriptSel; + mutable Selector RemoveObjectForKeySel; + + void warnIfNilExpr(const Expr *E, + const char *Msg, + CheckerContext &C) const; + + void warnIfNilArg(CheckerContext &C, + const ObjCMethodCall &msg, unsigned Arg, + FoundationClass Class, + bool CanBeSubscript = false) const; + + void generateBugReport(ExplodedNode *N, + StringRef Msg, + SourceRange Range, + const Expr *Expr, + CheckerContext &C) const; + + public: + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPostStmt(const ObjCDictionaryLiteral *DL, + CheckerContext &C) const; + void checkPostStmt(const ObjCArrayLiteral *AL, + CheckerContext &C) const; + }; +} // end anonymous namespace + +void NilArgChecker::warnIfNilExpr(const Expr *E, + const char *Msg, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (State->isNull(C.getSVal(E)).isConstrainedTrue()) { + + if (ExplodedNode *N = C.generateErrorNode()) { + generateBugReport(N, Msg, E->getSourceRange(), E, C); + } + } +} + +void NilArgChecker::warnIfNilArg(CheckerContext &C, + const ObjCMethodCall &msg, + unsigned int Arg, + FoundationClass Class, + bool CanBeSubscript) const { + // Check if the argument is nil. + ProgramStateRef State = C.getState(); + if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue()) + return; + + // NOTE: We cannot throw non-fatal errors from warnIfNilExpr, + // because it's called multiple times from some callers, so it'd cause + // an unwanted state split if two or more non-fatal errors are thrown + // within the same checker callback. For now we don't want to, but + // it'll need to be fixed if we ever want to. + if (ExplodedNode *N = C.generateErrorNode()) { + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + + if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) { + + if (Class == FC_NSArray) { + os << "Array element cannot be nil"; + } else if (Class == FC_NSDictionary) { + if (Arg == 0) { + os << "Value stored into '"; + os << GetReceiverInterfaceName(msg) << "' cannot be nil"; + } else { + assert(Arg == 1); + os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil"; + } + } else + llvm_unreachable("Missing foundation class for the subscript expr"); + + } else { + if (Class == FC_NSDictionary) { + if (Arg == 0) + os << "Value argument "; + else { + assert(Arg == 1); + os << "Key argument "; + } + os << "to '"; + msg.getSelector().print(os); + os << "' cannot be nil"; + } else { + os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '"; + msg.getSelector().print(os); + os << "' cannot be nil"; + } + } + + generateBugReport(N, os.str(), msg.getArgSourceRange(Arg), + msg.getArgExpr(Arg), C); + } +} + +void NilArgChecker::generateBugReport(ExplodedNode *N, + StringRef Msg, + SourceRange Range, + const Expr *E, + CheckerContext &C) const { + if (!BT) + BT.reset(new APIMisuse(this, "nil argument")); + + auto R = llvm::make_unique<BugReport>(*BT, Msg, N); + R->addRange(Range); + bugreporter::trackExpressionValue(N, E, *R); + C.emitReport(std::move(R)); +} + +void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg, + CheckerContext &C) const { + const ObjCInterfaceDecl *ID = msg.getReceiverInterface(); + if (!ID) + return; + + FoundationClass Class = findKnownClass(ID); + + static const unsigned InvalidArgIndex = UINT_MAX; + unsigned Arg = InvalidArgIndex; + bool CanBeSubscript = false; + + if (Class == FC_NSString) { + Selector S = msg.getSelector(); + + if (S.isUnarySelector()) + return; + + if (StringSelectors.empty()) { + ASTContext &Ctx = C.getASTContext(); + Selector Sels[] = { + getKeywordSelector(Ctx, "caseInsensitiveCompare"), + getKeywordSelector(Ctx, "compare"), + getKeywordSelector(Ctx, "compare", "options"), + getKeywordSelector(Ctx, "compare", "options", "range"), + getKeywordSelector(Ctx, "compare", "options", "range", "locale"), + getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet"), + getKeywordSelector(Ctx, "initWithFormat"), + getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare"), + getKeywordSelector(Ctx, "localizedCompare"), + getKeywordSelector(Ctx, "localizedStandardCompare"), + }; + for (Selector KnownSel : Sels) + StringSelectors[KnownSel] = 0; + } + auto I = StringSelectors.find(S); + if (I == StringSelectors.end()) + return; + Arg = I->second; + } else if (Class == FC_NSArray) { + Selector S = msg.getSelector(); + + if (S.isUnarySelector()) + return; + + if (ArrayWithObjectSel.isNull()) { + ASTContext &Ctx = C.getASTContext(); + ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject"); + AddObjectSel = getKeywordSelector(Ctx, "addObject"); + InsertObjectAtIndexSel = + getKeywordSelector(Ctx, "insertObject", "atIndex"); + ReplaceObjectAtIndexWithObjectSel = + getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject"); + SetObjectAtIndexedSubscriptSel = + getKeywordSelector(Ctx, "setObject", "atIndexedSubscript"); + ArrayByAddingObjectSel = getKeywordSelector(Ctx, "arrayByAddingObject"); + } + + if (S == ArrayWithObjectSel || S == AddObjectSel || + S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) { + Arg = 0; + } else if (S == SetObjectAtIndexedSubscriptSel) { + Arg = 0; + CanBeSubscript = true; + } else if (S == ReplaceObjectAtIndexWithObjectSel) { + Arg = 1; + } + } else if (Class == FC_NSDictionary) { + Selector S = msg.getSelector(); + + if (S.isUnarySelector()) + return; + + if (DictionaryWithObjectForKeySel.isNull()) { + ASTContext &Ctx = C.getASTContext(); + DictionaryWithObjectForKeySel = + getKeywordSelector(Ctx, "dictionaryWithObject", "forKey"); + SetObjectForKeySel = getKeywordSelector(Ctx, "setObject", "forKey"); + SetObjectForKeyedSubscriptSel = + getKeywordSelector(Ctx, "setObject", "forKeyedSubscript"); + RemoveObjectForKeySel = getKeywordSelector(Ctx, "removeObjectForKey"); + } + + if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) { + Arg = 0; + warnIfNilArg(C, msg, /* Arg */1, Class); + } else if (S == SetObjectForKeyedSubscriptSel) { + CanBeSubscript = true; + Arg = 1; + } else if (S == RemoveObjectForKeySel) { + Arg = 0; + } + } + + // If argument is '0', report a warning. + if ((Arg != InvalidArgIndex)) + warnIfNilArg(C, msg, Arg, Class, CanBeSubscript); +} + +void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL, + CheckerContext &C) const { + unsigned NumOfElements = AL->getNumElements(); + for (unsigned i = 0; i < NumOfElements; ++i) { + warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C); + } +} + +void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, + CheckerContext &C) const { + unsigned NumOfElements = DL->getNumElements(); + for (unsigned i = 0; i < NumOfElements; ++i) { + ObjCDictionaryElement Element = DL->getKeyValueElement(i); + warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C); + warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C); + } +} + +//===----------------------------------------------------------------------===// +// Checking for mismatched types passed to CFNumberCreate/CFNumberGetValue. +//===----------------------------------------------------------------------===// + +namespace { +class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > { + mutable std::unique_ptr<APIMisuse> BT; + mutable IdentifierInfo *ICreate, *IGetValue; +public: + CFNumberChecker() : ICreate(nullptr), IGetValue(nullptr) {} + + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + +private: + void EmitError(const TypedRegion* R, const Expr *Ex, + uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind); +}; +} // end anonymous namespace + +enum CFNumberType { + kCFNumberSInt8Type = 1, + kCFNumberSInt16Type = 2, + kCFNumberSInt32Type = 3, + kCFNumberSInt64Type = 4, + kCFNumberFloat32Type = 5, + kCFNumberFloat64Type = 6, + kCFNumberCharType = 7, + kCFNumberShortType = 8, + kCFNumberIntType = 9, + kCFNumberLongType = 10, + kCFNumberLongLongType = 11, + kCFNumberFloatType = 12, + kCFNumberDoubleType = 13, + kCFNumberCFIndexType = 14, + kCFNumberNSIntegerType = 15, + kCFNumberCGFloatType = 16 +}; + +static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) { + static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 }; + + if (i < kCFNumberCharType) + return FixedSize[i-1]; + + QualType T; + + switch (i) { + case kCFNumberCharType: T = Ctx.CharTy; break; + case kCFNumberShortType: T = Ctx.ShortTy; break; + case kCFNumberIntType: T = Ctx.IntTy; break; + case kCFNumberLongType: T = Ctx.LongTy; break; + case kCFNumberLongLongType: T = Ctx.LongLongTy; break; + case kCFNumberFloatType: T = Ctx.FloatTy; break; + case kCFNumberDoubleType: T = Ctx.DoubleTy; break; + case kCFNumberCFIndexType: + case kCFNumberNSIntegerType: + case kCFNumberCGFloatType: + // FIXME: We need a way to map from names to Type*. + default: + return None; + } + + return Ctx.getTypeSize(T); +} + +#if 0 +static const char* GetCFNumberTypeStr(uint64_t i) { + static const char* Names[] = { + "kCFNumberSInt8Type", + "kCFNumberSInt16Type", + "kCFNumberSInt32Type", + "kCFNumberSInt64Type", + "kCFNumberFloat32Type", + "kCFNumberFloat64Type", + "kCFNumberCharType", + "kCFNumberShortType", + "kCFNumberIntType", + "kCFNumberLongType", + "kCFNumberLongLongType", + "kCFNumberFloatType", + "kCFNumberDoubleType", + "kCFNumberCFIndexType", + "kCFNumberNSIntegerType", + "kCFNumberCGFloatType" + }; + + return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType"; +} +#endif + +void CFNumberChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef state = C.getState(); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD) + return; + + ASTContext &Ctx = C.getASTContext(); + if (!ICreate) { + ICreate = &Ctx.Idents.get("CFNumberCreate"); + IGetValue = &Ctx.Idents.get("CFNumberGetValue"); + } + if (!(FD->getIdentifier() == ICreate || FD->getIdentifier() == IGetValue) || + CE->getNumArgs() != 3) + return; + + // Get the value of the "theType" argument. + SVal TheTypeVal = C.getSVal(CE->getArg(1)); + + // FIXME: We really should allow ranges of valid theType values, and + // bifurcate the state appropriately. + Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); + if (!V) + return; + + uint64_t NumberKind = V->getValue().getLimitedValue(); + Optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind); + + // FIXME: In some cases we can emit an error. + if (!OptCFNumberSize) + return; + + uint64_t CFNumberSize = *OptCFNumberSize; + + // Look at the value of the integer being passed by reference. Essentially + // we want to catch cases where the value passed in is not equal to the + // size of the type being created. + SVal TheValueExpr = C.getSVal(CE->getArg(2)); + + // FIXME: Eventually we should handle arbitrary locations. We can do this + // by having an enhanced memory model that does low-level typing. + Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>(); + if (!LV) + return; + + const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts()); + if (!R) + return; + + QualType T = Ctx.getCanonicalType(R->getValueType()); + + // FIXME: If the pointee isn't an integer type, should we flag a warning? + // People can do weird stuff with pointers. + + if (!T->isIntegralOrEnumerationType()) + return; + + uint64_t PrimitiveTypeSize = Ctx.getTypeSize(T); + + if (PrimitiveTypeSize == CFNumberSize) + return; + + // FIXME: We can actually create an abstract "CFNumber" object that has + // the bits initialized to the provided values. + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (N) { + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + bool isCreate = (FD->getIdentifier() == ICreate); + + if (isCreate) { + os << (PrimitiveTypeSize == 8 ? "An " : "A ") + << PrimitiveTypeSize << "-bit integer is used to initialize a " + << "CFNumber object that represents " + << (CFNumberSize == 8 ? "an " : "a ") + << CFNumberSize << "-bit integer; "; + } else { + os << "A CFNumber object that represents " + << (CFNumberSize == 8 ? "an " : "a ") + << CFNumberSize << "-bit integer is used to initialize " + << (PrimitiveTypeSize == 8 ? "an " : "a ") + << PrimitiveTypeSize << "-bit integer; "; + } + + if (PrimitiveTypeSize < CFNumberSize) + os << (CFNumberSize - PrimitiveTypeSize) + << " bits of the CFNumber value will " + << (isCreate ? "be garbage." : "overwrite adjacent storage."); + else + os << (PrimitiveTypeSize - CFNumberSize) + << " bits of the integer value will be " + << (isCreate ? "lost." : "garbage."); + + if (!BT) + BT.reset(new APIMisuse(this, "Bad use of CFNumber APIs")); + + auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); + report->addRange(CE->getArg(2)->getSourceRange()); + C.emitReport(std::move(report)); + } +} + +//===----------------------------------------------------------------------===// +// CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments. +//===----------------------------------------------------------------------===// + +namespace { +class CFRetainReleaseChecker : public Checker<check::PreCall> { + mutable APIMisuse BT{this, "null passed to CF memory management function"}; + CallDescription CFRetain{"CFRetain", 1}, + CFRelease{"CFRelease", 1}, + CFMakeCollectable{"CFMakeCollectable", 1}, + CFAutorelease{"CFAutorelease", 1}; + +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; +}; +} // end anonymous namespace + +void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + // TODO: Make this check part of CallDescription. + if (!Call.isGlobalCFunction()) + return; + + // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease. + if (!(Call.isCalled(CFRetain) || Call.isCalled(CFRelease) || + Call.isCalled(CFMakeCollectable) || Call.isCalled(CFAutorelease))) + return; + + // Get the argument's value. + SVal ArgVal = Call.getArgSVal(0); + Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>(); + if (!DefArgVal) + return; + + // Is it null? + ProgramStateRef state = C.getState(); + ProgramStateRef stateNonNull, stateNull; + std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal); + + if (!stateNonNull) { + ExplodedNode *N = C.generateErrorNode(stateNull); + if (!N) + return; + + SmallString<64> Str; + raw_svector_ostream OS(Str); + OS << "Null pointer argument in call to " + << cast<FunctionDecl>(Call.getDecl())->getName(); + + auto report = llvm::make_unique<BugReport>(BT, OS.str(), N); + report->addRange(Call.getArgSourceRange(0)); + bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report); + C.emitReport(std::move(report)); + return; + } + + // From here on, we know the argument is non-null. + C.addTransition(stateNonNull); +} + +//===----------------------------------------------------------------------===// +// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. +//===----------------------------------------------------------------------===// + +namespace { +class ClassReleaseChecker : public Checker<check::PreObjCMessage> { + mutable Selector releaseS; + mutable Selector retainS; + mutable Selector autoreleaseS; + mutable Selector drainS; + mutable std::unique_ptr<BugType> BT; + +public: + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; +}; +} // end anonymous namespace + +void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg, + CheckerContext &C) const { + if (!BT) { + BT.reset(new APIMisuse( + this, "message incorrectly sent to class instead of class instance")); + + ASTContext &Ctx = C.getASTContext(); + releaseS = GetNullarySelector("release", Ctx); + retainS = GetNullarySelector("retain", Ctx); + autoreleaseS = GetNullarySelector("autorelease", Ctx); + drainS = GetNullarySelector("drain", Ctx); + } + + if (msg.isInstanceMessage()) + return; + const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); + assert(Class); + + Selector S = msg.getSelector(); + if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) + return; + + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << "The '"; + S.print(os); + os << "' message should be sent to instances " + "of class '" << Class->getName() + << "' and not the class directly"; + + auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); + report->addRange(msg.getSourceRange()); + C.emitReport(std::move(report)); + } +} + +//===----------------------------------------------------------------------===// +// Check for passing non-Objective-C types to variadic methods that expect +// only Objective-C types. +//===----------------------------------------------------------------------===// + +namespace { +class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> { + mutable Selector arrayWithObjectsS; + mutable Selector dictionaryWithObjectsAndKeysS; + mutable Selector setWithObjectsS; + mutable Selector orderedSetWithObjectsS; + mutable Selector initWithObjectsS; + mutable Selector initWithObjectsAndKeysS; + mutable std::unique_ptr<BugType> BT; + + bool isVariadicMessage(const ObjCMethodCall &msg) const; + +public: + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; +}; +} // end anonymous namespace + +/// isVariadicMessage - Returns whether the given message is a variadic message, +/// where all arguments must be Objective-C types. +bool +VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const { + const ObjCMethodDecl *MD = msg.getDecl(); + + if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext())) + return false; + + Selector S = msg.getSelector(); + + if (msg.isInstanceMessage()) { + // FIXME: Ideally we'd look at the receiver interface here, but that's not + // useful for init, because alloc returns 'id'. In theory, this could lead + // to false positives, for example if there existed a class that had an + // initWithObjects: implementation that does accept non-Objective-C pointer + // types, but the chance of that happening is pretty small compared to the + // gains that this analysis gives. + const ObjCInterfaceDecl *Class = MD->getClassInterface(); + + switch (findKnownClass(Class)) { + case FC_NSArray: + case FC_NSOrderedSet: + case FC_NSSet: + return S == initWithObjectsS; + case FC_NSDictionary: + return S == initWithObjectsAndKeysS; + default: + return false; + } + } else { + const ObjCInterfaceDecl *Class = msg.getReceiverInterface(); + + switch (findKnownClass(Class)) { + case FC_NSArray: + return S == arrayWithObjectsS; + case FC_NSOrderedSet: + return S == orderedSetWithObjectsS; + case FC_NSSet: + return S == setWithObjectsS; + case FC_NSDictionary: + return S == dictionaryWithObjectsAndKeysS; + default: + return false; + } + } +} + +void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg, + CheckerContext &C) const { + if (!BT) { + BT.reset(new APIMisuse(this, + "Arguments passed to variadic method aren't all " + "Objective-C pointer types")); + + ASTContext &Ctx = C.getASTContext(); + arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx); + dictionaryWithObjectsAndKeysS = + GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx); + setWithObjectsS = GetUnarySelector("setWithObjects", Ctx); + orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx); + + initWithObjectsS = GetUnarySelector("initWithObjects", Ctx); + initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx); + } + + if (!isVariadicMessage(msg)) + return; + + // We are not interested in the selector arguments since they have + // well-defined types, so the compiler will issue a warning for them. + unsigned variadicArgsBegin = msg.getSelector().getNumArgs(); + + // We're not interested in the last argument since it has to be nil or the + // compiler would have issued a warning for it elsewhere. + unsigned variadicArgsEnd = msg.getNumArgs() - 1; + + if (variadicArgsEnd <= variadicArgsBegin) + return; + + // Verify that all arguments have Objective-C types. + Optional<ExplodedNode*> errorNode; + + for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) { + QualType ArgTy = msg.getArgExpr(I)->getType(); + if (ArgTy->isObjCObjectPointerType()) + continue; + + // Block pointers are treaded as Objective-C pointers. + if (ArgTy->isBlockPointerType()) + continue; + + // Ignore pointer constants. + if (msg.getArgSVal(I).getAs<loc::ConcreteInt>()) + continue; + + // Ignore pointer types annotated with 'NSObject' attribute. + if (C.getASTContext().isObjCNSObjectType(ArgTy)) + continue; + + // Ignore CF references, which can be toll-free bridged. + if (coreFoundation::isCFObjectRef(ArgTy)) + continue; + + // Generate only one error node to use for all bug reports. + if (!errorNode.hasValue()) + errorNode = C.generateNonFatalErrorNode(); + + if (!errorNode.getValue()) + continue; + + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + + StringRef TypeName = GetReceiverInterfaceName(msg); + if (!TypeName.empty()) + os << "Argument to '" << TypeName << "' method '"; + else + os << "Argument to method '"; + + msg.getSelector().print(os); + os << "' should be an Objective-C pointer type, not '"; + ArgTy.print(os, C.getLangOpts()); + os << "'"; + + auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue()); + R->addRange(msg.getArgSourceRange(I)); + C.emitReport(std::move(R)); + } +} + +//===----------------------------------------------------------------------===// +// Improves the modeling of loops over Cocoa collections. +//===----------------------------------------------------------------------===// + +// The map from container symbol to the container count symbol. +// We currently will remember the last container count symbol encountered. +REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef) +REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool) + +namespace { +class ObjCLoopChecker + : public Checker<check::PostStmt<ObjCForCollectionStmt>, + check::PostObjCMessage, + check::DeadSymbols, + check::PointerEscape > { + mutable IdentifierInfo *CountSelectorII; + + bool isCollectionCountMethod(const ObjCMethodCall &M, + CheckerContext &C) const; + +public: + ObjCLoopChecker() : CountSelectorII(nullptr) {} + void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; +}; +} // end anonymous namespace + +static bool isKnownNonNilCollectionType(QualType T) { + const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); + if (!PT) + return false; + + const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); + if (!ID) + return false; + + switch (findKnownClass(ID)) { + case FC_NSArray: + case FC_NSDictionary: + case FC_NSEnumerator: + case FC_NSOrderedSet: + case FC_NSSet: + return true; + default: + return false; + } +} + +/// Assumes that the collection is non-nil. +/// +/// If the collection is known to be nil, returns NULL to indicate an infeasible +/// path. +static ProgramStateRef checkCollectionNonNil(CheckerContext &C, + ProgramStateRef State, + const ObjCForCollectionStmt *FCS) { + if (!State) + return nullptr; + + SVal CollectionVal = C.getSVal(FCS->getCollection()); + Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>(); + if (!KnownCollection) + return State; + + ProgramStateRef StNonNil, StNil; + std::tie(StNonNil, StNil) = State->assume(*KnownCollection); + if (StNil && !StNonNil) { + // The collection is nil. This path is infeasible. + return nullptr; + } + + return StNonNil; +} + +/// Assumes that the collection elements are non-nil. +/// +/// This only applies if the collection is one of those known not to contain +/// nil values. +static ProgramStateRef checkElementNonNil(CheckerContext &C, + ProgramStateRef State, + const ObjCForCollectionStmt *FCS) { + if (!State) + return nullptr; + + // See if the collection is one where we /know/ the elements are non-nil. + if (!isKnownNonNilCollectionType(FCS->getCollection()->getType())) + return State; + + const LocationContext *LCtx = C.getLocationContext(); + const Stmt *Element = FCS->getElement(); + + // FIXME: Copied from ExprEngineObjC. + Optional<Loc> ElementLoc; + if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) { + const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl()); + assert(ElemDecl->getInit() == nullptr); + ElementLoc = State->getLValue(ElemDecl, LCtx); + } else { + ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>(); + } + + if (!ElementLoc) + return State; + + // Go ahead and assume the value is non-nil. + SVal Val = State->getSVal(*ElementLoc); + return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); +} + +/// Returns NULL state if the collection is known to contain elements +/// (or is known not to contain elements if the Assumption parameter is false.) +static ProgramStateRef +assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, + SymbolRef CollectionS, bool Assumption) { + if (!State || !CollectionS) + return State; + + const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS); + if (!CountS) { + const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS); + if (!KnownNonEmpty) + return State->set<ContainerNonEmptyMap>(CollectionS, Assumption); + return (Assumption == *KnownNonEmpty) ? State : nullptr; + } + + SValBuilder &SvalBuilder = C.getSValBuilder(); + SVal CountGreaterThanZeroVal = + SvalBuilder.evalBinOp(State, BO_GT, + nonloc::SymbolVal(*CountS), + SvalBuilder.makeIntVal(0, (*CountS)->getType()), + SvalBuilder.getConditionType()); + Optional<DefinedSVal> CountGreaterThanZero = + CountGreaterThanZeroVal.getAs<DefinedSVal>(); + if (!CountGreaterThanZero) { + // The SValBuilder cannot construct a valid SVal for this condition. + // This means we cannot properly reason about it. + return State; + } + + return State->assume(*CountGreaterThanZero, Assumption); +} + +static ProgramStateRef +assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State, + const ObjCForCollectionStmt *FCS, + bool Assumption) { + if (!State) + return nullptr; + + SymbolRef CollectionS = C.getSVal(FCS->getCollection()).getAsSymbol(); + return assumeCollectionNonEmpty(C, State, CollectionS, Assumption); +} + +/// If the fist block edge is a back edge, we are reentering the loop. +static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N, + const ObjCForCollectionStmt *FCS) { + if (!N) + return false; + + ProgramPoint P = N->getLocation(); + if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { + return BE->getSrc()->getLoopTarget() == FCS; + } + + // Keep looking for a block edge. + for (ExplodedNode::const_pred_iterator I = N->pred_begin(), + E = N->pred_end(); I != E; ++I) { + if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS)) + return true; + } + + return false; +} + +void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // Check if this is the branch for the end of the loop. + SVal CollectionSentinel = C.getSVal(FCS); + if (CollectionSentinel.isZeroConstant()) { + if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS)) + State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false); + + // Otherwise, this is a branch that goes through the loop body. + } else { + State = checkCollectionNonNil(C, State, FCS); + State = checkElementNonNil(C, State, FCS); + State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true); + } + + if (!State) + C.generateSink(C.getState(), C.getPredecessor()); + else if (State != C.getState()) + C.addTransition(State); +} + +bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M, + CheckerContext &C) const { + Selector S = M.getSelector(); + // Initialize the identifiers on first use. + if (!CountSelectorII) + CountSelectorII = &C.getASTContext().Idents.get("count"); + + // If the method returns collection count, record the value. + return S.isUnarySelector() && + (S.getIdentifierInfoForSlot(0) == CountSelectorII); +} + +void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) const { + if (!M.isInstanceMessage()) + return; + + const ObjCInterfaceDecl *ClassID = M.getReceiverInterface(); + if (!ClassID) + return; + + FoundationClass Class = findKnownClass(ClassID); + if (Class != FC_NSDictionary && + Class != FC_NSArray && + Class != FC_NSSet && + Class != FC_NSOrderedSet) + return; + + SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol(); + if (!ContainerS) + return; + + // If we are processing a call to "count", get the symbolic value returned by + // a call to "count" and add it to the map. + if (!isCollectionCountMethod(M, C)) + return; + + const Expr *MsgExpr = M.getOriginExpr(); + SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol(); + if (CountS) { + ProgramStateRef State = C.getState(); + + C.getSymbolManager().addSymbolDependency(ContainerS, CountS); + State = State->set<ContainerCountMap>(ContainerS, CountS); + + if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) { + State = State->remove<ContainerNonEmptyMap>(ContainerS); + State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty); + } + + C.addTransition(State); + } +} + +static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) { + const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call); + if (!Message) + return nullptr; + + const ObjCMethodDecl *MD = Message->getDecl(); + if (!MD) + return nullptr; + + const ObjCInterfaceDecl *StaticClass; + if (isa<ObjCProtocolDecl>(MD->getDeclContext())) { + // We can't find out where the method was declared without doing more work. + // Instead, see if the receiver is statically typed as a known immutable + // collection. + StaticClass = Message->getOriginExpr()->getReceiverInterface(); + } else { + StaticClass = MD->getClassInterface(); + } + + if (!StaticClass) + return nullptr; + + switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) { + case FC_None: + return nullptr; + case FC_NSArray: + case FC_NSDictionary: + case FC_NSEnumerator: + case FC_NSNull: + case FC_NSOrderedSet: + case FC_NSSet: + case FC_NSString: + break; + } + + return Message->getReceiverSVal().getAsSymbol(); +} + +ProgramStateRef +ObjCLoopChecker::checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call); + + // Remove the invalidated symbols form the collection count map. + for (InvalidatedSymbols::const_iterator I = Escaped.begin(), + E = Escaped.end(); + I != E; ++I) { + SymbolRef Sym = *I; + + // Don't invalidate this symbol's count if we know the method being called + // is declared on an immutable class. This isn't completely correct if the + // receiver is also passed as an argument, but in most uses of NSArray, + // NSDictionary, etc. this isn't likely to happen in a dangerous way. + if (Sym == ImmutableReceiver) + continue; + + // The symbol escaped. Pessimistically, assume that the count could have + // changed. + State = State->remove<ContainerCountMap>(Sym); + State = State->remove<ContainerNonEmptyMap>(Sym); + } + return State; +} + +void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // Remove the dead symbols from the collection count map. + ContainerCountMapTy Tracked = State->get<ContainerCountMap>(); + for (ContainerCountMapTy::iterator I = Tracked.begin(), + E = Tracked.end(); I != E; ++I) { + SymbolRef Sym = I->first; + if (SymReaper.isDead(Sym)) { + State = State->remove<ContainerCountMap>(Sym); + State = State->remove<ContainerNonEmptyMap>(Sym); + } + } + + C.addTransition(State); +} + +namespace { +/// \class ObjCNonNilReturnValueChecker +/// The checker restricts the return values of APIs known to +/// never (or almost never) return 'nil'. +class ObjCNonNilReturnValueChecker + : public Checker<check::PostObjCMessage, + check::PostStmt<ObjCArrayLiteral>, + check::PostStmt<ObjCDictionaryLiteral>, + check::PostStmt<ObjCBoxedExpr> > { + mutable bool Initialized; + mutable Selector ObjectAtIndex; + mutable Selector ObjectAtIndexedSubscript; + mutable Selector NullSelector; + +public: + ObjCNonNilReturnValueChecker() : Initialized(false) {} + + ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr, + ProgramStateRef State, + CheckerContext &C) const; + void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const { + C.addTransition(assumeExprIsNonNull(E, C.getState(), C)); + } + + void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const { + assumeExprIsNonNull(E, C); + } + void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const { + assumeExprIsNonNull(E, C); + } + void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const { + assumeExprIsNonNull(E, C); + } + + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; +}; +} // end anonymous namespace + +ProgramStateRef +ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr, + ProgramStateRef State, + CheckerContext &C) const { + SVal Val = C.getSVal(NonNullExpr); + if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>()) + return State->assume(*DV, true); + return State; +} + +void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) + const { + ProgramStateRef State = C.getState(); + + if (!Initialized) { + ASTContext &Ctx = C.getASTContext(); + ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx); + ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx); + NullSelector = GetNullarySelector("null", Ctx); + } + + // Check the receiver type. + if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) { + + // Assume that object returned from '[self init]' or '[super init]' is not + // 'nil' if we are processing an inlined function/method. + // + // A defensive callee will (and should) check if the object returned by + // '[super init]' is 'nil' before doing it's own initialization. However, + // since 'nil' is rarely returned in practice, we should not warn when the + // caller to the defensive constructor uses the object in contexts where + // 'nil' is not accepted. + if (!C.inTopFrame() && M.getDecl() && + M.getDecl()->getMethodFamily() == OMF_init && + M.isReceiverSelfOrSuper()) { + State = assumeExprIsNonNull(M.getOriginExpr(), State, C); + } + + FoundationClass Cl = findKnownClass(Interface); + + // Objects returned from + // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript] + // are never 'nil'. + if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) { + Selector Sel = M.getSelector(); + if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) { + // Go ahead and assume the value is non-nil. + State = assumeExprIsNonNull(M.getOriginExpr(), State, C); + } + } + + // Objects returned from [NSNull null] are not nil. + if (Cl == FC_NSNull) { + if (M.getSelector() == NullSelector) { + // Go ahead and assume the value is non-nil. + State = assumeExprIsNonNull(M.getOriginExpr(), State, C); + } + } + } + C.addTransition(State); +} + +//===----------------------------------------------------------------------===// +// Check registration. +//===----------------------------------------------------------------------===// + +void ento::registerNilArgChecker(CheckerManager &mgr) { + mgr.registerChecker<NilArgChecker>(); +} + +void ento::registerCFNumberChecker(CheckerManager &mgr) { + mgr.registerChecker<CFNumberChecker>(); +} + +void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) { + mgr.registerChecker<CFRetainReleaseChecker>(); +} + +void ento::registerClassReleaseChecker(CheckerManager &mgr) { + mgr.registerChecker<ClassReleaseChecker>(); +} + +void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) { + mgr.registerChecker<VariadicMethodTypeChecker>(); +} + +void ento::registerObjCLoopChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCLoopChecker>(); +} + +void +ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCNonNilReturnValueChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp new file mode 100644 index 000000000000..00d08b371f37 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp @@ -0,0 +1,185 @@ +//===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines a checker for blocks in critical sections. This checker should find +// the calls to blocking functions (for example: sleep, getc, fgets, read, +// recv etc.) inside a critical section. When sleep(x) is called while a mutex +// is held, other threades cannot lock the same mutex. This might take some +// time, leading to bad performance or even deadlock. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { + +class BlockInCriticalSectionChecker : public Checker<check::PostCall> { + + mutable IdentifierInfo *IILockGuard, *IIUniqueLock; + + CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn, + PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn, + MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock; + + StringRef ClassLockGuard, ClassUniqueLock; + + mutable bool IdentifierInfoInitialized; + + std::unique_ptr<BugType> BlockInCritSectionBugType; + + void initIdentifierInfo(ASTContext &Ctx) const; + + void reportBlockInCritSection(SymbolRef FileDescSym, + const CallEvent &call, + CheckerContext &C) const; + +public: + BlockInCriticalSectionChecker(); + + bool isBlockingFunction(const CallEvent &Call) const; + bool isLockFunction(const CallEvent &Call) const; + bool isUnlockFunction(const CallEvent &Call) const; + + /// Process unlock. + /// Process lock. + /// Process blocking functions (sleep, getc, fgets, read, recv) + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; +}; + +} // end anonymous namespace + +REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned) + +BlockInCriticalSectionChecker::BlockInCriticalSectionChecker() + : IILockGuard(nullptr), IIUniqueLock(nullptr), + LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"), + FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"), + PthreadLockFn("pthread_mutex_lock"), + PthreadTryLockFn("pthread_mutex_trylock"), + PthreadUnlockFn("pthread_mutex_unlock"), + MtxLock("mtx_lock"), + MtxTimedLock("mtx_timedlock"), + MtxTryLock("mtx_trylock"), + MtxUnlock("mtx_unlock"), + ClassLockGuard("lock_guard"), + ClassUniqueLock("unique_lock"), + IdentifierInfoInitialized(false) { + // Initialize the bug type. + BlockInCritSectionBugType.reset( + new BugType(this, "Call to blocking function in critical section", + "Blocking Error")); +} + +void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const { + if (!IdentifierInfoInitialized) { + /* In case of checking C code, or when the corresponding headers are not + * included, we might end up query the identifier table every time when this + * function is called instead of early returning it. To avoid this, a bool + * variable (IdentifierInfoInitialized) is used and the function will be run + * only once. */ + IILockGuard = &Ctx.Idents.get(ClassLockGuard); + IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock); + IdentifierInfoInitialized = true; + } +} + +bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const { + if (Call.isCalled(SleepFn) + || Call.isCalled(GetcFn) + || Call.isCalled(FgetsFn) + || Call.isCalled(ReadFn) + || Call.isCalled(RecvFn)) { + return true; + } + return false; +} + +bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const { + if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { + auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier(); + if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock) + return true; + } + + if (Call.isCalled(LockFn) + || Call.isCalled(PthreadLockFn) + || Call.isCalled(PthreadTryLockFn) + || Call.isCalled(MtxLock) + || Call.isCalled(MtxTimedLock) + || Call.isCalled(MtxTryLock)) { + return true; + } + return false; +} + +bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const { + if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) { + const auto *DRecordDecl = dyn_cast<CXXRecordDecl>(Dtor->getDecl()->getParent()); + auto IdentifierInfo = DRecordDecl->getIdentifier(); + if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock) + return true; + } + + if (Call.isCalled(UnlockFn) + || Call.isCalled(PthreadUnlockFn) + || Call.isCalled(MtxUnlock)) { + return true; + } + return false; +} + +void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + initIdentifierInfo(C.getASTContext()); + + if (!isBlockingFunction(Call) + && !isLockFunction(Call) + && !isUnlockFunction(Call)) + return; + + ProgramStateRef State = C.getState(); + unsigned mutexCount = State->get<MutexCounter>(); + if (isUnlockFunction(Call) && mutexCount > 0) { + State = State->set<MutexCounter>(--mutexCount); + C.addTransition(State); + } else if (isLockFunction(Call)) { + State = State->set<MutexCounter>(++mutexCount); + C.addTransition(State); + } else if (mutexCount > 0) { + SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol(); + reportBlockInCritSection(BlockDesc, Call, C); + } +} + +void BlockInCriticalSectionChecker::reportBlockInCritSection( + SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const { + ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); + if (!ErrNode) + return; + + std::string msg; + llvm::raw_string_ostream os(msg); + os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName() + << "' inside of critical section"; + auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode); + R->addRange(Call.getSourceRange()); + R->markInteresting(BlockDescSym); + C.emitReport(std::move(R)); +} + +void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) { + mgr.registerChecker<BlockInCriticalSectionChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp new file mode 100644 index 000000000000..3008eddd397e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp @@ -0,0 +1,157 @@ +//== BoolAssignmentChecker.cpp - Boolean assignment checker -----*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines BoolAssignmentChecker, a builtin check in ExprEngine that +// performs checks for assignment of non-Boolean values to Boolean variables. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { + class BoolAssignmentChecker : public Checker< check::Bind > { + mutable std::unique_ptr<BuiltinBug> BT; + void emitReport(ProgramStateRef state, CheckerContext &C) const; + public: + void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; + }; +} // end anonymous namespace + +void BoolAssignmentChecker::emitReport(ProgramStateRef state, + CheckerContext &C) const { + if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { + if (!BT) + BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value")); + C.emitReport(llvm::make_unique<BugReport>(*BT, BT->getDescription(), N)); + } +} + +static bool isBooleanType(QualType Ty) { + if (Ty->isBooleanType()) // C++ or C99 + return true; + + if (const TypedefType *TT = Ty->getAs<TypedefType>()) + return TT->getDecl()->getName() == "BOOL" || // Objective-C + TT->getDecl()->getName() == "_Bool" || // stdbool.h < C99 + TT->getDecl()->getName() == "Boolean"; // MacTypes.h + + return false; +} + +void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S, + CheckerContext &C) const { + + // We are only interested in stores into Booleans. + const TypedValueRegion *TR = + dyn_cast_or_null<TypedValueRegion>(loc.getAsRegion()); + + if (!TR) + return; + + QualType valTy = TR->getValueType(); + + if (!isBooleanType(valTy)) + return; + + // Get the value of the right-hand side. We only care about values + // that are defined (UnknownVals and UndefinedVals are handled by other + // checkers). + Optional<DefinedSVal> DV = val.getAs<DefinedSVal>(); + if (!DV) + return; + + // Check if the assigned value meets our criteria for correctness. It must + // be a value that is either 0 or 1. One way to check this is to see if + // the value is possibly < 0 (for a negative value) or greater than 1. + ProgramStateRef state = C.getState(); + SValBuilder &svalBuilder = C.getSValBuilder(); + ConstraintManager &CM = C.getConstraintManager(); + + // First, ensure that the value is >= 0. + DefinedSVal zeroVal = svalBuilder.makeIntVal(0, valTy); + SVal greaterThanOrEqualToZeroVal = + svalBuilder.evalBinOp(state, BO_GE, *DV, zeroVal, + svalBuilder.getConditionType()); + + Optional<DefinedSVal> greaterThanEqualToZero = + greaterThanOrEqualToZeroVal.getAs<DefinedSVal>(); + + if (!greaterThanEqualToZero) { + // The SValBuilder cannot construct a valid SVal for this condition. + // This means we cannot properly reason about it. + return; + } + + ProgramStateRef stateLT, stateGE; + std::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero); + + // Is it possible for the value to be less than zero? + if (stateLT) { + // It is possible for the value to be less than zero. We only + // want to emit a warning, however, if that value is fully constrained. + // If it it possible for the value to be >= 0, then essentially the + // value is underconstrained and there is nothing left to be done. + if (!stateGE) + emitReport(stateLT, C); + + // In either case, we are done. + return; + } + + // If we reach here, it must be the case that the value is constrained + // to only be >= 0. + assert(stateGE == state); + + // At this point we know that the value is >= 0. + // Now check to ensure that the value is <= 1. + DefinedSVal OneVal = svalBuilder.makeIntVal(1, valTy); + SVal lessThanEqToOneVal = + svalBuilder.evalBinOp(state, BO_LE, *DV, OneVal, + svalBuilder.getConditionType()); + + Optional<DefinedSVal> lessThanEqToOne = + lessThanEqToOneVal.getAs<DefinedSVal>(); + + if (!lessThanEqToOne) { + // The SValBuilder cannot construct a valid SVal for this condition. + // This means we cannot properly reason about it. + return; + } + + ProgramStateRef stateGT, stateLE; + std::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne); + + // Is it possible for the value to be greater than one? + if (stateGT) { + // It is possible for the value to be greater than one. We only + // want to emit a warning, however, if that value is fully constrained. + // If it is possible for the value to be <= 1, then essentially the + // value is underconstrained and there is nothing left to be done. + if (!stateLE) + emitReport(stateGT, C); + + // In either case, we are done. + return; + } + + // If we reach here, it must be the case that the value is constrained + // to only be <= 1. + assert(stateLE == state); +} + +void ento::registerBoolAssignmentChecker(CheckerManager &mgr) { + mgr.registerChecker<BoolAssignmentChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp new file mode 100644 index 000000000000..f98027942e18 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -0,0 +1,122 @@ +//=== BuiltinFunctionChecker.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker evaluates clang builtin functions. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Basic/Builtins.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { + +class BuiltinFunctionChecker : public Checker<eval::Call> { +public: + bool evalCall(const CallExpr *CE, CheckerContext &C) const; +}; + +} + +bool BuiltinFunctionChecker::evalCall(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef state = C.getState(); + const FunctionDecl *FD = C.getCalleeDecl(CE); + const LocationContext *LCtx = C.getLocationContext(); + if (!FD) + return false; + + switch (FD->getBuiltinID()) { + default: + return false; + + case Builtin::BI__builtin_assume: { + assert (CE->arg_begin() != CE->arg_end()); + SVal ArgSVal = C.getSVal(CE->getArg(0)); + if (ArgSVal.isUndef()) + return true; // Return true to model purity. + + state = state->assume(ArgSVal.castAs<DefinedOrUnknownSVal>(), true); + // FIXME: do we want to warn here? Not right now. The most reports might + // come from infeasible paths, thus being false positives. + if (!state) { + C.generateSink(C.getState(), C.getPredecessor()); + return true; + } + + C.addTransition(state); + return true; + } + + case Builtin::BI__builtin_unpredictable: + case Builtin::BI__builtin_expect: + case Builtin::BI__builtin_assume_aligned: + case Builtin::BI__builtin_addressof: { + // For __builtin_unpredictable, __builtin_expect, and + // __builtin_assume_aligned, just return the value of the subexpression. + // __builtin_addressof is going from a reference to a pointer, but those + // are represented the same way in the analyzer. + assert (CE->arg_begin() != CE->arg_end()); + SVal X = C.getSVal(*(CE->arg_begin())); + C.addTransition(state->BindExpr(CE, LCtx, X)); + return true; + } + + case Builtin::BI__builtin_alloca_with_align: + case Builtin::BI__builtin_alloca: { + // FIXME: Refactor into StoreManager itself? + MemRegionManager& RM = C.getStoreManager().getRegionManager(); + const AllocaRegion* R = + RM.getAllocaRegion(CE, C.blockCount(), C.getLocationContext()); + + // Set the extent of the region in bytes. This enables us to use the + // SVal of the argument directly. If we save the extent in bits, we + // cannot represent values like symbol*8. + auto Size = C.getSVal(*(CE->arg_begin())).castAs<DefinedOrUnknownSVal>(); + + SValBuilder& svalBuilder = C.getSValBuilder(); + DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); + DefinedOrUnknownSVal extentMatchesSizeArg = + svalBuilder.evalEQ(state, Extent, Size); + state = state->assume(extentMatchesSizeArg, true); + assert(state && "The region should not have any previous constraints"); + + C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R))); + return true; + } + + case Builtin::BI__builtin_object_size: + case Builtin::BI__builtin_constant_p: { + // This must be resolvable at compile time, so we defer to the constant + // evaluator for a value. + SVal V = UnknownVal(); + Expr::EvalResult EVResult; + if (CE->EvaluateAsInt(EVResult, C.getASTContext(), Expr::SE_NoSideEffects)) { + // Make sure the result has the correct type. + llvm::APSInt Result = EVResult.Val.getInt(); + SValBuilder &SVB = C.getSValBuilder(); + BasicValueFactory &BVF = SVB.getBasicValueFactory(); + BVF.getAPSIntType(CE->getType()).apply(Result); + V = SVB.makeIntVal(Result); + } + + C.addTransition(state->BindExpr(CE, LCtx, V)); + return true; + } + } +} + +void ento::registerBuiltinFunctionChecker(CheckerManager &mgr) { + mgr.registerChecker<BuiltinFunctionChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp new file mode 100644 index 000000000000..8bffada69b9b --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -0,0 +1,2493 @@ +//= CStringChecker.cpp - Checks calls to C string functions --------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines CStringChecker, which is an assortment of checks on calls +// to functions in <string.h>. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "InterCheckerAPI.h" +#include "clang/Basic/CharInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class CStringChecker : public Checker< eval::Call, + check::PreStmt<DeclStmt>, + check::LiveSymbols, + check::DeadSymbols, + check::RegionChanges + > { + mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap, + BT_NotCString, BT_AdditionOverflow; + + mutable const char *CurrentFunctionDescription; + +public: + /// The filter is used to filter out the diagnostics which are not enabled by + /// the user. + struct CStringChecksFilter { + DefaultBool CheckCStringNullArg; + DefaultBool CheckCStringOutOfBounds; + DefaultBool CheckCStringBufferOverlap; + DefaultBool CheckCStringNotNullTerm; + + CheckName CheckNameCStringNullArg; + CheckName CheckNameCStringOutOfBounds; + CheckName CheckNameCStringBufferOverlap; + CheckName CheckNameCStringNotNullTerm; + }; + + CStringChecksFilter Filter; + + static void *getTag() { static int tag; return &tag; } + + bool evalCall(const CallExpr *CE, CheckerContext &C) const; + void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; + void checkLiveSymbols(ProgramStateRef state, SymbolReaper &SR) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + + ProgramStateRef + checkRegionChanges(ProgramStateRef state, + const InvalidatedSymbols *, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call) const; + + typedef void (CStringChecker::*FnCheck)(CheckerContext &, + const CallExpr *) const; + + void evalMemcpy(CheckerContext &C, const CallExpr *CE) const; + void evalMempcpy(CheckerContext &C, const CallExpr *CE) const; + void evalMemmove(CheckerContext &C, const CallExpr *CE) const; + void evalBcopy(CheckerContext &C, const CallExpr *CE) const; + void evalCopyCommon(CheckerContext &C, const CallExpr *CE, + ProgramStateRef state, + const Expr *Size, + const Expr *Source, + const Expr *Dest, + bool Restricted = false, + bool IsMempcpy = false) const; + + void evalMemcmp(CheckerContext &C, const CallExpr *CE) const; + + void evalstrLength(CheckerContext &C, const CallExpr *CE) const; + void evalstrnLength(CheckerContext &C, const CallExpr *CE) const; + void evalstrLengthCommon(CheckerContext &C, + const CallExpr *CE, + bool IsStrnlen = false) const; + + void evalStrcpy(CheckerContext &C, const CallExpr *CE) const; + void evalStrncpy(CheckerContext &C, const CallExpr *CE) const; + void evalStpcpy(CheckerContext &C, const CallExpr *CE) const; + void evalStrlcpy(CheckerContext &C, const CallExpr *CE) const; + void evalStrcpyCommon(CheckerContext &C, + const CallExpr *CE, + bool returnEnd, + bool isBounded, + bool isAppending, + bool returnPtr = true) const; + + void evalStrcat(CheckerContext &C, const CallExpr *CE) const; + void evalStrncat(CheckerContext &C, const CallExpr *CE) const; + void evalStrlcat(CheckerContext &C, const CallExpr *CE) const; + + void evalStrcmp(CheckerContext &C, const CallExpr *CE) const; + void evalStrncmp(CheckerContext &C, const CallExpr *CE) const; + void evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const; + void evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const; + void evalStrcmpCommon(CheckerContext &C, + const CallExpr *CE, + bool isBounded = false, + bool ignoreCase = false) const; + + void evalStrsep(CheckerContext &C, const CallExpr *CE) const; + + void evalStdCopy(CheckerContext &C, const CallExpr *CE) const; + void evalStdCopyBackward(CheckerContext &C, const CallExpr *CE) const; + void evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const; + void evalMemset(CheckerContext &C, const CallExpr *CE) const; + void evalBzero(CheckerContext &C, const CallExpr *CE) const; + + // Utility methods + std::pair<ProgramStateRef , ProgramStateRef > + static assumeZero(CheckerContext &C, + ProgramStateRef state, SVal V, QualType Ty); + + static ProgramStateRef setCStringLength(ProgramStateRef state, + const MemRegion *MR, + SVal strLength); + static SVal getCStringLengthForRegion(CheckerContext &C, + ProgramStateRef &state, + const Expr *Ex, + const MemRegion *MR, + bool hypothetical); + SVal getCStringLength(CheckerContext &C, + ProgramStateRef &state, + const Expr *Ex, + SVal Buf, + bool hypothetical = false) const; + + const StringLiteral *getCStringLiteral(CheckerContext &C, + ProgramStateRef &state, + const Expr *expr, + SVal val) const; + + static ProgramStateRef InvalidateBuffer(CheckerContext &C, + ProgramStateRef state, + const Expr *Ex, SVal V, + bool IsSourceBuffer, + const Expr *Size); + + static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx, + const MemRegion *MR); + + static bool memsetAux(const Expr *DstBuffer, SVal CharE, + const Expr *Size, CheckerContext &C, + ProgramStateRef &State); + + // Re-usable checks + ProgramStateRef checkNonNull(CheckerContext &C, + ProgramStateRef state, + const Expr *S, + SVal l) const; + ProgramStateRef CheckLocation(CheckerContext &C, + ProgramStateRef state, + const Expr *S, + SVal l, + const char *message = nullptr) const; + ProgramStateRef CheckBufferAccess(CheckerContext &C, + ProgramStateRef state, + const Expr *Size, + const Expr *FirstBuf, + const Expr *SecondBuf, + const char *firstMessage = nullptr, + const char *secondMessage = nullptr, + bool WarnAboutSize = false) const; + + ProgramStateRef CheckBufferAccess(CheckerContext &C, + ProgramStateRef state, + const Expr *Size, + const Expr *Buf, + const char *message = nullptr, + bool WarnAboutSize = false) const { + // This is a convenience overload. + return CheckBufferAccess(C, state, Size, Buf, nullptr, message, nullptr, + WarnAboutSize); + } + ProgramStateRef CheckOverlap(CheckerContext &C, + ProgramStateRef state, + const Expr *Size, + const Expr *First, + const Expr *Second) const; + void emitOverlapBug(CheckerContext &C, + ProgramStateRef state, + const Stmt *First, + const Stmt *Second) const; + + void emitNullArgBug(CheckerContext &C, ProgramStateRef State, const Stmt *S, + StringRef WarningMsg) const; + void emitOutOfBoundsBug(CheckerContext &C, ProgramStateRef State, + const Stmt *S, StringRef WarningMsg) const; + void emitNotCStringBug(CheckerContext &C, ProgramStateRef State, + const Stmt *S, StringRef WarningMsg) const; + void emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const; + + ProgramStateRef checkAdditionOverflow(CheckerContext &C, + ProgramStateRef state, + NonLoc left, + NonLoc right) const; + + // Return true if the destination buffer of the copy function may be in bound. + // Expects SVal of Size to be positive and unsigned. + // Expects SVal of FirstBuf to be a FieldRegion. + static bool IsFirstBufInBound(CheckerContext &C, + ProgramStateRef state, + const Expr *FirstBuf, + const Expr *Size); +}; + +} //end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal) + +//===----------------------------------------------------------------------===// +// Individual checks and utility methods. +//===----------------------------------------------------------------------===// + +std::pair<ProgramStateRef , ProgramStateRef > +CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V, + QualType Ty) { + Optional<DefinedSVal> val = V.getAs<DefinedSVal>(); + if (!val) + return std::pair<ProgramStateRef , ProgramStateRef >(state, state); + + SValBuilder &svalBuilder = C.getSValBuilder(); + DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal(Ty); + return state->assume(svalBuilder.evalEQ(state, *val, zero)); +} + +ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C, + ProgramStateRef state, + const Expr *S, SVal l) const { + // If a previous check has failed, propagate the failure. + if (!state) + return nullptr; + + ProgramStateRef stateNull, stateNonNull; + std::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType()); + + if (stateNull && !stateNonNull) { + if (Filter.CheckCStringNullArg) { + SmallString<80> buf; + llvm::raw_svector_ostream os(buf); + assert(CurrentFunctionDescription); + os << "Null pointer argument in call to " << CurrentFunctionDescription; + + emitNullArgBug(C, stateNull, S, os.str()); + } + return nullptr; + } + + // From here on, assume that the value is non-null. + assert(stateNonNull); + return stateNonNull; +} + +// FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor? +ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C, + ProgramStateRef state, + const Expr *S, SVal l, + const char *warningMsg) const { + // If a previous check has failed, propagate the failure. + if (!state) + return nullptr; + + // Check for out of bound array element access. + const MemRegion *R = l.getAsRegion(); + if (!R) + return state; + + const ElementRegion *ER = dyn_cast<ElementRegion>(R); + if (!ER) + return state; + + if (ER->getValueType() != C.getASTContext().CharTy) + return state; + + // Get the size of the array. + const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion()); + SValBuilder &svalBuilder = C.getSValBuilder(); + SVal Extent = + svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder)); + DefinedOrUnknownSVal Size = Extent.castAs<DefinedOrUnknownSVal>(); + + // Get the index of the accessed element. + DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); + + ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true); + ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false); + if (StOutBound && !StInBound) { + // These checks are either enabled by the CString out-of-bounds checker + // explicitly or implicitly by the Malloc checker. + // In the latter case we only do modeling but do not emit warning. + if (!Filter.CheckCStringOutOfBounds) + return nullptr; + // Emit a bug report. + if (warningMsg) { + emitOutOfBoundsBug(C, StOutBound, S, warningMsg); + } else { + assert(CurrentFunctionDescription); + assert(CurrentFunctionDescription[0] != '\0'); + + SmallString<80> buf; + llvm::raw_svector_ostream os(buf); + os << toUppercase(CurrentFunctionDescription[0]) + << &CurrentFunctionDescription[1] + << " accesses out-of-bound array element"; + emitOutOfBoundsBug(C, StOutBound, S, os.str()); + } + return nullptr; + } + + // Array bound check succeeded. From this point forward the array bound + // should always succeed. + return StInBound; +} + +ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C, + ProgramStateRef state, + const Expr *Size, + const Expr *FirstBuf, + const Expr *SecondBuf, + const char *firstMessage, + const char *secondMessage, + bool WarnAboutSize) const { + // If a previous check has failed, propagate the failure. + if (!state) + return nullptr; + + SValBuilder &svalBuilder = C.getSValBuilder(); + ASTContext &Ctx = svalBuilder.getContext(); + const LocationContext *LCtx = C.getLocationContext(); + + QualType sizeTy = Size->getType(); + QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); + + // Check that the first buffer is non-null. + SVal BufVal = C.getSVal(FirstBuf); + state = checkNonNull(C, state, FirstBuf, BufVal); + if (!state) + return nullptr; + + // If out-of-bounds checking is turned off, skip the rest. + if (!Filter.CheckCStringOutOfBounds) + return state; + + // Get the access length and make sure it is known. + // FIXME: This assumes the caller has already checked that the access length + // is positive. And that it's unsigned. + SVal LengthVal = C.getSVal(Size); + Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + if (!Length) + return state; + + // Compute the offset of the last element to be accessed: size-1. + NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>(); + SVal Offset = svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy); + if (Offset.isUnknown()) + return nullptr; + NonLoc LastOffset = Offset.castAs<NonLoc>(); + + // Check that the first buffer is sufficiently long. + SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); + if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) { + const Expr *warningExpr = (WarnAboutSize ? Size : FirstBuf); + + SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, + LastOffset, PtrTy); + state = CheckLocation(C, state, warningExpr, BufEnd, firstMessage); + + // If the buffer isn't large enough, abort. + if (!state) + return nullptr; + } + + // If there's a second buffer, check it as well. + if (SecondBuf) { + BufVal = state->getSVal(SecondBuf, LCtx); + state = checkNonNull(C, state, SecondBuf, BufVal); + if (!state) + return nullptr; + + BufStart = svalBuilder.evalCast(BufVal, PtrTy, SecondBuf->getType()); + if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) { + const Expr *warningExpr = (WarnAboutSize ? Size : SecondBuf); + + SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, + LastOffset, PtrTy); + state = CheckLocation(C, state, warningExpr, BufEnd, secondMessage); + } + } + + // Large enough or not, return this state! + return state; +} + +ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C, + ProgramStateRef state, + const Expr *Size, + const Expr *First, + const Expr *Second) const { + if (!Filter.CheckCStringBufferOverlap) + return state; + + // Do a simple check for overlap: if the two arguments are from the same + // buffer, see if the end of the first is greater than the start of the second + // or vice versa. + + // If a previous check has failed, propagate the failure. + if (!state) + return nullptr; + + ProgramStateRef stateTrue, stateFalse; + + // Get the buffer values and make sure they're known locations. + const LocationContext *LCtx = C.getLocationContext(); + SVal firstVal = state->getSVal(First, LCtx); + SVal secondVal = state->getSVal(Second, LCtx); + + Optional<Loc> firstLoc = firstVal.getAs<Loc>(); + if (!firstLoc) + return state; + + Optional<Loc> secondLoc = secondVal.getAs<Loc>(); + if (!secondLoc) + return state; + + // Are the two values the same? + SValBuilder &svalBuilder = C.getSValBuilder(); + std::tie(stateTrue, stateFalse) = + state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc)); + + if (stateTrue && !stateFalse) { + // If the values are known to be equal, that's automatically an overlap. + emitOverlapBug(C, stateTrue, First, Second); + return nullptr; + } + + // assume the two expressions are not equal. + assert(stateFalse); + state = stateFalse; + + // Which value comes first? + QualType cmpTy = svalBuilder.getConditionType(); + SVal reverse = svalBuilder.evalBinOpLL(state, BO_GT, + *firstLoc, *secondLoc, cmpTy); + Optional<DefinedOrUnknownSVal> reverseTest = + reverse.getAs<DefinedOrUnknownSVal>(); + if (!reverseTest) + return state; + + std::tie(stateTrue, stateFalse) = state->assume(*reverseTest); + if (stateTrue) { + if (stateFalse) { + // If we don't know which one comes first, we can't perform this test. + return state; + } else { + // Switch the values so that firstVal is before secondVal. + std::swap(firstLoc, secondLoc); + + // Switch the Exprs as well, so that they still correspond. + std::swap(First, Second); + } + } + + // Get the length, and make sure it too is known. + SVal LengthVal = state->getSVal(Size, LCtx); + Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + if (!Length) + return state; + + // Convert the first buffer's start address to char*. + // Bail out if the cast fails. + ASTContext &Ctx = svalBuilder.getContext(); + QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); + SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, + First->getType()); + Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>(); + if (!FirstStartLoc) + return state; + + // Compute the end of the first buffer. Bail out if THAT fails. + SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add, + *FirstStartLoc, *Length, CharPtrTy); + Optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>(); + if (!FirstEndLoc) + return state; + + // Is the end of the first buffer past the start of the second buffer? + SVal Overlap = svalBuilder.evalBinOpLL(state, BO_GT, + *FirstEndLoc, *secondLoc, cmpTy); + Optional<DefinedOrUnknownSVal> OverlapTest = + Overlap.getAs<DefinedOrUnknownSVal>(); + if (!OverlapTest) + return state; + + std::tie(stateTrue, stateFalse) = state->assume(*OverlapTest); + + if (stateTrue && !stateFalse) { + // Overlap! + emitOverlapBug(C, stateTrue, First, Second); + return nullptr; + } + + // assume the two expressions don't overlap. + assert(stateFalse); + return stateFalse; +} + +void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state, + const Stmt *First, const Stmt *Second) const { + ExplodedNode *N = C.generateErrorNode(state); + if (!N) + return; + + if (!BT_Overlap) + BT_Overlap.reset(new BugType(Filter.CheckNameCStringBufferOverlap, + categories::UnixAPI, "Improper arguments")); + + // Generate a report for this bug. + auto report = llvm::make_unique<BugReport>( + *BT_Overlap, "Arguments must not be overlapping buffers", N); + report->addRange(First->getSourceRange()); + report->addRange(Second->getSourceRange()); + + C.emitReport(std::move(report)); +} + +void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State, + const Stmt *S, StringRef WarningMsg) const { + if (ExplodedNode *N = C.generateErrorNode(State)) { + if (!BT_Null) + BT_Null.reset(new BuiltinBug( + Filter.CheckNameCStringNullArg, categories::UnixAPI, + "Null pointer argument in call to byte string function")); + + BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Null.get()); + auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N); + Report->addRange(S->getSourceRange()); + if (const auto *Ex = dyn_cast<Expr>(S)) + bugreporter::trackExpressionValue(N, Ex, *Report); + C.emitReport(std::move(Report)); + } +} + +void CStringChecker::emitOutOfBoundsBug(CheckerContext &C, + ProgramStateRef State, const Stmt *S, + StringRef WarningMsg) const { + if (ExplodedNode *N = C.generateErrorNode(State)) { + if (!BT_Bounds) + BT_Bounds.reset(new BuiltinBug( + Filter.CheckCStringOutOfBounds ? Filter.CheckNameCStringOutOfBounds + : Filter.CheckNameCStringNullArg, + "Out-of-bound array access", + "Byte string function accesses out-of-bound array element")); + + BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Bounds.get()); + + // FIXME: It would be nice to eventually make this diagnostic more clear, + // e.g., by referencing the original declaration or by saying *why* this + // reference is outside the range. + auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N); + Report->addRange(S->getSourceRange()); + C.emitReport(std::move(Report)); + } +} + +void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State, + const Stmt *S, + StringRef WarningMsg) const { + if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) { + if (!BT_NotCString) + BT_NotCString.reset(new BuiltinBug( + Filter.CheckNameCStringNotNullTerm, categories::UnixAPI, + "Argument is not a null-terminated string.")); + + auto Report = llvm::make_unique<BugReport>(*BT_NotCString, WarningMsg, N); + + Report->addRange(S->getSourceRange()); + C.emitReport(std::move(Report)); + } +} + +void CStringChecker::emitAdditionOverflowBug(CheckerContext &C, + ProgramStateRef State) const { + if (ExplodedNode *N = C.generateErrorNode(State)) { + if (!BT_NotCString) + BT_NotCString.reset( + new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API", + "Sum of expressions causes overflow.")); + + // This isn't a great error message, but this should never occur in real + // code anyway -- you'd have to create a buffer longer than a size_t can + // represent, which is sort of a contradiction. + const char *WarningMsg = + "This expression will create a string whose length is too big to " + "be represented as a size_t"; + + auto Report = llvm::make_unique<BugReport>(*BT_NotCString, WarningMsg, N); + C.emitReport(std::move(Report)); + } +} + +ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C, + ProgramStateRef state, + NonLoc left, + NonLoc right) const { + // If out-of-bounds checking is turned off, skip the rest. + if (!Filter.CheckCStringOutOfBounds) + return state; + + // If a previous check has failed, propagate the failure. + if (!state) + return nullptr; + + SValBuilder &svalBuilder = C.getSValBuilder(); + BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); + + QualType sizeTy = svalBuilder.getContext().getSizeType(); + const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy); + NonLoc maxVal = svalBuilder.makeIntVal(maxValInt); + + SVal maxMinusRight; + if (right.getAs<nonloc::ConcreteInt>()) { + maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right, + sizeTy); + } else { + // Try switching the operands. (The order of these two assignments is + // important!) + maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left, + sizeTy); + left = right; + } + + if (Optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) { + QualType cmpTy = svalBuilder.getConditionType(); + // If left > max - right, we have an overflow. + SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left, + *maxMinusRightNL, cmpTy); + + ProgramStateRef stateOverflow, stateOkay; + std::tie(stateOverflow, stateOkay) = + state->assume(willOverflow.castAs<DefinedOrUnknownSVal>()); + + if (stateOverflow && !stateOkay) { + // We have an overflow. Emit a bug report. + emitAdditionOverflowBug(C, stateOverflow); + return nullptr; + } + + // From now on, assume an overflow didn't occur. + assert(stateOkay); + state = stateOkay; + } + + return state; +} + +ProgramStateRef CStringChecker::setCStringLength(ProgramStateRef state, + const MemRegion *MR, + SVal strLength) { + assert(!strLength.isUndef() && "Attempt to set an undefined string length"); + + MR = MR->StripCasts(); + + switch (MR->getKind()) { + case MemRegion::StringRegionKind: + // FIXME: This can happen if we strcpy() into a string region. This is + // undefined [C99 6.4.5p6], but we should still warn about it. + return state; + + case MemRegion::SymbolicRegionKind: + case MemRegion::AllocaRegionKind: + case MemRegion::VarRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + // These are the types we can currently track string lengths for. + break; + + case MemRegion::ElementRegionKind: + // FIXME: Handle element regions by upper-bounding the parent region's + // string length. + return state; + + default: + // Other regions (mostly non-data) can't have a reliable C string length. + // For now, just ignore the change. + // FIXME: These are rare but not impossible. We should output some kind of + // warning for things like strcpy((char[]){'a', 0}, "b"); + return state; + } + + if (strLength.isUnknown()) + return state->remove<CStringLength>(MR); + + return state->set<CStringLength>(MR, strLength); +} + +SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, + ProgramStateRef &state, + const Expr *Ex, + const MemRegion *MR, + bool hypothetical) { + if (!hypothetical) { + // If there's a recorded length, go ahead and return it. + const SVal *Recorded = state->get<CStringLength>(MR); + if (Recorded) + return *Recorded; + } + + // Otherwise, get a new symbol and update the state. + SValBuilder &svalBuilder = C.getSValBuilder(); + QualType sizeTy = svalBuilder.getContext().getSizeType(); + SVal strLength = svalBuilder.getMetadataSymbolVal(CStringChecker::getTag(), + MR, Ex, sizeTy, + C.getLocationContext(), + C.blockCount()); + + if (!hypothetical) { + if (Optional<NonLoc> strLn = strLength.getAs<NonLoc>()) { + // In case of unbounded calls strlen etc bound the range to SIZE_MAX/4 + BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); + const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy); + llvm::APSInt fourInt = APSIntType(maxValInt).getValue(4); + const llvm::APSInt *maxLengthInt = BVF.evalAPSInt(BO_Div, maxValInt, + fourInt); + NonLoc maxLength = svalBuilder.makeIntVal(*maxLengthInt); + SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn, + maxLength, sizeTy); + state = state->assume(evalLength.castAs<DefinedOrUnknownSVal>(), true); + } + state = state->set<CStringLength>(MR, strLength); + } + + return strLength; +} + +SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state, + const Expr *Ex, SVal Buf, + bool hypothetical) const { + const MemRegion *MR = Buf.getAsRegion(); + if (!MR) { + // If we can't get a region, see if it's something we /know/ isn't a + // C string. In the context of locations, the only time we can issue such + // a warning is for labels. + if (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) { + if (Filter.CheckCStringNotNullTerm) { + SmallString<120> buf; + llvm::raw_svector_ostream os(buf); + assert(CurrentFunctionDescription); + os << "Argument to " << CurrentFunctionDescription + << " is the address of the label '" << Label->getLabel()->getName() + << "', which is not a null-terminated string"; + + emitNotCStringBug(C, state, Ex, os.str()); + } + return UndefinedVal(); + } + + // If it's not a region and not a label, give up. + return UnknownVal(); + } + + // If we have a region, strip casts from it and see if we can figure out + // its length. For anything we can't figure out, just return UnknownVal. + MR = MR->StripCasts(); + + switch (MR->getKind()) { + case MemRegion::StringRegionKind: { + // Modifying the contents of string regions is undefined [C99 6.4.5p6], + // so we can assume that the byte length is the correct C string length. + SValBuilder &svalBuilder = C.getSValBuilder(); + QualType sizeTy = svalBuilder.getContext().getSizeType(); + const StringLiteral *strLit = cast<StringRegion>(MR)->getStringLiteral(); + return svalBuilder.makeIntVal(strLit->getByteLength(), sizeTy); + } + case MemRegion::SymbolicRegionKind: + case MemRegion::AllocaRegionKind: + case MemRegion::VarRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + return getCStringLengthForRegion(C, state, Ex, MR, hypothetical); + case MemRegion::CompoundLiteralRegionKind: + // FIXME: Can we track this? Is it necessary? + return UnknownVal(); + case MemRegion::ElementRegionKind: + // FIXME: How can we handle this? It's not good enough to subtract the + // offset from the base string length; consider "123\x00567" and &a[5]. + return UnknownVal(); + default: + // Other regions (mostly non-data) can't have a reliable C string length. + // In this case, an error is emitted and UndefinedVal is returned. + // The caller should always be prepared to handle this case. + if (Filter.CheckCStringNotNullTerm) { + SmallString<120> buf; + llvm::raw_svector_ostream os(buf); + + assert(CurrentFunctionDescription); + os << "Argument to " << CurrentFunctionDescription << " is "; + + if (SummarizeRegion(os, C.getASTContext(), MR)) + os << ", which is not a null-terminated string"; + else + os << "not a null-terminated string"; + + emitNotCStringBug(C, state, Ex, os.str()); + } + return UndefinedVal(); + } +} + +const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C, + ProgramStateRef &state, const Expr *expr, SVal val) const { + + // Get the memory region pointed to by the val. + const MemRegion *bufRegion = val.getAsRegion(); + if (!bufRegion) + return nullptr; + + // Strip casts off the memory region. + bufRegion = bufRegion->StripCasts(); + + // Cast the memory region to a string region. + const StringRegion *strRegion= dyn_cast<StringRegion>(bufRegion); + if (!strRegion) + return nullptr; + + // Return the actual string in the string region. + return strRegion->getStringLiteral(); +} + +bool CStringChecker::IsFirstBufInBound(CheckerContext &C, + ProgramStateRef state, + const Expr *FirstBuf, + const Expr *Size) { + // If we do not know that the buffer is long enough we return 'true'. + // Otherwise the parent region of this field region would also get + // invalidated, which would lead to warnings based on an unknown state. + + // Originally copied from CheckBufferAccess and CheckLocation. + SValBuilder &svalBuilder = C.getSValBuilder(); + ASTContext &Ctx = svalBuilder.getContext(); + const LocationContext *LCtx = C.getLocationContext(); + + QualType sizeTy = Size->getType(); + QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); + SVal BufVal = state->getSVal(FirstBuf, LCtx); + + SVal LengthVal = state->getSVal(Size, LCtx); + Optional<NonLoc> Length = LengthVal.getAs<NonLoc>(); + if (!Length) + return true; // cf top comment. + + // Compute the offset of the last element to be accessed: size-1. + NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>(); + SVal Offset = svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy); + if (Offset.isUnknown()) + return true; // cf top comment + NonLoc LastOffset = Offset.castAs<NonLoc>(); + + // Check that the first buffer is sufficiently long. + SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); + Optional<Loc> BufLoc = BufStart.getAs<Loc>(); + if (!BufLoc) + return true; // cf top comment. + + SVal BufEnd = + svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, LastOffset, PtrTy); + + // Check for out of bound array element access. + const MemRegion *R = BufEnd.getAsRegion(); + if (!R) + return true; // cf top comment. + + const ElementRegion *ER = dyn_cast<ElementRegion>(R); + if (!ER) + return true; // cf top comment. + + // FIXME: Does this crash when a non-standard definition + // of a library function is encountered? + assert(ER->getValueType() == C.getASTContext().CharTy && + "IsFirstBufInBound should only be called with char* ElementRegions"); + + // Get the size of the array. + const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion()); + SVal Extent = + svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder)); + DefinedOrUnknownSVal ExtentSize = Extent.castAs<DefinedOrUnknownSVal>(); + + // Get the index of the accessed element. + DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); + + ProgramStateRef StInBound = state->assumeInBound(Idx, ExtentSize, true); + + return static_cast<bool>(StInBound); +} + +ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C, + ProgramStateRef state, + const Expr *E, SVal V, + bool IsSourceBuffer, + const Expr *Size) { + Optional<Loc> L = V.getAs<Loc>(); + if (!L) + return state; + + // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes + // some assumptions about the value that CFRefCount can't. Even so, it should + // probably be refactored. + if (Optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) { + const MemRegion *R = MR->getRegion()->StripCasts(); + + // Are we dealing with an ElementRegion? If so, we should be invalidating + // the super-region. + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + R = ER->getSuperRegion(); + // FIXME: What about layers of ElementRegions? + } + + // Invalidate this region. + const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); + + bool CausesPointerEscape = false; + RegionAndSymbolInvalidationTraits ITraits; + // Invalidate and escape only indirect regions accessible through the source + // buffer. + if (IsSourceBuffer) { + ITraits.setTrait(R->getBaseRegion(), + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape); + CausesPointerEscape = true; + } else { + const MemRegion::Kind& K = R->getKind(); + if (K == MemRegion::FieldRegionKind) + if (Size && IsFirstBufInBound(C, state, E, Size)) { + // If destination buffer is a field region and access is in bound, + // do not invalidate its super region. + ITraits.setTrait( + R, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + } + } + + return state->invalidateRegions(R, E, C.blockCount(), LCtx, + CausesPointerEscape, nullptr, nullptr, + &ITraits); + } + + // If we have a non-region value by chance, just remove the binding. + // FIXME: is this necessary or correct? This handles the non-Region + // cases. Is it ever valid to store to these? + return state->killBinding(*L); +} + +bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx, + const MemRegion *MR) { + const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR); + + switch (MR->getKind()) { + case MemRegion::FunctionCodeRegionKind: { + const NamedDecl *FD = cast<FunctionCodeRegion>(MR)->getDecl(); + if (FD) + os << "the address of the function '" << *FD << '\''; + else + os << "the address of a function"; + return true; + } + case MemRegion::BlockCodeRegionKind: + os << "block text"; + return true; + case MemRegion::BlockDataRegionKind: + os << "a block"; + return true; + case MemRegion::CXXThisRegionKind: + case MemRegion::CXXTempObjectRegionKind: + os << "a C++ temp object of type " << TVR->getValueType().getAsString(); + return true; + case MemRegion::VarRegionKind: + os << "a variable of type" << TVR->getValueType().getAsString(); + return true; + case MemRegion::FieldRegionKind: + os << "a field of type " << TVR->getValueType().getAsString(); + return true; + case MemRegion::ObjCIvarRegionKind: + os << "an instance variable of type " << TVR->getValueType().getAsString(); + return true; + default: + return false; + } +} + +bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal, + const Expr *Size, CheckerContext &C, + ProgramStateRef &State) { + SVal MemVal = C.getSVal(DstBuffer); + SVal SizeVal = C.getSVal(Size); + const MemRegion *MR = MemVal.getAsRegion(); + if (!MR) + return false; + + // We're about to model memset by producing a "default binding" in the Store. + // Our current implementation - RegionStore - doesn't support default bindings + // that don't cover the whole base region. So we should first get the offset + // and the base region to figure out whether the offset of buffer is 0. + RegionOffset Offset = MR->getAsOffset(); + const MemRegion *BR = Offset.getRegion(); + + Optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>(); + if (!SizeNL) + return false; + + SValBuilder &svalBuilder = C.getSValBuilder(); + ASTContext &Ctx = C.getASTContext(); + + // void *memset(void *dest, int ch, size_t count); + // For now we can only handle the case of offset is 0 and concrete char value. + if (Offset.isValid() && !Offset.hasSymbolicOffset() && + Offset.getOffset() == 0) { + // Get the base region's extent. + auto *SubReg = cast<SubRegion>(BR); + DefinedOrUnknownSVal Extent = SubReg->getExtent(svalBuilder); + + ProgramStateRef StateWholeReg, StateNotWholeReg; + std::tie(StateWholeReg, StateNotWholeReg) = + State->assume(svalBuilder.evalEQ(State, Extent, *SizeNL)); + + // With the semantic of 'memset()', we should convert the CharVal to + // unsigned char. + CharVal = svalBuilder.evalCast(CharVal, Ctx.UnsignedCharTy, Ctx.IntTy); + + ProgramStateRef StateNullChar, StateNonNullChar; + std::tie(StateNullChar, StateNonNullChar) = + assumeZero(C, State, CharVal, Ctx.UnsignedCharTy); + + if (StateWholeReg && !StateNotWholeReg && StateNullChar && + !StateNonNullChar) { + // If the 'memset()' acts on the whole region of destination buffer and + // the value of the second argument of 'memset()' is zero, bind the second + // argument's value to the destination buffer with 'default binding'. + // FIXME: Since there is no perfect way to bind the non-zero character, we + // can only deal with zero value here. In the future, we need to deal with + // the binding of non-zero value in the case of whole region. + State = State->bindDefaultZero(svalBuilder.makeLoc(BR), + C.getLocationContext()); + } else { + // If the destination buffer's extent is not equal to the value of + // third argument, just invalidate buffer. + State = InvalidateBuffer(C, State, DstBuffer, MemVal, + /*IsSourceBuffer*/ false, Size); + } + + if (StateNullChar && !StateNonNullChar) { + // If the value of the second argument of 'memset()' is zero, set the + // string length of destination buffer to 0 directly. + State = setCStringLength(State, MR, + svalBuilder.makeZeroVal(Ctx.getSizeType())); + } else if (!StateNullChar && StateNonNullChar) { + SVal NewStrLen = svalBuilder.getMetadataSymbolVal( + CStringChecker::getTag(), MR, DstBuffer, Ctx.getSizeType(), + C.getLocationContext(), C.blockCount()); + + // If the value of second argument is not zero, then the string length + // is at least the size argument. + SVal NewStrLenGESize = svalBuilder.evalBinOp( + State, BO_GE, NewStrLen, SizeVal, svalBuilder.getConditionType()); + + State = setCStringLength( + State->assume(NewStrLenGESize.castAs<DefinedOrUnknownSVal>(), true), + MR, NewStrLen); + } + } else { + // If the offset is not zero and char value is not concrete, we can do + // nothing but invalidate the buffer. + State = InvalidateBuffer(C, State, DstBuffer, MemVal, + /*IsSourceBuffer*/ false, Size); + } + return true; +} + +//===----------------------------------------------------------------------===// +// evaluation of individual function calls. +//===----------------------------------------------------------------------===// + +void CStringChecker::evalCopyCommon(CheckerContext &C, + const CallExpr *CE, + ProgramStateRef state, + const Expr *Size, const Expr *Dest, + const Expr *Source, bool Restricted, + bool IsMempcpy) const { + CurrentFunctionDescription = "memory copy function"; + + // See if the size argument is zero. + const LocationContext *LCtx = C.getLocationContext(); + SVal sizeVal = state->getSVal(Size, LCtx); + QualType sizeTy = Size->getType(); + + ProgramStateRef stateZeroSize, stateNonZeroSize; + std::tie(stateZeroSize, stateNonZeroSize) = + assumeZero(C, state, sizeVal, sizeTy); + + // Get the value of the Dest. + SVal destVal = state->getSVal(Dest, LCtx); + + // If the size is zero, there won't be any actual memory access, so + // just bind the return value to the destination buffer and return. + if (stateZeroSize && !stateNonZeroSize) { + stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, destVal); + C.addTransition(stateZeroSize); + return; + } + + // If the size can be nonzero, we have to check the other arguments. + if (stateNonZeroSize) { + state = stateNonZeroSize; + + // Ensure the destination is not null. If it is NULL there will be a + // NULL pointer dereference. + state = checkNonNull(C, state, Dest, destVal); + if (!state) + return; + + // Get the value of the Src. + SVal srcVal = state->getSVal(Source, LCtx); + + // Ensure the source is not null. If it is NULL there will be a + // NULL pointer dereference. + state = checkNonNull(C, state, Source, srcVal); + if (!state) + return; + + // Ensure the accesses are valid and that the buffers do not overlap. + const char * const writeWarning = + "Memory copy function overflows destination buffer"; + state = CheckBufferAccess(C, state, Size, Dest, Source, + writeWarning, /* sourceWarning = */ nullptr); + if (Restricted) + state = CheckOverlap(C, state, Size, Dest, Source); + + if (!state) + return; + + // If this is mempcpy, get the byte after the last byte copied and + // bind the expr. + if (IsMempcpy) { + // Get the byte after the last byte copied. + SValBuilder &SvalBuilder = C.getSValBuilder(); + ASTContext &Ctx = SvalBuilder.getContext(); + QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); + SVal DestRegCharVal = + SvalBuilder.evalCast(destVal, CharPtrTy, Dest->getType()); + SVal lastElement = C.getSValBuilder().evalBinOp( + state, BO_Add, DestRegCharVal, sizeVal, Dest->getType()); + // If we don't know how much we copied, we can at least + // conjure a return value for later. + if (lastElement.isUnknown()) + lastElement = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx, + C.blockCount()); + + // The byte after the last byte copied is the return value. + state = state->BindExpr(CE, LCtx, lastElement); + } else { + // All other copies return the destination buffer. + // (Well, bcopy() has a void return type, but this won't hurt.) + state = state->BindExpr(CE, LCtx, destVal); + } + + // Invalidate the destination (regular invalidation without pointer-escaping + // the address of the top-level region). + // FIXME: Even if we can't perfectly model the copy, we should see if we + // can use LazyCompoundVals to copy the source values into the destination. + // This would probably remove any existing bindings past the end of the + // copied region, but that's still an improvement over blank invalidation. + state = InvalidateBuffer(C, state, Dest, C.getSVal(Dest), + /*IsSourceBuffer*/false, Size); + + // Invalidate the source (const-invalidation without const-pointer-escaping + // the address of the top-level region). + state = InvalidateBuffer(C, state, Source, C.getSVal(Source), + /*IsSourceBuffer*/true, nullptr); + + C.addTransition(state); + } +} + + +void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + // void *memcpy(void *restrict dst, const void *restrict src, size_t n); + // The return value is the address of the destination buffer. + const Expr *Dest = CE->getArg(0); + ProgramStateRef state = C.getState(); + + evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true); +} + +void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + // void *mempcpy(void *restrict dst, const void *restrict src, size_t n); + // The return value is a pointer to the byte following the last written byte. + const Expr *Dest = CE->getArg(0); + ProgramStateRef state = C.getState(); + + evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true, true); +} + +void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + // void *memmove(void *dst, const void *src, size_t n); + // The return value is the address of the destination buffer. + const Expr *Dest = CE->getArg(0); + ProgramStateRef state = C.getState(); + + evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1)); +} + +void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + // void bcopy(const void *src, void *dst, size_t n); + evalCopyCommon(C, CE, C.getState(), + CE->getArg(2), CE->getArg(1), CE->getArg(0)); +} + +void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + // int memcmp(const void *s1, const void *s2, size_t n); + CurrentFunctionDescription = "memory comparison function"; + + const Expr *Left = CE->getArg(0); + const Expr *Right = CE->getArg(1); + const Expr *Size = CE->getArg(2); + + ProgramStateRef state = C.getState(); + SValBuilder &svalBuilder = C.getSValBuilder(); + + // See if the size argument is zero. + const LocationContext *LCtx = C.getLocationContext(); + SVal sizeVal = state->getSVal(Size, LCtx); + QualType sizeTy = Size->getType(); + + ProgramStateRef stateZeroSize, stateNonZeroSize; + std::tie(stateZeroSize, stateNonZeroSize) = + assumeZero(C, state, sizeVal, sizeTy); + + // If the size can be zero, the result will be 0 in that case, and we don't + // have to check either of the buffers. + if (stateZeroSize) { + state = stateZeroSize; + state = state->BindExpr(CE, LCtx, + svalBuilder.makeZeroVal(CE->getType())); + C.addTransition(state); + } + + // If the size can be nonzero, we have to check the other arguments. + if (stateNonZeroSize) { + state = stateNonZeroSize; + // If we know the two buffers are the same, we know the result is 0. + // First, get the two buffers' addresses. Another checker will have already + // made sure they're not undefined. + DefinedOrUnknownSVal LV = + state->getSVal(Left, LCtx).castAs<DefinedOrUnknownSVal>(); + DefinedOrUnknownSVal RV = + state->getSVal(Right, LCtx).castAs<DefinedOrUnknownSVal>(); + + // See if they are the same. + DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV); + ProgramStateRef StSameBuf, StNotSameBuf; + std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf); + + // If the two arguments might be the same buffer, we know the result is 0, + // and we only need to check one size. + if (StSameBuf) { + state = StSameBuf; + state = CheckBufferAccess(C, state, Size, Left); + if (state) { + state = StSameBuf->BindExpr(CE, LCtx, + svalBuilder.makeZeroVal(CE->getType())); + C.addTransition(state); + } + } + + // If the two arguments might be different buffers, we have to check the + // size of both of them. + if (StNotSameBuf) { + state = StNotSameBuf; + state = CheckBufferAccess(C, state, Size, Left, Right); + if (state) { + // The return value is the comparison result, which we don't know. + SVal CmpV = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, + C.blockCount()); + state = state->BindExpr(CE, LCtx, CmpV); + C.addTransition(state); + } + } + } +} + +void CStringChecker::evalstrLength(CheckerContext &C, + const CallExpr *CE) const { + if (CE->getNumArgs() < 1) + return; + + // size_t strlen(const char *s); + evalstrLengthCommon(C, CE, /* IsStrnlen = */ false); +} + +void CStringChecker::evalstrnLength(CheckerContext &C, + const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + + // size_t strnlen(const char *s, size_t maxlen); + evalstrLengthCommon(C, CE, /* IsStrnlen = */ true); +} + +void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE, + bool IsStrnlen) const { + CurrentFunctionDescription = "string length function"; + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + + if (IsStrnlen) { + const Expr *maxlenExpr = CE->getArg(1); + SVal maxlenVal = state->getSVal(maxlenExpr, LCtx); + + ProgramStateRef stateZeroSize, stateNonZeroSize; + std::tie(stateZeroSize, stateNonZeroSize) = + assumeZero(C, state, maxlenVal, maxlenExpr->getType()); + + // If the size can be zero, the result will be 0 in that case, and we don't + // have to check the string itself. + if (stateZeroSize) { + SVal zero = C.getSValBuilder().makeZeroVal(CE->getType()); + stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, zero); + C.addTransition(stateZeroSize); + } + + // If the size is GUARANTEED to be zero, we're done! + if (!stateNonZeroSize) + return; + + // Otherwise, record the assumption that the size is nonzero. + state = stateNonZeroSize; + } + + // Check that the string argument is non-null. + const Expr *Arg = CE->getArg(0); + SVal ArgVal = state->getSVal(Arg, LCtx); + + state = checkNonNull(C, state, Arg, ArgVal); + + if (!state) + return; + + SVal strLength = getCStringLength(C, state, Arg, ArgVal); + + // If the argument isn't a valid C string, there's no valid state to + // transition to. + if (strLength.isUndef()) + return; + + DefinedOrUnknownSVal result = UnknownVal(); + + // If the check is for strnlen() then bind the return value to no more than + // the maxlen value. + if (IsStrnlen) { + QualType cmpTy = C.getSValBuilder().getConditionType(); + + // It's a little unfortunate to be getting this again, + // but it's not that expensive... + const Expr *maxlenExpr = CE->getArg(1); + SVal maxlenVal = state->getSVal(maxlenExpr, LCtx); + + Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); + Optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>(); + + if (strLengthNL && maxlenValNL) { + ProgramStateRef stateStringTooLong, stateStringNotTooLong; + + // Check if the strLength is greater than the maxlen. + std::tie(stateStringTooLong, stateStringNotTooLong) = state->assume( + C.getSValBuilder() + .evalBinOpNN(state, BO_GT, *strLengthNL, *maxlenValNL, cmpTy) + .castAs<DefinedOrUnknownSVal>()); + + if (stateStringTooLong && !stateStringNotTooLong) { + // If the string is longer than maxlen, return maxlen. + result = *maxlenValNL; + } else if (stateStringNotTooLong && !stateStringTooLong) { + // If the string is shorter than maxlen, return its length. + result = *strLengthNL; + } + } + + if (result.isUnknown()) { + // If we don't have enough information for a comparison, there's + // no guarantee the full string length will actually be returned. + // All we know is the return value is the min of the string length + // and the limit. This is better than nothing. + result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx, + C.blockCount()); + NonLoc resultNL = result.castAs<NonLoc>(); + + if (strLengthNL) { + state = state->assume(C.getSValBuilder().evalBinOpNN( + state, BO_LE, resultNL, *strLengthNL, cmpTy) + .castAs<DefinedOrUnknownSVal>(), true); + } + + if (maxlenValNL) { + state = state->assume(C.getSValBuilder().evalBinOpNN( + state, BO_LE, resultNL, *maxlenValNL, cmpTy) + .castAs<DefinedOrUnknownSVal>(), true); + } + } + + } else { + // This is a plain strlen(), not strnlen(). + result = strLength.castAs<DefinedOrUnknownSVal>(); + + // If we don't know the length of the string, conjure a return + // value, so it can be used in constraints, at least. + if (result.isUnknown()) { + result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx, + C.blockCount()); + } + } + + // Bind the return value. + assert(!result.isUnknown() && "Should have conjured a value by now"); + state = state->BindExpr(CE, LCtx, result); + C.addTransition(state); +} + +void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + + // char *strcpy(char *restrict dst, const char *restrict src); + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, + /* isBounded = */ false, + /* isAppending = */ false); +} + +void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + // char *strncpy(char *restrict dst, const char *restrict src, size_t n); + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, + /* isBounded = */ true, + /* isAppending = */ false); +} + +void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + + // char *stpcpy(char *restrict dst, const char *restrict src); + evalStrcpyCommon(C, CE, + /* returnEnd = */ true, + /* isBounded = */ false, + /* isAppending = */ false); +} + +void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + // char *strlcpy(char *dst, const char *src, size_t n); + evalStrcpyCommon(C, CE, + /* returnEnd = */ true, + /* isBounded = */ true, + /* isAppending = */ false, + /* returnPtr = */ false); +} + +void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + + //char *strcat(char *restrict s1, const char *restrict s2); + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, + /* isBounded = */ false, + /* isAppending = */ true); +} + +void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + //char *strncat(char *restrict s1, const char *restrict s2, size_t n); + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, + /* isBounded = */ true, + /* isAppending = */ true); +} + +void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + //char *strlcat(char *s1, const char *s2, size_t n); + evalStrcpyCommon(C, CE, + /* returnEnd = */ false, + /* isBounded = */ true, + /* isAppending = */ true, + /* returnPtr = */ false); +} + +void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, + bool returnEnd, bool isBounded, + bool isAppending, bool returnPtr) const { + CurrentFunctionDescription = "string copy function"; + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + + // Check that the destination is non-null. + const Expr *Dst = CE->getArg(0); + SVal DstVal = state->getSVal(Dst, LCtx); + + state = checkNonNull(C, state, Dst, DstVal); + if (!state) + return; + + // Check that the source is non-null. + const Expr *srcExpr = CE->getArg(1); + SVal srcVal = state->getSVal(srcExpr, LCtx); + state = checkNonNull(C, state, srcExpr, srcVal); + if (!state) + return; + + // Get the string length of the source. + SVal strLength = getCStringLength(C, state, srcExpr, srcVal); + + // If the source isn't a valid C string, give up. + if (strLength.isUndef()) + return; + + SValBuilder &svalBuilder = C.getSValBuilder(); + QualType cmpTy = svalBuilder.getConditionType(); + QualType sizeTy = svalBuilder.getContext().getSizeType(); + + // These two values allow checking two kinds of errors: + // - actual overflows caused by a source that doesn't fit in the destination + // - potential overflows caused by a bound that could exceed the destination + SVal amountCopied = UnknownVal(); + SVal maxLastElementIndex = UnknownVal(); + const char *boundWarning = nullptr; + + state = CheckOverlap(C, state, isBounded ? CE->getArg(2) : CE->getArg(1), Dst, srcExpr); + + if (!state) + return; + + // If the function is strncpy, strncat, etc... it is bounded. + if (isBounded) { + // Get the max number of characters to copy. + const Expr *lenExpr = CE->getArg(2); + SVal lenVal = state->getSVal(lenExpr, LCtx); + + // Protect against misdeclared strncpy(). + lenVal = svalBuilder.evalCast(lenVal, sizeTy, lenExpr->getType()); + + Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>(); + Optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>(); + + // If we know both values, we might be able to figure out how much + // we're copying. + if (strLengthNL && lenValNL) { + ProgramStateRef stateSourceTooLong, stateSourceNotTooLong; + + // Check if the max number to copy is less than the length of the src. + // If the bound is equal to the source length, strncpy won't null- + // terminate the result! + std::tie(stateSourceTooLong, stateSourceNotTooLong) = state->assume( + svalBuilder.evalBinOpNN(state, BO_GE, *strLengthNL, *lenValNL, cmpTy) + .castAs<DefinedOrUnknownSVal>()); + + if (stateSourceTooLong && !stateSourceNotTooLong) { + // Max number to copy is less than the length of the src, so the actual + // strLength copied is the max number arg. + state = stateSourceTooLong; + amountCopied = lenVal; + + } else if (!stateSourceTooLong && stateSourceNotTooLong) { + // The source buffer entirely fits in the bound. + state = stateSourceNotTooLong; + amountCopied = strLength; + } + } + + // We still want to know if the bound is known to be too large. + if (lenValNL) { + if (isAppending) { + // For strncat, the check is strlen(dst) + lenVal < sizeof(dst) + + // Get the string length of the destination. If the destination is + // memory that can't have a string length, we shouldn't be copying + // into it anyway. + SVal dstStrLength = getCStringLength(C, state, Dst, DstVal); + if (dstStrLength.isUndef()) + return; + + if (Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>()) { + maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Add, + *lenValNL, + *dstStrLengthNL, + sizeTy); + boundWarning = "Size argument is greater than the free space in the " + "destination buffer"; + } + + } else { + // For strncpy, this is just checking that lenVal <= sizeof(dst) + // (Yes, strncpy and strncat differ in how they treat termination. + // strncat ALWAYS terminates, but strncpy doesn't.) + + // We need a special case for when the copy size is zero, in which + // case strncpy will do no work at all. Our bounds check uses n-1 + // as the last element accessed, so n == 0 is problematic. + ProgramStateRef StateZeroSize, StateNonZeroSize; + std::tie(StateZeroSize, StateNonZeroSize) = + assumeZero(C, state, *lenValNL, sizeTy); + + // If the size is known to be zero, we're done. + if (StateZeroSize && !StateNonZeroSize) { + if (returnPtr) { + StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal); + } else { + StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, *lenValNL); + } + C.addTransition(StateZeroSize); + return; + } + + // Otherwise, go ahead and figure out the last element we'll touch. + // We don't record the non-zero assumption here because we can't + // be sure. We won't warn on a possible zero. + NonLoc one = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>(); + maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL, + one, sizeTy); + boundWarning = "Size argument is greater than the length of the " + "destination buffer"; + } + } + + // If we couldn't pin down the copy length, at least bound it. + // FIXME: We should actually run this code path for append as well, but + // right now it creates problems with constraints (since we can end up + // trying to pass constraints from symbol to symbol). + if (amountCopied.isUnknown() && !isAppending) { + // Try to get a "hypothetical" string length symbol, which we can later + // set as a real value if that turns out to be the case. + amountCopied = getCStringLength(C, state, lenExpr, srcVal, true); + assert(!amountCopied.isUndef()); + + if (Optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>()) { + if (lenValNL) { + // amountCopied <= lenVal + SVal copiedLessThanBound = svalBuilder.evalBinOpNN(state, BO_LE, + *amountCopiedNL, + *lenValNL, + cmpTy); + state = state->assume( + copiedLessThanBound.castAs<DefinedOrUnknownSVal>(), true); + if (!state) + return; + } + + if (strLengthNL) { + // amountCopied <= strlen(source) + SVal copiedLessThanSrc = svalBuilder.evalBinOpNN(state, BO_LE, + *amountCopiedNL, + *strLengthNL, + cmpTy); + state = state->assume( + copiedLessThanSrc.castAs<DefinedOrUnknownSVal>(), true); + if (!state) + return; + } + } + } + + } else { + // The function isn't bounded. The amount copied should match the length + // of the source buffer. + amountCopied = strLength; + } + + assert(state); + + // This represents the number of characters copied into the destination + // buffer. (It may not actually be the strlen if the destination buffer + // is not terminated.) + SVal finalStrLength = UnknownVal(); + + // If this is an appending function (strcat, strncat...) then set the + // string length to strlen(src) + strlen(dst) since the buffer will + // ultimately contain both. + if (isAppending) { + // Get the string length of the destination. If the destination is memory + // that can't have a string length, we shouldn't be copying into it anyway. + SVal dstStrLength = getCStringLength(C, state, Dst, DstVal); + if (dstStrLength.isUndef()) + return; + + Optional<NonLoc> srcStrLengthNL = amountCopied.getAs<NonLoc>(); + Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>(); + + // If we know both string lengths, we might know the final string length. + if (srcStrLengthNL && dstStrLengthNL) { + // Make sure the two lengths together don't overflow a size_t. + state = checkAdditionOverflow(C, state, *srcStrLengthNL, *dstStrLengthNL); + if (!state) + return; + + finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *srcStrLengthNL, + *dstStrLengthNL, sizeTy); + } + + // If we couldn't get a single value for the final string length, + // we can at least bound it by the individual lengths. + if (finalStrLength.isUnknown()) { + // Try to get a "hypothetical" string length symbol, which we can later + // set as a real value if that turns out to be the case. + finalStrLength = getCStringLength(C, state, CE, DstVal, true); + assert(!finalStrLength.isUndef()); + + if (Optional<NonLoc> finalStrLengthNL = finalStrLength.getAs<NonLoc>()) { + if (srcStrLengthNL) { + // finalStrLength >= srcStrLength + SVal sourceInResult = svalBuilder.evalBinOpNN(state, BO_GE, + *finalStrLengthNL, + *srcStrLengthNL, + cmpTy); + state = state->assume(sourceInResult.castAs<DefinedOrUnknownSVal>(), + true); + if (!state) + return; + } + + if (dstStrLengthNL) { + // finalStrLength >= dstStrLength + SVal destInResult = svalBuilder.evalBinOpNN(state, BO_GE, + *finalStrLengthNL, + *dstStrLengthNL, + cmpTy); + state = + state->assume(destInResult.castAs<DefinedOrUnknownSVal>(), true); + if (!state) + return; + } + } + } + + } else { + // Otherwise, this is a copy-over function (strcpy, strncpy, ...), and + // the final string length will match the input string length. + finalStrLength = amountCopied; + } + + SVal Result; + + if (returnPtr) { + // The final result of the function will either be a pointer past the last + // copied element, or a pointer to the start of the destination buffer. + Result = (returnEnd ? UnknownVal() : DstVal); + } else { + Result = finalStrLength; + } + + assert(state); + + // If the destination is a MemRegion, try to check for a buffer overflow and + // record the new string length. + if (Optional<loc::MemRegionVal> dstRegVal = + DstVal.getAs<loc::MemRegionVal>()) { + QualType ptrTy = Dst->getType(); + + // If we have an exact value on a bounded copy, use that to check for + // overflows, rather than our estimate about how much is actually copied. + if (boundWarning) { + if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) { + SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, + *maxLastNL, ptrTy); + state = CheckLocation(C, state, CE->getArg(2), maxLastElement, + boundWarning); + if (!state) + return; + } + } + + // Then, if the final length is known... + if (Optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) { + SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal, + *knownStrLength, ptrTy); + + // ...and we haven't checked the bound, we'll check the actual copy. + if (!boundWarning) { + const char * const warningMsg = + "String copy function overflows destination buffer"; + state = CheckLocation(C, state, Dst, lastElement, warningMsg); + if (!state) + return; + } + + // If this is a stpcpy-style copy, the last element is the return value. + if (returnPtr && returnEnd) + Result = lastElement; + } + + // Invalidate the destination (regular invalidation without pointer-escaping + // the address of the top-level region). This must happen before we set the + // C string length because invalidation will clear the length. + // FIXME: Even if we can't perfectly model the copy, we should see if we + // can use LazyCompoundVals to copy the source values into the destination. + // This would probably remove any existing bindings past the end of the + // string, but that's still an improvement over blank invalidation. + state = InvalidateBuffer(C, state, Dst, *dstRegVal, + /*IsSourceBuffer*/false, nullptr); + + // Invalidate the source (const-invalidation without const-pointer-escaping + // the address of the top-level region). + state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true, + nullptr); + + // Set the C string length of the destination, if we know it. + if (isBounded && !isAppending) { + // strncpy is annoying in that it doesn't guarantee to null-terminate + // the result string. If the original string didn't fit entirely inside + // the bound (including the null-terminator), we don't know how long the + // result is. + if (amountCopied != strLength) + finalStrLength = UnknownVal(); + } + state = setCStringLength(state, dstRegVal->getRegion(), finalStrLength); + } + + assert(state); + + if (returnPtr) { + // If this is a stpcpy-style copy, but we were unable to check for a buffer + // overflow, we still need a result. Conjure a return value. + if (returnEnd && Result.isUnknown()) { + Result = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); + } + } + // Set the return value. + state = state->BindExpr(CE, LCtx, Result); + C.addTransition(state); +} + +void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + + //int strcmp(const char *s1, const char *s2); + evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ false); +} + +void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + //int strncmp(const char *s1, const char *s2, size_t n); + evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ false); +} + +void CStringChecker::evalStrcasecmp(CheckerContext &C, + const CallExpr *CE) const { + if (CE->getNumArgs() < 2) + return; + + //int strcasecmp(const char *s1, const char *s2); + evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true); +} + +void CStringChecker::evalStrncasecmp(CheckerContext &C, + const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + //int strncasecmp(const char *s1, const char *s2, size_t n); + evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ true); +} + +void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE, + bool isBounded, bool ignoreCase) const { + CurrentFunctionDescription = "string comparison function"; + ProgramStateRef state = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + + // Check that the first string is non-null + const Expr *s1 = CE->getArg(0); + SVal s1Val = state->getSVal(s1, LCtx); + state = checkNonNull(C, state, s1, s1Val); + if (!state) + return; + + // Check that the second string is non-null. + const Expr *s2 = CE->getArg(1); + SVal s2Val = state->getSVal(s2, LCtx); + state = checkNonNull(C, state, s2, s2Val); + if (!state) + return; + + // Get the string length of the first string or give up. + SVal s1Length = getCStringLength(C, state, s1, s1Val); + if (s1Length.isUndef()) + return; + + // Get the string length of the second string or give up. + SVal s2Length = getCStringLength(C, state, s2, s2Val); + if (s2Length.isUndef()) + return; + + // If we know the two buffers are the same, we know the result is 0. + // First, get the two buffers' addresses. Another checker will have already + // made sure they're not undefined. + DefinedOrUnknownSVal LV = s1Val.castAs<DefinedOrUnknownSVal>(); + DefinedOrUnknownSVal RV = s2Val.castAs<DefinedOrUnknownSVal>(); + + // See if they are the same. + SValBuilder &svalBuilder = C.getSValBuilder(); + DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV); + ProgramStateRef StSameBuf, StNotSameBuf; + std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf); + + // If the two arguments might be the same buffer, we know the result is 0, + // and we only need to check one size. + if (StSameBuf) { + StSameBuf = StSameBuf->BindExpr(CE, LCtx, + svalBuilder.makeZeroVal(CE->getType())); + C.addTransition(StSameBuf); + + // If the two arguments are GUARANTEED to be the same, we're done! + if (!StNotSameBuf) + return; + } + + assert(StNotSameBuf); + state = StNotSameBuf; + + // At this point we can go about comparing the two buffers. + // For now, we only do this if they're both known string literals. + + // Attempt to extract string literals from both expressions. + const StringLiteral *s1StrLiteral = getCStringLiteral(C, state, s1, s1Val); + const StringLiteral *s2StrLiteral = getCStringLiteral(C, state, s2, s2Val); + bool canComputeResult = false; + SVal resultVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, + C.blockCount()); + + if (s1StrLiteral && s2StrLiteral) { + StringRef s1StrRef = s1StrLiteral->getString(); + StringRef s2StrRef = s2StrLiteral->getString(); + + if (isBounded) { + // Get the max number of characters to compare. + const Expr *lenExpr = CE->getArg(2); + SVal lenVal = state->getSVal(lenExpr, LCtx); + + // If the length is known, we can get the right substrings. + if (const llvm::APSInt *len = svalBuilder.getKnownValue(state, lenVal)) { + // Create substrings of each to compare the prefix. + s1StrRef = s1StrRef.substr(0, (size_t)len->getZExtValue()); + s2StrRef = s2StrRef.substr(0, (size_t)len->getZExtValue()); + canComputeResult = true; + } + } else { + // This is a normal, unbounded strcmp. + canComputeResult = true; + } + + if (canComputeResult) { + // Real strcmp stops at null characters. + size_t s1Term = s1StrRef.find('\0'); + if (s1Term != StringRef::npos) + s1StrRef = s1StrRef.substr(0, s1Term); + + size_t s2Term = s2StrRef.find('\0'); + if (s2Term != StringRef::npos) + s2StrRef = s2StrRef.substr(0, s2Term); + + // Use StringRef's comparison methods to compute the actual result. + int compareRes = ignoreCase ? s1StrRef.compare_lower(s2StrRef) + : s1StrRef.compare(s2StrRef); + + // The strcmp function returns an integer greater than, equal to, or less + // than zero, [c11, p7.24.4.2]. + if (compareRes == 0) { + resultVal = svalBuilder.makeIntVal(compareRes, CE->getType()); + } + else { + DefinedSVal zeroVal = svalBuilder.makeIntVal(0, CE->getType()); + // Constrain strcmp's result range based on the result of StringRef's + // comparison methods. + BinaryOperatorKind op = (compareRes == 1) ? BO_GT : BO_LT; + SVal compareWithZero = + svalBuilder.evalBinOp(state, op, resultVal, zeroVal, + svalBuilder.getConditionType()); + DefinedSVal compareWithZeroVal = compareWithZero.castAs<DefinedSVal>(); + state = state->assume(compareWithZeroVal, true); + } + } + } + + state = state->BindExpr(CE, LCtx, resultVal); + + // Record this as a possible path. + C.addTransition(state); +} + +void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const { + //char *strsep(char **stringp, const char *delim); + if (CE->getNumArgs() < 2) + return; + + // Sanity: does the search string parameter match the return type? + const Expr *SearchStrPtr = CE->getArg(0); + QualType CharPtrTy = SearchStrPtr->getType()->getPointeeType(); + if (CharPtrTy.isNull() || + CE->getType().getUnqualifiedType() != CharPtrTy.getUnqualifiedType()) + return; + + CurrentFunctionDescription = "strsep()"; + ProgramStateRef State = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + + // Check that the search string pointer is non-null (though it may point to + // a null string). + SVal SearchStrVal = State->getSVal(SearchStrPtr, LCtx); + State = checkNonNull(C, State, SearchStrPtr, SearchStrVal); + if (!State) + return; + + // Check that the delimiter string is non-null. + const Expr *DelimStr = CE->getArg(1); + SVal DelimStrVal = State->getSVal(DelimStr, LCtx); + State = checkNonNull(C, State, DelimStr, DelimStrVal); + if (!State) + return; + + SValBuilder &SVB = C.getSValBuilder(); + SVal Result; + if (Optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) { + // Get the current value of the search string pointer, as a char*. + Result = State->getSVal(*SearchStrLoc, CharPtrTy); + + // Invalidate the search string, representing the change of one delimiter + // character to NUL. + State = InvalidateBuffer(C, State, SearchStrPtr, Result, + /*IsSourceBuffer*/false, nullptr); + + // Overwrite the search string pointer. The new value is either an address + // further along in the same string, or NULL if there are no more tokens. + State = State->bindLoc(*SearchStrLoc, + SVB.conjureSymbolVal(getTag(), + CE, + LCtx, + CharPtrTy, + C.blockCount()), + LCtx); + } else { + assert(SearchStrVal.isUnknown()); + // Conjure a symbolic value. It's the best we can do. + Result = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); + } + + // Set the return value, and finish. + State = State->BindExpr(CE, LCtx, Result); + C.addTransition(State); +} + +// These should probably be moved into a C++ standard library checker. +void CStringChecker::evalStdCopy(CheckerContext &C, const CallExpr *CE) const { + evalStdCopyCommon(C, CE); +} + +void CStringChecker::evalStdCopyBackward(CheckerContext &C, + const CallExpr *CE) const { + evalStdCopyCommon(C, CE); +} + +void CStringChecker::evalStdCopyCommon(CheckerContext &C, + const CallExpr *CE) const { + if (CE->getNumArgs() < 3) + return; + + ProgramStateRef State = C.getState(); + + const LocationContext *LCtx = C.getLocationContext(); + + // template <class _InputIterator, class _OutputIterator> + // _OutputIterator + // copy(_InputIterator __first, _InputIterator __last, + // _OutputIterator __result) + + // Invalidate the destination buffer + const Expr *Dst = CE->getArg(2); + SVal DstVal = State->getSVal(Dst, LCtx); + State = InvalidateBuffer(C, State, Dst, DstVal, /*IsSource=*/false, + /*Size=*/nullptr); + + SValBuilder &SVB = C.getSValBuilder(); + + SVal ResultVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount()); + State = State->BindExpr(CE, LCtx, ResultVal); + + C.addTransition(State); +} + +void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() != 3) + return; + + CurrentFunctionDescription = "memory set function"; + + const Expr *Mem = CE->getArg(0); + const Expr *CharE = CE->getArg(1); + const Expr *Size = CE->getArg(2); + ProgramStateRef State = C.getState(); + + // See if the size argument is zero. + const LocationContext *LCtx = C.getLocationContext(); + SVal SizeVal = State->getSVal(Size, LCtx); + QualType SizeTy = Size->getType(); + + ProgramStateRef StateZeroSize, StateNonZeroSize; + std::tie(StateZeroSize, StateNonZeroSize) = + assumeZero(C, State, SizeVal, SizeTy); + + // Get the value of the memory area. + SVal MemVal = State->getSVal(Mem, LCtx); + + // If the size is zero, there won't be any actual memory access, so + // just bind the return value to the Mem buffer and return. + if (StateZeroSize && !StateNonZeroSize) { + StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, MemVal); + C.addTransition(StateZeroSize); + return; + } + + // Ensure the memory area is not null. + // If it is NULL there will be a NULL pointer dereference. + State = checkNonNull(C, StateNonZeroSize, Mem, MemVal); + if (!State) + return; + + State = CheckBufferAccess(C, State, Size, Mem); + if (!State) + return; + + // According to the values of the arguments, bind the value of the second + // argument to the destination buffer and set string length, or just + // invalidate the destination buffer. + if (!memsetAux(Mem, C.getSVal(CharE), Size, C, State)) + return; + + State = State->BindExpr(CE, LCtx, MemVal); + C.addTransition(State); +} + +void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const { + if (CE->getNumArgs() != 2) + return; + + CurrentFunctionDescription = "memory clearance function"; + + const Expr *Mem = CE->getArg(0); + const Expr *Size = CE->getArg(1); + SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy); + + ProgramStateRef State = C.getState(); + + // See if the size argument is zero. + SVal SizeVal = C.getSVal(Size); + QualType SizeTy = Size->getType(); + + ProgramStateRef StateZeroSize, StateNonZeroSize; + std::tie(StateZeroSize, StateNonZeroSize) = + assumeZero(C, State, SizeVal, SizeTy); + + // If the size is zero, there won't be any actual memory access, + // In this case we just return. + if (StateZeroSize && !StateNonZeroSize) { + C.addTransition(StateZeroSize); + return; + } + + // Get the value of the memory area. + SVal MemVal = C.getSVal(Mem); + + // Ensure the memory area is not null. + // If it is NULL there will be a NULL pointer dereference. + State = checkNonNull(C, StateNonZeroSize, Mem, MemVal); + if (!State) + return; + + State = CheckBufferAccess(C, State, Size, Mem); + if (!State) + return; + + if (!memsetAux(Mem, Zero, Size, C, State)) + return; + + C.addTransition(State); +} + +static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) { + IdentifierInfo *II = FD->getIdentifier(); + if (!II) + return false; + + if (!AnalysisDeclContext::isInStdNamespace(FD)) + return false; + + if (II->getName().equals(Name)) + return true; + + return false; +} +//===----------------------------------------------------------------------===// +// The driver method, and other Checker callbacks. +//===----------------------------------------------------------------------===// + +static CStringChecker::FnCheck identifyCall(const CallExpr *CE, + CheckerContext &C) { + const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl) + return nullptr; + + // Pro-actively check that argument types are safe to do arithmetic upon. + // We do not want to crash if someone accidentally passes a structure + // into, say, a C++ overload of any of these functions. + if (isCPPStdLibraryFunction(FDecl, "copy")) { + if (CE->getNumArgs() < 3 || !CE->getArg(2)->getType()->isPointerType()) + return nullptr; + return &CStringChecker::evalStdCopy; + } else if (isCPPStdLibraryFunction(FDecl, "copy_backward")) { + if (CE->getNumArgs() < 3 || !CE->getArg(2)->getType()->isPointerType()) + return nullptr; + return &CStringChecker::evalStdCopyBackward; + } else { + // An umbrella check for all C library functions. + for (auto I: CE->arguments()) { + QualType T = I->getType(); + if (!T->isIntegralOrEnumerationType() && !T->isPointerType()) + return nullptr; + } + } + + // FIXME: Poorly-factored string switches are slow. + if (C.isCLibraryFunction(FDecl, "memcpy")) + return &CStringChecker::evalMemcpy; + else if (C.isCLibraryFunction(FDecl, "mempcpy")) + return &CStringChecker::evalMempcpy; + else if (C.isCLibraryFunction(FDecl, "memcmp")) + return &CStringChecker::evalMemcmp; + else if (C.isCLibraryFunction(FDecl, "memmove")) + return &CStringChecker::evalMemmove; + else if (C.isCLibraryFunction(FDecl, "memset") || + C.isCLibraryFunction(FDecl, "explicit_memset")) + return &CStringChecker::evalMemset; + else if (C.isCLibraryFunction(FDecl, "strcpy")) + return &CStringChecker::evalStrcpy; + else if (C.isCLibraryFunction(FDecl, "strncpy")) + return &CStringChecker::evalStrncpy; + else if (C.isCLibraryFunction(FDecl, "stpcpy")) + return &CStringChecker::evalStpcpy; + else if (C.isCLibraryFunction(FDecl, "strlcpy")) + return &CStringChecker::evalStrlcpy; + else if (C.isCLibraryFunction(FDecl, "strcat")) + return &CStringChecker::evalStrcat; + else if (C.isCLibraryFunction(FDecl, "strncat")) + return &CStringChecker::evalStrncat; + else if (C.isCLibraryFunction(FDecl, "strlcat")) + return &CStringChecker::evalStrlcat; + else if (C.isCLibraryFunction(FDecl, "strlen")) + return &CStringChecker::evalstrLength; + else if (C.isCLibraryFunction(FDecl, "strnlen")) + return &CStringChecker::evalstrnLength; + else if (C.isCLibraryFunction(FDecl, "strcmp")) + return &CStringChecker::evalStrcmp; + else if (C.isCLibraryFunction(FDecl, "strncmp")) + return &CStringChecker::evalStrncmp; + else if (C.isCLibraryFunction(FDecl, "strcasecmp")) + return &CStringChecker::evalStrcasecmp; + else if (C.isCLibraryFunction(FDecl, "strncasecmp")) + return &CStringChecker::evalStrncasecmp; + else if (C.isCLibraryFunction(FDecl, "strsep")) + return &CStringChecker::evalStrsep; + else if (C.isCLibraryFunction(FDecl, "bcopy")) + return &CStringChecker::evalBcopy; + else if (C.isCLibraryFunction(FDecl, "bcmp")) + return &CStringChecker::evalMemcmp; + else if (C.isCLibraryFunction(FDecl, "bzero") || + C.isCLibraryFunction(FDecl, "explicit_bzero")) + return &CStringChecker::evalBzero; + + return nullptr; +} + +bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { + + FnCheck evalFunction = identifyCall(CE, C); + + // If the callee isn't a string function, let another checker handle it. + if (!evalFunction) + return false; + + // Check and evaluate the call. + (this->*evalFunction)(C, CE); + + // If the evaluate call resulted in no change, chain to the next eval call + // handler. + // Note, the custom CString evaluation calls assume that basic safety + // properties are held. However, if the user chooses to turn off some of these + // checks, we ignore the issues and leave the call evaluation to a generic + // handler. + return C.isDifferent(); +} + +void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { + // Record string length for char a[] = "abc"; + ProgramStateRef state = C.getState(); + + for (const auto *I : DS->decls()) { + const VarDecl *D = dyn_cast<VarDecl>(I); + if (!D) + continue; + + // FIXME: Handle array fields of structs. + if (!D->getType()->isArrayType()) + continue; + + const Expr *Init = D->getInit(); + if (!Init) + continue; + if (!isa<StringLiteral>(Init)) + continue; + + Loc VarLoc = state->getLValue(D, C.getLocationContext()); + const MemRegion *MR = VarLoc.getAsRegion(); + if (!MR) + continue; + + SVal StrVal = C.getSVal(Init); + assert(StrVal.isValid() && "Initializer string is unknown or undefined"); + DefinedOrUnknownSVal strLength = + getCStringLength(C, state, Init, StrVal).castAs<DefinedOrUnknownSVal>(); + + state = state->set<CStringLength>(MR, strLength); + } + + C.addTransition(state); +} + +ProgramStateRef +CStringChecker::checkRegionChanges(ProgramStateRef state, + const InvalidatedSymbols *, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call) const { + CStringLengthTy Entries = state->get<CStringLength>(); + if (Entries.isEmpty()) + return state; + + llvm::SmallPtrSet<const MemRegion *, 8> Invalidated; + llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions; + + // First build sets for the changed regions and their super-regions. + for (ArrayRef<const MemRegion *>::iterator + I = Regions.begin(), E = Regions.end(); I != E; ++I) { + const MemRegion *MR = *I; + Invalidated.insert(MR); + + SuperRegions.insert(MR); + while (const SubRegion *SR = dyn_cast<SubRegion>(MR)) { + MR = SR->getSuperRegion(); + SuperRegions.insert(MR); + } + } + + CStringLengthTy::Factory &F = state->get_context<CStringLength>(); + + // Then loop over the entries in the current state. + for (CStringLengthTy::iterator I = Entries.begin(), + E = Entries.end(); I != E; ++I) { + const MemRegion *MR = I.getKey(); + + // Is this entry for a super-region of a changed region? + if (SuperRegions.count(MR)) { + Entries = F.remove(Entries, MR); + continue; + } + + // Is this entry for a sub-region of a changed region? + const MemRegion *Super = MR; + while (const SubRegion *SR = dyn_cast<SubRegion>(Super)) { + Super = SR->getSuperRegion(); + if (Invalidated.count(Super)) { + Entries = F.remove(Entries, MR); + break; + } + } + } + + return state->set<CStringLength>(Entries); +} + +void CStringChecker::checkLiveSymbols(ProgramStateRef state, + SymbolReaper &SR) const { + // Mark all symbols in our string length map as valid. + CStringLengthTy Entries = state->get<CStringLength>(); + + for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end(); + I != E; ++I) { + SVal Len = I.getData(); + + for (SymExpr::symbol_iterator si = Len.symbol_begin(), + se = Len.symbol_end(); si != se; ++si) + SR.markInUse(*si); + } +} + +void CStringChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + ProgramStateRef state = C.getState(); + CStringLengthTy Entries = state->get<CStringLength>(); + if (Entries.isEmpty()) + return; + + CStringLengthTy::Factory &F = state->get_context<CStringLength>(); + for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end(); + I != E; ++I) { + SVal Len = I.getData(); + if (SymbolRef Sym = Len.getAsSymbol()) { + if (SR.isDead(Sym)) + Entries = F.remove(Entries, I.getKey()); + } + } + + state = state->set<CStringLength>(Entries); + C.addTransition(state); +} + +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + CStringChecker *checker = mgr.registerChecker<CStringChecker>(); \ + checker->Filter.Check##name = true; \ + checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ + } + + REGISTER_CHECKER(CStringNullArg) + REGISTER_CHECKER(CStringOutOfBounds) + REGISTER_CHECKER(CStringBufferOverlap) +REGISTER_CHECKER(CStringNotNullTerm) + + void ento::registerCStringCheckerBasic(CheckerManager &Mgr) { + Mgr.registerChecker<CStringChecker>(); + } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp new file mode 100644 index 000000000000..bbeb41c5f3cf --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp @@ -0,0 +1,292 @@ +//== CStringSyntaxChecker.cpp - CoreFoundation containers API *- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// An AST checker that looks for common pitfalls when using C string APIs. +// - Identifies erroneous patterns in the last argument to strncat - the number +// of bytes to copy. +// +//===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Expr.h" +#include "clang/AST/OperationKinds.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/Basic/TypeTraits.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class WalkAST: public StmtVisitor<WalkAST> { + const CheckerBase *Checker; + BugReporter &BR; + AnalysisDeclContext* AC; + + /// Check if two expressions refer to the same declaration. + bool sameDecl(const Expr *A1, const Expr *A2) { + if (const auto *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts())) + if (const auto *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts())) + return D1->getDecl() == D2->getDecl(); + return false; + } + + /// Check if the expression E is a sizeof(WithArg). + bool isSizeof(const Expr *E, const Expr *WithArg) { + if (const auto *UE = dyn_cast<UnaryExprOrTypeTraitExpr>(E)) + if (UE->getKind() == UETT_SizeOf && !UE->isArgumentType()) + return sameDecl(UE->getArgumentExpr(), WithArg); + return false; + } + + /// Check if the expression E is a strlen(WithArg). + bool isStrlen(const Expr *E, const Expr *WithArg) { + if (const auto *CE = dyn_cast<CallExpr>(E)) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return false; + return (CheckerContext::isCLibraryFunction(FD, "strlen") && + sameDecl(CE->getArg(0), WithArg)); + } + return false; + } + + /// Check if the expression is an integer literal with value 1. + bool isOne(const Expr *E) { + if (const auto *IL = dyn_cast<IntegerLiteral>(E)) + return (IL->getValue().isIntN(1)); + return false; + } + + StringRef getPrintableName(const Expr *E) { + if (const auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) + return D->getDecl()->getName(); + return StringRef(); + } + + /// Identify erroneous patterns in the last argument to strncat - the number + /// of bytes to copy. + bool containsBadStrncatPattern(const CallExpr *CE); + + /// Identify erroneous patterns in the last argument to strlcpy - the number + /// of bytes to copy. + /// The bad pattern checked is when the size is known + /// to be larger than the destination can handle. + /// char dst[2]; + /// size_t cpy = 4; + /// strlcpy(dst, "abcd", sizeof("abcd") - 1); + /// strlcpy(dst, "abcd", 4); + /// strlcpy(dst + 3, "abcd", 2); + /// strlcpy(dst, "abcd", cpy); + /// Identify erroneous patterns in the last argument to strlcat - the number + /// of bytes to copy. + /// The bad pattern checked is when the last argument is basically + /// pointing to the destination buffer size or argument larger or + /// equal to. + /// char dst[2]; + /// strlcat(dst, src2, sizeof(dst)); + /// strlcat(dst, src2, 2); + /// strlcat(dst, src2, 10); + bool containsBadStrlcpyStrlcatPattern(const CallExpr *CE); + +public: + WalkAST(const CheckerBase *Checker, BugReporter &BR, AnalysisDeclContext *AC) + : Checker(Checker), BR(BR), AC(AC) {} + + // Statement visitor methods. + void VisitChildren(Stmt *S); + void VisitStmt(Stmt *S) { + VisitChildren(S); + } + void VisitCallExpr(CallExpr *CE); +}; +} // end anonymous namespace + +// The correct size argument should look like following: +// strncat(dst, src, sizeof(dst) - strlen(dest) - 1); +// We look for the following anti-patterns: +// - strncat(dst, src, sizeof(dst) - strlen(dst)); +// - strncat(dst, src, sizeof(dst) - 1); +// - strncat(dst, src, sizeof(dst)); +bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) { + if (CE->getNumArgs() != 3) + return false; + const Expr *DstArg = CE->getArg(0); + const Expr *SrcArg = CE->getArg(1); + const Expr *LenArg = CE->getArg(2); + + // Identify wrong size expressions, which are commonly used instead. + if (const auto *BE = dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) { + // - sizeof(dst) - strlen(dst) + if (BE->getOpcode() == BO_Sub) { + const Expr *L = BE->getLHS(); + const Expr *R = BE->getRHS(); + if (isSizeof(L, DstArg) && isStrlen(R, DstArg)) + return true; + + // - sizeof(dst) - 1 + if (isSizeof(L, DstArg) && isOne(R->IgnoreParenCasts())) + return true; + } + } + // - sizeof(dst) + if (isSizeof(LenArg, DstArg)) + return true; + + // - sizeof(src) + if (isSizeof(LenArg, SrcArg)) + return true; + return false; +} + +bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) { + if (CE->getNumArgs() != 3) + return false; + const FunctionDecl *FD = CE->getDirectCallee(); + bool Append = CheckerContext::isCLibraryFunction(FD, "strlcat"); + const Expr *DstArg = CE->getArg(0); + const Expr *LenArg = CE->getArg(2); + + const auto *DstArgDecl = dyn_cast<DeclRefExpr>(DstArg->IgnoreParenImpCasts()); + const auto *LenArgDecl = dyn_cast<DeclRefExpr>(LenArg->IgnoreParenLValueCasts()); + uint64_t DstOff = 0; + if (isSizeof(LenArg, DstArg)) + return false; + // - size_t dstlen = sizeof(dst) + if (LenArgDecl) { + const auto *LenArgVal = dyn_cast<VarDecl>(LenArgDecl->getDecl()); + if (LenArgVal->getInit()) + LenArg = LenArgVal->getInit(); + } + + // - integral value + // We try to figure out if the last argument is possibly longer + // than the destination can possibly handle if its size can be defined. + if (const auto *IL = dyn_cast<IntegerLiteral>(LenArg->IgnoreParenImpCasts())) { + uint64_t ILRawVal = IL->getValue().getZExtValue(); + + // Case when there is pointer arithmetic on the destination buffer + // especially when we offset from the base decreasing the + // buffer length accordingly. + if (!DstArgDecl) { + if (const auto *BE = dyn_cast<BinaryOperator>(DstArg->IgnoreParenImpCasts())) { + DstArgDecl = dyn_cast<DeclRefExpr>(BE->getLHS()->IgnoreParenImpCasts()); + if (BE->getOpcode() == BO_Add) { + if ((IL = dyn_cast<IntegerLiteral>(BE->getRHS()->IgnoreParenImpCasts()))) { + DstOff = IL->getValue().getZExtValue(); + } + } + } + } + if (DstArgDecl) { + if (const auto *Buffer = dyn_cast<ConstantArrayType>(DstArgDecl->getType())) { + ASTContext &C = BR.getContext(); + uint64_t BufferLen = C.getTypeSize(Buffer) / 8; + auto RemainingBufferLen = BufferLen - DstOff; + if (Append) { + if (RemainingBufferLen <= ILRawVal) + return true; + } else { + if (RemainingBufferLen < ILRawVal) + return true; + } + } + } + } + + return false; +} + +void WalkAST::VisitCallExpr(CallExpr *CE) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return; + + if (CheckerContext::isCLibraryFunction(FD, "strncat")) { + if (containsBadStrncatPattern(CE)) { + const Expr *DstArg = CE->getArg(0); + const Expr *LenArg = CE->getArg(2); + PathDiagnosticLocation Loc = + PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC); + + StringRef DstName = getPrintableName(DstArg); + + SmallString<256> S; + llvm::raw_svector_ostream os(S); + os << "Potential buffer overflow. "; + if (!DstName.empty()) { + os << "Replace with 'sizeof(" << DstName << ") " + "- strlen(" << DstName <<") - 1'"; + os << " or u"; + } else + os << "U"; + os << "se a safer 'strlcat' API"; + + BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument", + "C String API", os.str(), Loc, + LenArg->getSourceRange()); + } + } else if (CheckerContext::isCLibraryFunction(FD, "strlcpy") || + CheckerContext::isCLibraryFunction(FD, "strlcat")) { + if (containsBadStrlcpyStrlcatPattern(CE)) { + const Expr *DstArg = CE->getArg(0); + const Expr *LenArg = CE->getArg(2); + PathDiagnosticLocation Loc = + PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC); + + StringRef DstName = getPrintableName(DstArg); + + SmallString<256> S; + llvm::raw_svector_ostream os(S); + os << "The third argument allows to potentially copy more bytes than it should. "; + os << "Replace with the value "; + if (!DstName.empty()) + os << "sizeof(" << DstName << ")"; + else + os << "sizeof(<destination buffer>)"; + os << " or lower"; + + BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument", + "C String API", os.str(), Loc, + LenArg->getSourceRange()); + } + } + + // Recurse and check children. + VisitChildren(CE); +} + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt *Child : S->children()) + if (Child) + Visit(Child); +} + +namespace { +class CStringSyntaxChecker: public Checker<check::ASTCodeBody> { +public: + + void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, + BugReporter &BR) const { + WalkAST walker(this, BR, Mgr.getAnalysisDeclContext(D)); + walker.Visit(D->getBody()); + } +}; +} + +void ento::registerCStringSyntaxChecker(CheckerManager &mgr) { + mgr.registerChecker<CStringSyntaxChecker>(); +} + diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp new file mode 100644 index 000000000000..0b539e1188eb --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp @@ -0,0 +1,62 @@ +//=== CXXSelfAssignmentChecker.cpp -----------------------------*- 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 CXXSelfAssignmentChecker, which tests all custom defined +// copy and move assignment operators for the case of self assignment, thus +// where the parameter refers to the same location where the this pointer +// points to. The checker itself does not do any checks at all, but it +// causes the analyzer to check every copy and move assignment operator twice: +// once for when 'this' aliases with the parameter and once for when it may not. +// It is the task of the other enabled checkers to find the bugs in these two +// different cases. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { + +class CXXSelfAssignmentChecker : public Checker<check::BeginFunction> { +public: + CXXSelfAssignmentChecker(); + void checkBeginFunction(CheckerContext &C) const; +}; +} + +CXXSelfAssignmentChecker::CXXSelfAssignmentChecker() {} + +void CXXSelfAssignmentChecker::checkBeginFunction(CheckerContext &C) const { + if (!C.inTopFrame()) + return; + const auto *LCtx = C.getLocationContext(); + const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl()); + if (!MD) + return; + if (!MD->isCopyAssignmentOperator() && !MD->isMoveAssignmentOperator()) + return; + auto &State = C.getState(); + auto &SVB = C.getSValBuilder(); + auto ThisVal = + State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); + auto Param = SVB.makeLoc(State->getRegion(MD->getParamDecl(0), LCtx)); + auto ParamVal = State->getSVal(Param); + ProgramStateRef SelfAssignState = State->bindLoc(Param, ThisVal, LCtx); + C.addTransition(SelfAssignState); + ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal, LCtx); + C.addTransition(NonSelfAssignState); +} + +void ento::registerCXXSelfAssignmentChecker(CheckerManager &Mgr) { + Mgr.registerChecker<CXXSelfAssignmentChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp new file mode 100644 index 000000000000..ef30dc74c39d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -0,0 +1,620 @@ +//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines CallAndMessageChecker, a builtin checker that checks for various +// errors of call and objc message expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { + +struct ChecksFilter { + DefaultBool Check_CallAndMessageUnInitRefArg; + DefaultBool Check_CallAndMessageChecker; + + CheckName CheckName_CallAndMessageUnInitRefArg; + CheckName CheckName_CallAndMessageChecker; +}; + +class CallAndMessageChecker + : public Checker< check::PreStmt<CallExpr>, + check::PreStmt<CXXDeleteExpr>, + check::PreObjCMessage, + check::ObjCMessageNil, + check::PreCall > { + mutable std::unique_ptr<BugType> BT_call_null; + mutable std::unique_ptr<BugType> BT_call_undef; + mutable std::unique_ptr<BugType> BT_cxx_call_null; + mutable std::unique_ptr<BugType> BT_cxx_call_undef; + mutable std::unique_ptr<BugType> BT_call_arg; + mutable std::unique_ptr<BugType> BT_cxx_delete_undef; + mutable std::unique_ptr<BugType> BT_msg_undef; + mutable std::unique_ptr<BugType> BT_objc_prop_undef; + mutable std::unique_ptr<BugType> BT_objc_subscript_undef; + mutable std::unique_ptr<BugType> BT_msg_arg; + mutable std::unique_ptr<BugType> BT_msg_ret; + mutable std::unique_ptr<BugType> BT_call_few_args; + +public: + ChecksFilter Filter; + + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; + + /// Fill in the return value that results from messaging nil based on the + /// return type and architecture and diagnose if the return value will be + /// garbage. + void checkObjCMessageNil(const ObjCMethodCall &msg, CheckerContext &C) const; + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + +private: + bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange, + const Expr *ArgEx, int ArgumentNumber, + bool CheckUninitFields, const CallEvent &Call, + std::unique_ptr<BugType> &BT, + const ParmVarDecl *ParamDecl) const; + + static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE); + void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg, + ExplodedNode *N) const; + + void HandleNilReceiver(CheckerContext &C, + ProgramStateRef state, + const ObjCMethodCall &msg) const; + + void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const { + if (!BT) + BT.reset(new BuiltinBug(this, desc)); + } + bool uninitRefOrPointer(CheckerContext &C, const SVal &V, + SourceRange ArgRange, const Expr *ArgEx, + std::unique_ptr<BugType> &BT, + const ParmVarDecl *ParamDecl, const char *BD, + int ArgumentNumber) const; +}; +} // end anonymous namespace + +void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C, + const Expr *BadE) { + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N); + if (BadE) { + R->addRange(BadE->getSourceRange()); + if (BadE->isGLValue()) + BadE = bugreporter::getDerefExpr(BadE); + bugreporter::trackExpressionValue(N, BadE, *R); + } + C.emitReport(std::move(R)); +} + +static void describeUninitializedArgumentInCall(const CallEvent &Call, + int ArgumentNumber, + llvm::raw_svector_ostream &Os) { + switch (Call.getKind()) { + case CE_ObjCMessage: { + const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call); + switch (Msg.getMessageKind()) { + case OCM_Message: + Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) + << " argument in message expression is an uninitialized value"; + return; + case OCM_PropertyAccess: + assert(Msg.isSetter() && "Getters have no args"); + Os << "Argument for property setter is an uninitialized value"; + return; + case OCM_Subscript: + if (Msg.isSetter() && (ArgumentNumber == 0)) + Os << "Argument for subscript setter is an uninitialized value"; + else + Os << "Subscript index is an uninitialized value"; + return; + } + llvm_unreachable("Unknown message kind."); + } + case CE_Block: + Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) + << " block call argument is an uninitialized value"; + return; + default: + Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) + << " function call argument is an uninitialized value"; + return; + } +} + +bool CallAndMessageChecker::uninitRefOrPointer( + CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx, + std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD, + int ArgumentNumber) const { + if (!Filter.Check_CallAndMessageUnInitRefArg) + return false; + + // No parameter declaration available, i.e. variadic function argument. + if(!ParamDecl) + return false; + + // If parameter is declared as pointer to const in function declaration, + // then check if corresponding argument in function call is + // pointing to undefined symbol value (uninitialized memory). + SmallString<200> Buf; + llvm::raw_svector_ostream Os(Buf); + + if (ParamDecl->getType()->isPointerType()) { + Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) + << " function call argument is a pointer to uninitialized value"; + } else if (ParamDecl->getType()->isReferenceType()) { + Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1) + << " function call argument is an uninitialized value"; + } else + return false; + + if(!ParamDecl->getType()->getPointeeType().isConstQualified()) + return false; + + if (const MemRegion *SValMemRegion = V.getAsRegion()) { + const ProgramStateRef State = C.getState(); + const SVal PSV = State->getSVal(SValMemRegion, C.getASTContext().CharTy); + if (PSV.isUndef()) { + if (ExplodedNode *N = C.generateErrorNode()) { + LazyInit_BT(BD, BT); + auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N); + R->addRange(ArgRange); + if (ArgEx) + bugreporter::trackExpressionValue(N, ArgEx, *R); + + C.emitReport(std::move(R)); + } + return true; + } + } + return false; +} + +namespace { +class FindUninitializedField { +public: + SmallVector<const FieldDecl *, 10> FieldChain; + +private: + StoreManager &StoreMgr; + MemRegionManager &MrMgr; + Store store; + +public: + FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr, + Store s) + : StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} + + bool Find(const TypedValueRegion *R) { + QualType T = R->getValueType(); + if (const RecordType *RT = T->getAsStructureType()) { + const RecordDecl *RD = RT->getDecl()->getDefinition(); + assert(RD && "Referred record has no definition"); + for (const auto *I : RD->fields()) { + const FieldRegion *FR = MrMgr.getFieldRegion(I, R); + FieldChain.push_back(I); + T = I->getType(); + if (T->getAsStructureType()) { + if (Find(FR)) + return true; + } else { + const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR)); + if (V.isUndef()) + return true; + } + FieldChain.pop_back(); + } + } + + return false; + } +}; +} // namespace + +bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, + SVal V, + SourceRange ArgRange, + const Expr *ArgEx, + int ArgumentNumber, + bool CheckUninitFields, + const CallEvent &Call, + std::unique_ptr<BugType> &BT, + const ParmVarDecl *ParamDecl + ) const { + const char *BD = "Uninitialized argument value"; + + if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD, + ArgumentNumber)) + return true; + + if (V.isUndef()) { + if (ExplodedNode *N = C.generateErrorNode()) { + LazyInit_BT(BD, BT); + // Generate a report for this bug. + SmallString<200> Buf; + llvm::raw_svector_ostream Os(Buf); + describeUninitializedArgumentInCall(Call, ArgumentNumber, Os); + auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N); + + R->addRange(ArgRange); + if (ArgEx) + bugreporter::trackExpressionValue(N, ArgEx, *R); + C.emitReport(std::move(R)); + } + return true; + } + + if (!CheckUninitFields) + return false; + + if (auto LV = V.getAs<nonloc::LazyCompoundVal>()) { + const LazyCompoundValData *D = LV->getCVData(); + FindUninitializedField F(C.getState()->getStateManager().getStoreManager(), + C.getSValBuilder().getRegionManager(), + D->getStore()); + + if (F.Find(D->getRegion())) { + if (ExplodedNode *N = C.generateErrorNode()) { + LazyInit_BT(BD, BT); + SmallString<512> Str; + llvm::raw_svector_ostream os(Str); + os << "Passed-by-value struct argument contains uninitialized data"; + + if (F.FieldChain.size() == 1) + os << " (e.g., field: '" << *F.FieldChain[0] << "')"; + else { + os << " (e.g., via the field chain: '"; + bool first = true; + for (SmallVectorImpl<const FieldDecl *>::iterator + DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){ + if (first) + first = false; + else + os << '.'; + os << **DI; + } + os << "')"; + } + + // Generate a report for this bug. + auto R = llvm::make_unique<BugReport>(*BT, os.str(), N); + R->addRange(ArgRange); + + if (ArgEx) + bugreporter::trackExpressionValue(N, ArgEx, *R); + // FIXME: enhance track back for uninitialized value for arbitrary + // memregions + C.emitReport(std::move(R)); + } + return true; + } + } + + return false; +} + +void CallAndMessageChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const{ + + const Expr *Callee = CE->getCallee()->IgnoreParens(); + ProgramStateRef State = C.getState(); + const LocationContext *LCtx = C.getLocationContext(); + SVal L = State->getSVal(Callee, LCtx); + + if (L.isUndef()) { + if (!BT_call_undef) + BT_call_undef.reset(new BuiltinBug( + this, "Called function pointer is an uninitialized pointer value")); + emitBadCall(BT_call_undef.get(), C, Callee); + return; + } + + ProgramStateRef StNonNull, StNull; + std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>()); + + if (StNull && !StNonNull) { + if (!BT_call_null) + BT_call_null.reset(new BuiltinBug( + this, "Called function pointer is null (null dereference)")); + emitBadCall(BT_call_null.get(), C, Callee); + return; + } + + C.addTransition(StNonNull); +} + +void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE, + CheckerContext &C) const { + + SVal Arg = C.getSVal(DE->getArgument()); + if (Arg.isUndef()) { + StringRef Desc; + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + if (!BT_cxx_delete_undef) + BT_cxx_delete_undef.reset( + new BuiltinBug(this, "Uninitialized argument value")); + if (DE->isArrayFormAsWritten()) + Desc = "Argument to 'delete[]' is uninitialized"; + else + Desc = "Argument to 'delete' is uninitialized"; + BugType *BT = BT_cxx_delete_undef.get(); + auto R = llvm::make_unique<BugReport>(*BT, Desc, N); + bugreporter::trackExpressionValue(N, DE, *R); + C.emitReport(std::move(R)); + return; + } +} + +void CallAndMessageChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // If this is a call to a C++ method, check if the callee is null or + // undefined. + if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { + SVal V = CC->getCXXThisVal(); + if (V.isUndef()) { + if (!BT_cxx_call_undef) + BT_cxx_call_undef.reset( + new BuiltinBug(this, "Called C++ object pointer is uninitialized")); + emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr()); + return; + } + + ProgramStateRef StNonNull, StNull; + std::tie(StNonNull, StNull) = + State->assume(V.castAs<DefinedOrUnknownSVal>()); + + if (StNull && !StNonNull) { + if (!BT_cxx_call_null) + BT_cxx_call_null.reset( + new BuiltinBug(this, "Called C++ object pointer is null")); + emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr()); + return; + } + + State = StNonNull; + } + + const Decl *D = Call.getDecl(); + if (D && (isa<FunctionDecl>(D) || isa<BlockDecl>(D))) { + // If we have a function or block declaration, we can make sure we pass + // enough parameters. + unsigned Params = Call.parameters().size(); + if (Call.getNumArgs() < Params) { + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + LazyInit_BT("Function call with too few arguments", BT_call_few_args); + + SmallString<512> Str; + llvm::raw_svector_ostream os(Str); + if (isa<FunctionDecl>(D)) { + os << "Function "; + } else { + assert(isa<BlockDecl>(D)); + os << "Block "; + } + os << "taking " << Params << " argument" + << (Params == 1 ? "" : "s") << " is called with fewer (" + << Call.getNumArgs() << ")"; + + C.emitReport( + llvm::make_unique<BugReport>(*BT_call_few_args, os.str(), N)); + } + } + + // Don't check for uninitialized field values in arguments if the + // caller has a body that is available and we have the chance to inline it. + // This is a hack, but is a reasonable compromise betweens sometimes warning + // and sometimes not depending on if we decide to inline a function. + const bool checkUninitFields = + !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody())); + + std::unique_ptr<BugType> *BT; + if (isa<ObjCMethodCall>(Call)) + BT = &BT_msg_arg; + else + BT = &BT_call_arg; + + const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D); + for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) { + const ParmVarDecl *ParamDecl = nullptr; + if(FD && i < FD->getNumParams()) + ParamDecl = FD->getParamDecl(i); + if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i), + Call.getArgExpr(i), i, + checkUninitFields, Call, *BT, ParamDecl)) + return; + } + + // If we make it here, record our assumptions about the callee. + C.addTransition(State); +} + +void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg, + CheckerContext &C) const { + SVal recVal = msg.getReceiverSVal(); + if (recVal.isUndef()) { + if (ExplodedNode *N = C.generateErrorNode()) { + BugType *BT = nullptr; + switch (msg.getMessageKind()) { + case OCM_Message: + if (!BT_msg_undef) + BT_msg_undef.reset(new BuiltinBug(this, + "Receiver in message expression " + "is an uninitialized value")); + BT = BT_msg_undef.get(); + break; + case OCM_PropertyAccess: + if (!BT_objc_prop_undef) + BT_objc_prop_undef.reset(new BuiltinBug( + this, "Property access on an uninitialized object pointer")); + BT = BT_objc_prop_undef.get(); + break; + case OCM_Subscript: + if (!BT_objc_subscript_undef) + BT_objc_subscript_undef.reset(new BuiltinBug( + this, "Subscript access on an uninitialized object pointer")); + BT = BT_objc_subscript_undef.get(); + break; + } + assert(BT && "Unknown message kind."); + + auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N); + const ObjCMessageExpr *ME = msg.getOriginExpr(); + R->addRange(ME->getReceiverRange()); + + // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet. + if (const Expr *ReceiverE = ME->getInstanceReceiver()) + bugreporter::trackExpressionValue(N, ReceiverE, *R); + C.emitReport(std::move(R)); + } + return; + } +} + +void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg, + CheckerContext &C) const { + HandleNilReceiver(C, C.getState(), msg); +} + +void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, + const ObjCMethodCall &msg, + ExplodedNode *N) const { + + if (!BT_msg_ret) + BT_msg_ret.reset( + new BuiltinBug(this, "Receiver in message expression is 'nil'")); + + const ObjCMessageExpr *ME = msg.getOriginExpr(); + + QualType ResTy = msg.getResultType(); + + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + os << "The receiver of message '"; + ME->getSelector().print(os); + os << "' is nil"; + if (ResTy->isReferenceType()) { + os << ", which results in forming a null reference"; + } else { + os << " and returns a value of type '"; + msg.getResultType().print(os, C.getLangOpts()); + os << "' that will be garbage"; + } + + auto report = llvm::make_unique<BugReport>(*BT_msg_ret, os.str(), N); + report->addRange(ME->getReceiverRange()); + // FIXME: This won't track "self" in messages to super. + if (const Expr *receiver = ME->getInstanceReceiver()) { + bugreporter::trackExpressionValue(N, receiver, *report); + } + C.emitReport(std::move(report)); +} + +static bool supportsNilWithFloatRet(const llvm::Triple &triple) { + return (triple.getVendor() == llvm::Triple::Apple && + (triple.isiOS() || triple.isWatchOS() || + !triple.isMacOSXVersionLT(10,5))); +} + +void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, + ProgramStateRef state, + const ObjCMethodCall &Msg) const { + ASTContext &Ctx = C.getASTContext(); + static CheckerProgramPointTag Tag(this, "NilReceiver"); + + // Check the return type of the message expression. A message to nil will + // return different values depending on the return type and the architecture. + QualType RetTy = Msg.getResultType(); + CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); + const LocationContext *LCtx = C.getLocationContext(); + + if (CanRetTy->isStructureOrClassType()) { + // Structure returns are safe since the compiler zeroes them out. + SVal V = C.getSValBuilder().makeZeroVal(RetTy); + C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag); + return; + } + + // Other cases: check if sizeof(return type) > sizeof(void*) + if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap() + .isConsumedExpr(Msg.getOriginExpr())) { + // Compute: sizeof(void *) and sizeof(return type) + const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); + const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); + + if (CanRetTy.getTypePtr()->isReferenceType()|| + (voidPtrSize < returnTypeSize && + !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) && + (Ctx.FloatTy == CanRetTy || + Ctx.DoubleTy == CanRetTy || + Ctx.LongDoubleTy == CanRetTy || + Ctx.LongLongTy == CanRetTy || + Ctx.UnsignedLongLongTy == CanRetTy)))) { + if (ExplodedNode *N = C.generateErrorNode(state, &Tag)) + emitNilReceiverBug(C, Msg, N); + return; + } + + // Handle the safe cases where the return value is 0 if the + // receiver is nil. + // + // FIXME: For now take the conservative approach that we only + // return null values if we *know* that the receiver is nil. + // This is because we can have surprises like: + // + // ... = [[NSScreens screens] objectAtIndex:0]; + // + // What can happen is that [... screens] could return nil, but + // it most likely isn't nil. We should assume the semantics + // of this case unless we have *a lot* more knowledge. + // + SVal V = C.getSValBuilder().makeZeroVal(RetTy); + C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag); + return; + } + + C.addTransition(state); +} + +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + CallAndMessageChecker *Checker = \ + mgr.registerChecker<CallAndMessageChecker>(); \ + Checker->Filter.Check_##name = true; \ + Checker->Filter.CheckName_##name = mgr.getCurrentCheckName(); \ + } + +REGISTER_CHECKER(CallAndMessageUnInitRefArg) +REGISTER_CHECKER(CallAndMessageChecker) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp new file mode 100644 index 000000000000..5deb62d32311 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -0,0 +1,149 @@ +//=== CastSizeChecker.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// CastSizeChecker checks when casting a malloc'ed symbolic region to type T, +// whether the size of the symbolic region is a multiple of the size of T. +// +//===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/CharUnits.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > { + mutable std::unique_ptr<BuiltinBug> BT; + +public: + void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; +}; +} + +/// Check if we are casting to a struct with a flexible array at the end. +/// \code +/// struct foo { +/// size_t len; +/// struct bar data[]; +/// }; +/// \endcode +/// or +/// \code +/// struct foo { +/// size_t len; +/// struct bar data[0]; +/// } +/// \endcode +/// In these cases it is also valid to allocate size of struct foo + a multiple +/// of struct bar. +static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize, + CharUnits TypeSize, QualType ToPointeeTy) { + const RecordType *RT = ToPointeeTy->getAs<RecordType>(); + if (!RT) + return false; + + const RecordDecl *RD = RT->getDecl(); + RecordDecl::field_iterator Iter(RD->field_begin()); + RecordDecl::field_iterator End(RD->field_end()); + const FieldDecl *Last = nullptr; + for (; Iter != End; ++Iter) + Last = *Iter; + assert(Last && "empty structs should already be handled"); + + const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual(); + CharUnits FlexSize; + if (const ConstantArrayType *ArrayTy = + Ctx.getAsConstantArrayType(Last->getType())) { + FlexSize = Ctx.getTypeSizeInChars(ElemType); + if (ArrayTy->getSize() == 1 && TypeSize > FlexSize) + TypeSize -= FlexSize; + else if (ArrayTy->getSize() != 0) + return false; + } else if (RD->hasFlexibleArrayMember()) { + FlexSize = Ctx.getTypeSizeInChars(ElemType); + } else { + return false; + } + + if (FlexSize.isZero()) + return false; + + CharUnits Left = RegionSize - TypeSize; + if (Left.isNegative()) + return false; + + return Left % FlexSize == 0; +} + +void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const { + const Expr *E = CE->getSubExpr(); + ASTContext &Ctx = C.getASTContext(); + QualType ToTy = Ctx.getCanonicalType(CE->getType()); + const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); + + if (!ToPTy) + return; + + QualType ToPointeeTy = ToPTy->getPointeeType(); + + // Only perform the check if 'ToPointeeTy' is a complete type. + if (ToPointeeTy->isIncompleteType()) + return; + + ProgramStateRef state = C.getState(); + const MemRegion *R = C.getSVal(E).getAsRegion(); + if (!R) + return; + + const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R); + if (!SR) + return; + + SValBuilder &svalBuilder = C.getSValBuilder(); + SVal extent = SR->getExtent(svalBuilder); + const llvm::APSInt *extentInt = svalBuilder.getKnownValue(state, extent); + if (!extentInt) + return; + + CharUnits regionSize = CharUnits::fromQuantity(extentInt->getSExtValue()); + CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy); + + // Ignore void, and a few other un-sizeable types. + if (typeSize.isZero()) + return; + + if (regionSize % typeSize == 0) + return; + + if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy)) + return; + + if (ExplodedNode *errorNode = C.generateErrorNode()) { + if (!BT) + BT.reset(new BuiltinBug(this, "Cast region with wrong size.", + "Cast a region whose size is not a multiple" + " of the destination type size.")); + auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), errorNode); + R->addRange(CE->getSourceRange()); + C.emitReport(std::move(R)); + } +} + +void ento::registerCastSizeChecker(CheckerManager &mgr) { + // PR31226: C++ is more complicated than what this checker currently supports. + // There are derived-to-base casts, there are different rules for 0-size + // structures, no flexible arrays, etc. + // FIXME: Disabled on C++ for now. + if (!mgr.getLangOpts().CPlusPlus) + mgr.registerChecker<CastSizeChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp new file mode 100644 index 000000000000..2bd3879627cb --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -0,0 +1,122 @@ +//=== CastToStructChecker.cpp ----------------------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines CastToStructChecker, a builtin checker that checks for +// cast from non-struct pointer to struct pointer and widening struct data cast. +// This check corresponds to CWE-588. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class CastToStructVisitor : public RecursiveASTVisitor<CastToStructVisitor> { + BugReporter &BR; + const CheckerBase *Checker; + AnalysisDeclContext *AC; + +public: + explicit CastToStructVisitor(BugReporter &B, const CheckerBase *Checker, + AnalysisDeclContext *A) + : BR(B), Checker(Checker), AC(A) {} + bool VisitCastExpr(const CastExpr *CE); +}; +} + +bool CastToStructVisitor::VisitCastExpr(const CastExpr *CE) { + const Expr *E = CE->getSubExpr(); + ASTContext &Ctx = AC->getASTContext(); + QualType OrigTy = Ctx.getCanonicalType(E->getType()); + QualType ToTy = Ctx.getCanonicalType(CE->getType()); + + const PointerType *OrigPTy = dyn_cast<PointerType>(OrigTy.getTypePtr()); + const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr()); + + if (!ToPTy || !OrigPTy) + return true; + + QualType OrigPointeeTy = OrigPTy->getPointeeType(); + QualType ToPointeeTy = ToPTy->getPointeeType(); + + if (!ToPointeeTy->isStructureOrClassType()) + return true; + + // We allow cast from void*. + if (OrigPointeeTy->isVoidType()) + return true; + + // Now the cast-to-type is struct pointer, the original type is not void*. + if (!OrigPointeeTy->isRecordType()) { + SourceRange Sr[1] = {CE->getSourceRange()}; + PathDiagnosticLocation Loc(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport( + AC->getDecl(), Checker, "Cast from non-struct type to struct type", + categories::LogicError, "Casting a non-structure type to a structure " + "type and accessing a field can lead to memory " + "access errors or data corruption.", + Loc, Sr); + } else { + // Don't warn when size of data is unknown. + const auto *U = dyn_cast<UnaryOperator>(E); + if (!U || U->getOpcode() != UO_AddrOf) + return true; + + // Don't warn for references + const ValueDecl *VD = nullptr; + if (const auto *SE = dyn_cast<DeclRefExpr>(U->getSubExpr())) + VD = SE->getDecl(); + else if (const auto *SE = dyn_cast<MemberExpr>(U->getSubExpr())) + VD = SE->getMemberDecl(); + if (!VD || VD->getType()->isReferenceType()) + return true; + + if (ToPointeeTy->isIncompleteType() || + OrigPointeeTy->isIncompleteType()) + return true; + + // Warn when there is widening cast. + unsigned ToWidth = Ctx.getTypeInfo(ToPointeeTy).Width; + unsigned OrigWidth = Ctx.getTypeInfo(OrigPointeeTy).Width; + if (ToWidth <= OrigWidth) + return true; + + PathDiagnosticLocation Loc(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), Checker, "Widening cast to struct type", + categories::LogicError, + "Casting data to a larger structure type and accessing " + "a field can lead to memory access errors or data " + "corruption.", + Loc, CE->getSourceRange()); + } + + return true; +} + +namespace { +class CastToStructChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const { + CastToStructVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); + Visitor.TraverseDecl(const_cast<Decl *>(D)); + } +}; +} // end anonymous namespace + +void ento::registerCastToStructChecker(CheckerManager &mgr) { + mgr.registerChecker<CastToStructChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp new file mode 100644 index 000000000000..00a912f27a8d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -0,0 +1,1096 @@ +//==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker analyzes Objective-C -dealloc methods and their callees +// to warn about improper releasing of instance variables that back synthesized +// properties. It warns about missing releases in the following cases: +// - When a class has a synthesized instance variable for a 'retain' or 'copy' +// property and lacks a -dealloc method in its implementation. +// - When a class has a synthesized instance variable for a 'retain'/'copy' +// property but the ivar is not released in -dealloc by either -release +// or by nilling out the property. +// +// It warns about extra releases in -dealloc (but not in callees) when a +// synthesized instance variable is released in the following cases: +// - When the property is 'assign' and is not 'readonly'. +// - When the property is 'weak'. +// +// This checker only warns for instance variables synthesized to back +// properties. Handling the more general case would require inferring whether +// an instance variable is stored retained or not. For synthesized properties, +// this is specified in the property declaration itself. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +/// Indicates whether an instance variable is required to be released in +/// -dealloc. +enum class ReleaseRequirement { + /// The instance variable must be released, either by calling + /// -release on it directly or by nilling it out with a property setter. + MustRelease, + + /// The instance variable must not be directly released with -release. + MustNotReleaseDirectly, + + /// The requirement for the instance variable could not be determined. + Unknown +}; + +/// Returns true if the property implementation is synthesized and the +/// type of the property is retainable. +static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I, + const ObjCIvarDecl **ID, + const ObjCPropertyDecl **PD) { + + if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + return false; + + (*ID) = I->getPropertyIvarDecl(); + if (!(*ID)) + return false; + + QualType T = (*ID)->getType(); + if (!T->isObjCRetainableType()) + return false; + + (*PD) = I->getPropertyDecl(); + // Shouldn't be able to synthesize a property that doesn't exist. + assert(*PD); + + return true; +} + +namespace { + +class ObjCDeallocChecker + : public Checker<check::ASTDecl<ObjCImplementationDecl>, + check::PreObjCMessage, check::PostObjCMessage, + check::PreCall, + check::BeginFunction, check::EndFunction, + eval::Assume, + check::PointerEscape, + check::PreStmt<ReturnStmt>> { + + mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *XCTestCaseII, + *Block_releaseII, *CIFilterII; + + mutable Selector DeallocSel, ReleaseSel; + + std::unique_ptr<BugType> MissingReleaseBugType; + std::unique_ptr<BugType> ExtraReleaseBugType; + std::unique_ptr<BugType> MistakenDeallocBugType; + +public: + ObjCDeallocChecker(); + + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, + BugReporter &BR) const; + void checkBeginFunction(CheckerContext &Ctx) const; + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + + ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, + bool Assumption) const; + + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; + void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; + void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const; + +private: + void diagnoseMissingReleases(CheckerContext &C) const; + + bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M, + CheckerContext &C) const; + + bool diagnoseMistakenDealloc(SymbolRef DeallocedValue, + const ObjCMethodCall &M, + CheckerContext &C) const; + + SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M, + CheckerContext &C) const; + + const ObjCIvarRegion *getIvarRegionForIvarSymbol(SymbolRef IvarSym) const; + SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const; + + const ObjCPropertyImplDecl* + findPropertyOnDeallocatingInstance(SymbolRef IvarSym, + CheckerContext &C) const; + + ReleaseRequirement + getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const; + + bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const; + bool isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx, + SVal &SelfValOut) const; + bool instanceDeallocIsOnStack(const CheckerContext &C, + SVal &InstanceValOut) const; + + bool isSuperDeallocMessage(const ObjCMethodCall &M) const; + + const ObjCImplDecl *getContainingObjCImpl(const LocationContext *LCtx) const; + + const ObjCPropertyDecl * + findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const; + + void transitionToReleaseValue(CheckerContext &C, SymbolRef Value) const; + ProgramStateRef removeValueRequiringRelease(ProgramStateRef State, + SymbolRef InstanceSym, + SymbolRef ValueSym) const; + + void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; + + bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const; + + bool isReleasedByCIFilterDealloc(const ObjCPropertyImplDecl *PropImpl) const; + bool isNibLoadedIvarWithoutRetain(const ObjCPropertyImplDecl *PropImpl) const; +}; +} // End anonymous namespace. + + +/// Maps from the symbol for a class instance to the set of +/// symbols remaining that must be released in -dealloc. +REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef) +REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet) + + +/// An AST check that diagnose when the class requires a -dealloc method and +/// is missing one. +void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D, + AnalysisManager &Mgr, + BugReporter &BR) const { + assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly); + assert(!Mgr.getLangOpts().ObjCAutoRefCount); + initIdentifierInfoAndSelectors(Mgr.getASTContext()); + + const ObjCInterfaceDecl *ID = D->getClassInterface(); + // If the class is known to have a lifecycle with a separate teardown method + // then it may not require a -dealloc method. + if (classHasSeparateTeardown(ID)) + return; + + // Does the class contain any synthesized properties that are retainable? + // If not, skip the check entirely. + const ObjCPropertyImplDecl *PropImplRequiringRelease = nullptr; + bool HasOthers = false; + for (const auto *I : D->property_impls()) { + if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) { + if (!PropImplRequiringRelease) + PropImplRequiringRelease = I; + else { + HasOthers = true; + break; + } + } + } + + if (!PropImplRequiringRelease) + return; + + const ObjCMethodDecl *MD = nullptr; + + // Scan the instance methods for "dealloc". + for (const auto *I : D->instance_methods()) { + if (I->getSelector() == DeallocSel) { + MD = I; + break; + } + } + + if (!MD) { // No dealloc found. + const char* Name = "Missing -dealloc"; + + std::string Buf; + llvm::raw_string_ostream OS(Buf); + OS << "'" << *D << "' lacks a 'dealloc' instance method but " + << "must release '" << *PropImplRequiringRelease->getPropertyIvarDecl() + << "'"; + + if (HasOthers) + OS << " and others"; + PathDiagnosticLocation DLoc = + PathDiagnosticLocation::createBegin(D, BR.getSourceManager()); + + BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC, + OS.str(), DLoc); + return; + } +} + +/// If this is the beginning of -dealloc, mark the values initially stored in +/// instance variables that must be released by the end of -dealloc +/// as unreleased in the state. +void ObjCDeallocChecker::checkBeginFunction( + CheckerContext &C) const { + initIdentifierInfoAndSelectors(C.getASTContext()); + + // Only do this if the current method is -dealloc. + SVal SelfVal; + if (!isInInstanceDealloc(C, SelfVal)) + return; + + SymbolRef SelfSymbol = SelfVal.getAsSymbol(); + + const LocationContext *LCtx = C.getLocationContext(); + ProgramStateRef InitialState = C.getState(); + + ProgramStateRef State = InitialState; + + SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); + + // Symbols that must be released by the end of the -dealloc; + SymbolSet RequiredReleases = F.getEmptySet(); + + // If we're an inlined -dealloc, we should add our symbols to the existing + // set from our subclass. + if (const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol)) + RequiredReleases = *CurrSet; + + for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) { + ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl); + if (Requirement != ReleaseRequirement::MustRelease) + continue; + + SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal); + Optional<Loc> LValLoc = LVal.getAs<Loc>(); + if (!LValLoc) + continue; + + SVal InitialVal = State->getSVal(LValLoc.getValue()); + SymbolRef Symbol = InitialVal.getAsSymbol(); + if (!Symbol || !isa<SymbolRegionValue>(Symbol)) + continue; + + // Mark the value as requiring a release. + RequiredReleases = F.add(RequiredReleases, Symbol); + } + + if (!RequiredReleases.isEmpty()) { + State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases); + } + + if (State != InitialState) { + C.addTransition(State); + } +} + +/// Given a symbol for an ivar, return the ivar region it was loaded from. +/// Returns nullptr if the instance symbol cannot be found. +const ObjCIvarRegion * +ObjCDeallocChecker::getIvarRegionForIvarSymbol(SymbolRef IvarSym) const { + return dyn_cast_or_null<ObjCIvarRegion>(IvarSym->getOriginRegion()); +} + +/// Given a symbol for an ivar, return a symbol for the instance containing +/// the ivar. Returns nullptr if the instance symbol cannot be found. +SymbolRef +ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const { + + const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym); + if (!IvarRegion) + return nullptr; + + return IvarRegion->getSymbolicBase()->getSymbol(); +} + +/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is +/// a release or a nilling-out property setter. +void ObjCDeallocChecker::checkPreObjCMessage( + const ObjCMethodCall &M, CheckerContext &C) const { + // Only run if -dealloc is on the stack. + SVal DeallocedInstance; + if (!instanceDeallocIsOnStack(C, DeallocedInstance)) + return; + + SymbolRef ReleasedValue = nullptr; + + if (M.getSelector() == ReleaseSel) { + ReleasedValue = M.getReceiverSVal().getAsSymbol(); + } else if (M.getSelector() == DeallocSel && !M.isReceiverSelfOrSuper()) { + if (diagnoseMistakenDealloc(M.getReceiverSVal().getAsSymbol(), M, C)) + return; + } + + if (ReleasedValue) { + // An instance variable symbol was released with -release: + // [_property release]; + if (diagnoseExtraRelease(ReleasedValue,M, C)) + return; + } else { + // An instance variable symbol was released nilling out its property: + // self.property = nil; + ReleasedValue = getValueReleasedByNillingOut(M, C); + } + + if (!ReleasedValue) + return; + + transitionToReleaseValue(C, ReleasedValue); +} + +/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is +/// call to Block_release(). +void ObjCDeallocChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const IdentifierInfo *II = Call.getCalleeIdentifier(); + if (II != Block_releaseII) + return; + + if (Call.getNumArgs() != 1) + return; + + SymbolRef ReleasedValue = Call.getArgSVal(0).getAsSymbol(); + if (!ReleasedValue) + return; + + transitionToReleaseValue(C, ReleasedValue); +} +/// If the message was a call to '[super dealloc]', diagnose any missing +/// releases. +void ObjCDeallocChecker::checkPostObjCMessage( + const ObjCMethodCall &M, CheckerContext &C) const { + // We perform this check post-message so that if the super -dealloc + // calls a helper method and that this class overrides, any ivars released in + // the helper method will be recorded before checking. + if (isSuperDeallocMessage(M)) + diagnoseMissingReleases(C); +} + +/// Check for missing releases even when -dealloc does not call +/// '[super dealloc]'. +void ObjCDeallocChecker::checkEndFunction( + const ReturnStmt *RS, CheckerContext &C) const { + diagnoseMissingReleases(C); +} + +/// Check for missing releases on early return. +void ObjCDeallocChecker::checkPreStmt( + const ReturnStmt *RS, CheckerContext &C) const { + diagnoseMissingReleases(C); +} + +/// When a symbol is assumed to be nil, remove it from the set of symbols +/// require to be nil. +ProgramStateRef ObjCDeallocChecker::evalAssume(ProgramStateRef State, SVal Cond, + bool Assumption) const { + if (State->get<UnreleasedIvarMap>().isEmpty()) + return State; + + auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr()); + if (!CondBSE) + return State; + + BinaryOperator::Opcode OpCode = CondBSE->getOpcode(); + if (Assumption) { + if (OpCode != BO_EQ) + return State; + } else { + if (OpCode != BO_NE) + return State; + } + + SymbolRef NullSymbol = nullptr; + if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) { + const llvm::APInt &RHS = SIE->getRHS(); + if (RHS != 0) + return State; + NullSymbol = SIE->getLHS(); + } else if (auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) { + const llvm::APInt &LHS = SIE->getLHS(); + if (LHS != 0) + return State; + NullSymbol = SIE->getRHS(); + } else { + return State; + } + + SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol); + if (!InstanceSymbol) + return State; + + State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol); + + return State; +} + +/// If a symbol escapes conservatively assume unseen code released it. +ProgramStateRef ObjCDeallocChecker::checkPointerEscape( + ProgramStateRef State, const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind) const { + + if (State->get<UnreleasedIvarMap>().isEmpty()) + return State; + + // Don't treat calls to '[super dealloc]' as escaping for the purposes + // of this checker. Because the checker diagnoses missing releases in the + // post-message handler for '[super dealloc], escaping here would cause + // the checker to never warn. + auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call); + if (OMC && isSuperDeallocMessage(*OMC)) + return State; + + for (const auto &Sym : Escaped) { + if (!Call || (Call && !Call->isInSystemHeader())) { + // If Sym is a symbol for an object with instance variables that + // must be released, remove these obligations when the object escapes + // unless via a call to a system function. System functions are + // very unlikely to release instance variables on objects passed to them, + // and are frequently called on 'self' in -dealloc (e.g., to remove + // observers) -- we want to avoid false negatives from escaping on + // them. + State = State->remove<UnreleasedIvarMap>(Sym); + } + + + SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym); + if (!InstanceSymbol) + continue; + + State = removeValueRequiringRelease(State, InstanceSymbol, Sym); + } + + return State; +} + +/// Report any unreleased instance variables for the current instance being +/// dealloced. +void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + SVal SelfVal; + if (!isInInstanceDealloc(C, SelfVal)) + return; + + const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion(); + const LocationContext *LCtx = C.getLocationContext(); + + ExplodedNode *ErrNode = nullptr; + + SymbolRef SelfSym = SelfVal.getAsSymbol(); + if (!SelfSym) + return; + + const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym); + if (!OldUnreleased) + return; + + SymbolSet NewUnreleased = *OldUnreleased; + SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); + + ProgramStateRef InitialState = State; + + for (auto *IvarSymbol : *OldUnreleased) { + const TypedValueRegion *TVR = + cast<SymbolRegionValue>(IvarSymbol)->getRegion(); + const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR); + + // Don't warn if the ivar is not for this instance. + if (SelfRegion != IvarRegion->getSuperRegion()) + continue; + + const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); + // Prevent an inlined call to -dealloc in a super class from warning + // about the values the subclass's -dealloc should release. + if (IvarDecl->getContainingInterface() != + cast<ObjCMethodDecl>(LCtx->getDecl())->getClassInterface()) + continue; + + // Prevents diagnosing multiple times for the same instance variable + // at, for example, both a return and at the end of the function. + NewUnreleased = F.remove(NewUnreleased, IvarSymbol); + + if (State->getStateManager() + .getConstraintManager() + .isNull(State, IvarSymbol) + .isConstrainedTrue()) { + continue; + } + + // A missing release manifests as a leak, so treat as a non-fatal error. + if (!ErrNode) + ErrNode = C.generateNonFatalErrorNode(); + // If we've already reached this node on another path, return without + // diagnosing. + if (!ErrNode) + return; + + std::string Buf; + llvm::raw_string_ostream OS(Buf); + + const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface(); + // If the class is known to have a lifecycle with teardown that is + // separate from -dealloc, do not warn about missing releases. We + // suppress here (rather than not tracking for instance variables in + // such classes) because these classes are rare. + if (classHasSeparateTeardown(Interface)) + return; + + ObjCImplDecl *ImplDecl = Interface->getImplementation(); + + const ObjCPropertyImplDecl *PropImpl = + ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier()); + + const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); + + assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy || + PropDecl->getSetterKind() == ObjCPropertyDecl::Retain); + + OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl + << "' was "; + + if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain) + OS << "retained"; + else + OS << "copied"; + + OS << " by a synthesized property but not released" + " before '[super dealloc]'"; + + std::unique_ptr<BugReport> BR( + new BugReport(*MissingReleaseBugType, OS.str(), ErrNode)); + + C.emitReport(std::move(BR)); + } + + if (NewUnreleased.isEmpty()) { + State = State->remove<UnreleasedIvarMap>(SelfSym); + } else { + State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased); + } + + if (ErrNode) { + C.addTransition(State, ErrNode); + } else if (State != InitialState) { + C.addTransition(State); + } + + // Make sure that after checking in the top-most frame the list of + // tracked ivars is empty. This is intended to detect accidental leaks in + // the UnreleasedIvarMap program state. + assert(!LCtx->inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty()); +} + +/// Given a symbol, determine whether the symbol refers to an ivar on +/// the top-most deallocating instance. If so, find the property for that +/// ivar, if one exists. Otherwise return null. +const ObjCPropertyImplDecl * +ObjCDeallocChecker::findPropertyOnDeallocatingInstance( + SymbolRef IvarSym, CheckerContext &C) const { + SVal DeallocedInstance; + if (!isInInstanceDealloc(C, DeallocedInstance)) + return nullptr; + + // Try to get the region from which the ivar value was loaded. + auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym); + if (!IvarRegion) + return nullptr; + + // Don't try to find the property if the ivar was not loaded from the + // given instance. + if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() != + IvarRegion->getSuperRegion()) + return nullptr; + + const LocationContext *LCtx = C.getLocationContext(); + const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl(); + + const ObjCImplDecl *Container = getContainingObjCImpl(LCtx); + const ObjCPropertyImplDecl *PropImpl = + Container->FindPropertyImplIvarDecl(IvarDecl->getIdentifier()); + return PropImpl; +} + +/// Emits a warning if the current context is -dealloc and ReleasedValue +/// must not be directly released in a -dealloc. Returns true if a diagnostic +/// was emitted. +bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue, + const ObjCMethodCall &M, + CheckerContext &C) const { + // Try to get the region from which the released value was loaded. + // Note that, unlike diagnosing for missing releases, here we don't track + // values that must not be released in the state. This is because even if + // these values escape, it is still an error under the rules of MRR to + // release them in -dealloc. + const ObjCPropertyImplDecl *PropImpl = + findPropertyOnDeallocatingInstance(ReleasedValue, C); + + if (!PropImpl) + return false; + + // If the ivar belongs to a property that must not be released directly + // in dealloc, emit a warning. + if (getDeallocReleaseRequirement(PropImpl) != + ReleaseRequirement::MustNotReleaseDirectly) { + return false; + } + + // If the property is readwrite but it shadows a read-only property in its + // external interface, treat the property a read-only. If the outside + // world cannot write to a property then the internal implementation is free + // to make its own convention about whether the value is stored retained + // or not. We look up the shadow here rather than in + // getDeallocReleaseRequirement() because doing so can be expensive. + const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl); + if (PropDecl) { + if (PropDecl->isReadOnly()) + return false; + } else { + PropDecl = PropImpl->getPropertyDecl(); + } + + ExplodedNode *ErrNode = C.generateNonFatalErrorNode(); + if (!ErrNode) + return false; + + std::string Buf; + llvm::raw_string_ostream OS(Buf); + + assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak || + (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign && + !PropDecl->isReadOnly()) || + isReleasedByCIFilterDealloc(PropImpl) + ); + + const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext()); + OS << "The '" << *PropImpl->getPropertyIvarDecl() + << "' ivar in '" << *Container; + + + if (isReleasedByCIFilterDealloc(PropImpl)) { + OS << "' will be released by '-[CIFilter dealloc]' but also released here"; + } else { + OS << "' was synthesized for "; + + if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak) + OS << "a weak"; + else + OS << "an assign, readwrite"; + + OS << " property but was released in 'dealloc'"; + } + + std::unique_ptr<BugReport> BR( + new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode)); + BR->addRange(M.getOriginExpr()->getSourceRange()); + + C.emitReport(std::move(BR)); + + return true; +} + +/// Emits a warning if the current context is -dealloc and DeallocedValue +/// must not be directly dealloced in a -dealloc. Returns true if a diagnostic +/// was emitted. +bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue, + const ObjCMethodCall &M, + CheckerContext &C) const { + // TODO: Apart from unknown/undefined receivers, this may happen when + // dealloc is called as a class method. Should we warn? + if (!DeallocedValue) + return false; + + // Find the property backing the instance variable that M + // is dealloc'ing. + const ObjCPropertyImplDecl *PropImpl = + findPropertyOnDeallocatingInstance(DeallocedValue, C); + if (!PropImpl) + return false; + + if (getDeallocReleaseRequirement(PropImpl) != + ReleaseRequirement::MustRelease) { + return false; + } + + ExplodedNode *ErrNode = C.generateErrorNode(); + if (!ErrNode) + return false; + + std::string Buf; + llvm::raw_string_ostream OS(Buf); + + OS << "'" << *PropImpl->getPropertyIvarDecl() + << "' should be released rather than deallocated"; + + std::unique_ptr<BugReport> BR( + new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode)); + BR->addRange(M.getOriginExpr()->getSourceRange()); + + C.emitReport(std::move(BR)); + + return true; +} + +ObjCDeallocChecker::ObjCDeallocChecker() + : NSObjectII(nullptr), SenTestCaseII(nullptr), XCTestCaseII(nullptr), + CIFilterII(nullptr) { + + MissingReleaseBugType.reset( + new BugType(this, "Missing ivar release (leak)", + categories::MemoryRefCount)); + + ExtraReleaseBugType.reset( + new BugType(this, "Extra ivar release", + categories::MemoryRefCount)); + + MistakenDeallocBugType.reset( + new BugType(this, "Mistaken dealloc", + categories::MemoryRefCount)); +} + +void ObjCDeallocChecker::initIdentifierInfoAndSelectors( + ASTContext &Ctx) const { + if (NSObjectII) + return; + + NSObjectII = &Ctx.Idents.get("NSObject"); + SenTestCaseII = &Ctx.Idents.get("SenTestCase"); + XCTestCaseII = &Ctx.Idents.get("XCTestCase"); + Block_releaseII = &Ctx.Idents.get("_Block_release"); + CIFilterII = &Ctx.Idents.get("CIFilter"); + + IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc"); + IdentifierInfo *ReleaseII = &Ctx.Idents.get("release"); + DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII); + ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII); +} + +/// Returns true if M is a call to '[super dealloc]'. +bool ObjCDeallocChecker::isSuperDeallocMessage( + const ObjCMethodCall &M) const { + if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) + return false; + + return M.getSelector() == DeallocSel; +} + +/// Returns the ObjCImplDecl containing the method declaration in LCtx. +const ObjCImplDecl * +ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const { + auto *MD = cast<ObjCMethodDecl>(LCtx->getDecl()); + return cast<ObjCImplDecl>(MD->getDeclContext()); +} + +/// Returns the property that shadowed by PropImpl if one exists and +/// nullptr otherwise. +const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl( + const ObjCPropertyImplDecl *PropImpl) const { + const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl(); + + // Only readwrite properties can shadow. + if (PropDecl->isReadOnly()) + return nullptr; + + auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->getDeclContext()); + + // Only class extensions can contain shadowing properties. + if (!CatDecl || !CatDecl->IsClassExtension()) + return nullptr; + + IdentifierInfo *ID = PropDecl->getIdentifier(); + DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID); + for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) { + auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(*I); + if (!ShadowedPropDecl) + continue; + + if (ShadowedPropDecl->isInstanceProperty()) { + assert(ShadowedPropDecl->isReadOnly()); + return ShadowedPropDecl; + } + } + + return nullptr; +} + +/// Add a transition noting the release of the given value. +void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext &C, + SymbolRef Value) const { + assert(Value); + SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value); + if (!InstanceSym) + return; + ProgramStateRef InitialState = C.getState(); + + ProgramStateRef ReleasedState = + removeValueRequiringRelease(InitialState, InstanceSym, Value); + + if (ReleasedState != InitialState) { + C.addTransition(ReleasedState); + } +} + +/// Remove the Value requiring a release from the tracked set for +/// Instance and return the resultant state. +ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease( + ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const { + assert(Instance); + assert(Value); + const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value); + if (!RemovedRegion) + return State; + + const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance); + if (!Unreleased) + return State; + + // Mark the value as no longer requiring a release. + SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>(); + SymbolSet NewUnreleased = *Unreleased; + for (auto &Sym : *Unreleased) { + const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym); + assert(UnreleasedRegion); + if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) { + NewUnreleased = F.remove(NewUnreleased, Sym); + } + } + + if (NewUnreleased.isEmpty()) { + return State->remove<UnreleasedIvarMap>(Instance); + } + + return State->set<UnreleasedIvarMap>(Instance, NewUnreleased); +} + +/// Determines whether the instance variable for \p PropImpl must or must not be +/// released in -dealloc or whether it cannot be determined. +ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement( + const ObjCPropertyImplDecl *PropImpl) const { + const ObjCIvarDecl *IvarDecl; + const ObjCPropertyDecl *PropDecl; + if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl)) + return ReleaseRequirement::Unknown; + + ObjCPropertyDecl::SetterKind SK = PropDecl->getSetterKind(); + + switch (SK) { + // Retain and copy setters retain/copy their values before storing and so + // the value in their instance variables must be released in -dealloc. + case ObjCPropertyDecl::Retain: + case ObjCPropertyDecl::Copy: + if (isReleasedByCIFilterDealloc(PropImpl)) + return ReleaseRequirement::MustNotReleaseDirectly; + + if (isNibLoadedIvarWithoutRetain(PropImpl)) + return ReleaseRequirement::Unknown; + + return ReleaseRequirement::MustRelease; + + case ObjCPropertyDecl::Weak: + return ReleaseRequirement::MustNotReleaseDirectly; + + case ObjCPropertyDecl::Assign: + // It is common for the ivars for read-only assign properties to + // always be stored retained, so their release requirement cannot be + // be determined. + if (PropDecl->isReadOnly()) + return ReleaseRequirement::Unknown; + + return ReleaseRequirement::MustNotReleaseDirectly; + } + llvm_unreachable("Unrecognized setter kind"); +} + +/// Returns the released value if M is a call a setter that releases +/// and nils out its underlying instance variable. +SymbolRef +ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M, + CheckerContext &C) const { + SVal ReceiverVal = M.getReceiverSVal(); + if (!ReceiverVal.isValid()) + return nullptr; + + if (M.getNumArgs() == 0) + return nullptr; + + if (!M.getArgExpr(0)->getType()->isObjCRetainableType()) + return nullptr; + + // Is the first argument nil? + SVal Arg = M.getArgSVal(0); + ProgramStateRef notNilState, nilState; + std::tie(notNilState, nilState) = + M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>()); + if (!(nilState && !notNilState)) + return nullptr; + + const ObjCPropertyDecl *Prop = M.getAccessedProperty(); + if (!Prop) + return nullptr; + + ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl(); + if (!PropIvarDecl) + return nullptr; + + ProgramStateRef State = C.getState(); + + SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal); + Optional<Loc> LValLoc = LVal.getAs<Loc>(); + if (!LValLoc) + return nullptr; + + SVal CurrentValInIvar = State->getSVal(LValLoc.getValue()); + return CurrentValInIvar.getAsSymbol(); +} + +/// Returns true if the current context is a call to -dealloc and false +/// otherwise. If true, it also sets SelfValOut to the value of +/// 'self'. +bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, + SVal &SelfValOut) const { + return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut); +} + +/// Returns true if LCtx is a call to -dealloc and false +/// otherwise. If true, it also sets SelfValOut to the value of +/// 'self'. +bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C, + const LocationContext *LCtx, + SVal &SelfValOut) const { + auto *MD = dyn_cast<ObjCMethodDecl>(LCtx->getDecl()); + if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel) + return false; + + const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl(); + assert(SelfDecl && "No self in -dealloc?"); + + ProgramStateRef State = C.getState(); + SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx)); + return true; +} + +/// Returns true if there is a call to -dealloc anywhere on the stack and false +/// otherwise. If true, it also sets InstanceValOut to the value of +/// 'self' in the frame for -dealloc. +bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C, + SVal &InstanceValOut) const { + const LocationContext *LCtx = C.getLocationContext(); + + while (LCtx) { + if (isInInstanceDealloc(C, LCtx, InstanceValOut)) + return true; + + LCtx = LCtx->getParent(); + } + + return false; +} + +/// Returns true if the ID is a class in which which is known to have +/// a separate teardown lifecycle. In this case, -dealloc warnings +/// about missing releases should be suppressed. +bool ObjCDeallocChecker::classHasSeparateTeardown( + const ObjCInterfaceDecl *ID) const { + // Suppress if the class is not a subclass of NSObject. + for ( ; ID ; ID = ID->getSuperClass()) { + IdentifierInfo *II = ID->getIdentifier(); + + if (II == NSObjectII) + return false; + + // FIXME: For now, ignore classes that subclass SenTestCase and XCTestCase, + // as these don't need to implement -dealloc. They implement tear down in + // another way, which we should try and catch later. + // http://llvm.org/bugs/show_bug.cgi?id=3187 + if (II == XCTestCaseII || II == SenTestCaseII) + return true; + } + + return true; +} + +/// The -dealloc method in CIFilter highly unusual in that is will release +/// instance variables belonging to its *subclasses* if the variable name +/// starts with "input" or backs a property whose name starts with "input". +/// Subclasses should not release these ivars in their own -dealloc method -- +/// doing so could result in an over release. +/// +/// This method returns true if the property will be released by +/// -[CIFilter dealloc]. +bool ObjCDeallocChecker::isReleasedByCIFilterDealloc( + const ObjCPropertyImplDecl *PropImpl) const { + assert(PropImpl->getPropertyIvarDecl()); + StringRef PropName = PropImpl->getPropertyDecl()->getName(); + StringRef IvarName = PropImpl->getPropertyIvarDecl()->getName(); + + const char *ReleasePrefix = "input"; + if (!(PropName.startswith(ReleasePrefix) || + IvarName.startswith(ReleasePrefix))) { + return false; + } + + const ObjCInterfaceDecl *ID = + PropImpl->getPropertyIvarDecl()->getContainingInterface(); + for ( ; ID ; ID = ID->getSuperClass()) { + IdentifierInfo *II = ID->getIdentifier(); + if (II == CIFilterII) + return true; + } + + return false; +} + +/// Returns whether the ivar backing the property is an IBOutlet that +/// has its value set by nib loading code without retaining the value. +/// +/// On macOS, if there is no setter, the nib-loading code sets the ivar +/// directly, without retaining the value, +/// +/// On iOS and its derivatives, the nib-loading code will call +/// -setValue:forKey:, which retains the value before directly setting the ivar. +bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain( + const ObjCPropertyImplDecl *PropImpl) const { + const ObjCIvarDecl *IvarDecl = PropImpl->getPropertyIvarDecl(); + if (!IvarDecl->hasAttr<IBOutletAttr>()) + return false; + + const llvm::Triple &Target = + IvarDecl->getASTContext().getTargetInfo().getTriple(); + + if (!Target.isMacOSX()) + return false; + + if (PropImpl->getPropertyDecl()->getSetterMethodDecl()) + return false; + + return true; +} + +void ento::registerObjCDeallocChecker(CheckerManager &Mgr) { + const LangOptions &LangOpts = Mgr.getLangOpts(); + // These checker only makes sense under MRR. + if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount) + return; + + Mgr.registerChecker<ObjCDeallocChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp new file mode 100644 index 000000000000..fe6715595e6f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp @@ -0,0 +1,140 @@ +//=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- 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 a CheckObjCInstMethSignature, a flow-insenstive check +// that determines if an Objective-C class interface incorrectly redefines +// the method signature in a subclass. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Type.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +static bool AreTypesCompatible(QualType Derived, QualType Ancestor, + ASTContext &C) { + + // Right now don't compare the compatibility of pointers. That involves + // looking at subtyping relationships. FIXME: Future patch. + if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType()) + return true; + + return C.typesAreCompatible(Derived, Ancestor); +} + +static void CompareReturnTypes(const ObjCMethodDecl *MethDerived, + const ObjCMethodDecl *MethAncestor, + BugReporter &BR, ASTContext &Ctx, + const ObjCImplementationDecl *ID, + const CheckerBase *Checker) { + + QualType ResDerived = MethDerived->getReturnType(); + QualType ResAncestor = MethAncestor->getReturnType(); + + if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "The Objective-C class '" + << *MethDerived->getClassInterface() + << "', which is derived from class '" + << *MethAncestor->getClassInterface() + << "', defines the instance method '"; + MethDerived->getSelector().print(os); + os << "' whose return type is '" + << ResDerived.getAsString() + << "'. A method with the same name (same selector) is also defined in " + "class '" + << *MethAncestor->getClassInterface() + << "' and has a return type of '" + << ResAncestor.getAsString() + << "'. These two types are incompatible, and may result in undefined " + "behavior for clients of these classes."; + + PathDiagnosticLocation MethDLoc = + PathDiagnosticLocation::createBegin(MethDerived, + BR.getSourceManager()); + + BR.EmitBasicReport( + MethDerived, Checker, "Incompatible instance method return type", + categories::CoreFoundationObjectiveC, os.str(), MethDLoc); + } +} + +static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID, + BugReporter &BR, + const CheckerBase *Checker) { + + const ObjCInterfaceDecl *D = ID->getClassInterface(); + const ObjCInterfaceDecl *C = D->getSuperClass(); + + if (!C) + return; + + ASTContext &Ctx = BR.getContext(); + + // Build a DenseMap of the methods for quick querying. + typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy; + MapTy IMeths; + unsigned NumMethods = 0; + + for (auto *M : ID->instance_methods()) { + IMeths[M->getSelector()] = M; + ++NumMethods; + } + + // Now recurse the class hierarchy chain looking for methods with the + // same signatures. + while (C && NumMethods) { + for (const auto *M : C->instance_methods()) { + Selector S = M->getSelector(); + + MapTy::iterator MI = IMeths.find(S); + + if (MI == IMeths.end() || MI->second == nullptr) + continue; + + --NumMethods; + ObjCMethodDecl *MethDerived = MI->second; + MI->second = nullptr; + + CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker); + } + + C = C->getSuperClass(); + } +} + +//===----------------------------------------------------------------------===// +// ObjCMethSigsChecker +//===----------------------------------------------------------------------===// + +namespace { +class ObjCMethSigsChecker : public Checker< + check::ASTDecl<ObjCImplementationDecl> > { +public: + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, + BugReporter &BR) const { + CheckObjCInstMethSignature(D, BR, this); + } +}; +} + +void ento::registerObjCMethSigsChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCMethSigsChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp new file mode 100644 index 000000000000..163ca9d8556f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -0,0 +1,930 @@ +//==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- 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 a set of flow-insensitive security checks. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +static bool isArc4RandomAvailable(const ASTContext &Ctx) { + const llvm::Triple &T = Ctx.getTargetInfo().getTriple(); + return T.getVendor() == llvm::Triple::Apple || + T.getOS() == llvm::Triple::CloudABI || + T.isOSFreeBSD() || + T.isOSNetBSD() || + T.isOSOpenBSD() || + T.isOSDragonFly(); +} + +namespace { +struct ChecksFilter { + DefaultBool check_bcmp; + DefaultBool check_bcopy; + DefaultBool check_bzero; + DefaultBool check_gets; + DefaultBool check_getpw; + DefaultBool check_mktemp; + DefaultBool check_mkstemp; + DefaultBool check_strcpy; + DefaultBool check_rand; + DefaultBool check_vfork; + DefaultBool check_FloatLoopCounter; + DefaultBool check_UncheckedReturn; + + CheckName checkName_bcmp; + CheckName checkName_bcopy; + CheckName checkName_bzero; + CheckName checkName_gets; + CheckName checkName_getpw; + CheckName checkName_mktemp; + CheckName checkName_mkstemp; + CheckName checkName_strcpy; + CheckName checkName_rand; + CheckName checkName_vfork; + CheckName checkName_FloatLoopCounter; + CheckName checkName_UncheckedReturn; +}; + +class WalkAST : public StmtVisitor<WalkAST> { + BugReporter &BR; + AnalysisDeclContext* AC; + enum { num_setids = 6 }; + IdentifierInfo *II_setid[num_setids]; + + const bool CheckRand; + const ChecksFilter &filter; + +public: + WalkAST(BugReporter &br, AnalysisDeclContext* ac, + const ChecksFilter &f) + : BR(br), AC(ac), II_setid(), + CheckRand(isArc4RandomAvailable(BR.getContext())), + filter(f) {} + + // Statement visitor methods. + void VisitCallExpr(CallExpr *CE); + void VisitForStmt(ForStmt *S); + void VisitCompoundStmt (CompoundStmt *S); + void VisitStmt(Stmt *S) { VisitChildren(S); } + + void VisitChildren(Stmt *S); + + // Helpers. + bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD); + + typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *); + + // Checker-specific methods. + void checkLoopConditionForFloat(const ForStmt *FS); + void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_random(const CallExpr *CE, const FunctionDecl *FD); + void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD); + void checkUncheckedReturnValue(CallExpr *CE); +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// AST walking. +//===----------------------------------------------------------------------===// + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt *Child : S->children()) + if (Child) + Visit(Child); +} + +void WalkAST::VisitCallExpr(CallExpr *CE) { + // Get the callee. + const FunctionDecl *FD = CE->getDirectCallee(); + + if (!FD) + return; + + // Get the name of the callee. If it's a builtin, strip off the prefix. + IdentifierInfo *II = FD->getIdentifier(); + if (!II) // if no identifier, not a simple C function + return; + StringRef Name = II->getName(); + if (Name.startswith("__builtin_")) + Name = Name.substr(10); + + // Set the evaluation function by switching on the callee name. + FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) + .Case("bcmp", &WalkAST::checkCall_bcmp) + .Case("bcopy", &WalkAST::checkCall_bcopy) + .Case("bzero", &WalkAST::checkCall_bzero) + .Case("gets", &WalkAST::checkCall_gets) + .Case("getpw", &WalkAST::checkCall_getpw) + .Case("mktemp", &WalkAST::checkCall_mktemp) + .Case("mkstemp", &WalkAST::checkCall_mkstemp) + .Case("mkdtemp", &WalkAST::checkCall_mkstemp) + .Case("mkstemps", &WalkAST::checkCall_mkstemp) + .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy) + .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat) + .Case("drand48", &WalkAST::checkCall_rand) + .Case("erand48", &WalkAST::checkCall_rand) + .Case("jrand48", &WalkAST::checkCall_rand) + .Case("lrand48", &WalkAST::checkCall_rand) + .Case("mrand48", &WalkAST::checkCall_rand) + .Case("nrand48", &WalkAST::checkCall_rand) + .Case("lcong48", &WalkAST::checkCall_rand) + .Case("rand", &WalkAST::checkCall_rand) + .Case("rand_r", &WalkAST::checkCall_rand) + .Case("random", &WalkAST::checkCall_random) + .Case("vfork", &WalkAST::checkCall_vfork) + .Default(nullptr); + + // If the callee isn't defined, it is not of security concern. + // Check and evaluate the call. + if (evalFunction) + (this->*evalFunction)(CE, FD); + + // Recurse and check children. + VisitChildren(CE); +} + +void WalkAST::VisitCompoundStmt(CompoundStmt *S) { + for (Stmt *Child : S->children()) + if (Child) { + if (CallExpr *CE = dyn_cast<CallExpr>(Child)) + checkUncheckedReturnValue(CE); + Visit(Child); + } +} + +void WalkAST::VisitForStmt(ForStmt *FS) { + checkLoopConditionForFloat(FS); + + // Recurse and check children. + VisitChildren(FS); +} + +//===----------------------------------------------------------------------===// +// Check: floating point variable used as loop counter. +// Originally: <rdar://problem/6336718> +// Implements: CERT security coding advisory FLP-30. +//===----------------------------------------------------------------------===// + +static const DeclRefExpr* +getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) { + expr = expr->IgnoreParenCasts(); + + if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) { + if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() || + B->getOpcode() == BO_Comma)) + return nullptr; + + if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y)) + return lhs; + + if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y)) + return rhs; + + return nullptr; + } + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) { + const NamedDecl *ND = DR->getDecl(); + return ND == x || ND == y ? DR : nullptr; + } + + if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr)) + return U->isIncrementDecrementOp() + ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr; + + return nullptr; +} + +/// CheckLoopConditionForFloat - This check looks for 'for' statements that +/// use a floating point variable as a loop counter. +/// CERT: FLP30-C, FLP30-CPP. +/// +void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) { + if (!filter.check_FloatLoopCounter) + return; + + // Does the loop have a condition? + const Expr *condition = FS->getCond(); + + if (!condition) + return; + + // Does the loop have an increment? + const Expr *increment = FS->getInc(); + + if (!increment) + return; + + // Strip away '()' and casts. + condition = condition->IgnoreParenCasts(); + increment = increment->IgnoreParenCasts(); + + // Is the loop condition a comparison? + const BinaryOperator *B = dyn_cast<BinaryOperator>(condition); + + if (!B) + return; + + // Is this a comparison? + if (!(B->isRelationalOp() || B->isEqualityOp())) + return; + + // Are we comparing variables? + const DeclRefExpr *drLHS = + dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts()); + const DeclRefExpr *drRHS = + dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts()); + + // Does at least one of the variables have a floating point type? + drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr; + drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr; + + if (!drLHS && !drRHS) + return; + + const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr; + const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr; + + if (!vdLHS && !vdRHS) + return; + + // Does either variable appear in increment? + const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS); + + if (!drInc) + return; + + // Emit the error. First figure out which DeclRefExpr in the condition + // referenced the compared variable. + assert(drInc->getDecl()); + const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; + + SmallVector<SourceRange, 2> ranges; + SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); + + os << "Variable '" << drCond->getDecl()->getName() + << "' with floating point type '" << drCond->getType().getAsString() + << "' should not be used as a loop counter"; + + ranges.push_back(drCond->getSourceRange()); + ranges.push_back(drInc->getSourceRange()); + + const char *bugType = "Floating point variable used as loop counter"; + + PathDiagnosticLocation FSLoc = + PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter, + bugType, "Security", os.str(), + FSLoc, ranges); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of bcmp. +// CWE-477: Use of Obsolete Functions +// bcmp was deprecated in POSIX.1-2008 +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_bcmp) + return; + + const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); + if (!FPT) + return; + + // Verify that the function takes three arguments. + if (FPT->getNumParams() != 3) + return; + + for (int i = 0; i < 2; i++) { + // Verify the first and second argument type is void*. + const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) + return; + } + + // Verify the third argument type is integer. + if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType()) + return; + + // Issue a warning. + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp, + "Use of deprecated function in call to 'bcmp()'", + "Security", + "The bcmp() function is obsoleted by memcmp().", + CELoc, CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of bcopy. +// CWE-477: Use of Obsolete Functions +// bcopy was deprecated in POSIX.1-2008 +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_bcopy) + return; + + const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); + if (!FPT) + return; + + // Verify that the function takes three arguments. + if (FPT->getNumParams() != 3) + return; + + for (int i = 0; i < 2; i++) { + // Verify the first and second argument type is void*. + const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) + return; + } + + // Verify the third argument type is integer. + if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType()) + return; + + // Issue a warning. + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy, + "Use of deprecated function in call to 'bcopy()'", + "Security", + "The bcopy() function is obsoleted by memcpy() " + "or memmove().", + CELoc, CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of bzero. +// CWE-477: Use of Obsolete Functions +// bzero was deprecated in POSIX.1-2008 +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_bzero) + return; + + const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); + if (!FPT) + return; + + // Verify that the function takes two arguments. + if (FPT->getNumParams() != 2) + return; + + // Verify the first argument type is void*. + const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy) + return; + + // Verify the second argument type is integer. + if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType()) + return; + + // Issue a warning. + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero, + "Use of deprecated function in call to 'bzero()'", + "Security", + "The bzero() function is obsoleted by memset().", + CELoc, CE->getCallee()->getSourceRange()); +} + + +//===----------------------------------------------------------------------===// +// Check: Any use of 'gets' is insecure. +// Originally: <rdar://problem/6335715> +// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov) +// CWE-242: Use of Inherently Dangerous Function +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_gets) + return; + + const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); + if (!FPT) + return; + + // Verify that the function takes a single argument. + if (FPT->getNumParams() != 1) + return; + + // Is the argument a 'char*'? + const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a warning. + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets, + "Potential buffer overflow in call to 'gets'", + "Security", + "Call to function 'gets' is extremely insecure as it can " + "always result in a buffer overflow", + CELoc, CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'getpwd' is insecure. +// CWE-477: Use of Obsolete Functions +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_getpw) + return; + + const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); + if (!FPT) + return; + + // Verify that the function takes two arguments. + if (FPT->getNumParams() != 2) + return; + + // Verify the first argument type is integer. + if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType()) + return; + + // Verify the second argument type is char*. + const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>(); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a warning. + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw, + "Potential buffer overflow in call to 'getpw'", + "Security", + "The getpw() function is dangerous as it may overflow the " + "provided buffer. It is obsoleted by getpwuid().", + CELoc, CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp(). +// CWE-377: Insecure Temporary File +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_mktemp) { + // Fall back to the security check of looking for enough 'X's in the + // format string, since that is a less severe warning. + checkCall_mkstemp(CE, FD); + return; + } + + const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); + if(!FPT) + return; + + // Verify that the function takes a single argument. + if (FPT->getNumParams() != 1) + return; + + // Verify that the argument is Pointer Type. + const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>(); + if (!PT) + return; + + // Verify that the argument is a 'char*'. + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a warning. + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp, + "Potential insecure temporary file in call 'mktemp'", + "Security", + "Call to function 'mktemp' is insecure as it always " + "creates or uses insecure temporary file. Use 'mkstemp' " + "instead", + CELoc, CE->getCallee()->getSourceRange()); +} + + +//===----------------------------------------------------------------------===// +// Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's. +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_mkstemp) + return; + + StringRef Name = FD->getIdentifier()->getName(); + std::pair<signed, signed> ArgSuffix = + llvm::StringSwitch<std::pair<signed, signed> >(Name) + .Case("mktemp", std::make_pair(0,-1)) + .Case("mkstemp", std::make_pair(0,-1)) + .Case("mkdtemp", std::make_pair(0,-1)) + .Case("mkstemps", std::make_pair(0,1)) + .Default(std::make_pair(-1, -1)); + + assert(ArgSuffix.first >= 0 && "Unsupported function"); + + // Check if the number of arguments is consistent with out expectations. + unsigned numArgs = CE->getNumArgs(); + if ((signed) numArgs <= ArgSuffix.first) + return; + + const StringLiteral *strArg = + dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first) + ->IgnoreParenImpCasts()); + + // Currently we only handle string literals. It is possible to do better, + // either by looking at references to const variables, or by doing real + // flow analysis. + if (!strArg || strArg->getCharByteWidth() != 1) + return; + + // Count the number of X's, taking into account a possible cutoff suffix. + StringRef str = strArg->getString(); + unsigned numX = 0; + unsigned n = str.size(); + + // Take into account the suffix. + unsigned suffix = 0; + if (ArgSuffix.second >= 0) { + const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second); + Expr::EvalResult EVResult; + if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext())) + return; + llvm::APSInt Result = EVResult.Val.getInt(); + // FIXME: Issue a warning. + if (Result.isNegative()) + return; + suffix = (unsigned) Result.getZExtValue(); + n = (n > suffix) ? n - suffix : 0; + } + + for (unsigned i = 0; i < n; ++i) + if (str[i] == 'X') ++numX; + + if (numX >= 6) + return; + + // Issue a warning. + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + SmallString<512> buf; + llvm::raw_svector_ostream out(buf); + out << "Call to '" << Name << "' should have at least 6 'X's in the" + " format string to be secure (" << numX << " 'X'"; + if (numX != 1) + out << 's'; + out << " seen"; + if (suffix) { + out << ", " << suffix << " character"; + if (suffix > 1) + out << 's'; + out << " used as a suffix"; + } + out << ')'; + BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp, + "Insecure temporary file creation", "Security", + out.str(), CELoc, strArg->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'strcpy' is insecure. +// +// CWE-119: Improper Restriction of Operations within +// the Bounds of a Memory Buffer +//===----------------------------------------------------------------------===// +void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_strcpy) + return; + + if (!checkCall_strCommon(CE, FD)) + return; + + const auto *Target = CE->getArg(0)->IgnoreImpCasts(), + *Source = CE->getArg(1)->IgnoreImpCasts(); + + if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) { + uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8; + if (const auto *String = dyn_cast<StringLiteral>(Source)) { + if (ArraySize >= String->getLength() + 1) + return; + } + } + + // Issue a warning. + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, + "Potential insecure memory buffer bounds restriction in " + "call 'strcpy'", + "Security", + "Call to function 'strcpy' is insecure as it does not " + "provide bounding of the memory buffer. Replace " + "unbounded copy functions with analogous functions that " + "support length arguments such as 'strlcpy'. CWE-119.", + CELoc, CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'strcat' is insecure. +// +// CWE-119: Improper Restriction of Operations within +// the Bounds of a Memory Buffer +//===----------------------------------------------------------------------===// +void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_strcpy) + return; + + if (!checkCall_strCommon(CE, FD)) + return; + + // Issue a warning. + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy, + "Potential insecure memory buffer bounds restriction in " + "call 'strcat'", + "Security", + "Call to function 'strcat' is insecure as it does not " + "provide bounding of the memory buffer. Replace " + "unbounded copy functions with analogous functions that " + "support length arguments such as 'strlcat'. CWE-119.", + CELoc, CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// Common check for str* functions with no bounds parameters. +//===----------------------------------------------------------------------===// +bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) { + const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>(); + if (!FPT) + return false; + + // Verify the function takes two arguments, three in the _chk version. + int numArgs = FPT->getNumParams(); + if (numArgs != 2 && numArgs != 3) + return false; + + // Verify the type for both arguments. + for (int i = 0; i < 2; i++) { + // Verify that the arguments are pointers. + const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>(); + if (!PT) + return false; + + // Verify that the argument is a 'char*'. + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return false; + } + + return true; +} + +//===----------------------------------------------------------------------===// +// Check: Linear congruent random number generators should not be used +// Originally: <rdar://problem/63371000> +// CWE-338: Use of cryptographically weak prng +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_rand || !CheckRand) + return; + + const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); + if (!FTP) + return; + + if (FTP->getNumParams() == 1) { + // Is the argument an 'unsigned short *'? + // (Actually any integer type is allowed.) + const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>(); + if (!PT) + return; + + if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType()) + return; + } else if (FTP->getNumParams() != 0) + return; + + // Issue a warning. + SmallString<256> buf1; + llvm::raw_svector_ostream os1(buf1); + os1 << '\'' << *FD << "' is a poor random number generator"; + + SmallString<256> buf2; + llvm::raw_svector_ostream os2(buf2); + os2 << "Function '" << *FD + << "' is obsolete because it implements a poor random number generator." + << " Use 'arc4random' instead"; + + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(), + "Security", os2.str(), CELoc, + CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// Check: 'random' should not be used +// Originally: <rdar://problem/63371000> +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) { + if (!CheckRand || !filter.check_rand) + return; + + const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); + if (!FTP) + return; + + // Verify that the function takes no argument. + if (FTP->getNumParams() != 0) + return; + + // Issue a warning. + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, + "'random' is not a secure random number generator", + "Security", + "The 'random' function produces a sequence of values that " + "an adversary may be able to predict. Use 'arc4random' " + "instead", CELoc, CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// Check: 'vfork' should not be used. +// POS33-C: Do not use vfork(). +//===----------------------------------------------------------------------===// + +void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) { + if (!filter.check_vfork) + return; + + // All calls to vfork() are insecure, issue a warning. + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork, + "Potential insecure implementation-specific behavior in " + "call 'vfork'", + "Security", + "Call to function 'vfork' is insecure as it can lead to " + "denial of service situations in the parent process. " + "Replace calls to vfork with calls to the safer " + "'posix_spawn' function", + CELoc, CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// Check: Should check whether privileges are dropped successfully. +// Originally: <rdar://problem/6337132> +//===----------------------------------------------------------------------===// + +void WalkAST::checkUncheckedReturnValue(CallExpr *CE) { + if (!filter.check_UncheckedReturn) + return; + + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return; + + if (II_setid[0] == nullptr) { + static const char * const identifiers[num_setids] = { + "setuid", "setgid", "seteuid", "setegid", + "setreuid", "setregid" + }; + + for (size_t i = 0; i < num_setids; i++) + II_setid[i] = &BR.getContext().Idents.get(identifiers[i]); + } + + const IdentifierInfo *id = FD->getIdentifier(); + size_t identifierid; + + for (identifierid = 0; identifierid < num_setids; identifierid++) + if (id == II_setid[identifierid]) + break; + + if (identifierid >= num_setids) + return; + + const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>(); + if (!FTP) + return; + + // Verify that the function takes one or two arguments (depending on + // the function). + if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2)) + return; + + // The arguments must be integers. + for (unsigned i = 0; i < FTP->getNumParams(); i++) + if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType()) + return; + + // Issue a warning. + SmallString<256> buf1; + llvm::raw_svector_ostream os1(buf1); + os1 << "Return value is not checked in call to '" << *FD << '\''; + + SmallString<256> buf2; + llvm::raw_svector_ostream os2(buf2); + os2 << "The return value from the call to '" << *FD + << "' is not checked. If an error occurs in '" << *FD + << "', the following code may execute with unexpected privileges"; + + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(), + "Security", os2.str(), CELoc, + CE->getCallee()->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// SecuritySyntaxChecker +//===----------------------------------------------------------------------===// + +namespace { +class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> { +public: + ChecksFilter filter; + + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter); + walker.Visit(D->getBody()); + } +}; +} + +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + SecuritySyntaxChecker *checker = \ + mgr.registerChecker<SecuritySyntaxChecker>(); \ + checker->filter.check_##name = true; \ + checker->filter.checkName_##name = mgr.getCurrentCheckName(); \ + } + +REGISTER_CHECKER(bcmp) +REGISTER_CHECKER(bcopy) +REGISTER_CHECKER(bzero) +REGISTER_CHECKER(gets) +REGISTER_CHECKER(getpw) +REGISTER_CHECKER(mkstemp) +REGISTER_CHECKER(mktemp) +REGISTER_CHECKER(strcpy) +REGISTER_CHECKER(rand) +REGISTER_CHECKER(vfork) +REGISTER_CHECKER(FloatLoopCounter) +REGISTER_CHECKER(UncheckedReturn) + + diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp new file mode 100644 index 000000000000..7688b713b06b --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp @@ -0,0 +1,93 @@ +//==- CheckSizeofPointer.cpp - Check for sizeof on pointers ------*- 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 a check for unintended use of sizeof() on pointer +// expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" + +using namespace clang; +using namespace ento; + +namespace { +class WalkAST : public StmtVisitor<WalkAST> { + BugReporter &BR; + const CheckerBase *Checker; + AnalysisDeclContext* AC; + +public: + WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) + : BR(br), Checker(checker), AC(ac) {} + void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E); + void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitChildren(Stmt *S); +}; +} + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt *Child : S->children()) + if (Child) + Visit(Child); +} + +// CWE-467: Use of sizeof() on a Pointer Type +void WalkAST::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) { + if (E->getKind() != UETT_SizeOf) + return; + + // If an explicit type is used in the code, usually the coder knows what they are + // doing. + if (E->isArgumentType()) + return; + + QualType T = E->getTypeOfArgument(); + if (T->isPointerType()) { + + // Many false positives have the form 'sizeof *p'. This is reasonable + // because people know what they are doing when they intentionally + // dereference the pointer. + Expr *ArgEx = E->getArgumentExpr(); + if (!isa<DeclRefExpr>(ArgEx->IgnoreParens())) + return; + + PathDiagnosticLocation ELoc = + PathDiagnosticLocation::createBegin(E, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), Checker, + "Potential unintended use of sizeof() on pointer type", + categories::LogicError, + "The code calls sizeof() on a pointer type. " + "This can produce an unexpected result.", + ELoc, ArgEx->getSourceRange()); + } +} + +//===----------------------------------------------------------------------===// +// SizeofPointerChecker +//===----------------------------------------------------------------------===// + +namespace { +class SizeofPointerChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + WalkAST walker(BR, this, mgr.getAnalysisDeclContext(D)); + walker.Visit(D->getBody()); + } +}; +} + +void ento::registerSizeofPointerChecker(CheckerManager &mgr) { + mgr.registerChecker<SizeofPointerChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp new file mode 100644 index 000000000000..44fac0278bdd --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp @@ -0,0 +1,335 @@ +//===- CheckerDocumentation.cpp - Documentation checker ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker lists all the checker callbacks and provides documentation for +// checker writers. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +// All checkers should be placed into anonymous namespace. +// We place the CheckerDocumentation inside ento namespace to make the +// it visible in doxygen. +namespace clang { +namespace ento { + +/// This checker documents the callback functions checkers can use to implement +/// the custom handling of the specific events during path exploration as well +/// as reporting bugs. Most of the callbacks are targeted at path-sensitive +/// checking. +/// +/// \sa CheckerContext +class CheckerDocumentation : public Checker< check::PreStmt<ReturnStmt>, + check::PostStmt<DeclStmt>, + check::PreObjCMessage, + check::PostObjCMessage, + check::ObjCMessageNil, + check::PreCall, + check::PostCall, + check::BranchCondition, + check::NewAllocator, + check::Location, + check::Bind, + check::DeadSymbols, + check::BeginFunction, + check::EndFunction, + check::EndAnalysis, + check::EndOfTranslationUnit, + eval::Call, + eval::Assume, + check::LiveSymbols, + check::RegionChanges, + check::PointerEscape, + check::ConstPointerEscape, + check::Event<ImplicitNullDerefEvent>, + check::ASTDecl<FunctionDecl> > { +public: + /// Pre-visit the Statement. + /// + /// The method will be called before the analyzer core processes the + /// statement. The notification is performed for every explored CFGElement, + /// which does not include the control flow statements such as IfStmt. The + /// callback can be specialized to be called with any subclass of Stmt. + /// + /// See checkBranchCondition() callback for performing custom processing of + /// the branching statements. + /// + /// check::PreStmt<ReturnStmt> + void checkPreStmt(const ReturnStmt *DS, CheckerContext &C) const {} + + /// Post-visit the Statement. + /// + /// The method will be called after the analyzer core processes the + /// statement. The notification is performed for every explored CFGElement, + /// which does not include the control flow statements such as IfStmt. The + /// callback can be specialized to be called with any subclass of Stmt. + /// + /// check::PostStmt<DeclStmt> + void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const; + + /// Pre-visit the Objective C message. + /// + /// This will be called before the analyzer core processes the method call. + /// This is called for any action which produces an Objective-C message send, + /// including explicit message syntax and property access. + /// + /// check::PreObjCMessage + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {} + + /// Post-visit the Objective C message. + /// \sa checkPreObjCMessage() + /// + /// check::PostObjCMessage + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {} + + /// Visit an Objective-C message whose receiver is nil. + /// + /// This will be called when the analyzer core processes a method call whose + /// receiver is definitely nil. In this case, check{Pre/Post}ObjCMessage and + /// check{Pre/Post}Call will not be called. + /// + /// check::ObjCMessageNil + void checkObjCMessageNil(const ObjCMethodCall &M, CheckerContext &C) const {} + + /// Pre-visit an abstract "call" event. + /// + /// This is used for checkers that want to check arguments or attributed + /// behavior for functions and methods no matter how they are being invoked. + /// + /// Note that this includes ALL cross-body invocations, so if you want to + /// limit your checks to, say, function calls, you should test for that at the + /// beginning of your callback function. + /// + /// check::PreCall + void checkPreCall(const CallEvent &Call, CheckerContext &C) const {} + + /// Post-visit an abstract "call" event. + /// \sa checkPreObjCMessage() + /// + /// check::PostCall + void checkPostCall(const CallEvent &Call, CheckerContext &C) const {} + + /// Pre-visit of the condition statement of a branch (such as IfStmt). + void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {} + + /// Post-visit the C++ operator new's allocation call. + /// + /// Execution of C++ operator new consists of the following phases: (1) call + /// default or overridden operator new() to allocate memory (2) cast the + /// return value of operator new() from void pointer type to class pointer + /// type, (3) assuming that the value is non-null, call the object's + /// constructor over this pointer, (4) declare that the value of the + /// new-expression is this pointer. This callback is called between steps + /// (2) and (3). Post-call for the allocator is called after step (1). + /// Pre-statement for the new-expression is called on step (4) when the value + /// of the expression is evaluated. + /// \param NE The C++ new-expression that triggered the allocation. + /// \param Target The allocated region, casted to the class type. + void checkNewAllocator(const CXXNewExpr *NE, SVal Target, + CheckerContext &) const {} + + /// Called on a load from and a store to a location. + /// + /// The method will be called each time a location (pointer) value is + /// accessed. + /// \param Loc The value of the location (pointer). + /// \param IsLoad The flag specifying if the location is a store or a load. + /// \param S The load is performed while processing the statement. + /// + /// check::Location + void checkLocation(SVal Loc, bool IsLoad, const Stmt *S, + CheckerContext &) const {} + + /// Called on binding of a value to a location. + /// + /// \param Loc The value of the location (pointer). + /// \param Val The value which will be stored at the location Loc. + /// \param S The bind is performed while processing the statement S. + /// + /// check::Bind + void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &) const {} + + /// Called whenever a symbol becomes dead. + /// + /// This callback should be used by the checkers to aggressively clean + /// up/reduce the checker state, which is important for reducing the overall + /// memory usage. Specifically, if a checker keeps symbol specific information + /// in the state, it can and should be dropped after the symbol becomes dead. + /// In addition, reporting a bug as soon as the checker becomes dead leads to + /// more precise diagnostics. (For example, one should report that a malloced + /// variable is not freed right after it goes out of scope.) + /// + /// \param SR The SymbolReaper object can be queried to determine which + /// symbols are dead. + /// + /// check::DeadSymbols + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const {} + + + /// Called when the analyzer core starts analyzing a function, + /// regardless of whether it is analyzed at the top level or is inlined. + /// + /// check::BeginFunction + void checkBeginFunction(CheckerContext &Ctx) const {} + + /// Called when the analyzer core reaches the end of a + /// function being analyzed regardless of whether it is analyzed at the top + /// level or is inlined. + /// + /// check::EndFunction + void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const {} + + /// Called after all the paths in the ExplodedGraph reach end of path + /// - the symbolic execution graph is fully explored. + /// + /// This callback should be used in cases when a checker needs to have a + /// global view of the information generated on all paths. For example, to + /// compare execution summary/result several paths. + /// See IdempotentOperationChecker for a usage example. + /// + /// check::EndAnalysis + void checkEndAnalysis(ExplodedGraph &G, + BugReporter &BR, + ExprEngine &Eng) const {} + + /// Called after analysis of a TranslationUnit is complete. + /// + /// check::EndOfTranslationUnit + void checkEndOfTranslationUnit(const TranslationUnitDecl *TU, + AnalysisManager &Mgr, + BugReporter &BR) const {} + + /// Evaluates function call. + /// + /// The analysis core threats all function calls in the same way. However, some + /// functions have special meaning, which should be reflected in the program + /// state. This callback allows a checker to provide domain specific knowledge + /// about the particular functions it knows about. + /// + /// \returns true if the call has been successfully evaluated + /// and false otherwise. Note, that only one checker can evaluate a call. If + /// more than one checker claims that they can evaluate the same call the + /// first one wins. + /// + /// eval::Call + bool evalCall(const CallExpr *CE, CheckerContext &C) const { return true; } + + /// Handles assumptions on symbolic values. + /// + /// This method is called when a symbolic expression is assumed to be true or + /// false. For example, the assumptions are performed when evaluating a + /// condition at a branch. The callback allows checkers track the assumptions + /// performed on the symbols of interest and change the state accordingly. + /// + /// eval::Assume + ProgramStateRef evalAssume(ProgramStateRef State, + SVal Cond, + bool Assumption) const { return State; } + + /// Allows modifying SymbolReaper object. For example, checkers can explicitly + /// register symbols of interest as live. These symbols will not be marked + /// dead and removed. + /// + /// check::LiveSymbols + void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const {} + + /// Called when the contents of one or more regions change. + /// + /// This can occur in many different ways: an explicit bind, a blanket + /// invalidation of the region contents, or by passing a region to a function + /// call whose behavior the analyzer cannot model perfectly. + /// + /// \param State The current program state. + /// \param Invalidated A set of all symbols potentially touched by the change. + /// \param ExplicitRegions The regions explicitly requested for invalidation. + /// For a function call, this would be the arguments. For a bind, this + /// would be the region being bound to. + /// \param Regions The transitive closure of regions accessible from, + /// \p ExplicitRegions, i.e. all regions that may have been touched + /// by this change. For a simple bind, this list will be the same as + /// \p ExplicitRegions, since a bind does not affect the contents of + /// anything accessible through the base region. + /// \param LCtx LocationContext that is useful for getting various contextual + /// info, like callstack, CFG etc. + /// \param Call The opaque call triggering this invalidation. Will be 0 if the + /// change was not triggered by a call. + /// + /// check::RegionChanges + ProgramStateRef + checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call) const { + return State; + } + + /// Called when pointers escape. + /// + /// This notifies the checkers about pointer escape, which occurs whenever + /// the analyzer cannot track the symbol any more. For example, as a + /// result of assigning a pointer into a global or when it's passed to a + /// function call the analyzer cannot model. + /// + /// \param State The state at the point of escape. + /// \param Escaped The list of escaped symbols. + /// \param Call The corresponding CallEvent, if the symbols escape as + /// parameters to the given call. + /// \param Kind How the symbols have escaped. + /// \returns Checkers can modify the state by returning a new state. + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + return State; + } + + /// Called when const pointers escape. + /// + /// Note: in most cases checkPointerEscape callback is sufficient. + /// \sa checkPointerEscape + ProgramStateRef checkConstPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + return State; + } + + /// check::Event<ImplicitNullDerefEvent> + void checkEvent(ImplicitNullDerefEvent Event) const {} + + /// Check every declaration in the AST. + /// + /// An AST traversal callback, which should only be used when the checker is + /// not path sensitive. It will be called for every Declaration in the AST and + /// can be specialized to only be called on subclasses of Decl, for example, + /// FunctionDecl. + /// + /// check::ASTDecl<FunctionDecl> + void checkASTDecl(const FunctionDecl *D, + AnalysisManager &Mgr, + BugReporter &BR) const {} +}; + +void CheckerDocumentation::checkPostStmt(const DeclStmt *DS, + CheckerContext &C) const { +} + +} // end namespace ento +} // end namespace clang diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp new file mode 100644 index 000000000000..673608db1a1d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -0,0 +1,155 @@ +//===- Chrootchecker.cpp -------- Basic security checks ---------*- 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 chroot checker, which checks improper use of chroot. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" + +using namespace clang; +using namespace ento; + +namespace { + +// enum value that represent the jail state +enum Kind { NO_CHROOT, ROOT_CHANGED, JAIL_ENTERED }; + +bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; } +//bool isJailEntered(intptr_t k) { return k == JAIL_ENTERED; } + +// This checker checks improper use of chroot. +// The state transition: +// NO_CHROOT ---chroot(path)--> ROOT_CHANGED ---chdir(/) --> JAIL_ENTERED +// | | +// ROOT_CHANGED<--chdir(..)-- JAIL_ENTERED<--chdir(..)-- +// | | +// bug<--foo()-- JAIL_ENTERED<--foo()-- +class ChrootChecker : public Checker<eval::Call, check::PreStmt<CallExpr> > { + mutable IdentifierInfo *II_chroot, *II_chdir; + // This bug refers to possibly break out of a chroot() jail. + mutable std::unique_ptr<BuiltinBug> BT_BreakJail; + +public: + ChrootChecker() : II_chroot(nullptr), II_chdir(nullptr) {} + + static void *getTag() { + static int x; + return &x; + } + + bool evalCall(const CallExpr *CE, CheckerContext &C) const; + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + +private: + void Chroot(CheckerContext &C, const CallExpr *CE) const; + void Chdir(CheckerContext &C, const CallExpr *CE) const; +}; + +} // end anonymous namespace + +bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD) + return false; + + ASTContext &Ctx = C.getASTContext(); + if (!II_chroot) + II_chroot = &Ctx.Idents.get("chroot"); + if (!II_chdir) + II_chdir = &Ctx.Idents.get("chdir"); + + if (FD->getIdentifier() == II_chroot) { + Chroot(C, CE); + return true; + } + if (FD->getIdentifier() == II_chdir) { + Chdir(C, CE); + return true; + } + + return false; +} + +void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + ProgramStateManager &Mgr = state->getStateManager(); + + // Once encouter a chroot(), set the enum value ROOT_CHANGED directly in + // the GDM. + state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) ROOT_CHANGED); + C.addTransition(state); +} + +void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + ProgramStateManager &Mgr = state->getStateManager(); + + // If there are no jail state in the GDM, just return. + const void *k = state->FindGDM(ChrootChecker::getTag()); + if (!k) + return; + + // After chdir("/"), enter the jail, set the enum value JAIL_ENTERED. + const Expr *ArgExpr = CE->getArg(0); + SVal ArgVal = C.getSVal(ArgExpr); + + if (const MemRegion *R = ArgVal.getAsRegion()) { + R = R->StripCasts(); + if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) { + const StringLiteral* Str = StrRegion->getStringLiteral(); + if (Str->getString() == "/") + state = Mgr.addGDM(state, ChrootChecker::getTag(), + (void*) JAIL_ENTERED); + } + } + + C.addTransition(state); +} + +// Check the jail state before any function call except chroot and chdir(). +void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const { + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD) + return; + + ASTContext &Ctx = C.getASTContext(); + if (!II_chroot) + II_chroot = &Ctx.Idents.get("chroot"); + if (!II_chdir) + II_chdir = &Ctx.Idents.get("chdir"); + + // Ignore chroot and chdir. + if (FD->getIdentifier() == II_chroot || FD->getIdentifier() == II_chdir) + return; + + // If jail state is ROOT_CHANGED, generate BugReport. + void *const* k = C.getState()->FindGDM(ChrootChecker::getTag()); + if (k) + if (isRootChanged((intptr_t) *k)) + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + if (!BT_BreakJail) + BT_BreakJail.reset(new BuiltinBug( + this, "Break out of jail", "No call of chdir(\"/\") immediately " + "after chroot")); + C.emitReport(llvm::make_unique<BugReport>( + *BT_BreakJail, BT_BreakJail->getDescription(), N)); + } +} + +void ento::registerChrootChecker(CheckerManager &mgr) { + mgr.registerChecker<ChrootChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp new file mode 100644 index 000000000000..89354b866004 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp @@ -0,0 +1,204 @@ +//===--- CloneChecker.cpp - Clone detection checker -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// CloneChecker is a checker that reports clones in the current translation +/// unit. +/// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Analysis/CloneDetection.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class CloneChecker + : public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> { + mutable CloneDetector Detector; + mutable std::unique_ptr<BugType> BT_Exact, BT_Suspicious; + +public: + void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const; + + void checkEndOfTranslationUnit(const TranslationUnitDecl *TU, + AnalysisManager &Mgr, BugReporter &BR) const; + + /// Reports all clones to the user. + void reportClones(BugReporter &BR, AnalysisManager &Mgr, + std::vector<CloneDetector::CloneGroup> &CloneGroups) const; + + /// Reports only suspicious clones to the user along with information + /// that explain why they are suspicious. + void reportSuspiciousClones( + BugReporter &BR, AnalysisManager &Mgr, + std::vector<CloneDetector::CloneGroup> &CloneGroups) const; +}; +} // end anonymous namespace + +void CloneChecker::checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const { + // Every statement that should be included in the search for clones needs to + // be passed to the CloneDetector. + Detector.analyzeCodeBody(D); +} + +void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU, + AnalysisManager &Mgr, + BugReporter &BR) const { + // At this point, every statement in the translation unit has been analyzed by + // the CloneDetector. The only thing left to do is to report the found clones. + + int MinComplexity = Mgr.getAnalyzerOptions().getCheckerIntegerOption( + "MinimumCloneComplexity", 50, this); + assert(MinComplexity >= 0); + + bool ReportSuspiciousClones = Mgr.getAnalyzerOptions() + .getCheckerBooleanOption("ReportSuspiciousClones", true, this); + + bool ReportNormalClones = Mgr.getAnalyzerOptions().getCheckerBooleanOption( + "ReportNormalClones", true, this); + + StringRef IgnoredFilesPattern = Mgr.getAnalyzerOptions() + .getCheckerStringOption("IgnoredFilesPattern", "", this); + + // Let the CloneDetector create a list of clones from all the analyzed + // statements. We don't filter for matching variable patterns at this point + // because reportSuspiciousClones() wants to search them for errors. + std::vector<CloneDetector::CloneGroup> AllCloneGroups; + + Detector.findClones( + AllCloneGroups, FilenamePatternConstraint(IgnoredFilesPattern), + RecursiveCloneTypeIIHashConstraint(), MinGroupSizeConstraint(2), + MinComplexityConstraint(MinComplexity), + RecursiveCloneTypeIIVerifyConstraint(), OnlyLargestCloneConstraint()); + + if (ReportSuspiciousClones) + reportSuspiciousClones(BR, Mgr, AllCloneGroups); + + // We are done for this translation unit unless we also need to report normal + // clones. + if (!ReportNormalClones) + return; + + // Now that the suspicious clone detector has checked for pattern errors, + // we also filter all clones who don't have matching patterns + CloneDetector::constrainClones(AllCloneGroups, + MatchingVariablePatternConstraint(), + MinGroupSizeConstraint(2)); + + reportClones(BR, Mgr, AllCloneGroups); +} + +static PathDiagnosticLocation makeLocation(const StmtSequence &S, + AnalysisManager &Mgr) { + ASTContext &ACtx = Mgr.getASTContext(); + return PathDiagnosticLocation::createBegin( + S.front(), ACtx.getSourceManager(), + Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl())); +} + +void CloneChecker::reportClones( + BugReporter &BR, AnalysisManager &Mgr, + std::vector<CloneDetector::CloneGroup> &CloneGroups) const { + + if (!BT_Exact) + BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone")); + + for (const CloneDetector::CloneGroup &Group : CloneGroups) { + // We group the clones by printing the first as a warning and all others + // as a note. + auto R = llvm::make_unique<BugReport>(*BT_Exact, "Duplicate code detected", + makeLocation(Group.front(), Mgr)); + R->addRange(Group.front().getSourceRange()); + + for (unsigned i = 1; i < Group.size(); ++i) + R->addNote("Similar code here", makeLocation(Group[i], Mgr), + Group[i].getSourceRange()); + BR.emitReport(std::move(R)); + } +} + +void CloneChecker::reportSuspiciousClones( + BugReporter &BR, AnalysisManager &Mgr, + std::vector<CloneDetector::CloneGroup> &CloneGroups) const { + std::vector<VariablePattern::SuspiciousClonePair> Pairs; + + for (const CloneDetector::CloneGroup &Group : CloneGroups) { + for (unsigned i = 0; i < Group.size(); ++i) { + VariablePattern PatternA(Group[i]); + + for (unsigned j = i + 1; j < Group.size(); ++j) { + VariablePattern PatternB(Group[j]); + + VariablePattern::SuspiciousClonePair ClonePair; + // For now, we only report clones which break the variable pattern just + // once because multiple differences in a pattern are an indicator that + // those differences are maybe intended (e.g. because it's actually a + // different algorithm). + // FIXME: In very big clones even multiple variables can be unintended, + // so replacing this number with a percentage could better handle such + // cases. On the other hand it could increase the false-positive rate + // for all clones if the percentage is too high. + if (PatternA.countPatternDifferences(PatternB, &ClonePair) == 1) { + Pairs.push_back(ClonePair); + break; + } + } + } + } + + if (!BT_Suspicious) + BT_Suspicious.reset( + new BugType(this, "Suspicious code clone", "Code clone")); + + ASTContext &ACtx = BR.getContext(); + SourceManager &SM = ACtx.getSourceManager(); + AnalysisDeclContext *ADC = + Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl()); + + for (VariablePattern::SuspiciousClonePair &Pair : Pairs) { + // FIXME: We are ignoring the suggestions currently, because they are + // only 50% accurate (even if the second suggestion is unavailable), + // which may confuse the user. + // Think how to perform more accurate suggestions? + + auto R = llvm::make_unique<BugReport>( + *BT_Suspicious, + "Potential copy-paste error; did you really mean to use '" + + Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?", + PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM, + ADC)); + R->addRange(Pair.FirstCloneInfo.Mention->getSourceRange()); + + R->addNote("Similar code using '" + + Pair.SecondCloneInfo.Variable->getNameAsString() + "' here", + PathDiagnosticLocation::createBegin(Pair.SecondCloneInfo.Mention, + SM, ADC), + Pair.SecondCloneInfo.Mention->getSourceRange()); + + BR.emitReport(std::move(R)); + } +} + +//===----------------------------------------------------------------------===// +// Register CloneChecker +//===----------------------------------------------------------------------===// + +void ento::registerCloneChecker(CheckerManager &Mgr) { + Mgr.registerChecker<CloneChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp new file mode 100644 index 000000000000..a5c67c2a5b45 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp @@ -0,0 +1,198 @@ +//=== ConversionChecker.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Check that there is no loss of sign/precision in assignments, comparisons +// and multiplications. +// +// ConversionChecker uses path sensitive analysis to determine possible values +// of expressions. A warning is reported when: +// * a negative value is implicitly converted to an unsigned value in an +// assignment, comparison or multiplication. +// * assignment / initialization when the source value is greater than the max +// value of the target integer type +// * assignment / initialization when the source integer is above the range +// where the target floating point type can represent all integers +// +// Many compilers and tools have similar checks that are based on semantic +// analysis. Those checks are sound but have poor precision. ConversionChecker +// is an alternative to those checks. +// +//===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ParentMap.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/APFloat.h" + +#include <climits> + +using namespace clang; +using namespace ento; + +namespace { +class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> { +public: + void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const; + +private: + mutable std::unique_ptr<BuiltinBug> BT; + + bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType, + CheckerContext &C) const; + + bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const; + + void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const; +}; +} + +void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast, + CheckerContext &C) const { + // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for + // calculations also. + if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts())) + return; + + // Don't warn for loss of sign/precision in macros. + if (Cast->getExprLoc().isMacroID()) + return; + + // Get Parent. + const ParentMap &PM = C.getLocationContext()->getParentMap(); + const Stmt *Parent = PM.getParent(Cast); + if (!Parent) + return; + + bool LossOfSign = false; + bool LossOfPrecision = false; + + // Loss of sign/precision in binary operation. + if (const auto *B = dyn_cast<BinaryOperator>(Parent)) { + BinaryOperator::Opcode Opc = B->getOpcode(); + if (Opc == BO_Assign) { + LossOfSign = isLossOfSign(Cast, C); + LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); + } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) { + // No loss of sign. + LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); + } else if (Opc == BO_MulAssign) { + LossOfSign = isLossOfSign(Cast, C); + LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); + } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) { + LossOfSign = isLossOfSign(Cast, C); + // No loss of precision. + } else if (Opc == BO_AndAssign) { + LossOfSign = isLossOfSign(Cast, C); + // No loss of precision. + } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) { + LossOfSign = isLossOfSign(Cast, C); + LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C); + } else if (B->isRelationalOp() || B->isMultiplicativeOp()) { + LossOfSign = isLossOfSign(Cast, C); + } + } else if (isa<DeclStmt>(Parent)) { + LossOfSign = isLossOfSign(Cast, C); + LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C); + } + + if (LossOfSign || LossOfPrecision) { + // Generate an error node. + ExplodedNode *N = C.generateNonFatalErrorNode(C.getState()); + if (!N) + return; + if (LossOfSign) + reportBug(N, C, "Loss of sign in implicit conversion"); + if (LossOfPrecision) + reportBug(N, C, "Loss of precision in implicit conversion"); + } +} + +void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C, + const char Msg[]) const { + if (!BT) + BT.reset( + new BuiltinBug(this, "Conversion", "Possible loss of sign/precision.")); + + // Generate a report for this bug. + auto R = llvm::make_unique<BugReport>(*BT, Msg, N); + C.emitReport(std::move(R)); +} + +bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast, + QualType DestType, + CheckerContext &C) const { + // Don't warn about explicit loss of precision. + if (Cast->isEvaluatable(C.getASTContext())) + return false; + + QualType SubType = Cast->IgnoreParenImpCasts()->getType(); + + if (!DestType->isRealType() || !SubType->isIntegerType()) + return false; + + const bool isFloat = DestType->isFloatingType(); + + const auto &AC = C.getASTContext(); + + // We will find the largest RepresentsUntilExp value such that the DestType + // can exactly represent all nonnegative integers below 2^RepresentsUntilExp. + unsigned RepresentsUntilExp; + + if (isFloat) { + const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType); + RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema); + } else { + RepresentsUntilExp = AC.getIntWidth(DestType); + if (RepresentsUntilExp == 1) { + // This is just casting a number to bool, probably not a bug. + return false; + } + if (DestType->isSignedIntegerType()) + RepresentsUntilExp--; + } + + if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) { + // Avoid overflow in our later calculations. + return false; + } + + unsigned CorrectedSrcWidth = AC.getIntWidth(SubType); + if (SubType->isSignedIntegerType()) + CorrectedSrcWidth--; + + if (RepresentsUntilExp >= CorrectedSrcWidth) { + // Simple case: the destination can store all values of the source type. + return false; + } + + unsigned long long MaxVal = 1ULL << RepresentsUntilExp; + if (isFloat) { + // If this is a floating point type, it can also represent MaxVal exactly. + MaxVal++; + } + return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal); + // TODO: maybe also check negative values with too large magnitude. +} + +bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast, + CheckerContext &C) const { + QualType CastType = Cast->getType(); + QualType SubType = Cast->IgnoreParenImpCasts()->getType(); + + if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType()) + return false; + + return C.isNegative(Cast->getSubExpr()); +} + +void ento::registerConversionChecker(CheckerManager &mgr) { + mgr.registerChecker<ConversionChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp new file mode 100644 index 000000000000..4e0f6d3bedfd --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp @@ -0,0 +1,481 @@ +//==- DeadStoresChecker.cpp - Check for stores to dead variables -*- 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 a DeadStores, a flow-sensitive checker that looks for +// stores to variables that are no longer live. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/BitVector.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/SaveAndRestore.h" + +using namespace clang; +using namespace ento; + +namespace { + +/// A simple visitor to record what VarDecls occur in EH-handling code. +class EHCodeVisitor : public RecursiveASTVisitor<EHCodeVisitor> { +public: + bool inEH; + llvm::DenseSet<const VarDecl *> &S; + + bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) { + SaveAndRestore<bool> inFinally(inEH, true); + return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S); + } + + bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) { + SaveAndRestore<bool> inCatch(inEH, true); + return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S); + } + + bool TraverseCXXCatchStmt(CXXCatchStmt *S) { + SaveAndRestore<bool> inCatch(inEH, true); + return TraverseStmt(S->getHandlerBlock()); + } + + bool VisitDeclRefExpr(DeclRefExpr *DR) { + if (inEH) + if (const VarDecl *D = dyn_cast<VarDecl>(DR->getDecl())) + S.insert(D); + return true; + } + + EHCodeVisitor(llvm::DenseSet<const VarDecl *> &S) : + inEH(false), S(S) {} +}; + +// FIXME: Eventually migrate into its own file, and have it managed by +// AnalysisManager. +class ReachableCode { + const CFG &cfg; + llvm::BitVector reachable; +public: + ReachableCode(const CFG &cfg) + : cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {} + + void computeReachableBlocks(); + + bool isReachable(const CFGBlock *block) const { + return reachable[block->getBlockID()]; + } +}; +} + +void ReachableCode::computeReachableBlocks() { + if (!cfg.getNumBlockIDs()) + return; + + SmallVector<const CFGBlock*, 10> worklist; + worklist.push_back(&cfg.getEntry()); + + while (!worklist.empty()) { + const CFGBlock *block = worklist.pop_back_val(); + llvm::BitVector::reference isReachable = reachable[block->getBlockID()]; + if (isReachable) + continue; + isReachable = true; + for (CFGBlock::const_succ_iterator i = block->succ_begin(), + e = block->succ_end(); i != e; ++i) + if (const CFGBlock *succ = *i) + worklist.push_back(succ); + } +} + +static const Expr * +LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) { + while (Ex) { + const BinaryOperator *BO = + dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts()); + if (!BO) + break; + if (BO->getOpcode() == BO_Assign) { + Ex = BO->getRHS(); + continue; + } + if (BO->getOpcode() == BO_Comma) { + Ex = BO->getRHS(); + continue; + } + break; + } + return Ex; +} + +namespace { +class DeadStoreObs : public LiveVariables::Observer { + const CFG &cfg; + ASTContext &Ctx; + BugReporter& BR; + const CheckerBase *Checker; + AnalysisDeclContext* AC; + ParentMap& Parents; + llvm::SmallPtrSet<const VarDecl*, 20> Escaped; + std::unique_ptr<ReachableCode> reachableCode; + const CFGBlock *currentBlock; + std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH; + + enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit }; + +public: + DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br, + const CheckerBase *checker, AnalysisDeclContext *ac, + ParentMap &parents, + llvm::SmallPtrSet<const VarDecl *, 20> &escaped) + : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents), + Escaped(escaped), currentBlock(nullptr) {} + + ~DeadStoreObs() override {} + + bool isLive(const LiveVariables::LivenessValues &Live, const VarDecl *D) { + if (Live.isLive(D)) + return true; + // Lazily construct the set that records which VarDecls are in + // EH code. + if (!InEH.get()) { + InEH.reset(new llvm::DenseSet<const VarDecl *>()); + EHCodeVisitor V(*InEH.get()); + V.TraverseStmt(AC->getBody()); + } + // Treat all VarDecls that occur in EH code as being "always live" + // when considering to suppress dead stores. Frequently stores + // are followed by reads in EH code, but we don't have the ability + // to analyze that yet. + return InEH->count(D); + } + + void Report(const VarDecl *V, DeadStoreKind dsk, + PathDiagnosticLocation L, SourceRange R) { + if (Escaped.count(V)) + return; + + // Compute reachable blocks within the CFG for trivial cases + // where a bogus dead store can be reported because itself is unreachable. + if (!reachableCode.get()) { + reachableCode.reset(new ReachableCode(cfg)); + reachableCode->computeReachableBlocks(); + } + + if (!reachableCode->isReachable(currentBlock)) + return; + + SmallString<64> buf; + llvm::raw_svector_ostream os(buf); + const char *BugType = nullptr; + + switch (dsk) { + case DeadInit: + BugType = "Dead initialization"; + os << "Value stored to '" << *V + << "' during its initialization is never read"; + break; + + case DeadIncrement: + BugType = "Dead increment"; + LLVM_FALLTHROUGH; + case Standard: + if (!BugType) BugType = "Dead assignment"; + os << "Value stored to '" << *V << "' is never read"; + break; + + case Enclosing: + // Don't report issues in this case, e.g.: "if (x = foo())", + // where 'x' is unused later. We have yet to see a case where + // this is a real bug. + return; + } + + BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(), + L, R); + } + + void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val, + DeadStoreKind dsk, + const LiveVariables::LivenessValues &Live) { + + if (!VD->hasLocalStorage()) + return; + // Reference types confuse the dead stores checker. Skip them + // for now. + if (VD->getType()->getAs<ReferenceType>()) + return; + + if (!isLive(Live, VD) && + !(VD->hasAttr<UnusedAttr>() || VD->hasAttr<BlocksAttr>() || + VD->hasAttr<ObjCPreciseLifetimeAttr>())) { + + PathDiagnosticLocation ExLoc = + PathDiagnosticLocation::createBegin(Ex, BR.getSourceManager(), AC); + Report(VD, dsk, ExLoc, Val->getSourceRange()); + } + } + + void CheckDeclRef(const DeclRefExpr *DR, const Expr *Val, DeadStoreKind dsk, + const LiveVariables::LivenessValues& Live) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) + CheckVarDecl(VD, DR, Val, dsk, Live); + } + + bool isIncrement(VarDecl *VD, const BinaryOperator* B) { + if (B->isCompoundAssignmentOp()) + return true; + + const Expr *RHS = B->getRHS()->IgnoreParenCasts(); + const BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS); + + if (!BRHS) + return false; + + const DeclRefExpr *DR; + + if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts()))) + if (DR->getDecl() == VD) + return true; + + if ((DR = dyn_cast<DeclRefExpr>(BRHS->getRHS()->IgnoreParenCasts()))) + if (DR->getDecl() == VD) + return true; + + return false; + } + + void observeStmt(const Stmt *S, const CFGBlock *block, + const LiveVariables::LivenessValues &Live) override { + + currentBlock = block; + + // Skip statements in macros. + if (S->getBeginLoc().isMacroID()) + return; + + // Only cover dead stores from regular assignments. ++/-- dead stores + // have never flagged a real bug. + if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) { + if (!B->isAssignmentOp()) return; // Skip non-assignments. + + if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS())) + if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + // Special case: check for assigning null to a pointer. + // This is a common form of defensive programming. + const Expr *RHS = + LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS()); + RHS = RHS->IgnoreParenCasts(); + + QualType T = VD->getType(); + if (T.isVolatileQualified()) + return; + if (T->isPointerType() || T->isObjCObjectPointerType()) { + if (RHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull)) + return; + } + + // Special case: self-assignments. These are often used to shut up + // "unused variable" compiler warnings. + if (const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS)) + if (VD == dyn_cast<VarDecl>(RhsDR->getDecl())) + return; + + // Otherwise, issue a warning. + DeadStoreKind dsk = Parents.isConsumedExpr(B) + ? Enclosing + : (isIncrement(VD,B) ? DeadIncrement : Standard); + + CheckVarDecl(VD, DR, B->getRHS(), dsk, Live); + } + } + else if (const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) { + if (!U->isIncrementOp() || U->isPrefix()) + return; + + const Stmt *parent = Parents.getParentIgnoreParenCasts(U); + if (!parent || !isa<ReturnStmt>(parent)) + return; + + const Expr *Ex = U->getSubExpr()->IgnoreParenCasts(); + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex)) + CheckDeclRef(DR, U, DeadIncrement, Live); + } + else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) + // Iterate through the decls. Warn if any initializers are complex + // expressions that are not live (never used). + for (const auto *DI : DS->decls()) { + const auto *V = dyn_cast<VarDecl>(DI); + + if (!V) + continue; + + if (V->hasLocalStorage()) { + // Reference types confuse the dead stores checker. Skip them + // for now. + if (V->getType()->getAs<ReferenceType>()) + return; + + if (const Expr *E = V->getInit()) { + while (const FullExpr *FE = dyn_cast<FullExpr>(E)) + E = FE->getSubExpr(); + + // Look through transitive assignments, e.g.: + // int x = y = 0; + E = LookThroughTransitiveAssignmentsAndCommaOperators(E); + + // Don't warn on C++ objects (yet) until we can show that their + // constructors/destructors don't have side effects. + if (isa<CXXConstructExpr>(E)) + return; + + // A dead initialization is a variable that is dead after it + // is initialized. We don't flag warnings for those variables + // marked 'unused' or 'objc_precise_lifetime'. + if (!isLive(Live, V) && + !V->hasAttr<UnusedAttr>() && + !V->hasAttr<ObjCPreciseLifetimeAttr>()) { + // Special case: check for initializations with constants. + // + // e.g. : int x = 0; + // + // If x is EVER assigned a new value later, don't issue + // a warning. This is because such initialization can be + // due to defensive programming. + if (E->isEvaluatable(Ctx)) + return; + + if (const DeclRefExpr *DRE = + dyn_cast<DeclRefExpr>(E->IgnoreParenCasts())) + if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { + // Special case: check for initialization from constant + // variables. + // + // e.g. extern const int MyConstant; + // int x = MyConstant; + // + if (VD->hasGlobalStorage() && + VD->getType().isConstQualified()) + return; + // Special case: check for initialization from scalar + // parameters. This is often a form of defensive + // programming. Non-scalars are still an error since + // because it more likely represents an actual algorithmic + // bug. + if (isa<ParmVarDecl>(VD) && VD->getType()->isScalarType()) + return; + } + + PathDiagnosticLocation Loc = + PathDiagnosticLocation::create(V, BR.getSourceManager()); + Report(V, DeadInit, Loc, E->getSourceRange()); + } + } + } + } + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Driver function to invoke the Dead-Stores checker on a CFG. +//===----------------------------------------------------------------------===// + +namespace { +class FindEscaped { +public: + llvm::SmallPtrSet<const VarDecl*, 20> Escaped; + + void operator()(const Stmt *S) { + // Check for '&'. Any VarDecl whose address has been taken we treat as + // escaped. + // FIXME: What about references? + if (auto *LE = dyn_cast<LambdaExpr>(S)) { + findLambdaReferenceCaptures(LE); + return; + } + + const UnaryOperator *U = dyn_cast<UnaryOperator>(S); + if (!U) + return; + if (U->getOpcode() != UO_AddrOf) + return; + + const Expr *E = U->getSubExpr()->IgnoreParenCasts(); + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) + Escaped.insert(VD); + } + + // Treat local variables captured by reference in C++ lambdas as escaped. + void findLambdaReferenceCaptures(const LambdaExpr *LE) { + const CXXRecordDecl *LambdaClass = LE->getLambdaClass(); + llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields; + FieldDecl *ThisCaptureField; + LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField); + + for (const LambdaCapture &C : LE->captures()) { + if (!C.capturesVariable()) + continue; + + VarDecl *VD = C.getCapturedVar(); + const FieldDecl *FD = CaptureFields[VD]; + if (!FD) + continue; + + // If the capture field is a reference type, it is capture-by-reference. + if (FD->getType()->isReferenceType()) + Escaped.insert(VD); + } + } +}; +} // end anonymous namespace + + +//===----------------------------------------------------------------------===// +// DeadStoresChecker +//===----------------------------------------------------------------------===// + +namespace { +class DeadStoresChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + + // Don't do anything for template instantiations. + // Proving that code in a template instantiation is "dead" + // means proving that it is dead in all instantiations. + // This same problem exists with -Wunreachable-code. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + if (FD->isTemplateInstantiation()) + return; + + if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) { + CFG &cfg = *mgr.getCFG(D); + AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D); + ParentMap &pmap = mgr.getParentMap(D); + FindEscaped FS; + cfg.VisitBlockStmts(FS); + DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped); + L->runOnAllBlocks(A); + } + } +}; +} + +void ento::registerDeadStoresChecker(CheckerManager &mgr) { + mgr.registerChecker<DeadStoresChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp new file mode 100644 index 000000000000..90b1111aff0f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp @@ -0,0 +1,235 @@ +//==- DebugCheckers.cpp - Debugging Checkers ---------------------*- 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 checkers that display debugging information. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Analysis/Analyses/Dominators.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/CallGraph.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "llvm/Support/Process.h" + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// DominatorsTreeDumper +//===----------------------------------------------------------------------===// + +namespace { +class DominatorsTreeDumper : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D)) { + DominatorTree dom; + dom.buildDominatorTree(*AC); + dom.dump(); + } + } +}; +} + +void ento::registerDominatorsTreeDumper(CheckerManager &mgr) { + mgr.registerChecker<DominatorsTreeDumper>(); +} + +//===----------------------------------------------------------------------===// +// LiveVariablesDumper +//===----------------------------------------------------------------------===// + +namespace { +class LiveVariablesDumper : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (LiveVariables* L = mgr.getAnalysis<LiveVariables>(D)) { + L->dumpBlockLiveness(mgr.getSourceManager()); + } + } +}; +} + +void ento::registerLiveVariablesDumper(CheckerManager &mgr) { + mgr.registerChecker<LiveVariablesDumper>(); +} + +//===----------------------------------------------------------------------===// +// LiveStatementsDumper +//===----------------------------------------------------------------------===// + +namespace { +class LiveStatementsDumper : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, + BugReporter &BR) const { + if (LiveVariables *L = Mgr.getAnalysis<RelaxedLiveVariables>(D)) + L->dumpStmtLiveness(Mgr.getSourceManager()); + } +}; +} + +void ento::registerLiveStatementsDumper(CheckerManager &mgr) { + mgr.registerChecker<LiveStatementsDumper>(); +} + +//===----------------------------------------------------------------------===// +// CFGViewer +//===----------------------------------------------------------------------===// + +namespace { +class CFGViewer : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + if (CFG *cfg = mgr.getCFG(D)) { + cfg->viewCFG(mgr.getLangOpts()); + } + } +}; +} + +void ento::registerCFGViewer(CheckerManager &mgr) { + mgr.registerChecker<CFGViewer>(); +} + +//===----------------------------------------------------------------------===// +// CFGDumper +//===----------------------------------------------------------------------===// + +namespace { +class CFGDumper : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + PrintingPolicy Policy(mgr.getLangOpts()); + Policy.TerseOutput = true; + Policy.PolishForDeclaration = true; + D->print(llvm::errs(), Policy); + + if (CFG *cfg = mgr.getCFG(D)) { + cfg->dump(mgr.getLangOpts(), + llvm::sys::Process::StandardErrHasColors()); + } + } +}; +} + +void ento::registerCFGDumper(CheckerManager &mgr) { + mgr.registerChecker<CFGDumper>(); +} + +//===----------------------------------------------------------------------===// +// CallGraphViewer +//===----------------------------------------------------------------------===// + +namespace { +class CallGraphViewer : public Checker< check::ASTDecl<TranslationUnitDecl> > { +public: + void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager& mgr, + BugReporter &BR) const { + CallGraph CG; + CG.addToCallGraph(const_cast<TranslationUnitDecl*>(TU)); + CG.viewGraph(); + } +}; +} + +void ento::registerCallGraphViewer(CheckerManager &mgr) { + mgr.registerChecker<CallGraphViewer>(); +} + +//===----------------------------------------------------------------------===// +// CallGraphDumper +//===----------------------------------------------------------------------===// + +namespace { +class CallGraphDumper : public Checker< check::ASTDecl<TranslationUnitDecl> > { +public: + void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager& mgr, + BugReporter &BR) const { + CallGraph CG; + CG.addToCallGraph(const_cast<TranslationUnitDecl*>(TU)); + CG.dump(); + } +}; +} + +void ento::registerCallGraphDumper(CheckerManager &mgr) { + mgr.registerChecker<CallGraphDumper>(); +} + + +//===----------------------------------------------------------------------===// +// ConfigDumper +//===----------------------------------------------------------------------===// + +namespace { +class ConfigDumper : public Checker< check::EndOfTranslationUnit > { + typedef AnalyzerOptions::ConfigTable Table; + + static int compareEntry(const Table::MapEntryTy *const *LHS, + const Table::MapEntryTy *const *RHS) { + return (*LHS)->getKey().compare((*RHS)->getKey()); + } + +public: + void checkEndOfTranslationUnit(const TranslationUnitDecl *TU, + AnalysisManager& mgr, + BugReporter &BR) const { + const Table &Config = mgr.options.Config; + + SmallVector<const Table::MapEntryTy *, 32> Keys; + for (Table::const_iterator I = Config.begin(), E = Config.end(); I != E; + ++I) + Keys.push_back(&*I); + llvm::array_pod_sort(Keys.begin(), Keys.end(), compareEntry); + + llvm::errs() << "[config]\n"; + for (unsigned I = 0, E = Keys.size(); I != E; ++I) + llvm::errs() << Keys[I]->getKey() << " = " + << (Keys[I]->second.empty() ? "\"\"" : Keys[I]->second) + << '\n'; + + llvm::errs() << "[stats]\n" << "num-entries = " << Keys.size() << '\n'; + } +}; +} + +void ento::registerConfigDumper(CheckerManager &mgr) { + mgr.registerChecker<ConfigDumper>(); +} + +//===----------------------------------------------------------------------===// +// ExplodedGraph Viewer +//===----------------------------------------------------------------------===// + +namespace { +class ExplodedGraphViewer : public Checker< check::EndAnalysis > { +public: + ExplodedGraphViewer() {} + void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const { + Eng.ViewGraph(0); + } +}; + +} + +void ento::registerExplodedGraphViewer(CheckerManager &mgr) { + mgr.registerChecker<ExplodedGraphViewer>(); +} + diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp new file mode 100644 index 000000000000..adf5a8e77a74 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp @@ -0,0 +1,150 @@ +//===-- DeleteWithNonVirtualDtorChecker.cpp -----------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines a checker for the OOP52-CPP CERT rule: Do not delete a polymorphic +// object without a virtual destructor. +// +// Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor report if +// an object with a virtual function but a non-virtual destructor exists or is +// deleted, respectively. +// +// This check exceeds them by comparing the dynamic and static types of the +// object at the point of destruction and only warns if it happens through a +// pointer to a base type without a virtual destructor. The check places a note +// at the last point where the conversion from derived to base happened. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +namespace { +class DeleteWithNonVirtualDtorChecker + : public Checker<check::PreStmt<CXXDeleteExpr>> { + mutable std::unique_ptr<BugType> BT; + + class DeleteBugVisitor : public BugReporterVisitor { + public: + DeleteBugVisitor() : Satisfied(false) {} + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + } + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + bool Satisfied; + }; + +public: + void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; +}; +} // end anonymous namespace + +void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE, + CheckerContext &C) const { + const Expr *DeletedObj = DE->getArgument(); + const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion(); + if (!MR) + return; + + const auto *BaseClassRegion = MR->getAs<TypedValueRegion>(); + const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>(); + if (!BaseClassRegion || !DerivedClassRegion) + return; + + const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl(); + const auto *DerivedClass = + DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl(); + if (!BaseClass || !DerivedClass) + return; + + if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition()) + return; + + if (BaseClass->getDestructor()->isVirtual()) + return; + + if (!DerivedClass->isDerivedFrom(BaseClass)) + return; + + if (!BT) + BT.reset(new BugType(this, + "Destruction of a polymorphic object with no " + "virtual destructor", + "Logic error")); + + ExplodedNode *N = C.generateNonFatalErrorNode(); + auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N); + + // Mark region of problematic base class for later use in the BugVisitor. + R->markInteresting(BaseClassRegion); + R->addVisitor(llvm::make_unique<DeleteBugVisitor>()); + C.emitReport(std::move(R)); +} + +std::shared_ptr<PathDiagnosticPiece> +DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, + BugReport &BR) { + // Stop traversal after the first conversion was found on a path. + if (Satisfied) + return nullptr; + + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + + const auto *CastE = dyn_cast<CastExpr>(S); + if (!CastE) + return nullptr; + + // Only interested in DerivedToBase implicit casts. + // Explicit casts can have different CastKinds. + if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) { + if (ImplCastE->getCastKind() != CK_DerivedToBase) + return nullptr; + } + + // Region associated with the current cast expression. + const MemRegion *M = N->getSVal(CastE).getAsRegion(); + if (!M) + return nullptr; + + // Check if target region was marked as problematic previously. + if (!BR.isInteresting(M)) + return nullptr; + + // Stop traversal on this path. + Satisfied = true; + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "Conversion from derived to base happened here"; + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, + nullptr); +} + +void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) { + mgr.registerChecker<DeleteWithNonVirtualDtorChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp new file mode 100644 index 000000000000..d01a889d256a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -0,0 +1,306 @@ +//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines NullDerefChecker, a builtin check in ExprEngine that performs +// checks for null pointers at loads and stores. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ExprOpenMP.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class DereferenceChecker + : public Checker< check::Location, + check::Bind, + EventDispatcher<ImplicitNullDerefEvent> > { + mutable std::unique_ptr<BuiltinBug> BT_null; + mutable std::unique_ptr<BuiltinBug> BT_undef; + + void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C) const; + +public: + void checkLocation(SVal location, bool isLoad, const Stmt* S, + CheckerContext &C) const; + void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; + + static void AddDerefSource(raw_ostream &os, + SmallVectorImpl<SourceRange> &Ranges, + const Expr *Ex, const ProgramState *state, + const LocationContext *LCtx, + bool loadedFrom = false); +}; +} // end anonymous namespace + +void +DereferenceChecker::AddDerefSource(raw_ostream &os, + SmallVectorImpl<SourceRange> &Ranges, + const Expr *Ex, + const ProgramState *state, + const LocationContext *LCtx, + bool loadedFrom) { + Ex = Ex->IgnoreParenLValueCasts(); + switch (Ex->getStmtClass()) { + default: + break; + case Stmt::DeclRefExprClass: { + const DeclRefExpr *DR = cast<DeclRefExpr>(Ex); + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) { + os << " (" << (loadedFrom ? "loaded from" : "from") + << " variable '" << VD->getName() << "')"; + Ranges.push_back(DR->getSourceRange()); + } + break; + } + case Stmt::MemberExprClass: { + const MemberExpr *ME = cast<MemberExpr>(Ex); + os << " (" << (loadedFrom ? "loaded from" : "via") + << " field '" << ME->getMemberNameInfo() << "')"; + SourceLocation L = ME->getMemberLoc(); + Ranges.push_back(SourceRange(L, L)); + break; + } + case Stmt::ObjCIvarRefExprClass: { + const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex); + os << " (" << (loadedFrom ? "loaded from" : "via") + << " ivar '" << IV->getDecl()->getName() << "')"; + SourceLocation L = IV->getLocation(); + Ranges.push_back(SourceRange(L, L)); + break; + } + } +} + +static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){ + const Expr *E = nullptr; + + // Walk through lvalue casts to get the original expression + // that syntactically caused the load. + if (const Expr *expr = dyn_cast<Expr>(S)) + E = expr->IgnoreParenLValueCasts(); + + if (IsBind) { + const VarDecl *VD; + const Expr *Init; + std::tie(VD, Init) = parseAssignment(S); + if (VD && Init) + E = Init; + } + return E; +} + +static bool suppressReport(const Expr *E) { + // Do not report dereferences on memory in non-default address spaces. + return E->getType().getQualifiers().hasAddressSpace(); +} + +static bool isDeclRefExprToReference(const Expr *E) { + if (const auto *DRE = dyn_cast<DeclRefExpr>(E)) + return DRE->getDecl()->getType()->isReferenceType(); + return false; +} + +void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S, + CheckerContext &C) const { + // Generate an error node. + ExplodedNode *N = C.generateErrorNode(State); + if (!N) + return; + + // We know that 'location' cannot be non-null. This is what + // we call an "explicit" null dereference. + if (!BT_null) + BT_null.reset(new BuiltinBug(this, "Dereference of null pointer")); + + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + + SmallVector<SourceRange, 2> Ranges; + + switch (S->getStmtClass()) { + case Stmt::ArraySubscriptExprClass: { + os << "Array access"; + const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S); + AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), + State.get(), N->getLocationContext()); + os << " results in a null pointer dereference"; + break; + } + case Stmt::OMPArraySectionExprClass: { + os << "Array access"; + const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S); + AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(), + State.get(), N->getLocationContext()); + os << " results in a null pointer dereference"; + break; + } + case Stmt::UnaryOperatorClass: { + os << "Dereference of null pointer"; + const UnaryOperator *U = cast<UnaryOperator>(S); + AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), + State.get(), N->getLocationContext(), true); + break; + } + case Stmt::MemberExprClass: { + const MemberExpr *M = cast<MemberExpr>(S); + if (M->isArrow() || isDeclRefExprToReference(M->getBase())) { + os << "Access to field '" << M->getMemberNameInfo() + << "' results in a dereference of a null pointer"; + AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), + State.get(), N->getLocationContext(), true); + } + break; + } + case Stmt::ObjCIvarRefExprClass: { + const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S); + os << "Access to instance variable '" << *IV->getDecl() + << "' results in a dereference of a null pointer"; + AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(), + State.get(), N->getLocationContext(), true); + break; + } + default: + break; + } + + auto report = llvm::make_unique<BugReport>( + *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N); + + bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); + + for (SmallVectorImpl<SourceRange>::iterator + I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) + report->addRange(*I); + + C.emitReport(std::move(report)); +} + +void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S, + CheckerContext &C) const { + // Check for dereference of an undefined value. + if (l.isUndef()) { + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_undef) + BT_undef.reset( + new BuiltinBug(this, "Dereference of undefined pointer value")); + + auto report = + llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N); + bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report); + C.emitReport(std::move(report)); + } + return; + } + + DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>(); + + // Check for null dereferences. + if (!location.getAs<Loc>()) + return; + + ProgramStateRef state = C.getState(); + + ProgramStateRef notNullState, nullState; + std::tie(notNullState, nullState) = state->assume(location); + + // The explicit NULL case. + if (nullState) { + if (!notNullState) { + const Expr *expr = getDereferenceExpr(S); + if (!suppressReport(expr)) { + reportBug(nullState, expr, C); + return; + } + } + + // Otherwise, we have the case where the location could either be + // null or not-null. Record the error node as an "implicit" null + // dereference. + if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) { + ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(), + /*IsDirectDereference=*/true}; + dispatchEvent(event); + } + } + + // From this point forward, we know that the location is not null. + C.addTransition(notNullState); +} + +void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S, + CheckerContext &C) const { + // If we're binding to a reference, check if the value is known to be null. + if (V.isUndef()) + return; + + const MemRegion *MR = L.getAsRegion(); + const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR); + if (!TVR) + return; + + if (!TVR->getValueType()->isReferenceType()) + return; + + ProgramStateRef State = C.getState(); + + ProgramStateRef StNonNull, StNull; + std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>()); + + if (StNull) { + if (!StNonNull) { + const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true); + if (!suppressReport(expr)) { + reportBug(StNull, expr, C); + return; + } + } + + // At this point the value could be either null or non-null. + // Record this as an "implicit" null dereference. + if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) { + ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N, + &C.getBugReporter(), + /*IsDirectDereference=*/true}; + dispatchEvent(event); + } + } + + // Unlike a regular null dereference, initializing a reference with a + // dereferenced null pointer does not actually cause a runtime exception in + // Clang's implementation of references. + // + // int &r = *p; // safe?? + // if (p != NULL) return; // uh-oh + // r = 5; // trap here + // + // The standard says this is invalid as soon as we try to create a "null + // reference" (there is no such thing), but turning this into an assumption + // that 'p' is never null will not match our actual runtime behavior. + // So we do not record this assumption, allowing us to warn on the last line + // of this example. + // + // We do need to add a transition because we may have generated a sink for + // the "implicit" null dereference. + C.addTransition(State, this); +} + +void ento::registerDereferenceChecker(CheckerManager &mgr) { + mgr.registerChecker<DereferenceChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp new file mode 100644 index 000000000000..2a559422df34 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp @@ -0,0 +1,227 @@ +//=- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ ----*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Check that Objective C properties are set with the setter, not though a +// direct assignment. +// +// Two versions of a checker exist: one that checks all methods and the other +// that only checks the methods annotated with +// __attribute__((annotate("objc_no_direct_instance_variable_assignment"))) +// +// The checker does not warn about assignments to Ivars, annotated with +// __attribute__((objc_allow_direct_instance_variable_assignment"))). This +// annotation serves as a false positive suppression mechanism for the +// checker. The annotation is allowed on properties and Ivars. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/DenseMap.h" + +using namespace clang; +using namespace ento; + +namespace { + +/// The default method filter, which is used to filter out the methods on which +/// the check should not be performed. +/// +/// Checks for the init, dealloc, and any other functions that might be allowed +/// to perform direct instance variable assignment based on their name. +static bool DefaultMethodFilter(const ObjCMethodDecl *M) { + return M->getMethodFamily() == OMF_init || + M->getMethodFamily() == OMF_dealloc || + M->getMethodFamily() == OMF_copy || + M->getMethodFamily() == OMF_mutableCopy || + M->getSelector().getNameForSlot(0).find("init") != StringRef::npos || + M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos; +} + +class DirectIvarAssignment : + public Checker<check::ASTDecl<ObjCImplementationDecl> > { + + typedef llvm::DenseMap<const ObjCIvarDecl*, + const ObjCPropertyDecl*> IvarToPropertyMapTy; + + /// A helper class, which walks the AST and locates all assignments to ivars + /// in the given function. + class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { + const IvarToPropertyMapTy &IvarToPropMap; + const ObjCMethodDecl *MD; + const ObjCInterfaceDecl *InterfD; + BugReporter &BR; + const CheckerBase *Checker; + LocationOrAnalysisDeclContext DCtx; + + public: + MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD, + const ObjCInterfaceDecl *InID, BugReporter &InBR, + const CheckerBase *Checker, AnalysisDeclContext *InDCtx) + : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR), + Checker(Checker), DCtx(InDCtx) {} + + void VisitStmt(const Stmt *S) { VisitChildren(S); } + + void VisitBinaryOperator(const BinaryOperator *BO); + + void VisitChildren(const Stmt *S) { + for (const Stmt *Child : S->children()) + if (Child) + this->Visit(Child); + } + }; + +public: + bool (*ShouldSkipMethod)(const ObjCMethodDecl *); + + DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {} + + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, + BugReporter &BR) const; +}; + +static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD, + const ObjCInterfaceDecl *InterD, + ASTContext &Ctx) { + // Check for synthesized ivars. + ObjCIvarDecl *ID = PD->getPropertyIvarDecl(); + if (ID) + return ID; + + ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD); + + // Check for existing "_PropName". + ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx)); + if (ID) + return ID; + + // Check for existing "PropName". + IdentifierInfo *PropIdent = PD->getIdentifier(); + ID = NonConstInterD->lookupInstanceVariable(PropIdent); + + return ID; +} + +void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D, + AnalysisManager& Mgr, + BugReporter &BR) const { + const ObjCInterfaceDecl *InterD = D->getClassInterface(); + + + IvarToPropertyMapTy IvarToPropMap; + + // Find all properties for this class. + for (const auto *PD : InterD->instance_properties()) { + // Find the corresponding IVar. + const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD, + Mgr.getASTContext()); + + if (!ID) + continue; + + // Store the IVar to property mapping. + IvarToPropMap[ID] = PD; + } + + if (IvarToPropMap.empty()) + return; + + for (const auto *M : D->instance_methods()) { + AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M); + + if ((*ShouldSkipMethod)(M)) + continue; + + const Stmt *Body = M->getBody(); + assert(Body); + + MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this, + DCtx); + MC.VisitStmt(Body); + } +} + +static bool isAnnotatedToAllowDirectAssignment(const Decl *D) { + for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) + if (Ann->getAnnotation() == + "objc_allow_direct_instance_variable_assignment") + return true; + return false; +} + +void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator( + const BinaryOperator *BO) { + if (!BO->isAssignmentOp()) + return; + + const ObjCIvarRefExpr *IvarRef = + dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts()); + + if (!IvarRef) + return; + + if (const ObjCIvarDecl *D = IvarRef->getDecl()) { + IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D); + + if (I != IvarToPropMap.end()) { + const ObjCPropertyDecl *PD = I->second; + // Skip warnings on Ivars, annotated with + // objc_allow_direct_instance_variable_assignment. This annotation serves + // as a false positive suppression mechanism for the checker. The + // annotation is allowed on properties and ivars. + if (isAnnotatedToAllowDirectAssignment(PD) || + isAnnotatedToAllowDirectAssignment(D)) + return; + + ObjCMethodDecl *GetterMethod = + InterfD->getInstanceMethod(PD->getGetterName()); + ObjCMethodDecl *SetterMethod = + InterfD->getInstanceMethod(PD->getSetterName()); + + if (SetterMethod && SetterMethod->getCanonicalDecl() == MD) + return; + + if (GetterMethod && GetterMethod->getCanonicalDecl() == MD) + return; + + BR.EmitBasicReport( + MD, Checker, "Property access", categories::CoreFoundationObjectiveC, + "Direct assignment to an instance variable backing a property; " + "use the setter instead", + PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx)); + } + } +} +} + +// Register the checker that checks for direct accesses in all functions, +// except for the initialization and copy routines. +void ento::registerDirectIvarAssignment(CheckerManager &mgr) { + mgr.registerChecker<DirectIvarAssignment>(); +} + +// Register the checker that checks for direct accesses in functions annotated +// with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))). +static bool AttrFilter(const ObjCMethodDecl *M) { + for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) + if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment") + return false; + return true; +} + +void ento::registerDirectIvarAssignmentForAnnotatedFunctions( + CheckerManager &mgr) { + mgr.registerChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp new file mode 100644 index 000000000000..a220a0513e28 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -0,0 +1,101 @@ +//== DivZeroChecker.cpp - Division by zero checker --------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines DivZeroChecker, a builtin check in ExprEngine that performs +// checks for division by zeros. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > { + mutable std::unique_ptr<BuiltinBug> BT; + void reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C, + std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const; + +public: + void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; +}; +} // end anonymous namespace + +static const Expr *getDenomExpr(const ExplodedNode *N) { + const Stmt *S = N->getLocationAs<PreStmt>()->getStmt(); + if (const auto *BE = dyn_cast<BinaryOperator>(S)) + return BE->getRHS(); + return nullptr; +} + +void DivZeroChecker::reportBug( + const char *Msg, ProgramStateRef StateZero, CheckerContext &C, + std::unique_ptr<BugReporterVisitor> Visitor) const { + if (ExplodedNode *N = C.generateErrorNode(StateZero)) { + if (!BT) + BT.reset(new BuiltinBug(this, "Division by zero")); + + auto R = llvm::make_unique<BugReport>(*BT, Msg, N); + R->addVisitor(std::move(Visitor)); + bugreporter::trackExpressionValue(N, getDenomExpr(N), *R); + C.emitReport(std::move(R)); + } +} + +void DivZeroChecker::checkPreStmt(const BinaryOperator *B, + CheckerContext &C) const { + BinaryOperator::Opcode Op = B->getOpcode(); + if (Op != BO_Div && + Op != BO_Rem && + Op != BO_DivAssign && + Op != BO_RemAssign) + return; + + if (!B->getRHS()->getType()->isScalarType()) + return; + + SVal Denom = C.getSVal(B->getRHS()); + Optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>(); + + // Divide-by-undefined handled in the generic checking for uses of + // undefined values. + if (!DV) + return; + + // Check for divide by zero. + ConstraintManager &CM = C.getConstraintManager(); + ProgramStateRef stateNotZero, stateZero; + std::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV); + + if (!stateNotZero) { + assert(stateZero); + reportBug("Division by zero", stateZero, C); + return; + } + + bool TaintedD = C.getState()->isTainted(*DV); + if ((stateNotZero && stateZero && TaintedD)) { + reportBug("Division by a tainted value, possibly zero", stateZero, C, + llvm::make_unique<TaintBugVisitor>(*DV)); + return; + } + + // If we get here, then the denom should not be zero. We abandon the implicit + // zero denom case for now. + C.addTransition(stateNotZero); +} + +void ento::registerDivZeroChecker(CheckerManager &mgr) { + mgr.registerChecker<DivZeroChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp new file mode 100644 index 000000000000..803d7ae22a71 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp @@ -0,0 +1,208 @@ +//== DynamicTypeChecker.cpp ------------------------------------ -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker looks for cases where the dynamic type of an object is unrelated +// to its static type. The type information utilized by this check is collected +// by the DynamicTypePropagation checker. This check does not report any type +// error for ObjC Generic types, in order to avoid duplicate erros from the +// ObjC Generics checker. This checker is not supposed to modify the program +// state, it is just the observer of the type information provided by other +// checkers. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +namespace { +class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> { + mutable std::unique_ptr<BugType> BT; + void initBugType() const { + if (!BT) + BT.reset( + new BugType(this, "Dynamic and static type mismatch", "Type Error")); + } + + class DynamicTypeBugVisitor : public BugReporterVisitor { + public: + DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Reg); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + // The tracked region. + const MemRegion *Reg; + }; + + void reportTypeError(QualType DynamicType, QualType StaticType, + const MemRegion *Reg, const Stmt *ReportedNode, + CheckerContext &C) const; + +public: + void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const; +}; +} + +void DynamicTypeChecker::reportTypeError(QualType DynamicType, + QualType StaticType, + const MemRegion *Reg, + const Stmt *ReportedNode, + CheckerContext &C) const { + initBugType(); + SmallString<192> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "Object has a dynamic type '"; + QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(), + llvm::Twine()); + OS << "' which is incompatible with static type '"; + QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(), + llvm::Twine()); + OS << "'"; + std::unique_ptr<BugReport> R( + new BugReport(*BT, OS.str(), C.generateNonFatalErrorNode())); + R->markInteresting(Reg); + R->addVisitor(llvm::make_unique<DynamicTypeBugVisitor>(Reg)); + R->addRange(ReportedNode->getSourceRange()); + C.emitReport(std::move(R)); +} + +std::shared_ptr<PathDiagnosticPiece> +DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &) { + ProgramStateRef State = N->getState(); + ProgramStateRef StatePrev = N->getFirstPred()->getState(); + + DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg); + DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg); + if (!TrackedType.isValid()) + return nullptr; + + if (TrackedTypePrev.isValid() && + TrackedTypePrev.getType() == TrackedType.getType()) + return nullptr; + + // Retrieve the associated statement. + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + + const LangOptions &LangOpts = BRC.getASTContext().getLangOpts(); + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "Type '"; + QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS, + LangOpts, llvm::Twine()); + OS << "' is inferred from "; + + if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) { + OS << "explicit cast (from '"; + QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(), + Qualifiers(), OS, LangOpts, llvm::Twine()); + OS << "' to '"; + QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS, + LangOpts, llvm::Twine()); + OS << "')"; + } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) { + OS << "implicit cast (from '"; + QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(), + Qualifiers(), OS, LangOpts, llvm::Twine()); + OS << "' to '"; + QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS, + LangOpts, llvm::Twine()); + OS << "')"; + } else { + OS << "this context"; + } + + // Generate the extra diagnostic. + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, + nullptr); +} + +static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) { + const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl(); + if (!Decl) + return false; + + return Decl->getDefinition(); +} + +// TODO: consider checking explicit casts? +void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE, + CheckerContext &C) const { + // TODO: C++ support. + if (CE->getCastKind() != CK_BitCast) + return; + + const MemRegion *Region = C.getSVal(CE).getAsRegion(); + if (!Region) + return; + + ProgramStateRef State = C.getState(); + DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region); + + if (!DynTypeInfo.isValid()) + return; + + QualType DynType = DynTypeInfo.getType(); + QualType StaticType = CE->getType(); + + const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>(); + const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>(); + + if (!DynObjCType || !StaticObjCType) + return; + + if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType)) + return; + + ASTContext &ASTCtxt = C.getASTContext(); + + // Strip kindeofness to correctly detect subtyping relationships. + DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt); + StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt); + + // Specialized objects are handled by the generics checker. + if (StaticObjCType->isSpecialized()) + return; + + if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType)) + return; + + if (DynTypeInfo.canBeASubClass() && + ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType)) + return; + + reportTypeError(DynType, StaticType, Region, CE, C); +} + +void ento::registerDynamicTypeChecker(CheckerManager &mgr) { + mgr.registerChecker<DynamicTypeChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp new file mode 100644 index 000000000000..31d4eebe8968 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp @@ -0,0 +1,998 @@ +//===- DynamicTypePropagation.cpp ------------------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file contains two checkers. One helps the static analyzer core to track +// types, the other does type inference on Obj-C generics and report type +// errors. +// +// Dynamic Type Propagation: +// This checker defines the rules for dynamic type gathering and propagation. +// +// Generics Checker for Objective-C: +// This checker tries to find type errors that the compiler is not able to catch +// due to the implicit conversions that were introduced for backward +// compatibility. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/Builtins.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +// ProgramState trait - The type inflation is tracked by DynamicTypeMap. This is +// an auxiliary map that tracks more information about generic types, because in +// some cases the most derived type is not the most informative one about the +// type parameters. This types that are stored for each symbol in this map must +// be specialized. +// TODO: In some case the type stored in this map is exactly the same that is +// stored in DynamicTypeMap. We should no store duplicated information in those +// cases. +REGISTER_MAP_WITH_PROGRAMSTATE(MostSpecializedTypeArgsMap, SymbolRef, + const ObjCObjectPointerType *) + +namespace { +class DynamicTypePropagation: + public Checker< check::PreCall, + check::PostCall, + check::DeadSymbols, + check::PostStmt<CastExpr>, + check::PostStmt<CXXNewExpr>, + check::PreObjCMessage, + check::PostObjCMessage > { + const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, + CheckerContext &C) const; + + /// Return a better dynamic type if one can be derived from the cast. + const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE, + CheckerContext &C) const; + + ExplodedNode *dynamicTypePropagationOnCasts(const CastExpr *CE, + ProgramStateRef &State, + CheckerContext &C) const; + + mutable std::unique_ptr<BugType> ObjCGenericsBugType; + void initBugType() const { + if (!ObjCGenericsBugType) + ObjCGenericsBugType.reset( + new BugType(this, "Generics", categories::CoreFoundationObjectiveC)); + } + + class GenericsBugVisitor : public BugReporterVisitor { + public: + GenericsBugVisitor(SymbolRef S) : Sym(S) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Sym); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + // The tracked symbol. + SymbolRef Sym; + }; + + void reportGenericsBug(const ObjCObjectPointerType *From, + const ObjCObjectPointerType *To, ExplodedNode *N, + SymbolRef Sym, CheckerContext &C, + const Stmt *ReportedNode = nullptr) const; + +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostStmt(const CastExpr *CastE, CheckerContext &C) const; + void checkPostStmt(const CXXNewExpr *NewE, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + + /// This value is set to true, when the Generics checker is turned on. + DefaultBool CheckGenerics; +}; +} // end anonymous namespace + +void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + DynamicTypeMapImpl TypeMap = State->get<DynamicTypeMap>(); + for (DynamicTypeMapImpl::iterator I = TypeMap.begin(), E = TypeMap.end(); + I != E; ++I) { + if (!SR.isLiveRegion(I->first)) { + State = State->remove<DynamicTypeMap>(I->first); + } + } + + MostSpecializedTypeArgsMapTy TyArgMap = + State->get<MostSpecializedTypeArgsMap>(); + for (MostSpecializedTypeArgsMapTy::iterator I = TyArgMap.begin(), + E = TyArgMap.end(); + I != E; ++I) { + if (SR.isDead(I->first)) { + State = State->remove<MostSpecializedTypeArgsMap>(I->first); + } + } + + C.addTransition(State); +} + +static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD, + CheckerContext &C) { + assert(Region); + assert(MD); + + ASTContext &Ctx = C.getASTContext(); + QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent())); + + ProgramStateRef State = C.getState(); + State = setDynamicTypeInfo(State, Region, Ty, /*CanBeSubclass=*/false); + C.addTransition(State); +} + +void DynamicTypePropagation::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { + // C++11 [class.cdtor]p4: When a virtual function is called directly or + // indirectly from a constructor or from a destructor, including during + // the construction or destruction of the class's non-static data members, + // and the object to which the call applies is the object under + // construction or destruction, the function called is the final overrider + // in the constructor's or destructor's class and not one overriding it in + // a more-derived class. + + switch (Ctor->getOriginExpr()->getConstructionKind()) { + case CXXConstructExpr::CK_Complete: + case CXXConstructExpr::CK_Delegating: + // No additional type info necessary. + return; + case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructExpr::CK_VirtualBase: + if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) + recordFixedType(Target, Ctor->getDecl(), C); + return; + } + + return; + } + + if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) { + // C++11 [class.cdtor]p4 (see above) + if (!Dtor->isBaseDestructor()) + return; + + const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion(); + if (!Target) + return; + + const Decl *D = Dtor->getDecl(); + if (!D) + return; + + recordFixedType(Target, cast<CXXDestructorDecl>(D), C); + return; + } +} + +void DynamicTypePropagation::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + // We can obtain perfect type info for return values from some calls. + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) { + + // Get the returned value if it's a region. + const MemRegion *RetReg = Call.getReturnValue().getAsRegion(); + if (!RetReg) + return; + + ProgramStateRef State = C.getState(); + const ObjCMethodDecl *D = Msg->getDecl(); + + if (D && D->hasRelatedResultType()) { + switch (Msg->getMethodFamily()) { + default: + break; + + // We assume that the type of the object returned by alloc and new are the + // pointer to the object of the class specified in the receiver of the + // message. + case OMF_alloc: + case OMF_new: { + // Get the type of object that will get created. + const ObjCMessageExpr *MsgE = Msg->getOriginExpr(); + const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C); + if (!ObjTy) + return; + QualType DynResTy = + C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0)); + C.addTransition(setDynamicTypeInfo(State, RetReg, DynResTy, false)); + break; + } + case OMF_init: { + // Assume, the result of the init method has the same dynamic type as + // the receiver and propagate the dynamic type info. + const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion(); + if (!RecReg) + return; + DynamicTypeInfo RecDynType = getDynamicTypeInfo(State, RecReg); + C.addTransition(setDynamicTypeInfo(State, RetReg, RecDynType)); + break; + } + } + } + return; + } + + if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { + // We may need to undo the effects of our pre-call check. + switch (Ctor->getOriginExpr()->getConstructionKind()) { + case CXXConstructExpr::CK_Complete: + case CXXConstructExpr::CK_Delegating: + // No additional work necessary. + // Note: This will leave behind the actual type of the object for + // complete constructors, but arguably that's a good thing, since it + // means the dynamic type info will be correct even for objects + // constructed with operator new. + return; + case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructExpr::CK_VirtualBase: + if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) { + // We just finished a base constructor. Now we can use the subclass's + // type when resolving virtual calls. + const LocationContext *LCtx = C.getLocationContext(); + + // FIXME: In C++17 classes with non-virtual bases may be treated as + // aggregates, and in such case no top-frame constructor will be called. + // Figure out if we need to do anything in this case. + // FIXME: Instead of relying on the ParentMap, we should have the + // trigger-statement (InitListExpr in this case) available in this + // callback, ideally as part of CallEvent. + if (dyn_cast_or_null<InitListExpr>( + LCtx->getParentMap().getParent(Ctor->getOriginExpr()))) + return; + + recordFixedType(Target, cast<CXXConstructorDecl>(LCtx->getDecl()), C); + } + return; + } + } +} + +/// TODO: Handle explicit casts. +/// Handle C++ casts. +/// +/// Precondition: the cast is between ObjCObjectPointers. +ExplodedNode *DynamicTypePropagation::dynamicTypePropagationOnCasts( + const CastExpr *CE, ProgramStateRef &State, CheckerContext &C) const { + // We only track type info for regions. + const MemRegion *ToR = C.getSVal(CE).getAsRegion(); + if (!ToR) + return C.getPredecessor(); + + if (isa<ExplicitCastExpr>(CE)) + return C.getPredecessor(); + + if (const Type *NewTy = getBetterObjCType(CE, C)) { + State = setDynamicTypeInfo(State, ToR, QualType(NewTy, 0)); + return C.addTransition(State); + } + return C.getPredecessor(); +} + +void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE, + CheckerContext &C) const { + if (NewE->isArray()) + return; + + // We only track dynamic type info for regions. + const MemRegion *MR = C.getSVal(NewE).getAsRegion(); + if (!MR) + return; + + C.addTransition(setDynamicTypeInfo(C.getState(), MR, NewE->getType(), + /*CanBeSubclass=*/false)); +} + +const ObjCObjectType * +DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE, + CheckerContext &C) const { + if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) { + if (const ObjCObjectType *ObjTy + = MsgE->getClassReceiver()->getAs<ObjCObjectType>()) + return ObjTy; + } + + if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) { + if (const ObjCObjectType *ObjTy + = MsgE->getSuperType()->getAs<ObjCObjectType>()) + return ObjTy; + } + + const Expr *RecE = MsgE->getInstanceReceiver(); + if (!RecE) + return nullptr; + + RecE= RecE->IgnoreParenImpCasts(); + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) { + const StackFrameContext *SFCtx = C.getStackFrame(); + // Are we calling [self alloc]? If this is self, get the type of the + // enclosing ObjC class. + if (DRE->getDecl() == SFCtx->getSelfDecl()) { + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl())) + if (const ObjCObjectType *ObjTy = + dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl())) + return ObjTy; + } + } + return nullptr; +} + +// Return a better dynamic type if one can be derived from the cast. +// Compare the current dynamic type of the region and the new type to which we +// are casting. If the new type is lower in the inheritance hierarchy, pick it. +const ObjCObjectPointerType * +DynamicTypePropagation::getBetterObjCType(const Expr *CastE, + CheckerContext &C) const { + const MemRegion *ToR = C.getSVal(CastE).getAsRegion(); + assert(ToR); + + // Get the old and new types. + const ObjCObjectPointerType *NewTy = + CastE->getType()->getAs<ObjCObjectPointerType>(); + if (!NewTy) + return nullptr; + QualType OldDTy = getDynamicTypeInfo(C.getState(), ToR).getType(); + if (OldDTy.isNull()) { + return NewTy; + } + const ObjCObjectPointerType *OldTy = + OldDTy->getAs<ObjCObjectPointerType>(); + if (!OldTy) + return nullptr; + + // Id the old type is 'id', the new one is more precise. + if (OldTy->isObjCIdType() && !NewTy->isObjCIdType()) + return NewTy; + + // Return new if it's a subclass of old. + const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl(); + const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl(); + if (ToI && FromI && FromI->isSuperClassOf(ToI)) + return NewTy; + + return nullptr; +} + +static const ObjCObjectPointerType *getMostInformativeDerivedClassImpl( + const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, + const ObjCObjectPointerType *MostInformativeCandidate, ASTContext &C) { + // Checking if from and to are the same classes modulo specialization. + if (From->getInterfaceDecl()->getCanonicalDecl() == + To->getInterfaceDecl()->getCanonicalDecl()) { + if (To->isSpecialized()) { + assert(MostInformativeCandidate->isSpecialized()); + return MostInformativeCandidate; + } + return From; + } + + if (To->getObjectType()->getSuperClassType().isNull()) { + // If To has no super class and From and To aren't the same then + // To was not actually a descendent of From. In this case the best we can + // do is 'From'. + return From; + } + + const auto *SuperOfTo = + To->getObjectType()->getSuperClassType()->getAs<ObjCObjectType>(); + assert(SuperOfTo); + QualType SuperPtrOfToQual = + C.getObjCObjectPointerType(QualType(SuperOfTo, 0)); + const auto *SuperPtrOfTo = SuperPtrOfToQual->getAs<ObjCObjectPointerType>(); + if (To->isUnspecialized()) + return getMostInformativeDerivedClassImpl(From, SuperPtrOfTo, SuperPtrOfTo, + C); + else + return getMostInformativeDerivedClassImpl(From, SuperPtrOfTo, + MostInformativeCandidate, C); +} + +/// A downcast may loose specialization information. E. g.: +/// MutableMap<T, U> : Map +/// The downcast to MutableMap looses the information about the types of the +/// Map (due to the type parameters are not being forwarded to Map), and in +/// general there is no way to recover that information from the +/// declaration. In order to have to most information, lets find the most +/// derived type that has all the type parameters forwarded. +/// +/// Get the a subclass of \p From (which has a lower bound \p To) that do not +/// loose information about type parameters. \p To has to be a subclass of +/// \p From. From has to be specialized. +static const ObjCObjectPointerType * +getMostInformativeDerivedClass(const ObjCObjectPointerType *From, + const ObjCObjectPointerType *To, ASTContext &C) { + return getMostInformativeDerivedClassImpl(From, To, To, C); +} + +/// Inputs: +/// \param StaticLowerBound Static lower bound for a symbol. The dynamic lower +/// bound might be the subclass of this type. +/// \param StaticUpperBound A static upper bound for a symbol. +/// \p StaticLowerBound expected to be the subclass of \p StaticUpperBound. +/// \param Current The type that was inferred for a symbol in a previous +/// context. Might be null when this is the first time that inference happens. +/// Precondition: +/// \p StaticLowerBound or \p StaticUpperBound is specialized. If \p Current +/// is not null, it is specialized. +/// Possible cases: +/// (1) The \p Current is null and \p StaticLowerBound <: \p StaticUpperBound +/// (2) \p StaticLowerBound <: \p Current <: \p StaticUpperBound +/// (3) \p Current <: \p StaticLowerBound <: \p StaticUpperBound +/// (4) \p StaticLowerBound <: \p StaticUpperBound <: \p Current +/// Effect: +/// Use getMostInformativeDerivedClass with the upper and lower bound of the +/// set {\p StaticLowerBound, \p Current, \p StaticUpperBound}. The computed +/// lower bound must be specialized. If the result differs from \p Current or +/// \p Current is null, store the result. +static bool +storeWhenMoreInformative(ProgramStateRef &State, SymbolRef Sym, + const ObjCObjectPointerType *const *Current, + const ObjCObjectPointerType *StaticLowerBound, + const ObjCObjectPointerType *StaticUpperBound, + ASTContext &C) { + // TODO: The above 4 cases are not exhaustive. In particular, it is possible + // for Current to be incomparable with StaticLowerBound, StaticUpperBound, + // or both. + // + // For example, suppose Foo<T> and Bar<T> are unrelated types. + // + // Foo<T> *f = ... + // Bar<T> *b = ... + // + // id t1 = b; + // f = t1; + // id t2 = f; // StaticLowerBound is Foo<T>, Current is Bar<T> + // + // We should either constrain the callers of this function so that the stated + // preconditions hold (and assert it) or rewrite the function to expicitly + // handle the additional cases. + + // Precondition + assert(StaticUpperBound->isSpecialized() || + StaticLowerBound->isSpecialized()); + assert(!Current || (*Current)->isSpecialized()); + + // Case (1) + if (!Current) { + if (StaticUpperBound->isUnspecialized()) { + State = State->set<MostSpecializedTypeArgsMap>(Sym, StaticLowerBound); + return true; + } + // Upper bound is specialized. + const ObjCObjectPointerType *WithMostInfo = + getMostInformativeDerivedClass(StaticUpperBound, StaticLowerBound, C); + State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo); + return true; + } + + // Case (3) + if (C.canAssignObjCInterfaces(StaticLowerBound, *Current)) { + return false; + } + + // Case (4) + if (C.canAssignObjCInterfaces(*Current, StaticUpperBound)) { + // The type arguments might not be forwarded at any point of inheritance. + const ObjCObjectPointerType *WithMostInfo = + getMostInformativeDerivedClass(*Current, StaticUpperBound, C); + WithMostInfo = + getMostInformativeDerivedClass(WithMostInfo, StaticLowerBound, C); + if (WithMostInfo == *Current) + return false; + State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo); + return true; + } + + // Case (2) + const ObjCObjectPointerType *WithMostInfo = + getMostInformativeDerivedClass(*Current, StaticLowerBound, C); + if (WithMostInfo != *Current) { + State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo); + return true; + } + + return false; +} + +/// Type inference based on static type information that is available for the +/// cast and the tracked type information for the given symbol. When the tracked +/// symbol and the destination type of the cast are unrelated, report an error. +void DynamicTypePropagation::checkPostStmt(const CastExpr *CE, + CheckerContext &C) const { + if (CE->getCastKind() != CK_BitCast) + return; + + QualType OriginType = CE->getSubExpr()->getType(); + QualType DestType = CE->getType(); + + const auto *OrigObjectPtrType = OriginType->getAs<ObjCObjectPointerType>(); + const auto *DestObjectPtrType = DestType->getAs<ObjCObjectPointerType>(); + + if (!OrigObjectPtrType || !DestObjectPtrType) + return; + + ProgramStateRef State = C.getState(); + ExplodedNode *AfterTypeProp = dynamicTypePropagationOnCasts(CE, State, C); + + ASTContext &ASTCtxt = C.getASTContext(); + + // This checker detects the subtyping relationships using the assignment + // rules. In order to be able to do this the kindofness must be stripped + // first. The checker treats every type as kindof type anyways: when the + // tracked type is the subtype of the static type it tries to look up the + // methods in the tracked type first. + OrigObjectPtrType = OrigObjectPtrType->stripObjCKindOfTypeAndQuals(ASTCtxt); + DestObjectPtrType = DestObjectPtrType->stripObjCKindOfTypeAndQuals(ASTCtxt); + + if (OrigObjectPtrType->isUnspecialized() && + DestObjectPtrType->isUnspecialized()) + return; + + SymbolRef Sym = C.getSVal(CE).getAsSymbol(); + if (!Sym) + return; + + const ObjCObjectPointerType *const *TrackedType = + State->get<MostSpecializedTypeArgsMap>(Sym); + + if (isa<ExplicitCastExpr>(CE)) { + // Treat explicit casts as an indication from the programmer that the + // Objective-C type system is not rich enough to express the needed + // invariant. In such cases, forget any existing information inferred + // about the type arguments. We don't assume the casted-to specialized + // type here because the invariant the programmer specifies in the cast + // may only hold at this particular program point and not later ones. + // We don't want a suppressing cast to require a cascade of casts down the + // line. + if (TrackedType) { + State = State->remove<MostSpecializedTypeArgsMap>(Sym); + C.addTransition(State, AfterTypeProp); + } + return; + } + + // Check which assignments are legal. + bool OrigToDest = + ASTCtxt.canAssignObjCInterfaces(DestObjectPtrType, OrigObjectPtrType); + bool DestToOrig = + ASTCtxt.canAssignObjCInterfaces(OrigObjectPtrType, DestObjectPtrType); + + // The tracked type should be the sub or super class of the static destination + // type. When an (implicit) upcast or a downcast happens according to static + // types, and there is no subtyping relationship between the tracked and the + // static destination types, it indicates an error. + if (TrackedType && + !ASTCtxt.canAssignObjCInterfaces(DestObjectPtrType, *TrackedType) && + !ASTCtxt.canAssignObjCInterfaces(*TrackedType, DestObjectPtrType)) { + static CheckerProgramPointTag IllegalConv(this, "IllegalConversion"); + ExplodedNode *N = C.addTransition(State, AfterTypeProp, &IllegalConv); + reportGenericsBug(*TrackedType, DestObjectPtrType, N, Sym, C); + return; + } + + // Handle downcasts and upcasts. + + const ObjCObjectPointerType *LowerBound = DestObjectPtrType; + const ObjCObjectPointerType *UpperBound = OrigObjectPtrType; + if (OrigToDest && !DestToOrig) + std::swap(LowerBound, UpperBound); + + // The id type is not a real bound. Eliminate it. + LowerBound = LowerBound->isObjCIdType() ? UpperBound : LowerBound; + UpperBound = UpperBound->isObjCIdType() ? LowerBound : UpperBound; + + if (storeWhenMoreInformative(State, Sym, TrackedType, LowerBound, UpperBound, + ASTCtxt)) { + C.addTransition(State, AfterTypeProp); + } +} + +static const Expr *stripCastsAndSugar(const Expr *E) { + E = E->IgnoreParenImpCasts(); + if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) + E = POE->getSyntacticForm()->IgnoreParenImpCasts(); + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) + E = OVE->getSourceExpr()->IgnoreParenImpCasts(); + return E; +} + +static bool isObjCTypeParamDependent(QualType Type) { + // It is illegal to typedef parameterized types inside an interface. Therefore + // an Objective-C type can only be dependent on a type parameter when the type + // parameter structurally present in the type itself. + class IsObjCTypeParamDependentTypeVisitor + : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> { + public: + IsObjCTypeParamDependentTypeVisitor() : Result(false) {} + bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) { + if (isa<ObjCTypeParamDecl>(Type->getDecl())) { + Result = true; + return false; + } + return true; + } + + bool Result; + }; + + IsObjCTypeParamDependentTypeVisitor Visitor; + Visitor.TraverseType(Type); + return Visitor.Result; +} + +/// A method might not be available in the interface indicated by the static +/// type. However it might be available in the tracked type. In order to +/// properly substitute the type parameters we need the declaration context of +/// the method. The more specialized the enclosing class of the method is, the +/// more likely that the parameter substitution will be successful. +static const ObjCMethodDecl * +findMethodDecl(const ObjCMessageExpr *MessageExpr, + const ObjCObjectPointerType *TrackedType, ASTContext &ASTCtxt) { + const ObjCMethodDecl *Method = nullptr; + + QualType ReceiverType = MessageExpr->getReceiverType(); + const auto *ReceiverObjectPtrType = + ReceiverType->getAs<ObjCObjectPointerType>(); + + // Do this "devirtualization" on instance and class methods only. Trust the + // static type on super and super class calls. + if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Instance || + MessageExpr->getReceiverKind() == ObjCMessageExpr::Class) { + // When the receiver type is id, Class, or some super class of the tracked + // type, look up the method in the tracked type, not in the receiver type. + // This way we preserve more information. + if (ReceiverType->isObjCIdType() || ReceiverType->isObjCClassType() || + ASTCtxt.canAssignObjCInterfaces(ReceiverObjectPtrType, TrackedType)) { + const ObjCInterfaceDecl *InterfaceDecl = TrackedType->getInterfaceDecl(); + // The method might not be found. + Selector Sel = MessageExpr->getSelector(); + Method = InterfaceDecl->lookupInstanceMethod(Sel); + if (!Method) + Method = InterfaceDecl->lookupClassMethod(Sel); + } + } + + // Fallback to statick method lookup when the one based on the tracked type + // failed. + return Method ? Method : MessageExpr->getMethodDecl(); +} + +/// Get the returned ObjCObjectPointerType by a method based on the tracked type +/// information, or null pointer when the returned type is not an +/// ObjCObjectPointerType. +static QualType getReturnTypeForMethod( + const ObjCMethodDecl *Method, ArrayRef<QualType> TypeArgs, + const ObjCObjectPointerType *SelfType, ASTContext &C) { + QualType StaticResultType = Method->getReturnType(); + + // Is the return type declared as instance type? + if (StaticResultType == C.getObjCInstanceType()) + return QualType(SelfType, 0); + + // Check whether the result type depends on a type parameter. + if (!isObjCTypeParamDependent(StaticResultType)) + return QualType(); + + QualType ResultType = StaticResultType.substObjCTypeArgs( + C, TypeArgs, ObjCSubstitutionContext::Result); + + return ResultType; +} + +/// When the receiver has a tracked type, use that type to validate the +/// argumments of the message expression and the return value. +void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolRef Sym = M.getReceiverSVal().getAsSymbol(); + if (!Sym) + return; + + const ObjCObjectPointerType *const *TrackedType = + State->get<MostSpecializedTypeArgsMap>(Sym); + if (!TrackedType) + return; + + // Get the type arguments from tracked type and substitute type arguments + // before do the semantic check. + + ASTContext &ASTCtxt = C.getASTContext(); + const ObjCMessageExpr *MessageExpr = M.getOriginExpr(); + const ObjCMethodDecl *Method = + findMethodDecl(MessageExpr, *TrackedType, ASTCtxt); + + // It is possible to call non-existent methods in Obj-C. + if (!Method) + return; + + // If the method is declared on a class that has a non-invariant + // type parameter, don't warn about parameter mismatches after performing + // substitution. This prevents warning when the programmer has purposely + // casted the receiver to a super type or unspecialized type but the analyzer + // has a more precise tracked type than the programmer intends at the call + // site. + // + // For example, consider NSArray (which has a covariant type parameter) + // and NSMutableArray (a subclass of NSArray where the type parameter is + // invariant): + // NSMutableArray *a = [[NSMutableArray<NSString *> alloc] init; + // + // [a containsObject:number]; // Safe: -containsObject is defined on NSArray. + // NSArray<NSObject *> *other = [a arrayByAddingObject:number] // Safe + // + // [a addObject:number] // Unsafe: -addObject: is defined on NSMutableArray + // + + const ObjCInterfaceDecl *Interface = Method->getClassInterface(); + if (!Interface) + return; + + ObjCTypeParamList *TypeParams = Interface->getTypeParamList(); + if (!TypeParams) + return; + + for (ObjCTypeParamDecl *TypeParam : *TypeParams) { + if (TypeParam->getVariance() != ObjCTypeParamVariance::Invariant) + return; + } + + Optional<ArrayRef<QualType>> TypeArgs = + (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); + // This case might happen when there is an unspecialized override of a + // specialized method. + if (!TypeArgs) + return; + + for (unsigned i = 0; i < Method->param_size(); i++) { + const Expr *Arg = MessageExpr->getArg(i); + const ParmVarDecl *Param = Method->parameters()[i]; + + QualType OrigParamType = Param->getType(); + if (!isObjCTypeParamDependent(OrigParamType)) + continue; + + QualType ParamType = OrigParamType.substObjCTypeArgs( + ASTCtxt, *TypeArgs, ObjCSubstitutionContext::Parameter); + // Check if it can be assigned + const auto *ParamObjectPtrType = ParamType->getAs<ObjCObjectPointerType>(); + const auto *ArgObjectPtrType = + stripCastsAndSugar(Arg)->getType()->getAs<ObjCObjectPointerType>(); + if (!ParamObjectPtrType || !ArgObjectPtrType) + continue; + + // Check if we have more concrete tracked type that is not a super type of + // the static argument type. + SVal ArgSVal = M.getArgSVal(i); + SymbolRef ArgSym = ArgSVal.getAsSymbol(); + if (ArgSym) { + const ObjCObjectPointerType *const *TrackedArgType = + State->get<MostSpecializedTypeArgsMap>(ArgSym); + if (TrackedArgType && + ASTCtxt.canAssignObjCInterfaces(ArgObjectPtrType, *TrackedArgType)) { + ArgObjectPtrType = *TrackedArgType; + } + } + + // Warn when argument is incompatible with the parameter. + if (!ASTCtxt.canAssignObjCInterfaces(ParamObjectPtrType, + ArgObjectPtrType)) { + static CheckerProgramPointTag Tag(this, "ArgTypeMismatch"); + ExplodedNode *N = C.addTransition(State, &Tag); + reportGenericsBug(ArgObjectPtrType, ParamObjectPtrType, N, Sym, C, Arg); + return; + } + } +} + +/// This callback is used to infer the types for Class variables. This info is +/// used later to validate messages that sent to classes. Class variables are +/// initialized with by invoking the 'class' method on a class. +/// This method is also used to infer the type information for the return +/// types. +// TODO: right now it only tracks generic types. Extend this to track every +// type in the DynamicTypeMap and diagnose type errors! +void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) const { + const ObjCMessageExpr *MessageExpr = M.getOriginExpr(); + + SymbolRef RetSym = M.getReturnValue().getAsSymbol(); + if (!RetSym) + return; + + Selector Sel = MessageExpr->getSelector(); + ProgramStateRef State = C.getState(); + // Inference for class variables. + // We are only interested in cases where the class method is invoked on a + // class. This method is provided by the runtime and available on all classes. + if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class && + Sel.getAsString() == "class") { + QualType ReceiverType = MessageExpr->getClassReceiver(); + const auto *ReceiverClassType = ReceiverType->getAs<ObjCObjectType>(); + QualType ReceiverClassPointerType = + C.getASTContext().getObjCObjectPointerType( + QualType(ReceiverClassType, 0)); + + if (!ReceiverClassType->isSpecialized()) + return; + const auto *InferredType = + ReceiverClassPointerType->getAs<ObjCObjectPointerType>(); + assert(InferredType); + + State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType); + C.addTransition(State); + return; + } + + // Tracking for return types. + SymbolRef RecSym = M.getReceiverSVal().getAsSymbol(); + if (!RecSym) + return; + + const ObjCObjectPointerType *const *TrackedType = + State->get<MostSpecializedTypeArgsMap>(RecSym); + if (!TrackedType) + return; + + ASTContext &ASTCtxt = C.getASTContext(); + const ObjCMethodDecl *Method = + findMethodDecl(MessageExpr, *TrackedType, ASTCtxt); + if (!Method) + return; + + Optional<ArrayRef<QualType>> TypeArgs = + (*TrackedType)->getObjCSubstitutions(Method->getDeclContext()); + if (!TypeArgs) + return; + + QualType ResultType = + getReturnTypeForMethod(Method, *TypeArgs, *TrackedType, ASTCtxt); + // The static type is the same as the deduced type. + if (ResultType.isNull()) + return; + + const MemRegion *RetRegion = M.getReturnValue().getAsRegion(); + ExplodedNode *Pred = C.getPredecessor(); + // When there is an entry available for the return symbol in DynamicTypeMap, + // the call was inlined, and the information in the DynamicTypeMap is should + // be precise. + if (RetRegion && !State->get<DynamicTypeMap>(RetRegion)) { + // TODO: we have duplicated information in DynamicTypeMap and + // MostSpecializedTypeArgsMap. We should only store anything in the later if + // the stored data differs from the one stored in the former. + State = setDynamicTypeInfo(State, RetRegion, ResultType, + /*CanBeSubclass=*/true); + Pred = C.addTransition(State); + } + + const auto *ResultPtrType = ResultType->getAs<ObjCObjectPointerType>(); + + if (!ResultPtrType || ResultPtrType->isUnspecialized()) + return; + + // When the result is a specialized type and it is not tracked yet, track it + // for the result symbol. + if (!State->get<MostSpecializedTypeArgsMap>(RetSym)) { + State = State->set<MostSpecializedTypeArgsMap>(RetSym, ResultPtrType); + C.addTransition(State, Pred); + } +} + +void DynamicTypePropagation::reportGenericsBug( + const ObjCObjectPointerType *From, const ObjCObjectPointerType *To, + ExplodedNode *N, SymbolRef Sym, CheckerContext &C, + const Stmt *ReportedNode) const { + if (!CheckGenerics) + return; + + initBugType(); + SmallString<192> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "Conversion from value of type '"; + QualType::print(From, Qualifiers(), OS, C.getLangOpts(), llvm::Twine()); + OS << "' to incompatible type '"; + QualType::print(To, Qualifiers(), OS, C.getLangOpts(), llvm::Twine()); + OS << "'"; + std::unique_ptr<BugReport> R( + new BugReport(*ObjCGenericsBugType, OS.str(), N)); + R->markInteresting(Sym); + R->addVisitor(llvm::make_unique<GenericsBugVisitor>(Sym)); + if (ReportedNode) + R->addRange(ReportedNode->getSourceRange()); + C.emitReport(std::move(R)); +} + +std::shared_ptr<PathDiagnosticPiece> +DynamicTypePropagation::GenericsBugVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) { + ProgramStateRef state = N->getState(); + ProgramStateRef statePrev = N->getFirstPred()->getState(); + + const ObjCObjectPointerType *const *TrackedType = + state->get<MostSpecializedTypeArgsMap>(Sym); + const ObjCObjectPointerType *const *TrackedTypePrev = + statePrev->get<MostSpecializedTypeArgsMap>(Sym); + if (!TrackedType) + return nullptr; + + if (TrackedTypePrev && *TrackedTypePrev == *TrackedType) + return nullptr; + + // Retrieve the associated statement. + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + + const LangOptions &LangOpts = BRC.getASTContext().getLangOpts(); + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "Type '"; + QualType::print(*TrackedType, Qualifiers(), OS, LangOpts, llvm::Twine()); + OS << "' is inferred from "; + + if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) { + OS << "explicit cast (from '"; + QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(), + Qualifiers(), OS, LangOpts, llvm::Twine()); + OS << "' to '"; + QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS, + LangOpts, llvm::Twine()); + OS << "')"; + } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) { + OS << "implicit cast (from '"; + QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(), + Qualifiers(), OS, LangOpts, llvm::Twine()); + OS << "' to '"; + QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS, + LangOpts, llvm::Twine()); + OS << "')"; + } else { + OS << "this context"; + } + + // Generate the extra diagnostic. + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, + nullptr); +} + +/// Register checkers. +void ento::registerObjCGenericsChecker(CheckerManager &mgr) { + DynamicTypePropagation *checker = + mgr.registerChecker<DynamicTypePropagation>(); + checker->CheckGenerics = true; +} + +void ento::registerDynamicTypePropagation(CheckerManager &mgr) { + mgr.registerChecker<DynamicTypePropagation>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp new file mode 100644 index 000000000000..4e51cffaa744 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp @@ -0,0 +1,128 @@ +//===- EnumCastOutOfRangeChecker.cpp ---------------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// The EnumCastOutOfRangeChecker is responsible for checking integer to +// enumeration casts that could result in undefined values. This could happen +// if the value that we cast from is out of the value range of the enumeration. +// Reference: +// [ISO/IEC 14882-2014] ISO/IEC 14882-2014. +// Programming Languages — C++, Fourth Edition. 2014. +// C++ Standard, [dcl.enum], in paragraph 8, which defines the range of an enum +// C++ Standard, [expr.static.cast], paragraph 10, which defines the behaviour +// of casting an integer value that is out of range +// SEI CERT C++ Coding Standard, INT50-CPP. Do not cast to an out-of-range +// enumeration value +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +// This evaluator checks two SVals for equality. The first SVal is provided via +// the constructor, the second is the parameter of the overloaded () operator. +// It uses the in-built ConstraintManager to resolve the equlity to possible or +// not possible ProgramStates. +class ConstraintBasedEQEvaluator { + const DefinedOrUnknownSVal CompareValue; + const ProgramStateRef PS; + SValBuilder &SVB; + +public: + ConstraintBasedEQEvaluator(CheckerContext &C, + const DefinedOrUnknownSVal CompareValue) + : CompareValue(CompareValue), PS(C.getState()), SVB(C.getSValBuilder()) {} + + bool operator()(const llvm::APSInt &EnumDeclInitValue) { + DefinedOrUnknownSVal EnumDeclValue = SVB.makeIntVal(EnumDeclInitValue); + DefinedOrUnknownSVal ElemEqualsValueToCast = + SVB.evalEQ(PS, EnumDeclValue, CompareValue); + + return static_cast<bool>(PS->assume(ElemEqualsValueToCast, true)); + } +}; + +// This checker checks CastExpr statements. +// If the value provided to the cast is one of the values the enumeration can +// represent, the said value matches the enumeration. If the checker can +// establish the impossibility of matching it gives a warning. +// Being conservative, it does not warn if there is slight possibility the +// value can be matching. +class EnumCastOutOfRangeChecker : public Checker<check::PreStmt<CastExpr>> { + mutable std::unique_ptr<BuiltinBug> EnumValueCastOutOfRange; + void reportWarning(CheckerContext &C) const; + +public: + void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; +}; + +using EnumValueVector = llvm::SmallVector<llvm::APSInt, 6>; + +// Collects all of the values an enum can represent (as SVals). +EnumValueVector getDeclValuesForEnum(const EnumDecl *ED) { + EnumValueVector DeclValues( + std::distance(ED->enumerator_begin(), ED->enumerator_end())); + llvm::transform(ED->enumerators(), DeclValues.begin(), + [](const EnumConstantDecl *D) { return D->getInitVal(); }); + return DeclValues; +} +} // namespace + +void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const { + if (const ExplodedNode *N = C.generateNonFatalErrorNode()) { + if (!EnumValueCastOutOfRange) + EnumValueCastOutOfRange.reset( + new BuiltinBug(this, "Enum cast out of range", + "The value provided to the cast expression is not in " + "the valid range of values for the enum")); + C.emitReport(llvm::make_unique<BugReport>( + *EnumValueCastOutOfRange, EnumValueCastOutOfRange->getDescription(), + N)); + } +} + +void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE, + CheckerContext &C) const { + // Get the value of the expression to cast. + const llvm::Optional<DefinedOrUnknownSVal> ValueToCast = + C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>(); + + // If the value cannot be reasoned about (not even a DefinedOrUnknownSVal), + // don't analyze further. + if (!ValueToCast) + return; + + const QualType T = CE->getType(); + // Check whether the cast type is an enum. + if (!T->isEnumeralType()) + return; + + // If the cast is an enum, get its declaration. + // If the isEnumeralType() returned true, then the declaration must exist + // even if it is a stub declaration. It is up to the getDeclValuesForEnum() + // function to handle this. + const EnumDecl *ED = T->castAs<EnumType>()->getDecl(); + + EnumValueVector DeclValues = getDeclValuesForEnum(ED); + // Check if any of the enum values possibly match. + bool PossibleValueMatch = llvm::any_of( + DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast)); + + // If there is no value that can possibly match any of the enum values, then + // warn. + if (!PossibleValueMatch) + reportWarning(C); +} + +void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) { + mgr.registerChecker<EnumCastOutOfRangeChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp new file mode 100644 index 000000000000..2553f54bbcac --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -0,0 +1,411 @@ +//==- ExprInspectionChecker.cpp - Used for regression tests ------*- 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/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Checkers/SValExplainer.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/IssueHash.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/ScopedPrinter.h" + +using namespace clang; +using namespace ento; + +namespace { +class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols, + check::EndAnalysis> { + mutable std::unique_ptr<BugType> BT; + + // These stats are per-analysis, not per-branch, hence they shouldn't + // stay inside the program state. + struct ReachedStat { + ExplodedNode *ExampleNode; + unsigned NumTimesReached; + }; + mutable llvm::DenseMap<const CallExpr *, ReachedStat> ReachedStats; + + void analyzerEval(const CallExpr *CE, CheckerContext &C) const; + void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const; + void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const; + void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const; + void analyzerCrash(const CallExpr *CE, CheckerContext &C) const; + void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const; + void analyzerDump(const CallExpr *CE, CheckerContext &C) const; + void analyzerExplain(const CallExpr *CE, CheckerContext &C) const; + void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const; + void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const; + void analyzerHashDump(const CallExpr *CE, CheckerContext &C) const; + void analyzerDenote(const CallExpr *CE, CheckerContext &C) const; + void analyzerExpress(const CallExpr *CE, CheckerContext &C) const; + + typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *, + CheckerContext &C) const; + + ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C) const; + ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR, + ExplodedNode *N) const; + +public: + bool evalCall(const CallExpr *CE, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, + ExprEngine &Eng) const; +}; +} + +REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef) +REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *) + +bool ExprInspectionChecker::evalCall(const CallExpr *CE, + CheckerContext &C) const { + // These checks should have no effect on the surrounding environment + // (globals should not be invalidated, etc), hence the use of evalCall. + FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE)) + .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval) + .Case("clang_analyzer_checkInlined", + &ExprInspectionChecker::analyzerCheckInlined) + .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash) + .Case("clang_analyzer_warnIfReached", + &ExprInspectionChecker::analyzerWarnIfReached) + .Case("clang_analyzer_warnOnDeadSymbol", + &ExprInspectionChecker::analyzerWarnOnDeadSymbol) + .StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain) + .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump) + .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent) + .Case("clang_analyzer_printState", + &ExprInspectionChecker::analyzerPrintState) + .Case("clang_analyzer_numTimesReached", + &ExprInspectionChecker::analyzerNumTimesReached) + .Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump) + .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote) + .Case("clang_analyzer_express", &ExprInspectionChecker::analyzerExpress) + .Default(nullptr); + + if (!Handler) + return false; + + (this->*Handler)(CE, C); + return true; +} + +static const char *getArgumentValueString(const CallExpr *CE, + CheckerContext &C) { + if (CE->getNumArgs() == 0) + return "Missing assertion argument"; + + ExplodedNode *N = C.getPredecessor(); + const LocationContext *LC = N->getLocationContext(); + ProgramStateRef State = N->getState(); + + const Expr *Assertion = CE->getArg(0); + SVal AssertionVal = State->getSVal(Assertion, LC); + + if (AssertionVal.isUndef()) + return "UNDEFINED"; + + ProgramStateRef StTrue, StFalse; + std::tie(StTrue, StFalse) = + State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>()); + + if (StTrue) { + if (StFalse) + return "UNKNOWN"; + else + return "TRUE"; + } else { + if (StFalse) + return "FALSE"; + else + llvm_unreachable("Invalid constraint; neither true or false."); + } +} + +ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, + CheckerContext &C) const { + ExplodedNode *N = C.generateNonFatalErrorNode(); + reportBug(Msg, C.getBugReporter(), N); + return N; +} + +ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg, + BugReporter &BR, + ExplodedNode *N) const { + if (!N) + return nullptr; + + if (!BT) + BT.reset(new BugType(this, "Checking analyzer assumptions", "debug")); + + BR.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N)); + return N; +} + +void ExprInspectionChecker::analyzerEval(const CallExpr *CE, + CheckerContext &C) const { + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + + // A specific instantiation of an inlined function may have more constrained + // values than can generally be assumed. Skip the check. + if (LC->getStackFrame()->getParent() != nullptr) + return; + + reportBug(getArgumentValueString(CE, C), C); +} + +void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE, + CheckerContext &C) const { + reportBug("REACHABLE", C); +} + +void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE, + CheckerContext &C) const { + ++ReachedStats[CE].NumTimesReached; + if (!ReachedStats[CE].ExampleNode) { + // Later, in checkEndAnalysis, we'd throw a report against it. + ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode(); + } +} + +void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE, + CheckerContext &C) const { + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + + // An inlined function could conceivably also be analyzed as a top-level + // function. We ignore this case and only emit a message (TRUE or FALSE) + // when we are analyzing it as an inlined function. This means that + // clang_analyzer_checkInlined(true) should always print TRUE, but + // clang_analyzer_checkInlined(false) should never actually print anything. + if (LC->getStackFrame()->getParent() == nullptr) + return; + + reportBug(getArgumentValueString(CE, C), C); +} + +void ExprInspectionChecker::analyzerExplain(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) { + reportBug("Missing argument for explaining", C); + return; + } + + SVal V = C.getSVal(CE->getArg(0)); + SValExplainer Ex(C.getASTContext()); + reportBug(Ex.Visit(V), C); +} + +void ExprInspectionChecker::analyzerDump(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) { + reportBug("Missing argument for dumping", C); + return; + } + + SVal V = C.getSVal(CE->getArg(0)); + + llvm::SmallString<32> Str; + llvm::raw_svector_ostream OS(Str); + V.dumpToStream(OS); + reportBug(OS.str(), C); +} + +void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) { + reportBug("Missing region for obtaining extent", C); + return; + } + + auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion()); + if (!MR) { + reportBug("Obtaining extent of a non-region", C); + return; + } + + ProgramStateRef State = C.getState(); + State = State->BindExpr(CE, C.getLocationContext(), + MR->getExtent(C.getSValBuilder())); + C.addTransition(State); +} + +void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE, + CheckerContext &C) const { + C.getState()->dump(); +} + +void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) + return; + SVal Val = C.getSVal(CE->getArg(0)); + SymbolRef Sym = Val.getAsSymbol(); + if (!Sym) + return; + + ProgramStateRef State = C.getState(); + State = State->add<MarkedSymbols>(Sym); + C.addTransition(State); +} + +void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>(); + ExplodedNode *N = C.getPredecessor(); + for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) { + SymbolRef Sym = *I; + if (!SymReaper.isDead(Sym)) + continue; + + // The non-fatal error node should be the same for all reports. + if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C)) + N = BugNode; + State = State->remove<MarkedSymbols>(Sym); + } + + for (auto I : State->get<DenotedSymbols>()) { + SymbolRef Sym = I.first; + if (!SymReaper.isLive(Sym)) + State = State->remove<DenotedSymbols>(Sym); + } + + C.addTransition(State, N); +} + +void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR, + ExprEngine &Eng) const { + for (auto Item: ReachedStats) { + unsigned NumTimesReached = Item.second.NumTimesReached; + ExplodedNode *N = Item.second.ExampleNode; + + reportBug(llvm::to_string(NumTimesReached), BR, N); + } + ReachedStats.clear(); +} + +void ExprInspectionChecker::analyzerCrash(const CallExpr *CE, + CheckerContext &C) const { + LLVM_BUILTIN_TRAP; +} + +void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE, + CheckerContext &C) const { + const LangOptions &Opts = C.getLangOpts(); + const SourceManager &SM = C.getSourceManager(); + FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM); + std::string HashContent = + GetIssueString(SM, FL, getCheckName().getName(), "Category", + C.getLocationContext()->getDecl(), Opts); + + reportBug(HashContent, C); +} + +void ExprInspectionChecker::analyzerDenote(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() < 2) { + reportBug("clang_analyzer_denote() requires a symbol and a string literal", + C); + return; + } + + SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol(); + if (!Sym) { + reportBug("Not a symbol", C); + return; + } + + const auto *E = dyn_cast<StringLiteral>(CE->getArg(1)->IgnoreParenCasts()); + if (!E) { + reportBug("Not a string literal", C); + return; + } + + ProgramStateRef State = C.getState(); + + C.addTransition(C.getState()->set<DenotedSymbols>(Sym, E)); +} + +namespace { +class SymbolExpressor + : public SymExprVisitor<SymbolExpressor, Optional<std::string>> { + ProgramStateRef State; + +public: + SymbolExpressor(ProgramStateRef State) : State(State) {} + + Optional<std::string> lookup(const SymExpr *S) { + if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) { + const StringLiteral *SL = *SLPtr; + return std::string(SL->getBytes()); + } + return None; + } + + Optional<std::string> VisitSymExpr(const SymExpr *S) { + return lookup(S); + } + + Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) { + if (Optional<std::string> Str = lookup(S)) + return Str; + if (Optional<std::string> Str = Visit(S->getLHS())) + return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " + + std::to_string(S->getRHS().getLimitedValue()) + + (S->getRHS().isUnsigned() ? "U" : "")) + .str(); + return None; + } + + Optional<std::string> VisitSymSymExpr(const SymSymExpr *S) { + if (Optional<std::string> Str = lookup(S)) + return Str; + if (Optional<std::string> Str1 = Visit(S->getLHS())) + if (Optional<std::string> Str2 = Visit(S->getRHS())) + return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + + " " + *Str2).str(); + return None; + } + + Optional<std::string> VisitSymbolCast(const SymbolCast *S) { + if (Optional<std::string> Str = lookup(S)) + return Str; + if (Optional<std::string> Str = Visit(S->getOperand())) + return (Twine("(") + S->getType().getAsString() + ")" + *Str).str(); + return None; + } +}; +} // namespace + +void ExprInspectionChecker::analyzerExpress(const CallExpr *CE, + CheckerContext &C) const { + if (CE->getNumArgs() == 0) { + reportBug("clang_analyzer_express() requires a symbol", C); + return; + } + + SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol(); + if (!Sym) { + reportBug("Not a symbol", C); + return; + } + + SymbolExpressor V(C.getState()); + auto Str = V.Visit(Sym); + if (!Str) { + reportBug("Unable to express", C); + return; + } + + reportBug(*Str, C); +} + +void ento::registerExprInspectionChecker(CheckerManager &Mgr) { + Mgr.registerChecker<ExprInspectionChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp new file mode 100644 index 000000000000..165a4e4490eb --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -0,0 +1,67 @@ +//=== FixedAddressChecker.cpp - Fixed address usage checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines FixedAddressChecker, a builtin checker that checks for +// assignment of a fixed address to a pointer. +// This check corresponds to CWE-587. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class FixedAddressChecker + : public Checker< check::PreStmt<BinaryOperator> > { + mutable std::unique_ptr<BuiltinBug> BT; + +public: + void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; +}; +} + +void FixedAddressChecker::checkPreStmt(const BinaryOperator *B, + CheckerContext &C) const { + // Using a fixed address is not portable because that address will probably + // not be valid in all environments or platforms. + + if (B->getOpcode() != BO_Assign) + return; + + QualType T = B->getType(); + if (!T->isPointerType()) + return; + + SVal RV = C.getSVal(B->getRHS()); + + if (!RV.isConstant() || RV.isZeroConstant()) + return; + + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + if (!BT) + BT.reset( + new BuiltinBug(this, "Use fixed address", + "Using a fixed address is not portable because that " + "address will probably not be valid in all " + "environments or platforms.")); + auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); + R->addRange(B->getRHS()->getSourceRange()); + C.emitReport(std::move(R)); + } +} + +void ento::registerFixedAddressChecker(CheckerManager &mgr) { + mgr.registerChecker<FixedAddressChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp new file mode 100644 index 000000000000..248b9c3f7693 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp @@ -0,0 +1,229 @@ +//===- GCDAntipatternChecker.cpp ---------------------------------*- 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 GCDAntipatternChecker which checks against a common +// antipattern when synchronous API is emulated from asynchronous callbacks +// using a semaphore: +// +// dispatch_semaphore_t sema = dispatch_semaphore_create(0); +// +// AnyCFunctionCall(^{ +// // code… +// dispatch_semaphore_signal(sema); +// }) +// dispatch_semaphore_wait(sema, *) +// +// Such code is a common performance problem, due to inability of GCD to +// properly handle QoS when a combination of queues and semaphores is used. +// Good code would either use asynchronous API (when available), or perform +// the necessary action in asynchronous callback. +// +// Currently, the check is performed using a simple heuristical AST pattern +// matching. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/Support/Debug.h" + +using namespace clang; +using namespace ento; +using namespace ast_matchers; + +namespace { + +// ID of a node at which the diagnostic would be emitted. +const char *WarnAtNode = "waitcall"; + +class GCDAntipatternChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const; +}; + +auto callsName(const char *FunctionName) + -> decltype(callee(functionDecl())) { + return callee(functionDecl(hasName(FunctionName))); +} + +auto equalsBoundArgDecl(int ArgIdx, const char *DeclName) + -> decltype(hasArgument(0, expr())) { + return hasArgument(ArgIdx, ignoringParenCasts(declRefExpr( + to(varDecl(equalsBoundNode(DeclName)))))); +} + +auto bindAssignmentToDecl(const char *DeclName) -> decltype(hasLHS(expr())) { + return hasLHS(ignoringParenImpCasts( + declRefExpr(to(varDecl().bind(DeclName))))); +} + +/// The pattern is very common in tests, and it is OK to use it there. +/// We have to heuristics for detecting tests: method name starts with "test" +/// (used in XCTest), and a class name contains "mock" or "test" (used in +/// helpers which are not tests themselves, but used exclusively in tests). +static bool isTest(const Decl *D) { + if (const auto* ND = dyn_cast<NamedDecl>(D)) { + std::string DeclName = ND->getNameAsString(); + if (StringRef(DeclName).startswith("test")) + return true; + } + if (const auto *OD = dyn_cast<ObjCMethodDecl>(D)) { + if (const auto *CD = dyn_cast<ObjCContainerDecl>(OD->getParent())) { + std::string ContainerName = CD->getNameAsString(); + StringRef CN(ContainerName); + if (CN.contains_lower("test") || CN.contains_lower("mock")) + return true; + } + } + return false; +} + +static auto findGCDAntiPatternWithSemaphore() -> decltype(compoundStmt()) { + + const char *SemaphoreBinding = "semaphore_name"; + auto SemaphoreCreateM = callExpr(allOf( + callsName("dispatch_semaphore_create"), + hasArgument(0, ignoringParenCasts(integerLiteral(equals(0)))))); + + auto SemaphoreBindingM = anyOf( + forEachDescendant( + varDecl(hasDescendant(SemaphoreCreateM)).bind(SemaphoreBinding)), + forEachDescendant(binaryOperator(bindAssignmentToDecl(SemaphoreBinding), + hasRHS(SemaphoreCreateM)))); + + auto HasBlockArgumentM = hasAnyArgument(hasType( + hasCanonicalType(blockPointerType()) + )); + + auto ArgCallsSignalM = hasAnyArgument(stmt(hasDescendant(callExpr( + allOf( + callsName("dispatch_semaphore_signal"), + equalsBoundArgDecl(0, SemaphoreBinding) + ))))); + + auto HasBlockAndCallsSignalM = allOf(HasBlockArgumentM, ArgCallsSignalM); + + auto HasBlockCallingSignalM = + forEachDescendant( + stmt(anyOf( + callExpr(HasBlockAndCallsSignalM), + objcMessageExpr(HasBlockAndCallsSignalM) + ))); + + auto SemaphoreWaitM = forEachDescendant( + callExpr( + allOf( + callsName("dispatch_semaphore_wait"), + equalsBoundArgDecl(0, SemaphoreBinding) + ) + ).bind(WarnAtNode)); + + return compoundStmt( + SemaphoreBindingM, HasBlockCallingSignalM, SemaphoreWaitM); +} + +static auto findGCDAntiPatternWithGroup() -> decltype(compoundStmt()) { + + const char *GroupBinding = "group_name"; + auto DispatchGroupCreateM = callExpr(callsName("dispatch_group_create")); + + auto GroupBindingM = anyOf( + forEachDescendant( + varDecl(hasDescendant(DispatchGroupCreateM)).bind(GroupBinding)), + forEachDescendant(binaryOperator(bindAssignmentToDecl(GroupBinding), + hasRHS(DispatchGroupCreateM)))); + + auto GroupEnterM = forEachDescendant( + stmt(callExpr(allOf(callsName("dispatch_group_enter"), + equalsBoundArgDecl(0, GroupBinding))))); + + auto HasBlockArgumentM = hasAnyArgument(hasType( + hasCanonicalType(blockPointerType()) + )); + + auto ArgCallsSignalM = hasAnyArgument(stmt(hasDescendant(callExpr( + allOf( + callsName("dispatch_group_leave"), + equalsBoundArgDecl(0, GroupBinding) + ))))); + + auto HasBlockAndCallsLeaveM = allOf(HasBlockArgumentM, ArgCallsSignalM); + + auto AcceptsBlockM = + forEachDescendant( + stmt(anyOf( + callExpr(HasBlockAndCallsLeaveM), + objcMessageExpr(HasBlockAndCallsLeaveM) + ))); + + auto GroupWaitM = forEachDescendant( + callExpr( + allOf( + callsName("dispatch_group_wait"), + equalsBoundArgDecl(0, GroupBinding) + ) + ).bind(WarnAtNode)); + + return compoundStmt(GroupBindingM, GroupEnterM, AcceptsBlockM, GroupWaitM); +} + +static void emitDiagnostics(const BoundNodes &Nodes, + const char* Type, + BugReporter &BR, + AnalysisDeclContext *ADC, + const GCDAntipatternChecker *Checker) { + const auto *SW = Nodes.getNodeAs<CallExpr>(WarnAtNode); + assert(SW); + + std::string Diagnostics; + llvm::raw_string_ostream OS(Diagnostics); + OS << "Waiting on a callback using a " << Type << " creates useless threads " + << "and is subject to priority inversion; consider " + << "using a synchronous API or changing the caller to be asynchronous"; + + BR.EmitBasicReport( + ADC->getDecl(), + Checker, + /*Name=*/"GCD performance anti-pattern", + /*Category=*/"Performance", + OS.str(), + PathDiagnosticLocation::createBegin(SW, BR.getSourceManager(), ADC), + SW->getSourceRange()); +} + +void GCDAntipatternChecker::checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const { + if (isTest(D)) + return; + + AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D); + + auto SemaphoreMatcherM = findGCDAntiPatternWithSemaphore(); + auto Matches = match(SemaphoreMatcherM, *D->getBody(), AM.getASTContext()); + for (BoundNodes Match : Matches) + emitDiagnostics(Match, "semaphore", BR, ADC, this); + + auto GroupMatcherM = findGCDAntiPatternWithGroup(); + Matches = match(GroupMatcherM, *D->getBody(), AM.getASTContext()); + for (BoundNodes Match : Matches) + emitDiagnostics(Match, "group", BR, ADC, this); +} + +} + +void ento::registerGCDAntipattern(CheckerManager &Mgr) { + Mgr.registerChecker<GCDAntipatternChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp new file mode 100644 index 000000000000..818716dd6070 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp @@ -0,0 +1,299 @@ +//==- GTestChecker.cpp - Model gtest API --*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker models the behavior of un-inlined APIs from the gtest +// unit-testing library to avoid false positives when using assertions from +// that library. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Expr.h" +#include "clang/Basic/LangOptions.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +// Modeling of un-inlined AssertionResult constructors +// +// The gtest unit testing API provides macros for assertions that expand +// into an if statement that calls a series of constructors and returns +// when the "assertion" is false. +// +// For example, +// +// ASSERT_TRUE(a == b) +// +// expands into: +// +// switch (0) +// case 0: +// default: +// if (const ::testing::AssertionResult gtest_ar_ = +// ::testing::AssertionResult((a == b))) +// ; +// else +// return ::testing::internal::AssertHelper( +// ::testing::TestPartResult::kFatalFailure, +// "<path to project>", +// <line number>, +// ::testing::internal::GetBoolAssertionFailureMessage( +// gtest_ar_, "a == b", "false", "true") +// .c_str()) = ::testing::Message(); +// +// where AssertionResult is defined similarly to +// +// class AssertionResult { +// public: +// AssertionResult(const AssertionResult& other); +// explicit AssertionResult(bool success) : success_(success) {} +// operator bool() const { return success_; } +// ... +// private: +// bool success_; +// }; +// +// In order for the analyzer to correctly handle this assertion, it needs to +// know that the boolean value of the expression "a == b" is stored the +// 'success_' field of the original AssertionResult temporary and propagated +// (via the copy constructor) into the 'success_' field of the object stored +// in 'gtest_ar_'. That boolean value will then be returned from the bool +// conversion method in the if statement. This guarantees that the assertion +// holds when the return path is not taken. +// +// If the success value is not properly propagated, then the eager case split +// on evaluating the expression can cause pernicious false positives +// on the non-return path: +// +// ASSERT(ptr != NULL) +// *ptr = 7; // False positive null pointer dereference here +// +// Unfortunately, the bool constructor cannot be inlined (because its +// implementation is not present in the headers) and the copy constructor is +// not inlined (because it is constructed into a temporary and the analyzer +// does not inline these since it does not yet reliably call temporary +// destructors). +// +// This checker compensates for the missing inlining by propagating the +// _success value across the bool and copy constructors so the assertion behaves +// as expected. + +namespace { +class GTestChecker : public Checker<check::PostCall> { + + mutable IdentifierInfo *AssertionResultII; + mutable IdentifierInfo *SuccessII; + +public: + GTestChecker(); + + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + +private: + void modelAssertionResultBoolConstructor(const CXXConstructorCall *Call, + bool IsRef, CheckerContext &C) const; + + void modelAssertionResultCopyConstructor(const CXXConstructorCall *Call, + CheckerContext &C) const; + + void initIdentifierInfo(ASTContext &Ctx) const; + + SVal + getAssertionResultSuccessFieldValue(const CXXRecordDecl *AssertionResultDecl, + SVal Instance, + ProgramStateRef State) const; + + static ProgramStateRef assumeValuesEqual(SVal Val1, SVal Val2, + ProgramStateRef State, + CheckerContext &C); +}; +} // End anonymous namespace. + +GTestChecker::GTestChecker() : AssertionResultII(nullptr), SuccessII(nullptr) {} + +/// Model a call to an un-inlined AssertionResult(bool) or +/// AssertionResult(bool &, ...). +/// To do so, constrain the value of the newly-constructed instance's 'success_' +/// field to be equal to the passed-in boolean value. +/// +/// \param IsRef Whether the boolean parameter is a reference or not. +void GTestChecker::modelAssertionResultBoolConstructor( + const CXXConstructorCall *Call, bool IsRef, CheckerContext &C) const { + assert(Call->getNumArgs() >= 1 && Call->getNumArgs() <= 2); + + ProgramStateRef State = C.getState(); + SVal BooleanArgVal = Call->getArgSVal(0); + if (IsRef) { + // The argument is a reference, so load from it to get the boolean value. + if (!BooleanArgVal.getAs<Loc>()) + return; + BooleanArgVal = C.getState()->getSVal(BooleanArgVal.castAs<Loc>()); + } + + SVal ThisVal = Call->getCXXThisVal(); + + SVal ThisSuccess = getAssertionResultSuccessFieldValue( + Call->getDecl()->getParent(), ThisVal, State); + + State = assumeValuesEqual(ThisSuccess, BooleanArgVal, State, C); + C.addTransition(State); +} + +/// Model a call to an un-inlined AssertionResult copy constructor: +/// +/// AssertionResult(const &AssertionResult other) +/// +/// To do so, constrain the value of the newly-constructed instance's +/// 'success_' field to be equal to the value of the pass-in instance's +/// 'success_' field. +void GTestChecker::modelAssertionResultCopyConstructor( + const CXXConstructorCall *Call, CheckerContext &C) const { + assert(Call->getNumArgs() == 1); + + // The first parameter of the copy constructor must be the other + // instance to initialize this instances fields from. + SVal OtherVal = Call->getArgSVal(0); + SVal ThisVal = Call->getCXXThisVal(); + + const CXXRecordDecl *AssertResultClassDecl = Call->getDecl()->getParent(); + ProgramStateRef State = C.getState(); + + SVal ThisSuccess = getAssertionResultSuccessFieldValue(AssertResultClassDecl, + ThisVal, State); + SVal OtherSuccess = getAssertionResultSuccessFieldValue(AssertResultClassDecl, + OtherVal, State); + + State = assumeValuesEqual(ThisSuccess, OtherSuccess, State, C); + C.addTransition(State); +} + +/// Model calls to AssertionResult constructors that are not inlined. +void GTestChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + /// If the constructor was inlined, there is no need model it. + if (C.wasInlined) + return; + + initIdentifierInfo(C.getASTContext()); + + auto *CtorCall = dyn_cast<CXXConstructorCall>(&Call); + if (!CtorCall) + return; + + const CXXConstructorDecl *CtorDecl = CtorCall->getDecl(); + const CXXRecordDecl *CtorParent = CtorDecl->getParent(); + if (CtorParent->getIdentifier() != AssertionResultII) + return; + + unsigned ParamCount = CtorDecl->getNumParams(); + + // Call the appropriate modeling method based the parameters and their + // types. + + // We have AssertionResult(const &AssertionResult) + if (CtorDecl->isCopyConstructor() && ParamCount == 1) { + modelAssertionResultCopyConstructor(CtorCall, C); + return; + } + + // There are two possible boolean constructors, depending on which + // version of gtest is being used: + // + // v1.7 and earlier: + // AssertionResult(bool success) + // + // v1.8 and greater: + // template <typename T> + // AssertionResult(const T& success, + // typename internal::EnableIf< + // !internal::ImplicitlyConvertible<T, + // AssertionResult>::value>::type*) + // + CanQualType BoolTy = C.getASTContext().BoolTy; + if (ParamCount == 1 && CtorDecl->getParamDecl(0)->getType() == BoolTy) { + // We have AssertionResult(bool) + modelAssertionResultBoolConstructor(CtorCall, /*IsRef=*/false, C); + return; + } + if (ParamCount == 2){ + auto *RefTy = CtorDecl->getParamDecl(0)->getType()->getAs<ReferenceType>(); + if (RefTy && + RefTy->getPointeeType()->getCanonicalTypeUnqualified() == BoolTy) { + // We have AssertionResult(bool &, ...) + modelAssertionResultBoolConstructor(CtorCall, /*IsRef=*/true, C); + return; + } + } +} + +void GTestChecker::initIdentifierInfo(ASTContext &Ctx) const { + if (AssertionResultII) + return; + + AssertionResultII = &Ctx.Idents.get("AssertionResult"); + SuccessII = &Ctx.Idents.get("success_"); +} + +/// Returns the value stored in the 'success_' field of the passed-in +/// AssertionResult instance. +SVal GTestChecker::getAssertionResultSuccessFieldValue( + const CXXRecordDecl *AssertionResultDecl, SVal Instance, + ProgramStateRef State) const { + + DeclContext::lookup_result Result = AssertionResultDecl->lookup(SuccessII); + if (Result.empty()) + return UnknownVal(); + + auto *SuccessField = dyn_cast<FieldDecl>(Result.front()); + if (!SuccessField) + return UnknownVal(); + + Optional<Loc> FieldLoc = + State->getLValue(SuccessField, Instance).getAs<Loc>(); + if (!FieldLoc.hasValue()) + return UnknownVal(); + + return State->getSVal(*FieldLoc); +} + +/// Constrain the passed-in state to assume two values are equal. +ProgramStateRef GTestChecker::assumeValuesEqual(SVal Val1, SVal Val2, + ProgramStateRef State, + CheckerContext &C) { + if (!Val1.getAs<DefinedOrUnknownSVal>() || + !Val2.getAs<DefinedOrUnknownSVal>()) + return State; + + auto ValuesEqual = + C.getSValBuilder().evalEQ(State, Val1.castAs<DefinedOrUnknownSVal>(), + Val2.castAs<DefinedOrUnknownSVal>()); + + if (!ValuesEqual.getAs<DefinedSVal>()) + return State; + + State = C.getConstraintManager().assume( + State, ValuesEqual.castAs<DefinedSVal>(), true); + + return State; +} + +void ento::registerGTestChecker(CheckerManager &Mgr) { + const LangOptions &LangOpts = Mgr.getLangOpts(); + // gtest is a C++ API so there is no sense running the checker + // if not compiling for C++. + if (!LangOpts.CPlusPlus) + return; + + Mgr.registerChecker<GTestChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp new file mode 100644 index 000000000000..32fed202d3ab --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp @@ -0,0 +1,748 @@ +//== GenericTaintChecker.cpp ----------------------------------- -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker defines the attack surface for generic taint propagation. +// +// The taint information produced by it might be useful to other checkers. For +// example, checkers should report errors which involve tainted data more +// aggressively, even if the involved symbols are under constrained. +// +//===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Attr.h" +#include "clang/Basic/Builtins.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include <climits> + +using namespace clang; +using namespace ento; + +namespace { +class GenericTaintChecker + : public Checker<check::PostStmt<CallExpr>, check::PreStmt<CallExpr>> { +public: + static void *getTag() { + static int Tag; + return &Tag; + } + + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + +private: + static const unsigned InvalidArgIndex = UINT_MAX; + /// Denotes the return vale. + static const unsigned ReturnValueIndex = UINT_MAX - 1; + + mutable std::unique_ptr<BugType> BT; + inline void initBugType() const { + if (!BT) + BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data")); + } + + /// Catch taint related bugs. Check if tainted data is passed to a + /// system call etc. + bool checkPre(const CallExpr *CE, CheckerContext &C) const; + + /// Add taint sources on a pre-visit. + void addSourcesPre(const CallExpr *CE, CheckerContext &C) const; + + /// Propagate taint generated at pre-visit. + bool propagateFromPre(const CallExpr *CE, CheckerContext &C) const; + + /// Add taint sources on a post visit. + void addSourcesPost(const CallExpr *CE, CheckerContext &C) const; + + /// Check if the region the expression evaluates to is the standard input, + /// and thus, is tainted. + static bool isStdin(const Expr *E, CheckerContext &C); + + /// Given a pointer argument, return the value it points to. + static Optional<SVal> getPointedToSVal(CheckerContext &C, const Expr *Arg); + + /// Functions defining the attack surface. + typedef ProgramStateRef (GenericTaintChecker::*FnCheck)( + const CallExpr *, CheckerContext &C) const; + ProgramStateRef postScanf(const CallExpr *CE, CheckerContext &C) const; + ProgramStateRef postSocket(const CallExpr *CE, CheckerContext &C) const; + ProgramStateRef postRetTaint(const CallExpr *CE, CheckerContext &C) const; + + /// Taint the scanned input if the file is tainted. + ProgramStateRef preFscanf(const CallExpr *CE, CheckerContext &C) const; + + /// Check for CWE-134: Uncontrolled Format String. + static const char MsgUncontrolledFormatString[]; + bool checkUncontrolledFormatString(const CallExpr *CE, + CheckerContext &C) const; + + /// Check for: + /// CERT/STR02-C. "Sanitize data passed to complex subsystems" + /// CWE-78, "Failure to Sanitize Data into an OS Command" + static const char MsgSanitizeSystemArgs[]; + bool checkSystemCall(const CallExpr *CE, StringRef Name, + CheckerContext &C) const; + + /// Check if tainted data is used as a buffer size ins strn.. functions, + /// and allocators. + static const char MsgTaintedBufferSize[]; + bool checkTaintedBufferSize(const CallExpr *CE, const FunctionDecl *FDecl, + CheckerContext &C) const; + + /// Generate a report if the expression is tainted or points to tainted data. + bool generateReportIfTainted(const Expr *E, const char Msg[], + CheckerContext &C) const; + + typedef SmallVector<unsigned, 2> ArgVector; + + /// A struct used to specify taint propagation rules for a function. + /// + /// If any of the possible taint source arguments is tainted, all of the + /// destination arguments should also be tainted. Use InvalidArgIndex in the + /// src list to specify that all of the arguments can introduce taint. Use + /// InvalidArgIndex in the dst arguments to signify that all the non-const + /// pointer and reference arguments might be tainted on return. If + /// ReturnValueIndex is added to the dst list, the return value will be + /// tainted. + struct TaintPropagationRule { + /// List of arguments which can be taint sources and should be checked. + ArgVector SrcArgs; + /// List of arguments which should be tainted on function return. + ArgVector DstArgs; + // TODO: Check if using other data structures would be more optimal. + + TaintPropagationRule() {} + + TaintPropagationRule(unsigned SArg, unsigned DArg, bool TaintRet = false) { + SrcArgs.push_back(SArg); + DstArgs.push_back(DArg); + if (TaintRet) + DstArgs.push_back(ReturnValueIndex); + } + + TaintPropagationRule(unsigned SArg1, unsigned SArg2, unsigned DArg, + bool TaintRet = false) { + SrcArgs.push_back(SArg1); + SrcArgs.push_back(SArg2); + DstArgs.push_back(DArg); + if (TaintRet) + DstArgs.push_back(ReturnValueIndex); + } + + /// Get the propagation rule for a given function. + static TaintPropagationRule + getTaintPropagationRule(const FunctionDecl *FDecl, StringRef Name, + CheckerContext &C); + + inline void addSrcArg(unsigned A) { SrcArgs.push_back(A); } + inline void addDstArg(unsigned A) { DstArgs.push_back(A); } + + inline bool isNull() const { return SrcArgs.empty(); } + + inline bool isDestinationArgument(unsigned ArgNum) const { + return (std::find(DstArgs.begin(), DstArgs.end(), ArgNum) != + DstArgs.end()); + } + + static inline bool isTaintedOrPointsToTainted(const Expr *E, + ProgramStateRef State, + CheckerContext &C) { + if (State->isTainted(E, C.getLocationContext()) || isStdin(E, C)) + return true; + + if (!E->getType().getTypePtr()->isPointerType()) + return false; + + Optional<SVal> V = getPointedToSVal(C, E); + return (V && State->isTainted(*V)); + } + + /// Pre-process a function which propagates taint according to the + /// taint rule. + ProgramStateRef process(const CallExpr *CE, CheckerContext &C) const; + }; +}; + +const unsigned GenericTaintChecker::ReturnValueIndex; +const unsigned GenericTaintChecker::InvalidArgIndex; + +const char GenericTaintChecker::MsgUncontrolledFormatString[] = + "Untrusted data is used as a format string " + "(CWE-134: Uncontrolled Format String)"; + +const char GenericTaintChecker::MsgSanitizeSystemArgs[] = + "Untrusted data is passed to a system call " + "(CERT/STR02-C. Sanitize data passed to complex subsystems)"; + +const char GenericTaintChecker::MsgTaintedBufferSize[] = + "Untrusted data is used to specify the buffer size " + "(CERT/STR31-C. Guarantee that storage for strings has sufficient space " + "for " + "character data and the null terminator)"; + +} // end of anonymous namespace + +/// A set which is used to pass information from call pre-visit instruction +/// to the call post-visit. The values are unsigned integers, which are either +/// ReturnValueIndex, or indexes of the pointer/reference argument, which +/// points to data, which should be tainted on return. +REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, unsigned) + +GenericTaintChecker::TaintPropagationRule +GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule( + const FunctionDecl *FDecl, StringRef Name, CheckerContext &C) { + // TODO: Currently, we might lose precision here: we always mark a return + // value as tainted even if it's just a pointer, pointing to tainted data. + + // Check for exact name match for functions without builtin substitutes. + TaintPropagationRule Rule = + llvm::StringSwitch<TaintPropagationRule>(Name) + .Case("atoi", TaintPropagationRule(0, ReturnValueIndex)) + .Case("atol", TaintPropagationRule(0, ReturnValueIndex)) + .Case("atoll", TaintPropagationRule(0, ReturnValueIndex)) + .Case("getc", TaintPropagationRule(0, ReturnValueIndex)) + .Case("fgetc", TaintPropagationRule(0, ReturnValueIndex)) + .Case("getc_unlocked", TaintPropagationRule(0, ReturnValueIndex)) + .Case("getw", TaintPropagationRule(0, ReturnValueIndex)) + .Case("toupper", TaintPropagationRule(0, ReturnValueIndex)) + .Case("tolower", TaintPropagationRule(0, ReturnValueIndex)) + .Case("strchr", TaintPropagationRule(0, ReturnValueIndex)) + .Case("strrchr", TaintPropagationRule(0, ReturnValueIndex)) + .Case("read", TaintPropagationRule(0, 2, 1, true)) + .Case("pread", TaintPropagationRule(InvalidArgIndex, 1, true)) + .Case("gets", TaintPropagationRule(InvalidArgIndex, 0, true)) + .Case("fgets", TaintPropagationRule(2, 0, true)) + .Case("getline", TaintPropagationRule(2, 0)) + .Case("getdelim", TaintPropagationRule(3, 0)) + .Case("fgetln", TaintPropagationRule(0, ReturnValueIndex)) + .Default(TaintPropagationRule()); + + if (!Rule.isNull()) + return Rule; + + // Check if it's one of the memory setting/copying functions. + // This check is specialized but faster then calling isCLibraryFunction. + unsigned BId = 0; + if ((BId = FDecl->getMemoryFunctionKind())) + switch (BId) { + case Builtin::BImemcpy: + case Builtin::BImemmove: + case Builtin::BIstrncpy: + case Builtin::BIstrncat: + return TaintPropagationRule(1, 2, 0, true); + case Builtin::BIstrlcpy: + case Builtin::BIstrlcat: + return TaintPropagationRule(1, 2, 0, false); + case Builtin::BIstrndup: + return TaintPropagationRule(0, 1, ReturnValueIndex); + + default: + break; + }; + + // Process all other functions which could be defined as builtins. + if (Rule.isNull()) { + if (C.isCLibraryFunction(FDecl, "snprintf") || + C.isCLibraryFunction(FDecl, "sprintf")) + return TaintPropagationRule(InvalidArgIndex, 0, true); + else if (C.isCLibraryFunction(FDecl, "strcpy") || + C.isCLibraryFunction(FDecl, "stpcpy") || + C.isCLibraryFunction(FDecl, "strcat")) + return TaintPropagationRule(1, 0, true); + else if (C.isCLibraryFunction(FDecl, "bcopy")) + return TaintPropagationRule(0, 2, 1, false); + else if (C.isCLibraryFunction(FDecl, "strdup") || + C.isCLibraryFunction(FDecl, "strdupa")) + return TaintPropagationRule(0, ReturnValueIndex); + else if (C.isCLibraryFunction(FDecl, "wcsdup")) + return TaintPropagationRule(0, ReturnValueIndex); + } + + // Skipping the following functions, since they might be used for cleansing + // or smart memory copy: + // - memccpy - copying until hitting a special character. + + return TaintPropagationRule(); +} + +void GenericTaintChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + // Check for errors first. + if (checkPre(CE, C)) + return; + + // Add taint second. + addSourcesPre(CE, C); +} + +void GenericTaintChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + if (propagateFromPre(CE, C)) + return; + addSourcesPost(CE, C); +} + +void GenericTaintChecker::addSourcesPre(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = nullptr; + const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl || FDecl->getKind() != Decl::Function) + return; + + StringRef Name = C.getCalleeName(FDecl); + if (Name.empty()) + return; + + // First, try generating a propagation rule for this function. + TaintPropagationRule Rule = + TaintPropagationRule::getTaintPropagationRule(FDecl, Name, C); + if (!Rule.isNull()) { + State = Rule.process(CE, C); + if (!State) + return; + C.addTransition(State); + return; + } + + // Otherwise, check if we have custom pre-processing implemented. + FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name) + .Case("fscanf", &GenericTaintChecker::preFscanf) + .Default(nullptr); + // Check and evaluate the call. + if (evalFunction) + State = (this->*evalFunction)(CE, C); + if (!State) + return; + C.addTransition(State); +} + +bool GenericTaintChecker::propagateFromPre(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // Depending on what was tainted at pre-visit, we determined a set of + // arguments which should be tainted after the function returns. These are + // stored in the state as TaintArgsOnPostVisit set. + TaintArgsOnPostVisitTy TaintArgs = State->get<TaintArgsOnPostVisit>(); + if (TaintArgs.isEmpty()) + return false; + + for (llvm::ImmutableSet<unsigned>::iterator I = TaintArgs.begin(), + E = TaintArgs.end(); + I != E; ++I) { + unsigned ArgNum = *I; + + // Special handling for the tainted return value. + if (ArgNum == ReturnValueIndex) { + State = State->addTaint(CE, C.getLocationContext()); + continue; + } + + // The arguments are pointer arguments. The data they are pointing at is + // tainted after the call. + if (CE->getNumArgs() < (ArgNum + 1)) + return false; + const Expr *Arg = CE->getArg(ArgNum); + Optional<SVal> V = getPointedToSVal(C, Arg); + if (V) + State = State->addTaint(*V); + } + + // Clear up the taint info from the state. + State = State->remove<TaintArgsOnPostVisit>(); + + if (State != C.getState()) { + C.addTransition(State); + return true; + } + return false; +} + +void GenericTaintChecker::addSourcesPost(const CallExpr *CE, + CheckerContext &C) const { + // Define the attack surface. + // Set the evaluation function by switching on the callee name. + const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl || FDecl->getKind() != Decl::Function) + return; + + StringRef Name = C.getCalleeName(FDecl); + if (Name.empty()) + return; + FnCheck evalFunction = + llvm::StringSwitch<FnCheck>(Name) + .Case("scanf", &GenericTaintChecker::postScanf) + // TODO: Add support for vfscanf & family. + .Case("getchar", &GenericTaintChecker::postRetTaint) + .Case("getchar_unlocked", &GenericTaintChecker::postRetTaint) + .Case("getenv", &GenericTaintChecker::postRetTaint) + .Case("fopen", &GenericTaintChecker::postRetTaint) + .Case("fdopen", &GenericTaintChecker::postRetTaint) + .Case("freopen", &GenericTaintChecker::postRetTaint) + .Case("getch", &GenericTaintChecker::postRetTaint) + .Case("wgetch", &GenericTaintChecker::postRetTaint) + .Case("socket", &GenericTaintChecker::postSocket) + .Default(nullptr); + + // If the callee isn't defined, it is not of security concern. + // Check and evaluate the call. + ProgramStateRef State = nullptr; + if (evalFunction) + State = (this->*evalFunction)(CE, C); + if (!State) + return; + + C.addTransition(State); +} + +bool GenericTaintChecker::checkPre(const CallExpr *CE, + CheckerContext &C) const { + + if (checkUncontrolledFormatString(CE, C)) + return true; + + const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl || FDecl->getKind() != Decl::Function) + return false; + + StringRef Name = C.getCalleeName(FDecl); + if (Name.empty()) + return false; + + if (checkSystemCall(CE, Name, C)) + return true; + + if (checkTaintedBufferSize(CE, FDecl, C)) + return true; + + return false; +} + +Optional<SVal> GenericTaintChecker::getPointedToSVal(CheckerContext &C, + const Expr *Arg) { + ProgramStateRef State = C.getState(); + SVal AddrVal = C.getSVal(Arg->IgnoreParens()); + if (AddrVal.isUnknownOrUndef()) + return None; + + Optional<Loc> AddrLoc = AddrVal.getAs<Loc>(); + if (!AddrLoc) + return None; + + QualType ArgTy = Arg->getType().getCanonicalType(); + if (!ArgTy->isPointerType()) + return None; + + QualType ValTy = ArgTy->getPointeeType(); + + // Do not dereference void pointers. Treat them as byte pointers instead. + // FIXME: we might want to consider more than just the first byte. + if (ValTy->isVoidType()) + ValTy = C.getASTContext().CharTy; + + return State->getSVal(*AddrLoc, ValTy); +} + +ProgramStateRef +GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // Check for taint in arguments. + bool IsTainted = false; + for (ArgVector::const_iterator I = SrcArgs.begin(), E = SrcArgs.end(); I != E; + ++I) { + unsigned ArgNum = *I; + + if (ArgNum == InvalidArgIndex) { + // Check if any of the arguments is tainted, but skip the + // destination arguments. + for (unsigned int i = 0; i < CE->getNumArgs(); ++i) { + if (isDestinationArgument(i)) + continue; + if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(i), State, C))) + break; + } + break; + } + + if (CE->getNumArgs() < (ArgNum + 1)) + return State; + if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(ArgNum), State, C))) + break; + } + if (!IsTainted) + return State; + + // Mark the arguments which should be tainted after the function returns. + for (ArgVector::const_iterator I = DstArgs.begin(), E = DstArgs.end(); I != E; + ++I) { + unsigned ArgNum = *I; + + // Should we mark all arguments as tainted? + if (ArgNum == InvalidArgIndex) { + // For all pointer and references that were passed in: + // If they are not pointing to const data, mark data as tainted. + // TODO: So far we are just going one level down; ideally we'd need to + // recurse here. + for (unsigned int i = 0; i < CE->getNumArgs(); ++i) { + const Expr *Arg = CE->getArg(i); + // Process pointer argument. + const Type *ArgTy = Arg->getType().getTypePtr(); + QualType PType = ArgTy->getPointeeType(); + if ((!PType.isNull() && !PType.isConstQualified()) || + (ArgTy->isReferenceType() && !Arg->getType().isConstQualified())) + State = State->add<TaintArgsOnPostVisit>(i); + } + continue; + } + + // Should mark the return value? + if (ArgNum == ReturnValueIndex) { + State = State->add<TaintArgsOnPostVisit>(ReturnValueIndex); + continue; + } + + // Mark the given argument. + assert(ArgNum < CE->getNumArgs()); + State = State->add<TaintArgsOnPostVisit>(ArgNum); + } + + return State; +} + +// If argument 0 (file descriptor) is tainted, all arguments except for arg 0 +// and arg 1 should get taint. +ProgramStateRef GenericTaintChecker::preFscanf(const CallExpr *CE, + CheckerContext &C) const { + assert(CE->getNumArgs() >= 2); + ProgramStateRef State = C.getState(); + + // Check is the file descriptor is tainted. + if (State->isTainted(CE->getArg(0), C.getLocationContext()) || + isStdin(CE->getArg(0), C)) { + // All arguments except for the first two should get taint. + for (unsigned int i = 2; i < CE->getNumArgs(); ++i) + State = State->add<TaintArgsOnPostVisit>(i); + return State; + } + + return nullptr; +} + +// If argument 0(protocol domain) is network, the return value should get taint. +ProgramStateRef GenericTaintChecker::postSocket(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (CE->getNumArgs() < 3) + return State; + + SourceLocation DomLoc = CE->getArg(0)->getExprLoc(); + StringRef DomName = C.getMacroNameOrSpelling(DomLoc); + // White list the internal communication protocols. + if (DomName.equals("AF_SYSTEM") || DomName.equals("AF_LOCAL") || + DomName.equals("AF_UNIX") || DomName.equals("AF_RESERVED_36")) + return State; + State = State->addTaint(CE, C.getLocationContext()); + return State; +} + +ProgramStateRef GenericTaintChecker::postScanf(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (CE->getNumArgs() < 2) + return State; + + // All arguments except for the very first one should get taint. + for (unsigned int i = 1; i < CE->getNumArgs(); ++i) { + // The arguments are pointer arguments. The data they are pointing at is + // tainted after the call. + const Expr *Arg = CE->getArg(i); + Optional<SVal> V = getPointedToSVal(C, Arg); + if (V) + State = State->addTaint(*V); + } + return State; +} + +ProgramStateRef GenericTaintChecker::postRetTaint(const CallExpr *CE, + CheckerContext &C) const { + return C.getState()->addTaint(CE, C.getLocationContext()); +} + +bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) { + ProgramStateRef State = C.getState(); + SVal Val = C.getSVal(E); + + // stdin is a pointer, so it would be a region. + const MemRegion *MemReg = Val.getAsRegion(); + + // The region should be symbolic, we do not know it's value. + const SymbolicRegion *SymReg = dyn_cast_or_null<SymbolicRegion>(MemReg); + if (!SymReg) + return false; + + // Get it's symbol and find the declaration region it's pointing to. + const SymbolRegionValue *Sm = + dyn_cast<SymbolRegionValue>(SymReg->getSymbol()); + if (!Sm) + return false; + const DeclRegion *DeclReg = dyn_cast_or_null<DeclRegion>(Sm->getRegion()); + if (!DeclReg) + return false; + + // This region corresponds to a declaration, find out if it's a global/extern + // variable named stdin with the proper type. + if (const VarDecl *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) { + D = D->getCanonicalDecl(); + if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC()) + if (const PointerType *PtrTy = + dyn_cast<PointerType>(D->getType().getTypePtr())) + if (PtrTy->getPointeeType().getCanonicalType() == + C.getASTContext().getFILEType().getCanonicalType()) + return true; + } + return false; +} + +static bool getPrintfFormatArgumentNum(const CallExpr *CE, + const CheckerContext &C, + unsigned int &ArgNum) { + // Find if the function contains a format string argument. + // Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf, + // vsnprintf, syslog, custom annotated functions. + const FunctionDecl *FDecl = C.getCalleeDecl(CE); + if (!FDecl) + return false; + for (const auto *Format : FDecl->specific_attrs<FormatAttr>()) { + ArgNum = Format->getFormatIdx() - 1; + if ((Format->getType()->getName() == "printf") && CE->getNumArgs() > ArgNum) + return true; + } + + // Or if a function is named setproctitle (this is a heuristic). + if (C.getCalleeName(CE).find("setproctitle") != StringRef::npos) { + ArgNum = 0; + return true; + } + + return false; +} + +bool GenericTaintChecker::generateReportIfTainted(const Expr *E, + const char Msg[], + CheckerContext &C) const { + assert(E); + + // Check for taint. + ProgramStateRef State = C.getState(); + Optional<SVal> PointedToSVal = getPointedToSVal(C, E); + SVal TaintedSVal; + if (PointedToSVal && State->isTainted(*PointedToSVal)) + TaintedSVal = *PointedToSVal; + else if (State->isTainted(E, C.getLocationContext())) + TaintedSVal = C.getSVal(E); + else + return false; + + // Generate diagnostic. + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + initBugType(); + auto report = llvm::make_unique<BugReport>(*BT, Msg, N); + report->addRange(E->getSourceRange()); + report->addVisitor(llvm::make_unique<TaintBugVisitor>(TaintedSVal)); + C.emitReport(std::move(report)); + return true; + } + return false; +} + +bool GenericTaintChecker::checkUncontrolledFormatString( + const CallExpr *CE, CheckerContext &C) const { + // Check if the function contains a format string argument. + unsigned int ArgNum = 0; + if (!getPrintfFormatArgumentNum(CE, C, ArgNum)) + return false; + + // If either the format string content or the pointer itself are tainted, + // warn. + return generateReportIfTainted(CE->getArg(ArgNum), + MsgUncontrolledFormatString, C); +} + +bool GenericTaintChecker::checkSystemCall(const CallExpr *CE, StringRef Name, + CheckerContext &C) const { + // TODO: It might make sense to run this check on demand. In some cases, + // we should check if the environment has been cleansed here. We also might + // need to know if the user was reset before these calls(seteuid). + unsigned ArgNum = llvm::StringSwitch<unsigned>(Name) + .Case("system", 0) + .Case("popen", 0) + .Case("execl", 0) + .Case("execle", 0) + .Case("execlp", 0) + .Case("execv", 0) + .Case("execvp", 0) + .Case("execvP", 0) + .Case("execve", 0) + .Case("dlopen", 0) + .Default(UINT_MAX); + + if (ArgNum == UINT_MAX || CE->getNumArgs() < (ArgNum + 1)) + return false; + + return generateReportIfTainted(CE->getArg(ArgNum), MsgSanitizeSystemArgs, C); +} + +// TODO: Should this check be a part of the CString checker? +// If yes, should taint be a global setting? +bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE, + const FunctionDecl *FDecl, + CheckerContext &C) const { + // If the function has a buffer size argument, set ArgNum. + unsigned ArgNum = InvalidArgIndex; + unsigned BId = 0; + if ((BId = FDecl->getMemoryFunctionKind())) + switch (BId) { + case Builtin::BImemcpy: + case Builtin::BImemmove: + case Builtin::BIstrncpy: + ArgNum = 2; + break; + case Builtin::BIstrndup: + ArgNum = 1; + break; + default: + break; + }; + + if (ArgNum == InvalidArgIndex) { + if (C.isCLibraryFunction(FDecl, "malloc") || + C.isCLibraryFunction(FDecl, "calloc") || + C.isCLibraryFunction(FDecl, "alloca")) + ArgNum = 0; + else if (C.isCLibraryFunction(FDecl, "memccpy")) + ArgNum = 3; + else if (C.isCLibraryFunction(FDecl, "realloc")) + ArgNum = 1; + else if (C.isCLibraryFunction(FDecl, "bcopy")) + ArgNum = 2; + } + + return ArgNum != InvalidArgIndex && CE->getNumArgs() > ArgNum && + generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C); +} + +void ento::registerGenericTaintChecker(CheckerManager &mgr) { + mgr.registerChecker<GenericTaintChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp new file mode 100644 index 000000000000..4c2a229428d9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp @@ -0,0 +1,515 @@ +//== IdenticalExprChecker.cpp - Identical expression checker----------------==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This defines IdenticalExprChecker, a check that warns about +/// unintended use of identical expressions. +/// +/// It checks for use of identical expressions with comparison operators and +/// inside conditional expressions. +/// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1, + const Stmt *Stmt2, bool IgnoreSideEffects = false); +//===----------------------------------------------------------------------===// +// FindIdenticalExprVisitor - Identify nodes using identical expressions. +//===----------------------------------------------------------------------===// + +namespace { +class FindIdenticalExprVisitor + : public RecursiveASTVisitor<FindIdenticalExprVisitor> { + BugReporter &BR; + const CheckerBase *Checker; + AnalysisDeclContext *AC; +public: + explicit FindIdenticalExprVisitor(BugReporter &B, + const CheckerBase *Checker, + AnalysisDeclContext *A) + : BR(B), Checker(Checker), AC(A) {} + // FindIdenticalExprVisitor only visits nodes + // that are binary operators, if statements or + // conditional operators. + bool VisitBinaryOperator(const BinaryOperator *B); + bool VisitIfStmt(const IfStmt *I); + bool VisitConditionalOperator(const ConditionalOperator *C); + +private: + void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise, + ArrayRef<SourceRange> Sr); + void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise); + void checkComparisonOp(const BinaryOperator *B); +}; +} // end anonymous namespace + +void FindIdenticalExprVisitor::reportIdenticalExpr(const BinaryOperator *B, + bool CheckBitwise, + ArrayRef<SourceRange> Sr) { + StringRef Message; + if (CheckBitwise) + Message = "identical expressions on both sides of bitwise operator"; + else + Message = "identical expressions on both sides of logical operator"; + + PathDiagnosticLocation ELoc = + PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager()); + BR.EmitBasicReport(AC->getDecl(), Checker, + "Use of identical expressions", + categories::LogicError, + Message, ELoc, Sr); +} + +void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B, + bool CheckBitwise) { + SourceRange Sr[2]; + + const Expr *LHS = B->getLHS(); + const Expr *RHS = B->getRHS(); + + // Split operators as long as we still have operators to split on. We will + // get called for every binary operator in an expression so there is no need + // to check every one against each other here, just the right most one with + // the others. + while (const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS)) { + if (B->getOpcode() != B2->getOpcode()) + break; + if (isIdenticalStmt(AC->getASTContext(), RHS, B2->getRHS())) { + Sr[0] = RHS->getSourceRange(); + Sr[1] = B2->getRHS()->getSourceRange(); + reportIdenticalExpr(B, CheckBitwise, Sr); + } + LHS = B2->getLHS(); + } + + if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) { + Sr[0] = RHS->getSourceRange(); + Sr[1] = LHS->getSourceRange(); + reportIdenticalExpr(B, CheckBitwise, Sr); + } +} + +bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) { + const Stmt *Stmt1 = I->getThen(); + const Stmt *Stmt2 = I->getElse(); + + // Check for identical inner condition: + // + // if (x<10) { + // if (x<10) { + // .. + if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(Stmt1)) { + if (!CS->body_empty()) { + const IfStmt *InnerIf = dyn_cast<IfStmt>(*CS->body_begin()); + if (InnerIf && isIdenticalStmt(AC->getASTContext(), I->getCond(), InnerIf->getCond(), /*ignoreSideEffects=*/ false)) { + PathDiagnosticLocation ELoc(InnerIf->getCond(), BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions", + categories::LogicError, + "conditions of the inner and outer statements are identical", + ELoc); + } + } + } + + // Check for identical conditions: + // + // if (b) { + // foo1(); + // } else if (b) { + // foo2(); + // } + if (Stmt1 && Stmt2) { + const Expr *Cond1 = I->getCond(); + const Stmt *Else = Stmt2; + while (const IfStmt *I2 = dyn_cast_or_null<IfStmt>(Else)) { + const Expr *Cond2 = I2->getCond(); + if (isIdenticalStmt(AC->getASTContext(), Cond1, Cond2, false)) { + SourceRange Sr = Cond1->getSourceRange(); + PathDiagnosticLocation ELoc(Cond2, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions", + categories::LogicError, + "expression is identical to previous condition", + ELoc, Sr); + } + Else = I2->getElse(); + } + } + + if (!Stmt1 || !Stmt2) + return true; + + // Special handling for code like: + // + // if (b) { + // i = 1; + // } else + // i = 1; + if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) { + if (CompStmt->size() == 1) + Stmt1 = CompStmt->body_back(); + } + if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt2)) { + if (CompStmt->size() == 1) + Stmt2 = CompStmt->body_back(); + } + + if (isIdenticalStmt(AC->getASTContext(), Stmt1, Stmt2, true)) { + PathDiagnosticLocation ELoc = + PathDiagnosticLocation::createBegin(I, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), Checker, + "Identical branches", + categories::LogicError, + "true and false branches are identical", ELoc); + } + return true; +} + +bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) { + BinaryOperator::Opcode Op = B->getOpcode(); + + if (BinaryOperator::isBitwiseOp(Op)) + checkBitwiseOrLogicalOp(B, true); + + if (BinaryOperator::isLogicalOp(Op)) + checkBitwiseOrLogicalOp(B, false); + + if (BinaryOperator::isComparisonOp(Op)) + checkComparisonOp(B); + + // We want to visit ALL nodes (subexpressions of binary comparison + // expressions too) that contains comparison operators. + // True is always returned to traverse ALL nodes. + return true; +} + +void FindIdenticalExprVisitor::checkComparisonOp(const BinaryOperator *B) { + BinaryOperator::Opcode Op = B->getOpcode(); + + // + // Special case for floating-point representation. + // + // If expressions on both sides of comparison operator are of type float, + // then for some comparison operators no warning shall be + // reported even if the expressions are identical from a symbolic point of + // view. Comparison between expressions, declared variables and literals + // are treated differently. + // + // != and == between float literals that have the same value should NOT warn. + // < > between float literals that have the same value SHOULD warn. + // + // != and == between the same float declaration should NOT warn. + // < > between the same float declaration SHOULD warn. + // + // != and == between eq. expressions that evaluates into float + // should NOT warn. + // < > between eq. expressions that evaluates into float + // should NOT warn. + // + const Expr *LHS = B->getLHS()->IgnoreParenImpCasts(); + const Expr *RHS = B->getRHS()->IgnoreParenImpCasts(); + + const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS); + const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS); + const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS); + const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS); + if ((DeclRef1) && (DeclRef2)) { + if ((DeclRef1->getType()->hasFloatingRepresentation()) && + (DeclRef2->getType()->hasFloatingRepresentation())) { + if (DeclRef1->getDecl() == DeclRef2->getDecl()) { + if ((Op == BO_EQ) || (Op == BO_NE)) { + return; + } + } + } + } else if ((FloatLit1) && (FloatLit2)) { + if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) { + if ((Op == BO_EQ) || (Op == BO_NE)) { + return; + } + } + } else if (LHS->getType()->hasFloatingRepresentation()) { + // If any side of comparison operator still has floating-point + // representation, then it's an expression. Don't warn. + // Here only LHS is checked since RHS will be implicit casted to float. + return; + } else { + // No special case with floating-point representation, report as usual. + } + + if (isIdenticalStmt(AC->getASTContext(), B->getLHS(), B->getRHS())) { + PathDiagnosticLocation ELoc = + PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager()); + StringRef Message; + if (Op == BO_Cmp) + Message = "comparison of identical expressions always evaluates to " + "'equal'"; + else if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE))) + Message = "comparison of identical expressions always evaluates to true"; + else + Message = "comparison of identical expressions always evaluates to false"; + BR.EmitBasicReport(AC->getDecl(), Checker, + "Compare of identical expressions", + categories::LogicError, Message, ELoc); + } +} + +bool FindIdenticalExprVisitor::VisitConditionalOperator( + const ConditionalOperator *C) { + + // Check if expressions in conditional expression are identical + // from a symbolic point of view. + + if (isIdenticalStmt(AC->getASTContext(), C->getTrueExpr(), + C->getFalseExpr(), true)) { + PathDiagnosticLocation ELoc = + PathDiagnosticLocation::createConditionalColonLoc( + C, BR.getSourceManager()); + + SourceRange Sr[2]; + Sr[0] = C->getTrueExpr()->getSourceRange(); + Sr[1] = C->getFalseExpr()->getSourceRange(); + BR.EmitBasicReport( + AC->getDecl(), Checker, + "Identical expressions in conditional expression", + categories::LogicError, + "identical expressions on both sides of ':' in conditional expression", + ELoc, Sr); + } + // We want to visit ALL nodes (expressions in conditional + // expressions too) that contains conditional operators, + // thus always return true to traverse ALL nodes. + return true; +} + +/// Determines whether two statement trees are identical regarding +/// operators and symbols. +/// +/// Exceptions: expressions containing macros or functions with possible side +/// effects are never considered identical. +/// Limitations: (t + u) and (u + t) are not considered identical. +/// t*(u + t) and t*u + t*t are not considered identical. +/// +static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1, + const Stmt *Stmt2, bool IgnoreSideEffects) { + + if (!Stmt1 || !Stmt2) { + return !Stmt1 && !Stmt2; + } + + // If Stmt1 & Stmt2 are of different class then they are not + // identical statements. + if (Stmt1->getStmtClass() != Stmt2->getStmtClass()) + return false; + + const Expr *Expr1 = dyn_cast<Expr>(Stmt1); + const Expr *Expr2 = dyn_cast<Expr>(Stmt2); + + if (Expr1 && Expr2) { + // If Stmt1 has side effects then don't warn even if expressions + // are identical. + if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx)) + return false; + // If either expression comes from a macro then don't warn even if + // the expressions are identical. + if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID())) + return false; + + // If all children of two expressions are identical, return true. + Expr::const_child_iterator I1 = Expr1->child_begin(); + Expr::const_child_iterator I2 = Expr2->child_begin(); + while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) { + if (!*I1 || !*I2 || !isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects)) + return false; + ++I1; + ++I2; + } + // If there are different number of children in the statements, return + // false. + if (I1 != Expr1->child_end()) + return false; + if (I2 != Expr2->child_end()) + return false; + } + + switch (Stmt1->getStmtClass()) { + default: + return false; + case Stmt::CallExprClass: + case Stmt::ArraySubscriptExprClass: + case Stmt::OMPArraySectionExprClass: + case Stmt::ImplicitCastExprClass: + case Stmt::ParenExprClass: + case Stmt::BreakStmtClass: + case Stmt::ContinueStmtClass: + case Stmt::NullStmtClass: + return true; + case Stmt::CStyleCastExprClass: { + const CStyleCastExpr* CastExpr1 = cast<CStyleCastExpr>(Stmt1); + const CStyleCastExpr* CastExpr2 = cast<CStyleCastExpr>(Stmt2); + + return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten(); + } + case Stmt::ReturnStmtClass: { + const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1); + const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2); + + return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(), + ReturnStmt2->getRetValue(), IgnoreSideEffects); + } + case Stmt::ForStmtClass: { + const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1); + const ForStmt *ForStmt2 = cast<ForStmt>(Stmt2); + + if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(), + IgnoreSideEffects)) + return false; + return true; + } + case Stmt::DoStmtClass: { + const DoStmt *DStmt1 = cast<DoStmt>(Stmt1); + const DoStmt *DStmt2 = cast<DoStmt>(Stmt2); + + if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(), + IgnoreSideEffects)) + return false; + return true; + } + case Stmt::WhileStmtClass: { + const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1); + const WhileStmt *WStmt2 = cast<WhileStmt>(Stmt2); + + if (!isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, WStmt1->getBody(), WStmt2->getBody(), + IgnoreSideEffects)) + return false; + return true; + } + case Stmt::IfStmtClass: { + const IfStmt *IStmt1 = cast<IfStmt>(Stmt1); + const IfStmt *IStmt2 = cast<IfStmt>(Stmt2); + + if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(), + IgnoreSideEffects)) + return false; + if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(), + IgnoreSideEffects)) + return false; + return true; + } + case Stmt::CompoundStmtClass: { + const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1); + const CompoundStmt *CompStmt2 = cast<CompoundStmt>(Stmt2); + + if (CompStmt1->size() != CompStmt2->size()) + return false; + + CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin(); + CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin(); + while (I1 != CompStmt1->body_end() && I2 != CompStmt2->body_end()) { + if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects)) + return false; + ++I1; + ++I2; + } + + return true; + } + case Stmt::CompoundAssignOperatorClass: + case Stmt::BinaryOperatorClass: { + const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1); + const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2); + return BinOp1->getOpcode() == BinOp2->getOpcode(); + } + case Stmt::CharacterLiteralClass: { + const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1); + const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2); + return CharLit1->getValue() == CharLit2->getValue(); + } + case Stmt::DeclRefExprClass: { + const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1); + const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2); + return DeclRef1->getDecl() == DeclRef2->getDecl(); + } + case Stmt::IntegerLiteralClass: { + const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1); + const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2); + + llvm::APInt I1 = IntLit1->getValue(); + llvm::APInt I2 = IntLit2->getValue(); + if (I1.getBitWidth() != I2.getBitWidth()) + return false; + return I1 == I2; + } + case Stmt::FloatingLiteralClass: { + const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1); + const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2); + return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue()); + } + case Stmt::StringLiteralClass: { + const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1); + const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2); + return StringLit1->getBytes() == StringLit2->getBytes(); + } + case Stmt::MemberExprClass: { + const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1); + const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2); + return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl(); + } + case Stmt::UnaryOperatorClass: { + const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1); + const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2); + return UnaryOp1->getOpcode() == UnaryOp2->getOpcode(); + } + } +} + +//===----------------------------------------------------------------------===// +// FindIdenticalExprChecker +//===----------------------------------------------------------------------===// + +namespace { +class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const { + FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); + Visitor.TraverseDecl(const_cast<Decl *>(D)); + } +}; +} // end anonymous namespace + +void ento::registerIdenticalExprChecker(CheckerManager &Mgr) { + Mgr.registerChecker<FindIdenticalExprChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp new file mode 100644 index 000000000000..a4f47d727a8f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp @@ -0,0 +1,312 @@ +//=== InnerPointerChecker.cpp -------------------------------------*- 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 a check that marks a raw pointer to a C++ container's +// inner buffer released when the object is destroyed. This information can +// be used by MallocChecker to detect use-after-free problems. +// +//===----------------------------------------------------------------------===// + +#include "AllocationState.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "InterCheckerAPI.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +// Associate container objects with a set of raw pointer symbols. +REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(PtrSet, SymbolRef) +REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet) + + +namespace { + +class InnerPointerChecker + : public Checker<check::DeadSymbols, check::PostCall> { + + CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn, + InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn, + ShrinkToFitFn, SwapFn; + +public: + class InnerPointerBRVisitor : public BugReporterVisitor { + SymbolRef PtrToBuf; + + public: + InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {} + + static void *getTag() { + static int Tag = 0; + return &Tag; + } + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.AddPointer(getTag()); + } + + virtual std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + // FIXME: Scan the map once in the visitor's constructor and do a direct + // lookup by region. + bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) { + RawPtrMapTy Map = State->get<RawPtrMap>(); + for (const auto Entry : Map) { + if (Entry.second.contains(Sym)) + return true; + } + return false; + } + }; + + InnerPointerChecker() + : AppendFn({"std", "basic_string", "append"}), + AssignFn({"std", "basic_string", "assign"}), + ClearFn({"std", "basic_string", "clear"}), + CStrFn({"std", "basic_string", "c_str"}), + DataFn({"std", "basic_string", "data"}), + EraseFn({"std", "basic_string", "erase"}), + InsertFn({"std", "basic_string", "insert"}), + PopBackFn({"std", "basic_string", "pop_back"}), + PushBackFn({"std", "basic_string", "push_back"}), + ReplaceFn({"std", "basic_string", "replace"}), + ReserveFn({"std", "basic_string", "reserve"}), + ResizeFn({"std", "basic_string", "resize"}), + ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}), + SwapFn({"std", "basic_string", "swap"}) {} + + /// Check whether the called member function potentially invalidates + /// pointers referring to the container object's inner buffer. + bool isInvalidatingMemberFunction(const CallEvent &Call) const; + + /// Mark pointer symbols associated with the given memory region released + /// in the program state. + void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State, + const MemRegion *ObjRegion, + CheckerContext &C) const; + + /// Standard library functions that take a non-const `basic_string` argument by + /// reference may invalidate its inner pointers. Check for these cases and + /// mark the pointers released. + void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State, + CheckerContext &C) const; + + /// Record the connection between raw pointers referring to a container + /// object's inner buffer and the object's memory region in the program state. + /// Mark potentially invalidated pointers released. + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + + /// Clean up the program state map. + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; +}; + +} // end anonymous namespace + +bool InnerPointerChecker::isInvalidatingMemberFunction( + const CallEvent &Call) const { + if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) { + OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator(); + if (Opc == OO_Equal || Opc == OO_PlusEqual) + return true; + return false; + } + return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) || + Call.isCalled(AssignFn) || Call.isCalled(ClearFn) || + Call.isCalled(EraseFn) || Call.isCalled(InsertFn) || + Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) || + Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) || + Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) || + Call.isCalled(SwapFn)); +} + +void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call, + ProgramStateRef State, + const MemRegion *MR, + CheckerContext &C) const { + if (const PtrSet *PS = State->get<RawPtrMap>(MR)) { + const Expr *Origin = Call.getOriginExpr(); + for (const auto Symbol : *PS) { + // NOTE: `Origin` may be null, and will be stored so in the symbol's + // `RefState` in MallocChecker's `RegionState` program state map. + State = allocation_state::markReleased(State, Symbol, Origin); + } + State = State->remove<RawPtrMap>(MR); + C.addTransition(State); + return; + } +} + +void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call, + ProgramStateRef State, + CheckerContext &C) const { + if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) { + const FunctionDecl *FD = FC->getDecl(); + if (!FD || !FD->isInStdNamespace()) + return; + + for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) { + QualType ParamTy = FD->getParamDecl(I)->getType(); + if (!ParamTy->isReferenceType() || + ParamTy->getPointeeType().isConstQualified()) + continue; + + // In case of member operator calls, `this` is counted as an + // argument but not as a parameter. + bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC); + unsigned ArgI = isaMemberOpCall ? I+1 : I; + + SVal Arg = FC->getArgSVal(ArgI); + const auto *ArgRegion = + dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion()); + if (!ArgRegion) + continue; + + markPtrSymbolsReleased(Call, State, ArgRegion, C); + } + } +} + +// [string.require] +// +// "References, pointers, and iterators referring to the elements of a +// basic_string sequence may be invalidated by the following uses of that +// basic_string object: +// +// -- As an argument to any standard library function taking a reference +// to non-const basic_string as an argument. For example, as an argument to +// non-member functions swap(), operator>>(), and getline(), or as an argument +// to basic_string::swap(). +// +// -- Calling non-const member functions, except operator[], at, front, back, +// begin, rbegin, end, and rend." + +void InnerPointerChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) { + // TODO: Do we need these to be typed? + const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>( + ICall->getCXXThisVal().getAsRegion()); + if (!ObjRegion) + return; + + if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) { + SVal RawPtr = Call.getReturnValue(); + if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) { + // Start tracking this raw pointer by adding it to the set of symbols + // associated with this container object in the program state map. + + PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); + const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion); + PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet(); + assert(C.wasInlined || !Set.contains(Sym)); + Set = F.add(Set, Sym); + + State = State->set<RawPtrMap>(ObjRegion, Set); + C.addTransition(State); + } + return; + } + + // Check [string.require] / second point. + if (isInvalidatingMemberFunction(Call)) { + markPtrSymbolsReleased(Call, State, ObjRegion, C); + return; + } + } + + // Check [string.require] / first point. + checkFunctionArguments(Call, State, C); +} + +void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>(); + RawPtrMapTy RPM = State->get<RawPtrMap>(); + for (const auto Entry : RPM) { + if (!SymReaper.isLiveRegion(Entry.first)) { + // Due to incomplete destructor support, some dead regions might + // remain in the program state map. Clean them up. + State = State->remove<RawPtrMap>(Entry.first); + } + if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) { + PtrSet CleanedUpSet = *OldSet; + for (const auto Symbol : Entry.second) { + if (!SymReaper.isLive(Symbol)) + CleanedUpSet = F.remove(CleanedUpSet, Symbol); + } + State = CleanedUpSet.isEmpty() + ? State->remove<RawPtrMap>(Entry.first) + : State->set<RawPtrMap>(Entry.first, CleanedUpSet); + } + } + C.addTransition(State); +} + +namespace clang { +namespace ento { +namespace allocation_state { + +std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) { + return llvm::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym); +} + +const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) { + RawPtrMapTy Map = State->get<RawPtrMap>(); + for (const auto Entry : Map) { + if (Entry.second.contains(Sym)) { + return Entry.first; + } + } + return nullptr; +} + +} // end namespace allocation_state +} // end namespace ento +} // end namespace clang + +std::shared_ptr<PathDiagnosticPiece> +InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &) { + if (!isSymbolTracked(N->getState(), PtrToBuf) || + isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf)) + return nullptr; + + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + + const MemRegion *ObjRegion = + allocation_state::getContainerObjRegion(N->getState(), PtrToBuf); + const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion); + QualType ObjTy = TypedRegion->getValueType(); + + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << "Pointer to inner buffer of '" << ObjTy.getAsString() + << "' obtained here"; + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true, + nullptr); +} + +void ento::registerInnerPointerChecker(CheckerManager &Mgr) { + registerInnerPointerCheckerAux(Mgr); + Mgr.registerChecker<InnerPointerChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h new file mode 100644 index 000000000000..81c95a4813a6 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h @@ -0,0 +1,27 @@ +//==--- InterCheckerAPI.h ---------------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This file allows introduction of checker dependencies. It contains APIs for +// inter-checker communications. +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H +namespace clang { +class CheckerManager; + +namespace ento { + +/// Register the checker which evaluates CString API calls. +void registerCStringCheckerBasic(CheckerManager &Mgr); + +/// Register the part of MallocChecker connected to InnerPointerChecker. +void registerInnerPointerCheckerAux(CheckerManager &Mgr); + +}} +#endif /* INTERCHECKERAPI_H_ */ diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp new file mode 100644 index 000000000000..e719e19d68e9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp @@ -0,0 +1,2408 @@ +//===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines a checker for using iterators outside their range (past end). Usage +// means here dereferencing, incrementing etc. +// +//===----------------------------------------------------------------------===// +// +// In the code, iterator can be represented as a: +// * type-I: typedef-ed pointer. Operations over such iterator, such as +// comparisons or increments, are modeled straightforwardly by the +// analyzer. +// * type-II: structure with its method bodies available. Operations over such +// iterator are inlined by the analyzer, and results of modeling +// these operations are exposing implementation details of the +// iterators, which is not necessarily helping. +// * type-III: completely opaque structure. Operations over such iterator are +// modeled conservatively, producing conjured symbols everywhere. +// +// To handle all these types in a common way we introduce a structure called +// IteratorPosition which is an abstraction of the position the iterator +// represents using symbolic expressions. The checker handles all the +// operations on this structure. +// +// Additionally, depending on the circumstances, operators of types II and III +// can be represented as: +// * type-IIa, type-IIIa: conjured structure symbols - when returned by value +// from conservatively evaluated methods such as +// `.begin()`. +// * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as +// variables or temporaries, when the iterator object is +// currently treated as an lvalue. +// * type-IIc, type-IIIc: compound values of iterator-typed objects, when the +// iterator object is treated as an rvalue taken of a +// particular lvalue, eg. a copy of "type-a" iterator +// object, or an iterator that existed before the +// analysis has started. +// +// To handle any of these three different representations stored in an SVal we +// use setter and getters functions which separate the three cases. To store +// them we use a pointer union of symbol and memory region. +// +// The checker works the following way: We record the begin and the +// past-end iterator for all containers whenever their `.begin()` and `.end()` +// are called. Since the Constraint Manager cannot handle such SVals we need +// to take over its role. We post-check equality and non-equality comparisons +// and record that the two sides are equal if we are in the 'equal' branch +// (true-branch for `==` and false-branch for `!=`). +// +// In case of type-I or type-II iterators we get a concrete integer as a result +// of the comparison (1 or 0) but in case of type-III we only get a Symbol. In +// this latter case we record the symbol and reload it in evalAssume() and do +// the propagation there. We also handle (maybe double) negated comparisons +// which are represented in the form of (x == 0 or x != 0) where x is the +// comparison itself. +// +// Since `SimpleConstraintManager` cannot handle complex symbolic expressions +// we only use expressions of the format S, S+n or S-n for iterator positions +// where S is a conjured symbol and n is an unsigned concrete integer. When +// making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as +// a constraint which we later retrieve when doing an actual comparison. + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" + +#include <utility> + +using namespace clang; +using namespace ento; + +namespace { + +// Abstract position of an iterator. This helps to handle all three kinds +// of operators in a common way by using a symbolic position. +struct IteratorPosition { +private: + + // Container the iterator belongs to + const MemRegion *Cont; + + // Whether iterator is valid + const bool Valid; + + // Abstract offset + const SymbolRef Offset; + + IteratorPosition(const MemRegion *C, bool V, SymbolRef Of) + : Cont(C), Valid(V), Offset(Of) {} + +public: + const MemRegion *getContainer() const { return Cont; } + bool isValid() const { return Valid; } + SymbolRef getOffset() const { return Offset; } + + IteratorPosition invalidate() const { + return IteratorPosition(Cont, false, Offset); + } + + static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) { + return IteratorPosition(C, true, Of); + } + + IteratorPosition setTo(SymbolRef NewOf) const { + return IteratorPosition(Cont, Valid, NewOf); + } + + IteratorPosition reAssign(const MemRegion *NewCont) const { + return IteratorPosition(NewCont, Valid, Offset); + } + + bool operator==(const IteratorPosition &X) const { + return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset; + } + + bool operator!=(const IteratorPosition &X) const { + return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(Cont); + ID.AddInteger(Valid); + ID.Add(Offset); + } +}; + +typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol; + +// Structure to record the symbolic begin and end position of a container +struct ContainerData { +private: + const SymbolRef Begin, End; + + ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {} + +public: + static ContainerData fromBegin(SymbolRef B) { + return ContainerData(B, nullptr); + } + + static ContainerData fromEnd(SymbolRef E) { + return ContainerData(nullptr, E); + } + + SymbolRef getBegin() const { return Begin; } + SymbolRef getEnd() const { return End; } + + ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); } + + ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); } + + bool operator==(const ContainerData &X) const { + return Begin == X.Begin && End == X.End; + } + + bool operator!=(const ContainerData &X) const { + return Begin != X.Begin || End != X.End; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.Add(Begin); + ID.Add(End); + } +}; + +// Structure fo recording iterator comparisons. We needed to retrieve the +// original comparison expression in assumptions. +struct IteratorComparison { +private: + RegionOrSymbol Left, Right; + bool Equality; + +public: + IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq) + : Left(L), Right(R), Equality(Eq) {} + + RegionOrSymbol getLeft() const { return Left; } + RegionOrSymbol getRight() const { return Right; } + bool isEquality() const { return Equality; } + bool operator==(const IteratorComparison &X) const { + return Left == X.Left && Right == X.Right && Equality == X.Equality; + } + bool operator!=(const IteratorComparison &X) const { + return Left != X.Left || Right != X.Right || Equality != X.Equality; + } + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); } +}; + +class IteratorChecker + : public Checker<check::PreCall, check::PostCall, + check::PostStmt<MaterializeTemporaryExpr>, check::Bind, + check::LiveSymbols, check::DeadSymbols, + eval::Assume> { + + std::unique_ptr<BugType> OutOfRangeBugType; + std::unique_ptr<BugType> MismatchedBugType; + std::unique_ptr<BugType> InvalidatedBugType; + + void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal, + const SVal &RVal, OverloadedOperatorKind Op) const; + void verifyAccess(CheckerContext &C, const SVal &Val) const; + void verifyDereference(CheckerContext &C, const SVal &Val) const; + void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, + bool Postfix) const; + void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter, + bool Postfix) const; + void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op, + const SVal &RetVal, const SVal &LHS, + const SVal &RHS) const; + void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal, + const SVal &Cont) const; + void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal, + const SVal &Cont) const; + void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal, + const MemRegion *Cont) const; + void handleAssign(CheckerContext &C, const SVal &Cont, + const Expr *CE = nullptr, + const SVal &OldCont = UndefinedVal()) const; + void handleClear(CheckerContext &C, const SVal &Cont) const; + void handlePushBack(CheckerContext &C, const SVal &Cont) const; + void handlePopBack(CheckerContext &C, const SVal &Cont) const; + void handlePushFront(CheckerContext &C, const SVal &Cont) const; + void handlePopFront(CheckerContext &C, const SVal &Cont) const; + void handleInsert(CheckerContext &C, const SVal &Iter) const; + void handleErase(CheckerContext &C, const SVal &Iter) const; + void handleErase(CheckerContext &C, const SVal &Iter1, + const SVal &Iter2) const; + void handleEraseAfter(CheckerContext &C, const SVal &Iter) const; + void handleEraseAfter(CheckerContext &C, const SVal &Iter1, + const SVal &Iter2) const; + void verifyIncrement(CheckerContext &C, const SVal &Iter) const; + void verifyDecrement(CheckerContext &C, const SVal &Iter) const; + void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op, + const SVal &LHS, const SVal &RHS) const; + void verifyMatch(CheckerContext &C, const SVal &Iter, + const MemRegion *Cont) const; + void verifyMatch(CheckerContext &C, const SVal &Iter1, + const SVal &Iter2) const; + IteratorPosition advancePosition(CheckerContext &C, OverloadedOperatorKind Op, + const IteratorPosition &Pos, + const SVal &Distance) const; + void reportOutOfRangeBug(const StringRef &Message, const SVal &Val, + CheckerContext &C, ExplodedNode *ErrNode) const; + void reportMismatchedBug(const StringRef &Message, const SVal &Val1, + const SVal &Val2, CheckerContext &C, + ExplodedNode *ErrNode) const; + void reportMismatchedBug(const StringRef &Message, const SVal &Val, + const MemRegion *Reg, CheckerContext &C, + ExplodedNode *ErrNode) const; + void reportInvalidatedBug(const StringRef &Message, const SVal &Val, + CheckerContext &C, ExplodedNode *ErrNode) const; + +public: + IteratorChecker(); + + enum CheckKind { + CK_IteratorRangeChecker, + CK_MismatchedIteratorChecker, + CK_InvalidatedIteratorChecker, + CK_NumCheckKinds + }; + + DefaultBool ChecksEnabled[CK_NumCheckKinds]; + CheckName CheckNames[CK_NumCheckKinds]; + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const; + void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const; + void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const; + void checkPostStmt(const MaterializeTemporaryExpr *MTE, + CheckerContext &C) const; + void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond, + bool Assumption) const; +}; +} // namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition) +REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *, + IteratorPosition) + +REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData) + +REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *, + IteratorComparison) + +namespace { + +bool isIteratorType(const QualType &Type); +bool isIterator(const CXXRecordDecl *CRD); +bool isComparisonOperator(OverloadedOperatorKind OK); +bool isBeginCall(const FunctionDecl *Func); +bool isEndCall(const FunctionDecl *Func); +bool isAssignCall(const FunctionDecl *Func); +bool isClearCall(const FunctionDecl *Func); +bool isPushBackCall(const FunctionDecl *Func); +bool isEmplaceBackCall(const FunctionDecl *Func); +bool isPopBackCall(const FunctionDecl *Func); +bool isPushFrontCall(const FunctionDecl *Func); +bool isEmplaceFrontCall(const FunctionDecl *Func); +bool isPopFrontCall(const FunctionDecl *Func); +bool isInsertCall(const FunctionDecl *Func); +bool isEraseCall(const FunctionDecl *Func); +bool isEraseAfterCall(const FunctionDecl *Func); +bool isEmplaceCall(const FunctionDecl *Func); +bool isAssignmentOperator(OverloadedOperatorKind OK); +bool isSimpleComparisonOperator(OverloadedOperatorKind OK); +bool isAccessOperator(OverloadedOperatorKind OK); +bool isDereferenceOperator(OverloadedOperatorKind OK); +bool isIncrementOperator(OverloadedOperatorKind OK); +bool isDecrementOperator(OverloadedOperatorKind OK); +bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK); +bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg); +bool frontModifiable(ProgramStateRef State, const MemRegion *Reg); +bool backModifiable(ProgramStateRef State, const MemRegion *Reg); +BinaryOperator::Opcode getOpcode(const SymExpr *SE); +const RegionOrSymbol getRegionOrSymbol(const SVal &Val); +const ProgramStateRef processComparison(ProgramStateRef State, + RegionOrSymbol LVal, + RegionOrSymbol RVal, bool Equal); +const ProgramStateRef saveComparison(ProgramStateRef State, + const SymExpr *Condition, const SVal &LVal, + const SVal &RVal, bool Eq); +const IteratorComparison *loadComparison(ProgramStateRef State, + const SymExpr *Condition); +SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont); +SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont); +ProgramStateRef createContainerBegin(ProgramStateRef State, + const MemRegion *Cont, + const SymbolRef Sym); +ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, + const SymbolRef Sym); +const IteratorPosition *getIteratorPosition(ProgramStateRef State, + const SVal &Val); +const IteratorPosition *getIteratorPosition(ProgramStateRef State, + RegionOrSymbol RegOrSym); +ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val, + const IteratorPosition &Pos); +ProgramStateRef setIteratorPosition(ProgramStateRef State, + RegionOrSymbol RegOrSym, + const IteratorPosition &Pos); +ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val); +ProgramStateRef adjustIteratorPosition(ProgramStateRef State, + RegionOrSymbol RegOrSym, + const IteratorPosition &Pos, bool Equal); +ProgramStateRef relateIteratorPositions(ProgramStateRef State, + const IteratorPosition &Pos1, + const IteratorPosition &Pos2, + bool Equal); +ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, + const MemRegion *Cont); +ProgramStateRef +invalidateAllIteratorPositionsExcept(ProgramStateRef State, + const MemRegion *Cont, SymbolRef Offset, + BinaryOperator::Opcode Opc); +ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, + SymbolRef Offset, + BinaryOperator::Opcode Opc); +ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, + SymbolRef Offset1, + BinaryOperator::Opcode Opc1, + SymbolRef Offset2, + BinaryOperator::Opcode Opc2); +ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, + const MemRegion *Cont, + const MemRegion *NewCont); +ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, + const MemRegion *Cont, + const MemRegion *NewCont, + SymbolRef Offset, + BinaryOperator::Opcode Opc); +ProgramStateRef rebaseSymbolInIteratorPositionsIf( + ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, + SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc); +const ContainerData *getContainerData(ProgramStateRef State, + const MemRegion *Cont); +ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, + const ContainerData &CData); +bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont); +bool isBoundThroughLazyCompoundVal(const Environment &Env, + const MemRegion *Reg); +bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos); +bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos); +bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos); +bool isZero(ProgramStateRef State, const NonLoc &Val); +} // namespace + +IteratorChecker::IteratorChecker() { + OutOfRangeBugType.reset( + new BugType(this, "Iterator out of range", "Misuse of STL APIs")); + OutOfRangeBugType->setSuppressOnSink(true); + MismatchedBugType.reset( + new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs")); + MismatchedBugType->setSuppressOnSink(true); + InvalidatedBugType.reset( + new BugType(this, "Iterator invalidated", "Misuse of STL APIs")); + InvalidatedBugType->setSuppressOnSink(true); +} + +void IteratorChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + // Check for out of range access or access of invalidated position and + // iterator mismatches + const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); + if (!Func) + return; + + if (Func->isOverloadedOperator()) { + if (ChecksEnabled[CK_InvalidatedIteratorChecker] && + isAccessOperator(Func->getOverloadedOperator())) { + // Check for any kind of access of invalidated iterator positions + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + verifyAccess(C, InstCall->getCXXThisVal()); + } else { + verifyAccess(C, Call.getArgSVal(0)); + } + } + if (ChecksEnabled[CK_IteratorRangeChecker]) { + if (isIncrementOperator(Func->getOverloadedOperator())) { + // Check for out-of-range incrementions + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + verifyIncrement(C, InstCall->getCXXThisVal()); + } else { + if (Call.getNumArgs() >= 1) { + verifyIncrement(C, Call.getArgSVal(0)); + } + } + } else if (isDecrementOperator(Func->getOverloadedOperator())) { + // Check for out-of-range decrementions + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + verifyDecrement(C, InstCall->getCXXThisVal()); + } else { + if (Call.getNumArgs() >= 1) { + verifyDecrement(C, Call.getArgSVal(0)); + } + } + } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + // Check for out-of-range incrementions and decrementions + if (Call.getNumArgs() >= 1) { + verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), + InstCall->getCXXThisVal(), + Call.getArgSVal(0)); + } + } else { + if (Call.getNumArgs() >= 2) { + verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(), + Call.getArgSVal(0), Call.getArgSVal(1)); + } + } + } else if (isDereferenceOperator(Func->getOverloadedOperator())) { + // Check for dereference of out-of-range iterators + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + verifyDereference(C, InstCall->getCXXThisVal()); + } else { + verifyDereference(C, Call.getArgSVal(0)); + } + } + } else if (ChecksEnabled[CK_MismatchedIteratorChecker] && + isComparisonOperator(Func->getOverloadedOperator())) { + // Check for comparisons of iterators of different containers + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + if (Call.getNumArgs() < 1) + return; + + if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) || + !isIteratorType(Call.getArgExpr(0)->getType())) + return; + + verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0)); + } else { + if (Call.getNumArgs() < 2) + return; + + if (!isIteratorType(Call.getArgExpr(0)->getType()) || + !isIteratorType(Call.getArgExpr(1)->getType())) + return; + + verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1)); + } + } + } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + if (!ChecksEnabled[CK_MismatchedIteratorChecker]) + return; + + const auto *ContReg = InstCall->getCXXThisVal().getAsRegion(); + if (!ContReg) + return; + // Check for erase, insert and emplace using iterator of another container + if (isEraseCall(Func) || isEraseAfterCall(Func)) { + verifyMatch(C, Call.getArgSVal(0), + InstCall->getCXXThisVal().getAsRegion()); + if (Call.getNumArgs() == 2) { + verifyMatch(C, Call.getArgSVal(1), + InstCall->getCXXThisVal().getAsRegion()); + } + } else if (isInsertCall(Func)) { + verifyMatch(C, Call.getArgSVal(0), + InstCall->getCXXThisVal().getAsRegion()); + if (Call.getNumArgs() == 3 && + isIteratorType(Call.getArgExpr(1)->getType()) && + isIteratorType(Call.getArgExpr(2)->getType())) { + verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2)); + } + } else if (isEmplaceCall(Func)) { + verifyMatch(C, Call.getArgSVal(0), + InstCall->getCXXThisVal().getAsRegion()); + } + } else if (isa<CXXConstructorCall>(&Call)) { + // Check match of first-last iterator pair in a constructor of a container + if (Call.getNumArgs() < 2) + return; + + const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl()); + if (Ctr->getNumParams() < 2) + return; + + if (Ctr->getParamDecl(0)->getName() != "first" || + Ctr->getParamDecl(1)->getName() != "last") + return; + + if (!isIteratorType(Call.getArgExpr(0)->getType()) || + !isIteratorType(Call.getArgExpr(1)->getType())) + return; + + verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1)); + } else { + // The main purpose of iterators is to abstract away from different + // containers and provide a (maybe limited) uniform access to them. + // This implies that any correctly written template function that + // works on multiple containers using iterators takes different + // template parameters for different containers. So we can safely + // assume that passing iterators of different containers as arguments + // whose type replaces the same template parameter is a bug. + // + // Example: + // template<typename I1, typename I2> + // void f(I1 first1, I1 last1, I2 first2, I2 last2); + // + // In this case the first two arguments to f() must be iterators must belong + // to the same container and the last to also to the same container but + // not necessarily to the same as the first two. + + if (!ChecksEnabled[CK_MismatchedIteratorChecker]) + return; + + const auto *Templ = Func->getPrimaryTemplate(); + if (!Templ) + return; + + const auto *TParams = Templ->getTemplateParameters(); + const auto *TArgs = Func->getTemplateSpecializationArgs(); + + // Iterate over all the template parameters + for (size_t I = 0; I < TParams->size(); ++I) { + const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I)); + if (!TPDecl) + continue; + + if (TPDecl->isParameterPack()) + continue; + + const auto TAType = TArgs->get(I).getAsType(); + if (!isIteratorType(TAType)) + continue; + + SVal LHS = UndefinedVal(); + + // For every template parameter which is an iterator type in the + // instantiation look for all functions' parameters' type by it and + // check whether they belong to the same container + for (auto J = 0U; J < Func->getNumParams(); ++J) { + const auto *Param = Func->getParamDecl(J); + const auto *ParamType = + Param->getType()->getAs<SubstTemplateTypeParmType>(); + if (!ParamType || + ParamType->getReplacedParameter()->getDecl() != TPDecl) + continue; + if (LHS.isUndef()) { + LHS = Call.getArgSVal(J); + } else { + verifyMatch(C, LHS, Call.getArgSVal(J)); + } + } + } + } +} + +void IteratorChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + // Record new iterator positions and iterator position changes + const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); + if (!Func) + return; + + if (Func->isOverloadedOperator()) { + const auto Op = Func->getOverloadedOperator(); + if (isAssignmentOperator(Op)) { + const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call); + if (Func->getParamDecl(0)->getType()->isRValueReferenceType()) { + handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(), + Call.getArgSVal(0)); + } else { + handleAssign(C, InstCall->getCXXThisVal()); + } + } else if (isSimpleComparisonOperator(Op)) { + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(), + Call.getArgSVal(0), Op); + } else { + handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0), + Call.getArgSVal(1), Op); + } + } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) { + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + if (Call.getNumArgs() >= 1) { + handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), + Call.getReturnValue(), + InstCall->getCXXThisVal(), Call.getArgSVal(0)); + } + } else { + if (Call.getNumArgs() >= 2) { + handleRandomIncrOrDecr(C, Func->getOverloadedOperator(), + Call.getReturnValue(), Call.getArgSVal(0), + Call.getArgSVal(1)); + } + } + } else if (isIncrementOperator(Func->getOverloadedOperator())) { + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), + Call.getNumArgs()); + } else { + handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0), + Call.getNumArgs()); + } + } else if (isDecrementOperator(Func->getOverloadedOperator())) { + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(), + Call.getNumArgs()); + } else { + handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0), + Call.getNumArgs()); + } + } + } else { + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + if (isAssignCall(Func)) { + handleAssign(C, InstCall->getCXXThisVal()); + } else if (isClearCall(Func)) { + handleClear(C, InstCall->getCXXThisVal()); + } else if (isPushBackCall(Func) || isEmplaceBackCall(Func)) { + handlePushBack(C, InstCall->getCXXThisVal()); + } else if (isPopBackCall(Func)) { + handlePopBack(C, InstCall->getCXXThisVal()); + } else if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) { + handlePushFront(C, InstCall->getCXXThisVal()); + } else if (isPopFrontCall(Func)) { + handlePopFront(C, InstCall->getCXXThisVal()); + } else if (isInsertCall(Func) || isEmplaceCall(Func)) { + handleInsert(C, Call.getArgSVal(0)); + } else if (isEraseCall(Func)) { + if (Call.getNumArgs() == 1) { + handleErase(C, Call.getArgSVal(0)); + } else if (Call.getNumArgs() == 2) { + handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1)); + } + } else if (isEraseAfterCall(Func)) { + if (Call.getNumArgs() == 1) { + handleEraseAfter(C, Call.getArgSVal(0)); + } else if (Call.getNumArgs() == 2) { + handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1)); + } + } + } + + const auto *OrigExpr = Call.getOriginExpr(); + if (!OrigExpr) + return; + + if (!isIteratorType(Call.getResultType())) + return; + + auto State = C.getState(); + + if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) { + if (isBeginCall(Func)) { + handleBegin(C, OrigExpr, Call.getReturnValue(), + InstCall->getCXXThisVal()); + return; + } + if (isEndCall(Func)) { + handleEnd(C, OrigExpr, Call.getReturnValue(), + InstCall->getCXXThisVal()); + return; + } + } + + // Already bound to container? + if (getIteratorPosition(State, Call.getReturnValue())) + return; + + // Copy-like and move constructors + if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) { + if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) { + State = setIteratorPosition(State, Call.getReturnValue(), *Pos); + if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) { + State = removeIteratorPosition(State, Call.getArgSVal(0)); + } + C.addTransition(State); + return; + } + } + + // Assumption: if return value is an iterator which is not yet bound to a + // container, then look for the first iterator argument, and + // bind the return value to the same container. This approach + // works for STL algorithms. + // FIXME: Add a more conservative mode + for (unsigned i = 0; i < Call.getNumArgs(); ++i) { + if (isIteratorType(Call.getArgExpr(i)->getType())) { + if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) { + assignToContainer(C, OrigExpr, Call.getReturnValue(), + Pos->getContainer()); + return; + } + } + } + } +} + +void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S, + CheckerContext &C) const { + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Val); + if (Pos) { + State = setIteratorPosition(State, Loc, *Pos); + C.addTransition(State); + } else { + const auto *OldPos = getIteratorPosition(State, Loc); + if (OldPos) { + State = removeIteratorPosition(State, Loc); + C.addTransition(State); + } + } +} + +void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE, + CheckerContext &C) const { + /* Transfer iterator state to temporary objects */ + auto State = C.getState(); + const auto *Pos = + getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr())); + if (!Pos) + return; + State = setIteratorPosition(State, C.getSVal(MTE), *Pos); + C.addTransition(State); +} + +void IteratorChecker::checkLiveSymbols(ProgramStateRef State, + SymbolReaper &SR) const { + // Keep symbolic expressions of iterator positions, container begins and ends + // alive + auto RegionMap = State->get<IteratorRegionMap>(); + for (const auto Reg : RegionMap) { + const auto Offset = Reg.second.getOffset(); + for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) + if (isa<SymbolData>(*i)) + SR.markLive(*i); + } + + auto SymbolMap = State->get<IteratorSymbolMap>(); + for (const auto Sym : SymbolMap) { + const auto Offset = Sym.second.getOffset(); + for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i) + if (isa<SymbolData>(*i)) + SR.markLive(*i); + } + + auto ContMap = State->get<ContainerMap>(); + for (const auto Cont : ContMap) { + const auto CData = Cont.second; + if (CData.getBegin()) { + SR.markLive(CData.getBegin()); + if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin())) + SR.markLive(SIE->getLHS()); + } + if (CData.getEnd()) { + SR.markLive(CData.getEnd()); + if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd())) + SR.markLive(SIE->getLHS()); + } + } +} + +void IteratorChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + // Cleanup + auto State = C.getState(); + + auto RegionMap = State->get<IteratorRegionMap>(); + for (const auto Reg : RegionMap) { + if (!SR.isLiveRegion(Reg.first)) { + // The region behind the `LazyCompoundVal` is often cleaned up before + // the `LazyCompoundVal` itself. If there are iterator positions keyed + // by these regions their cleanup must be deferred. + if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) { + State = State->remove<IteratorRegionMap>(Reg.first); + } + } + } + + auto SymbolMap = State->get<IteratorSymbolMap>(); + for (const auto Sym : SymbolMap) { + if (!SR.isLive(Sym.first)) { + State = State->remove<IteratorSymbolMap>(Sym.first); + } + } + + auto ContMap = State->get<ContainerMap>(); + for (const auto Cont : ContMap) { + if (!SR.isLiveRegion(Cont.first)) { + // We must keep the container data while it has live iterators to be able + // to compare them to the begin and the end of the container. + if (!hasLiveIterators(State, Cont.first)) { + State = State->remove<ContainerMap>(Cont.first); + } + } + } + + auto ComparisonMap = State->get<IteratorComparisonMap>(); + for (const auto Comp : ComparisonMap) { + if (!SR.isLive(Comp.first)) { + State = State->remove<IteratorComparisonMap>(Comp.first); + } + } + + C.addTransition(State); +} + +ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond, + bool Assumption) const { + // Load recorded comparison and transfer iterator state between sides + // according to comparison operator and assumption + const auto *SE = Cond.getAsSymExpr(); + if (!SE) + return State; + + auto Opc = getOpcode(SE); + if (Opc != BO_EQ && Opc != BO_NE) + return State; + + bool Negated = false; + const auto *Comp = loadComparison(State, SE); + if (!Comp) { + // Try negated comparison, which is a SymExpr to 0 integer comparison + const auto *SIE = dyn_cast<SymIntExpr>(SE); + if (!SIE) + return State; + + if (SIE->getRHS() != 0) + return State; + + SE = SIE->getLHS(); + Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation + Opc = getOpcode(SE); + if (Opc != BO_EQ && Opc != BO_NE) + return State; + + Comp = loadComparison(State, SE); + if (!Comp) + return State; + } + + return processComparison(State, Comp->getLeft(), Comp->getRight(), + (Comp->isEquality() == Assumption) != Negated); +} + +void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal, + const SVal &LVal, const SVal &RVal, + OverloadedOperatorKind Op) const { + // Record the operands and the operator of the comparison for the next + // evalAssume, if the result is a symbolic expression. If it is a concrete + // value (only one branch is possible), then transfer the state between + // the operands according to the operator and the result + auto State = C.getState(); + if (const auto *Condition = RetVal.getAsSymbolicExpression()) { + const auto *LPos = getIteratorPosition(State, LVal); + const auto *RPos = getIteratorPosition(State, RVal); + if (!LPos && !RPos) + return; + State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual); + C.addTransition(State); + } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) { + if ((State = processComparison( + State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal), + (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) { + C.addTransition(State); + } else { + C.generateSink(State, C.getPredecessor()); + } + } +} + +void IteratorChecker::verifyDereference(CheckerContext &C, + const SVal &Val) const { + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Val); + if (Pos && isPastTheEnd(State, *Pos)) { + auto *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N); + return; + } +} + +void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const { + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Val); + if (Pos && !Pos->isValid()) { + auto *N = C.generateNonFatalErrorNode(State); + if (!N) { + return; + } + reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N); + } +} + +void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal, + const SVal &Iter, bool Postfix) const { + // Increment the symbolic expressions which represents the position of the + // iterator + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Iter); + if (Pos) { + auto &SymMgr = C.getSymbolManager(); + auto &BVF = SymMgr.getBasicVals(); + const auto NewPos = + advancePosition(C, OO_Plus, *Pos, + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); + State = setIteratorPosition(State, Iter, NewPos); + State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos); + C.addTransition(State); + } +} + +void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal, + const SVal &Iter, bool Postfix) const { + // Decrement the symbolic expressions which represents the position of the + // iterator + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Iter); + if (Pos) { + auto &SymMgr = C.getSymbolManager(); + auto &BVF = SymMgr.getBasicVals(); + const auto NewPos = + advancePosition(C, OO_Minus, *Pos, + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); + State = setIteratorPosition(State, Iter, NewPos); + State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos); + C.addTransition(State); + } +} + +// This function tells the analyzer's engine that symbols produced by our +// checker, most notably iterator positions, are relatively small. +// A distance between items in the container should not be very large. +// By assuming that it is within around 1/8 of the address space, +// we can help the analyzer perform operations on these symbols +// without being afraid of integer overflows. +// FIXME: Should we provide it as an API, so that all checkers could use it? +static ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym, + long Scale) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + BasicValueFactory &BV = SVB.getBasicValueFactory(); + + QualType T = Sym->getType(); + assert(T->isSignedIntegerOrEnumerationType()); + APSIntType AT = BV.getAPSIntType(T); + + ProgramStateRef NewState = State; + + llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale); + SVal IsCappedFromAbove = + SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym), + nonloc::ConcreteInt(Max), SVB.getConditionType()); + if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) { + NewState = NewState->assume(*DV, true); + if (!NewState) + return State; + } + + llvm::APSInt Min = -Max; + SVal IsCappedFromBelow = + SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym), + nonloc::ConcreteInt(Min), SVB.getConditionType()); + if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) { + NewState = NewState->assume(*DV, true); + if (!NewState) + return State; + } + + return NewState; +} + +void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C, + OverloadedOperatorKind Op, + const SVal &RetVal, + const SVal &LHS, + const SVal &RHS) const { + // Increment or decrement the symbolic expressions which represents the + // position of the iterator + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, LHS); + if (!Pos) + return; + + const auto *value = &RHS; + if (auto loc = RHS.getAs<Loc>()) { + const auto val = State->getRawSVal(*loc); + value = &val; + } + + auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal; + State = + setIteratorPosition(State, TgtVal, advancePosition(C, Op, *Pos, *value)); + C.addTransition(State); +} + +void IteratorChecker::verifyIncrement(CheckerContext &C, + const SVal &Iter) const { + auto &BVF = C.getSValBuilder().getBasicValueFactory(); + verifyRandomIncrOrDecr(C, OO_Plus, Iter, + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); +} + +void IteratorChecker::verifyDecrement(CheckerContext &C, + const SVal &Iter) const { + auto &BVF = C.getSValBuilder().getBasicValueFactory(); + verifyRandomIncrOrDecr(C, OO_Minus, Iter, + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1)))); +} + +void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C, + OverloadedOperatorKind Op, + const SVal &LHS, + const SVal &RHS) const { + auto State = C.getState(); + + // If the iterator is initially inside its range, then the operation is valid + const auto *Pos = getIteratorPosition(State, LHS); + if (!Pos) + return; + + auto Value = RHS; + if (auto ValAsLoc = RHS.getAs<Loc>()) { + Value = State->getRawSVal(*ValAsLoc); + } + + if (Value.isUnknown()) + return; + + // Incremention or decremention by 0 is never a bug. + if (isZero(State, Value.castAs<NonLoc>())) + return; + + // The result may be the past-end iterator of the container, but any other + // out of range position is undefined behaviour + if (isAheadOfRange(State, advancePosition(C, Op, *Pos, Value))) { + auto *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS, + C, N); + } + if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) { + auto *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + reportOutOfRangeBug("Iterator incremented behind the past-the-end " + "iterator.", LHS, C, N); + } +} + +void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter, + const MemRegion *Cont) const { + // Verify match between a container and the container of an iterator + Cont = Cont->getMostDerivedObjectRegion(); + + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Iter); + if (Pos && Pos->getContainer() != Cont) { + auto *N = C.generateNonFatalErrorNode(State); + if (!N) { + return; + } + reportMismatchedBug("Container accessed using foreign iterator argument.", Iter, Cont, C, N); + } +} + +void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1, + const SVal &Iter2) const { + // Verify match between the containers of two iterators + auto State = C.getState(); + const auto *Pos1 = getIteratorPosition(State, Iter1); + const auto *Pos2 = getIteratorPosition(State, Iter2); + if (Pos1 && Pos2 && Pos1->getContainer() != Pos2->getContainer()) { + auto *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + reportMismatchedBug("Iterators of different containers used where the " + "same container is expected.", Iter1, Iter2, C, N); + } +} + +void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE, + const SVal &RetVal, const SVal &Cont) const { + const auto *ContReg = Cont.getAsRegion(); + if (!ContReg) + return; + + ContReg = ContReg->getMostDerivedObjectRegion(); + + // If the container already has a begin symbol then use it. Otherwise first + // create a new one. + auto State = C.getState(); + auto BeginSym = getContainerBegin(State, ContReg); + if (!BeginSym) { + auto &SymMgr = C.getSymbolManager(); + BeginSym = SymMgr.conjureSymbol(CE, C.getLocationContext(), + C.getASTContext().LongTy, C.blockCount()); + State = assumeNoOverflow(State, BeginSym, 4); + State = createContainerBegin(State, ContReg, BeginSym); + } + State = setIteratorPosition(State, RetVal, + IteratorPosition::getPosition(ContReg, BeginSym)); + C.addTransition(State); +} + +void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE, + const SVal &RetVal, const SVal &Cont) const { + const auto *ContReg = Cont.getAsRegion(); + if (!ContReg) + return; + + ContReg = ContReg->getMostDerivedObjectRegion(); + + // If the container already has an end symbol then use it. Otherwise first + // create a new one. + auto State = C.getState(); + auto EndSym = getContainerEnd(State, ContReg); + if (!EndSym) { + auto &SymMgr = C.getSymbolManager(); + EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(), + C.getASTContext().LongTy, C.blockCount()); + State = assumeNoOverflow(State, EndSym, 4); + State = createContainerEnd(State, ContReg, EndSym); + } + State = setIteratorPosition(State, RetVal, + IteratorPosition::getPosition(ContReg, EndSym)); + C.addTransition(State); +} + +void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE, + const SVal &RetVal, + const MemRegion *Cont) const { + Cont = Cont->getMostDerivedObjectRegion(); + + auto State = C.getState(); + auto &SymMgr = C.getSymbolManager(); + auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(), + C.getASTContext().LongTy, C.blockCount()); + State = assumeNoOverflow(State, Sym, 4); + State = setIteratorPosition(State, RetVal, + IteratorPosition::getPosition(Cont, Sym)); + C.addTransition(State); +} + +void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont, + const Expr *CE, const SVal &OldCont) const { + const auto *ContReg = Cont.getAsRegion(); + if (!ContReg) + return; + + ContReg = ContReg->getMostDerivedObjectRegion(); + + // Assignment of a new value to a container always invalidates all its + // iterators + auto State = C.getState(); + const auto CData = getContainerData(State, ContReg); + if (CData) { + State = invalidateAllIteratorPositions(State, ContReg); + } + + // In case of move, iterators of the old container (except the past-end + // iterators) remain valid but refer to the new container + if (!OldCont.isUndef()) { + const auto *OldContReg = OldCont.getAsRegion(); + if (OldContReg) { + OldContReg = OldContReg->getMostDerivedObjectRegion(); + const auto OldCData = getContainerData(State, OldContReg); + if (OldCData) { + if (const auto OldEndSym = OldCData->getEnd()) { + // If we already assigned an "end" symbol to the old container, then + // first reassign all iterator positions to the new container which + // are not past the container (thus not greater or equal to the + // current "end" symbol). + State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg, + OldEndSym, BO_GE); + auto &SymMgr = C.getSymbolManager(); + auto &SVB = C.getSValBuilder(); + // Then generate and assign a new "end" symbol for the new container. + auto NewEndSym = + SymMgr.conjureSymbol(CE, C.getLocationContext(), + C.getASTContext().LongTy, C.blockCount()); + State = assumeNoOverflow(State, NewEndSym, 4); + if (CData) { + State = setContainerData(State, ContReg, CData->newEnd(NewEndSym)); + } else { + State = setContainerData(State, ContReg, + ContainerData::fromEnd(NewEndSym)); + } + // Finally, replace the old "end" symbol in the already reassigned + // iterator positions with the new "end" symbol. + State = rebaseSymbolInIteratorPositionsIf( + State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT); + } else { + // There was no "end" symbol assigned yet to the old container, + // so reassign all iterator positions to the new container. + State = reassignAllIteratorPositions(State, OldContReg, ContReg); + } + if (const auto OldBeginSym = OldCData->getBegin()) { + // If we already assigned a "begin" symbol to the old container, then + // assign it to the new container and remove it from the old one. + if (CData) { + State = + setContainerData(State, ContReg, CData->newBegin(OldBeginSym)); + } else { + State = setContainerData(State, ContReg, + ContainerData::fromBegin(OldBeginSym)); + } + State = + setContainerData(State, OldContReg, OldCData->newEnd(nullptr)); + } + } else { + // There was neither "begin" nor "end" symbol assigned yet to the old + // container, so reassign all iterator positions to the new container. + State = reassignAllIteratorPositions(State, OldContReg, ContReg); + } + } + } + C.addTransition(State); +} + +void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const { + const auto *ContReg = Cont.getAsRegion(); + if (!ContReg) + return; + + ContReg = ContReg->getMostDerivedObjectRegion(); + + // The clear() operation invalidates all the iterators, except the past-end + // iterators of list-like containers + auto State = C.getState(); + if (!hasSubscriptOperator(State, ContReg) || + !backModifiable(State, ContReg)) { + const auto CData = getContainerData(State, ContReg); + if (CData) { + if (const auto EndSym = CData->getEnd()) { + State = + invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE); + C.addTransition(State); + return; + } + } + } + State = invalidateAllIteratorPositions(State, ContReg); + C.addTransition(State); +} + +void IteratorChecker::handlePushBack(CheckerContext &C, + const SVal &Cont) const { + const auto *ContReg = Cont.getAsRegion(); + if (!ContReg) + return; + + ContReg = ContReg->getMostDerivedObjectRegion(); + + // For deque-like containers invalidate all iterator positions + auto State = C.getState(); + if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) { + State = invalidateAllIteratorPositions(State, ContReg); + C.addTransition(State); + return; + } + + const auto CData = getContainerData(State, ContReg); + if (!CData) + return; + + // For vector-like containers invalidate the past-end iterator positions + if (const auto EndSym = CData->getEnd()) { + if (hasSubscriptOperator(State, ContReg)) { + State = invalidateIteratorPositions(State, EndSym, BO_GE); + } + auto &SymMgr = C.getSymbolManager(); + auto &BVF = SymMgr.getBasicVals(); + auto &SVB = C.getSValBuilder(); + const auto newEndSym = + SVB.evalBinOp(State, BO_Add, + nonloc::SymbolVal(EndSym), + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), + SymMgr.getType(EndSym)).getAsSymbol(); + State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); + } + C.addTransition(State); +} + +void IteratorChecker::handlePopBack(CheckerContext &C, const SVal &Cont) const { + const auto *ContReg = Cont.getAsRegion(); + if (!ContReg) + return; + + ContReg = ContReg->getMostDerivedObjectRegion(); + + auto State = C.getState(); + const auto CData = getContainerData(State, ContReg); + if (!CData) + return; + + if (const auto EndSym = CData->getEnd()) { + auto &SymMgr = C.getSymbolManager(); + auto &BVF = SymMgr.getBasicVals(); + auto &SVB = C.getSValBuilder(); + const auto BackSym = + SVB.evalBinOp(State, BO_Sub, + nonloc::SymbolVal(EndSym), + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), + SymMgr.getType(EndSym)).getAsSymbol(); + // For vector-like and deque-like containers invalidate the last and the + // past-end iterator positions. For list-like containers only invalidate + // the last position + if (hasSubscriptOperator(State, ContReg) && + backModifiable(State, ContReg)) { + State = invalidateIteratorPositions(State, BackSym, BO_GE); + State = setContainerData(State, ContReg, CData->newEnd(nullptr)); + } else { + State = invalidateIteratorPositions(State, BackSym, BO_EQ); + } + auto newEndSym = BackSym; + State = setContainerData(State, ContReg, CData->newEnd(newEndSym)); + C.addTransition(State); + } +} + +void IteratorChecker::handlePushFront(CheckerContext &C, + const SVal &Cont) const { + const auto *ContReg = Cont.getAsRegion(); + if (!ContReg) + return; + + ContReg = ContReg->getMostDerivedObjectRegion(); + + // For deque-like containers invalidate all iterator positions + auto State = C.getState(); + if (hasSubscriptOperator(State, ContReg)) { + State = invalidateAllIteratorPositions(State, ContReg); + C.addTransition(State); + } else { + const auto CData = getContainerData(State, ContReg); + if (!CData) + return; + + if (const auto BeginSym = CData->getBegin()) { + auto &SymMgr = C.getSymbolManager(); + auto &BVF = SymMgr.getBasicVals(); + auto &SVB = C.getSValBuilder(); + const auto newBeginSym = + SVB.evalBinOp(State, BO_Sub, + nonloc::SymbolVal(BeginSym), + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), + SymMgr.getType(BeginSym)).getAsSymbol(); + State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); + C.addTransition(State); + } + } +} + +void IteratorChecker::handlePopFront(CheckerContext &C, + const SVal &Cont) const { + const auto *ContReg = Cont.getAsRegion(); + if (!ContReg) + return; + + ContReg = ContReg->getMostDerivedObjectRegion(); + + auto State = C.getState(); + const auto CData = getContainerData(State, ContReg); + if (!CData) + return; + + // For deque-like containers invalidate all iterator positions. For list-like + // iterators only invalidate the first position + if (const auto BeginSym = CData->getBegin()) { + if (hasSubscriptOperator(State, ContReg)) { + State = invalidateIteratorPositions(State, BeginSym, BO_LE); + } else { + State = invalidateIteratorPositions(State, BeginSym, BO_EQ); + } + auto &SymMgr = C.getSymbolManager(); + auto &BVF = SymMgr.getBasicVals(); + auto &SVB = C.getSValBuilder(); + const auto newBeginSym = + SVB.evalBinOp(State, BO_Add, + nonloc::SymbolVal(BeginSym), + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), + SymMgr.getType(BeginSym)).getAsSymbol(); + State = setContainerData(State, ContReg, CData->newBegin(newBeginSym)); + C.addTransition(State); + } +} + +void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const { + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Iter); + if (!Pos) + return; + + // For deque-like containers invalidate all iterator positions. For + // vector-like containers invalidate iterator positions after the insertion. + const auto *Cont = Pos->getContainer(); + if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) { + if (frontModifiable(State, Cont)) { + State = invalidateAllIteratorPositions(State, Cont); + } else { + State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); + } + if (const auto *CData = getContainerData(State, Cont)) { + if (const auto EndSym = CData->getEnd()) { + State = invalidateIteratorPositions(State, EndSym, BO_GE); + State = setContainerData(State, Cont, CData->newEnd(nullptr)); + } + } + C.addTransition(State); + } +} + +void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const { + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Iter); + if (!Pos) + return; + + // For deque-like containers invalidate all iterator positions. For + // vector-like containers invalidate iterator positions at and after the + // deletion. For list-like containers only invalidate the deleted position. + const auto *Cont = Pos->getContainer(); + if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) { + if (frontModifiable(State, Cont)) { + State = invalidateAllIteratorPositions(State, Cont); + } else { + State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE); + } + if (const auto *CData = getContainerData(State, Cont)) { + if (const auto EndSym = CData->getEnd()) { + State = invalidateIteratorPositions(State, EndSym, BO_GE); + State = setContainerData(State, Cont, CData->newEnd(nullptr)); + } + } + } else { + State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ); + } + C.addTransition(State); +} + +void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1, + const SVal &Iter2) const { + auto State = C.getState(); + const auto *Pos1 = getIteratorPosition(State, Iter1); + const auto *Pos2 = getIteratorPosition(State, Iter2); + if (!Pos1 || !Pos2) + return; + + // For deque-like containers invalidate all iterator positions. For + // vector-like containers invalidate iterator positions at and after the + // deletion range. For list-like containers only invalidate the deleted + // position range [first..last]. + const auto *Cont = Pos1->getContainer(); + if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) { + if (frontModifiable(State, Cont)) { + State = invalidateAllIteratorPositions(State, Cont); + } else { + State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE); + } + if (const auto *CData = getContainerData(State, Cont)) { + if (const auto EndSym = CData->getEnd()) { + State = invalidateIteratorPositions(State, EndSym, BO_GE); + State = setContainerData(State, Cont, CData->newEnd(nullptr)); + } + } + } else { + State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE, + Pos2->getOffset(), BO_LT); + } + C.addTransition(State); +} + +void IteratorChecker::handleEraseAfter(CheckerContext &C, + const SVal &Iter) const { + auto State = C.getState(); + const auto *Pos = getIteratorPosition(State, Iter); + if (!Pos) + return; + + // Invalidate the deleted iterator position, which is the position of the + // parameter plus one. + auto &SymMgr = C.getSymbolManager(); + auto &BVF = SymMgr.getBasicVals(); + auto &SVB = C.getSValBuilder(); + const auto NextSym = + SVB.evalBinOp(State, BO_Add, + nonloc::SymbolVal(Pos->getOffset()), + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))), + SymMgr.getType(Pos->getOffset())).getAsSymbol(); + State = invalidateIteratorPositions(State, NextSym, BO_EQ); + C.addTransition(State); +} + +void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1, + const SVal &Iter2) const { + auto State = C.getState(); + const auto *Pos1 = getIteratorPosition(State, Iter1); + const auto *Pos2 = getIteratorPosition(State, Iter2); + if (!Pos1 || !Pos2) + return; + + // Invalidate the deleted iterator position range (first..last) + State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT, + Pos2->getOffset(), BO_LT); + C.addTransition(State); +} + +IteratorPosition IteratorChecker::advancePosition(CheckerContext &C, + OverloadedOperatorKind Op, + const IteratorPosition &Pos, + const SVal &Distance) const { + auto State = C.getState(); + auto &SymMgr = C.getSymbolManager(); + auto &SVB = C.getSValBuilder(); + + assert ((Op == OO_Plus || Op == OO_PlusEqual || + Op == OO_Minus || Op == OO_MinusEqual) && + "Advance operator must be one of +, -, += and -=."); + auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub; + if (const auto IntDist = Distance.getAs<nonloc::ConcreteInt>()) { + // For concrete integers we can calculate the new position + return Pos.setTo(SVB.evalBinOp(State, BinOp, + nonloc::SymbolVal(Pos.getOffset()), *IntDist, + SymMgr.getType(Pos.getOffset())) + .getAsSymbol()); + } else { + // For other symbols create a new symbol to keep expressions simple + const auto &LCtx = C.getLocationContext(); + const auto NewPosSym = SymMgr.conjureSymbol(nullptr, LCtx, + SymMgr.getType(Pos.getOffset()), + C.blockCount()); + State = assumeNoOverflow(State, NewPosSym, 4); + return Pos.setTo(NewPosSym); + } +} + +void IteratorChecker::reportOutOfRangeBug(const StringRef &Message, + const SVal &Val, CheckerContext &C, + ExplodedNode *ErrNode) const { + auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode); + R->markInteresting(Val); + C.emitReport(std::move(R)); +} + +void IteratorChecker::reportMismatchedBug(const StringRef &Message, + const SVal &Val1, const SVal &Val2, + CheckerContext &C, + ExplodedNode *ErrNode) const { + auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode); + R->markInteresting(Val1); + R->markInteresting(Val2); + C.emitReport(std::move(R)); +} + +void IteratorChecker::reportMismatchedBug(const StringRef &Message, + const SVal &Val, const MemRegion *Reg, + CheckerContext &C, + ExplodedNode *ErrNode) const { + auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode); + R->markInteresting(Val); + R->markInteresting(Reg); + C.emitReport(std::move(R)); +} + +void IteratorChecker::reportInvalidatedBug(const StringRef &Message, + const SVal &Val, CheckerContext &C, + ExplodedNode *ErrNode) const { + auto R = llvm::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode); + R->markInteresting(Val); + C.emitReport(std::move(R)); +} + +namespace { + +bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); +bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); +bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2); +bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2, + BinaryOperator::Opcode Opc); +bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2, + BinaryOperator::Opcode Opc); +const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State, + const MemRegion *Reg); +SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr, + SymbolRef OldSym, SymbolRef NewSym); + +bool isIteratorType(const QualType &Type) { + if (Type->isPointerType()) + return true; + + const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl(); + return isIterator(CRD); +} + +bool isIterator(const CXXRecordDecl *CRD) { + if (!CRD) + return false; + + const auto Name = CRD->getName(); + if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") || + Name.endswith_lower("it"))) + return false; + + bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false, + HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false; + for (const auto *Method : CRD->methods()) { + if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) { + if (Ctor->isCopyConstructor()) { + HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public; + } + continue; + } + if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) { + HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public; + continue; + } + if (Method->isCopyAssignmentOperator()) { + HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public; + continue; + } + if (!Method->isOverloadedOperator()) + continue; + const auto OPK = Method->getOverloadedOperator(); + if (OPK == OO_PlusPlus) { + HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0); + HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1); + continue; + } + if (OPK == OO_Star) { + HasDerefOp = (Method->getNumParams() == 0); + continue; + } + } + + return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp && + HasPostIncrOp && HasDerefOp; +} + +bool isComparisonOperator(OverloadedOperatorKind OK) { + return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less || + OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual; +} + +bool isBeginCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + return IdInfo->getName().endswith_lower("begin"); +} + +bool isEndCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + return IdInfo->getName().endswith_lower("end"); +} + +bool isAssignCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() > 2) + return false; + return IdInfo->getName() == "assign"; +} + +bool isClearCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() > 0) + return false; + return IdInfo->getName() == "clear"; +} + +bool isPushBackCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() != 1) + return false; + return IdInfo->getName() == "push_back"; +} + +bool isEmplaceBackCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 1) + return false; + return IdInfo->getName() == "emplace_back"; +} + +bool isPopBackCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() > 0) + return false; + return IdInfo->getName() == "pop_back"; +} + +bool isPushFrontCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() != 1) + return false; + return IdInfo->getName() == "push_front"; +} + +bool isEmplaceFrontCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 1) + return false; + return IdInfo->getName() == "emplace_front"; +} + +bool isPopFrontCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() > 0) + return false; + return IdInfo->getName() == "pop_front"; +} + +bool isInsertCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 2 || Func->getNumParams() > 3) + return false; + if (!isIteratorType(Func->getParamDecl(0)->getType())) + return false; + return IdInfo->getName() == "insert"; +} + +bool isEmplaceCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 2) + return false; + if (!isIteratorType(Func->getParamDecl(0)->getType())) + return false; + return IdInfo->getName() == "emplace"; +} + +bool isEraseCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 1 || Func->getNumParams() > 2) + return false; + if (!isIteratorType(Func->getParamDecl(0)->getType())) + return false; + if (Func->getNumParams() == 2 && + !isIteratorType(Func->getParamDecl(1)->getType())) + return false; + return IdInfo->getName() == "erase"; +} + +bool isEraseAfterCall(const FunctionDecl *Func) { + const auto *IdInfo = Func->getIdentifier(); + if (!IdInfo) + return false; + if (Func->getNumParams() < 1 || Func->getNumParams() > 2) + return false; + if (!isIteratorType(Func->getParamDecl(0)->getType())) + return false; + if (Func->getNumParams() == 2 && + !isIteratorType(Func->getParamDecl(1)->getType())) + return false; + return IdInfo->getName() == "erase_after"; +} + +bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; } + +bool isSimpleComparisonOperator(OverloadedOperatorKind OK) { + return OK == OO_EqualEqual || OK == OO_ExclaimEqual; +} + +bool isAccessOperator(OverloadedOperatorKind OK) { + return isDereferenceOperator(OK) || isIncrementOperator(OK) || + isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK); +} + +bool isDereferenceOperator(OverloadedOperatorKind OK) { + return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar || + OK == OO_Subscript; +} + +bool isIncrementOperator(OverloadedOperatorKind OK) { + return OK == OO_PlusPlus; +} + +bool isDecrementOperator(OverloadedOperatorKind OK) { + return OK == OO_MinusMinus; +} + +bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) { + return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus || + OK == OO_MinusEqual; +} + +BinaryOperator::Opcode getOpcode(const SymExpr *SE) { + if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) { + return BSE->getOpcode(); + } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) { + const auto *COE = dyn_cast_or_null<CXXOperatorCallExpr>(SC->getStmt()); + if (!COE) + return BO_Comma; // Extremal value, neither EQ nor NE + if (COE->getOperator() == OO_EqualEqual) { + return BO_EQ; + } else if (COE->getOperator() == OO_ExclaimEqual) { + return BO_NE; + } + return BO_Comma; // Extremal value, neither EQ nor NE + } + return BO_Comma; // Extremal value, neither EQ nor NE +} + +bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) { + const auto *CRD = getCXXRecordDecl(State, Reg); + if (!CRD) + return false; + + for (const auto *Method : CRD->methods()) { + if (!Method->isOverloadedOperator()) + continue; + const auto OPK = Method->getOverloadedOperator(); + if (OPK == OO_Subscript) { + return true; + } + } + return false; +} + +bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) { + const auto *CRD = getCXXRecordDecl(State, Reg); + if (!CRD) + return false; + + for (const auto *Method : CRD->methods()) { + if (!Method->getDeclName().isIdentifier()) + continue; + if (Method->getName() == "push_front" || Method->getName() == "pop_front") { + return true; + } + } + return false; +} + +bool backModifiable(ProgramStateRef State, const MemRegion *Reg) { + const auto *CRD = getCXXRecordDecl(State, Reg); + if (!CRD) + return false; + + for (const auto *Method : CRD->methods()) { + if (!Method->getDeclName().isIdentifier()) + continue; + if (Method->getName() == "push_back" || Method->getName() == "pop_back") { + return true; + } + } + return false; +} + +const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State, + const MemRegion *Reg) { + auto TI = getDynamicTypeInfo(State, Reg); + if (!TI.isValid()) + return nullptr; + + auto Type = TI.getType(); + if (const auto *RefT = Type->getAs<ReferenceType>()) { + Type = RefT->getPointeeType(); + } + + return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl(); +} + +const RegionOrSymbol getRegionOrSymbol(const SVal &Val) { + if (const auto Reg = Val.getAsRegion()) { + return Reg; + } else if (const auto Sym = Val.getAsSymbol()) { + return Sym; + } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { + return LCVal->getRegion(); + } + return RegionOrSymbol(); +} + +const ProgramStateRef processComparison(ProgramStateRef State, + RegionOrSymbol LVal, + RegionOrSymbol RVal, bool Equal) { + const auto *LPos = getIteratorPosition(State, LVal); + const auto *RPos = getIteratorPosition(State, RVal); + if (LPos && !RPos) { + State = adjustIteratorPosition(State, RVal, *LPos, Equal); + } else if (!LPos && RPos) { + State = adjustIteratorPosition(State, LVal, *RPos, Equal); + } else if (LPos && RPos) { + State = relateIteratorPositions(State, *LPos, *RPos, Equal); + } + return State; +} + +const ProgramStateRef saveComparison(ProgramStateRef State, + const SymExpr *Condition, const SVal &LVal, + const SVal &RVal, bool Eq) { + const auto Left = getRegionOrSymbol(LVal); + const auto Right = getRegionOrSymbol(RVal); + if (!Left || !Right) + return State; + return State->set<IteratorComparisonMap>(Condition, + IteratorComparison(Left, Right, Eq)); +} + +const IteratorComparison *loadComparison(ProgramStateRef State, + const SymExpr *Condition) { + return State->get<IteratorComparisonMap>(Condition); +} + +SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) { + const auto *CDataPtr = getContainerData(State, Cont); + if (!CDataPtr) + return nullptr; + + return CDataPtr->getBegin(); +} + +SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) { + const auto *CDataPtr = getContainerData(State, Cont); + if (!CDataPtr) + return nullptr; + + return CDataPtr->getEnd(); +} + +ProgramStateRef createContainerBegin(ProgramStateRef State, + const MemRegion *Cont, + const SymbolRef Sym) { + // Only create if it does not exist + const auto *CDataPtr = getContainerData(State, Cont); + if (CDataPtr) { + if (CDataPtr->getBegin()) { + return State; + } + const auto CData = CDataPtr->newBegin(Sym); + return setContainerData(State, Cont, CData); + } + const auto CData = ContainerData::fromBegin(Sym); + return setContainerData(State, Cont, CData); +} + +ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont, + const SymbolRef Sym) { + // Only create if it does not exist + const auto *CDataPtr = getContainerData(State, Cont); + if (CDataPtr) { + if (CDataPtr->getEnd()) { + return State; + } + const auto CData = CDataPtr->newEnd(Sym); + return setContainerData(State, Cont, CData); + } + const auto CData = ContainerData::fromEnd(Sym); + return setContainerData(State, Cont, CData); +} + +const ContainerData *getContainerData(ProgramStateRef State, + const MemRegion *Cont) { + return State->get<ContainerMap>(Cont); +} + +ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont, + const ContainerData &CData) { + return State->set<ContainerMap>(Cont, CData); +} + +const IteratorPosition *getIteratorPosition(ProgramStateRef State, + const SVal &Val) { + if (auto Reg = Val.getAsRegion()) { + Reg = Reg->getMostDerivedObjectRegion(); + return State->get<IteratorRegionMap>(Reg); + } else if (const auto Sym = Val.getAsSymbol()) { + return State->get<IteratorSymbolMap>(Sym); + } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { + return State->get<IteratorRegionMap>(LCVal->getRegion()); + } + return nullptr; +} + +const IteratorPosition *getIteratorPosition(ProgramStateRef State, + RegionOrSymbol RegOrSym) { + if (RegOrSym.is<const MemRegion *>()) { + auto Reg = RegOrSym.get<const MemRegion *>()->getMostDerivedObjectRegion(); + return State->get<IteratorRegionMap>(Reg); + } else if (RegOrSym.is<SymbolRef>()) { + return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>()); + } + return nullptr; +} + +ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val, + const IteratorPosition &Pos) { + if (auto Reg = Val.getAsRegion()) { + Reg = Reg->getMostDerivedObjectRegion(); + return State->set<IteratorRegionMap>(Reg, Pos); + } else if (const auto Sym = Val.getAsSymbol()) { + return State->set<IteratorSymbolMap>(Sym, Pos); + } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { + return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos); + } + return nullptr; +} + +ProgramStateRef setIteratorPosition(ProgramStateRef State, + RegionOrSymbol RegOrSym, + const IteratorPosition &Pos) { + if (RegOrSym.is<const MemRegion *>()) { + auto Reg = RegOrSym.get<const MemRegion *>()->getMostDerivedObjectRegion(); + return State->set<IteratorRegionMap>(Reg, Pos); + } else if (RegOrSym.is<SymbolRef>()) { + return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos); + } + return nullptr; +} + +ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) { + if (auto Reg = Val.getAsRegion()) { + Reg = Reg->getMostDerivedObjectRegion(); + return State->remove<IteratorRegionMap>(Reg); + } else if (const auto Sym = Val.getAsSymbol()) { + return State->remove<IteratorSymbolMap>(Sym); + } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) { + return State->remove<IteratorRegionMap>(LCVal->getRegion()); + } + return nullptr; +} + +ProgramStateRef adjustIteratorPosition(ProgramStateRef State, + RegionOrSymbol RegOrSym, + const IteratorPosition &Pos, + bool Equal) { + if (Equal) { + return setIteratorPosition(State, RegOrSym, Pos); + } else { + return State; + } +} + +ProgramStateRef relateIteratorPositions(ProgramStateRef State, + const IteratorPosition &Pos1, + const IteratorPosition &Pos2, + bool Equal) { + auto &SVB = State->getStateManager().getSValBuilder(); + + // FIXME: This code should be reworked as follows: + // 1. Subtract the operands using evalBinOp(). + // 2. Assume that the result doesn't overflow. + // 3. Compare the result to 0. + // 4. Assume the result of the comparison. + const auto comparison = + SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()), + nonloc::SymbolVal(Pos2.getOffset()), + SVB.getConditionType()); + + assert(comparison.getAs<DefinedSVal>() && + "Symbol comparison must be a `DefinedSVal`"); + + auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal); + if (const auto CompSym = comparison.getAsSymbol()) { + assert(isa<SymIntExpr>(CompSym) && + "Symbol comparison must be a `SymIntExpr`"); + assert(BinaryOperator::isComparisonOp( + cast<SymIntExpr>(CompSym)->getOpcode()) && + "Symbol comparison must be a comparison"); + return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2); + } + + return NewState; +} + +bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) { + auto RegionMap = State->get<IteratorRegionMap>(); + for (const auto Reg : RegionMap) { + if (Reg.second.getContainer() == Cont) + return true; + } + + auto SymbolMap = State->get<IteratorSymbolMap>(); + for (const auto Sym : SymbolMap) { + if (Sym.second.getContainer() == Cont) + return true; + } + + return false; +} + +bool isBoundThroughLazyCompoundVal(const Environment &Env, + const MemRegion *Reg) { + for (const auto Binding: Env) { + if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) { + if (LCVal->getRegion() == Reg) + return true; + } + } + + return false; +} + +template <typename Condition, typename Process> +ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond, + Process Proc) { + auto &RegionMapFactory = State->get_context<IteratorRegionMap>(); + auto RegionMap = State->get<IteratorRegionMap>(); + bool Changed = false; + for (const auto Reg : RegionMap) { + if (Cond(Reg.second)) { + RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second)); + Changed = true; + } + } + + if (Changed) + State = State->set<IteratorRegionMap>(RegionMap); + + auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>(); + auto SymbolMap = State->get<IteratorSymbolMap>(); + Changed = false; + for (const auto Sym : SymbolMap) { + if (Cond(Sym.second)) { + SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second)); + Changed = true; + } + } + + if (Changed) + State = State->set<IteratorSymbolMap>(SymbolMap); + + return State; +} + +ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State, + const MemRegion *Cont) { + auto MatchCont = [&](const IteratorPosition &Pos) { + return Pos.getContainer() == Cont; + }; + auto Invalidate = [&](const IteratorPosition &Pos) { + return Pos.invalidate(); + }; + return processIteratorPositions(State, MatchCont, Invalidate); +} + +ProgramStateRef +invalidateAllIteratorPositionsExcept(ProgramStateRef State, + const MemRegion *Cont, SymbolRef Offset, + BinaryOperator::Opcode Opc) { + auto MatchContAndCompare = [&](const IteratorPosition &Pos) { + return Pos.getContainer() == Cont && + !compare(State, Pos.getOffset(), Offset, Opc); + }; + auto Invalidate = [&](const IteratorPosition &Pos) { + return Pos.invalidate(); + }; + return processIteratorPositions(State, MatchContAndCompare, Invalidate); +} + +ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, + SymbolRef Offset, + BinaryOperator::Opcode Opc) { + auto Compare = [&](const IteratorPosition &Pos) { + return compare(State, Pos.getOffset(), Offset, Opc); + }; + auto Invalidate = [&](const IteratorPosition &Pos) { + return Pos.invalidate(); + }; + return processIteratorPositions(State, Compare, Invalidate); +} + +ProgramStateRef invalidateIteratorPositions(ProgramStateRef State, + SymbolRef Offset1, + BinaryOperator::Opcode Opc1, + SymbolRef Offset2, + BinaryOperator::Opcode Opc2) { + auto Compare = [&](const IteratorPosition &Pos) { + return compare(State, Pos.getOffset(), Offset1, Opc1) && + compare(State, Pos.getOffset(), Offset2, Opc2); + }; + auto Invalidate = [&](const IteratorPosition &Pos) { + return Pos.invalidate(); + }; + return processIteratorPositions(State, Compare, Invalidate); +} + +ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State, + const MemRegion *Cont, + const MemRegion *NewCont) { + auto MatchCont = [&](const IteratorPosition &Pos) { + return Pos.getContainer() == Cont; + }; + auto ReAssign = [&](const IteratorPosition &Pos) { + return Pos.reAssign(NewCont); + }; + return processIteratorPositions(State, MatchCont, ReAssign); +} + +ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State, + const MemRegion *Cont, + const MemRegion *NewCont, + SymbolRef Offset, + BinaryOperator::Opcode Opc) { + auto MatchContAndCompare = [&](const IteratorPosition &Pos) { + return Pos.getContainer() == Cont && + !compare(State, Pos.getOffset(), Offset, Opc); + }; + auto ReAssign = [&](const IteratorPosition &Pos) { + return Pos.reAssign(NewCont); + }; + return processIteratorPositions(State, MatchContAndCompare, ReAssign); +} + +// This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`, +// `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator +// position offsets where `CondSym` is true. +ProgramStateRef rebaseSymbolInIteratorPositionsIf( + ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym, + SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) { + auto LessThanEnd = [&](const IteratorPosition &Pos) { + return compare(State, Pos.getOffset(), CondSym, Opc); + }; + auto RebaseSymbol = [&](const IteratorPosition &Pos) { + return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym, + NewSym)); + }; + return processIteratorPositions(State, LessThanEnd, RebaseSymbol); +} + +// This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`, +// `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression +// `OrigExpr`. +SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, + SymbolRef OrigExpr, SymbolRef OldExpr, + SymbolRef NewSym) { + auto &SymMgr = SVB.getSymbolManager(); + auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr), + nonloc::SymbolVal(OldExpr), + SymMgr.getType(OrigExpr)); + + const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>(); + if (!DiffInt) + return OrigExpr; + + return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym), + SymMgr.getType(OrigExpr)).getAsSymbol(); +} + +bool isZero(ProgramStateRef State, const NonLoc &Val) { + auto &BVF = State->getBasicVals(); + return compare(State, Val, + nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))), + BO_EQ); +} + +bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) { + const auto *Cont = Pos.getContainer(); + const auto *CData = getContainerData(State, Cont); + if (!CData) + return false; + + const auto End = CData->getEnd(); + if (End) { + if (isEqual(State, Pos.getOffset(), End)) { + return true; + } + } + + return false; +} + +bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) { + const auto *Cont = Pos.getContainer(); + const auto *CData = getContainerData(State, Cont); + if (!CData) + return false; + + const auto Beg = CData->getBegin(); + if (Beg) { + if (isLess(State, Pos.getOffset(), Beg)) { + return true; + } + } + + return false; +} + +bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) { + const auto *Cont = Pos.getContainer(); + const auto *CData = getContainerData(State, Cont); + if (!CData) + return false; + + const auto End = CData->getEnd(); + if (End) { + if (isGreater(State, Pos.getOffset(), End)) { + return true; + } + } + + return false; +} + +bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) { + return compare(State, Sym1, Sym2, BO_LT); +} + +bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) { + return compare(State, Sym1, Sym2, BO_GT); +} + +bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) { + return compare(State, Sym1, Sym2, BO_EQ); +} + +bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2, + BinaryOperator::Opcode Opc) { + return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc); +} + + +bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2, + BinaryOperator::Opcode Opc) { + auto &SVB = State->getStateManager().getSValBuilder(); + + const auto comparison = + SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType()); + + assert(comparison.getAs<DefinedSVal>() && + "Symbol comparison must be a `DefinedSVal`"); + + return !State->assume(comparison.castAs<DefinedSVal>(), false); +} + +} // namespace + +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &Mgr) { \ + auto *checker = Mgr.registerChecker<IteratorChecker>(); \ + checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \ + checker->CheckNames[IteratorChecker::CK_##name] = \ + Mgr.getCurrentCheckName(); \ + } + +REGISTER_CHECKER(IteratorRangeChecker) +REGISTER_CHECKER(MismatchedIteratorChecker) +REGISTER_CHECKER(InvalidatedIteratorChecker) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp new file mode 100644 index 000000000000..aade62fd7491 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp @@ -0,0 +1,748 @@ +//===- IvarInvalidationChecker.cpp ------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker implements annotation driven invalidation checking. If a class +// contains a method annotated with 'objc_instance_variable_invalidator', +// - (void) foo +// __attribute__((annotate("objc_instance_variable_invalidator"))); +// all the "ivalidatable" instance variables of this class should be +// invalidated. We call an instance variable ivalidatable if it is an object of +// a class which contains an invalidation method. There could be multiple +// methods annotated with such annotations per class, either one can be used +// to invalidate the ivar. An ivar or property are considered to be +// invalidated if they are being assigned 'nil' or an invalidation method has +// been called on them. An invalidation method should either invalidate all +// the ivars or call another invalidation method (on self). +// +// Partial invalidor annotation allows to address cases when ivars are +// invalidated by other methods, which might or might not be called from +// the invalidation method. The checker checks that each invalidation +// method and all the partial methods cumulatively invalidate all ivars. +// __attribute__((annotate("objc_instance_variable_invalidator_partial"))); +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/SmallString.h" + +using namespace clang; +using namespace ento; + +namespace { +struct ChecksFilter { + /// Check for missing invalidation method declarations. + DefaultBool check_MissingInvalidationMethod; + /// Check that all ivars are invalidated. + DefaultBool check_InstanceVariableInvalidation; + + CheckName checkName_MissingInvalidationMethod; + CheckName checkName_InstanceVariableInvalidation; +}; + +class IvarInvalidationCheckerImpl { + typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet; + typedef llvm::DenseMap<const ObjCMethodDecl*, + const ObjCIvarDecl*> MethToIvarMapTy; + typedef llvm::DenseMap<const ObjCPropertyDecl*, + const ObjCIvarDecl*> PropToIvarMapTy; + typedef llvm::DenseMap<const ObjCIvarDecl*, + const ObjCPropertyDecl*> IvarToPropMapTy; + + struct InvalidationInfo { + /// Has the ivar been invalidated? + bool IsInvalidated; + + /// The methods which can be used to invalidate the ivar. + MethodSet InvalidationMethods; + + InvalidationInfo() : IsInvalidated(false) {} + void addInvalidationMethod(const ObjCMethodDecl *MD) { + InvalidationMethods.insert(MD); + } + + bool needsInvalidation() const { + return !InvalidationMethods.empty(); + } + + bool hasMethod(const ObjCMethodDecl *MD) { + if (IsInvalidated) + return true; + for (MethodSet::iterator I = InvalidationMethods.begin(), + E = InvalidationMethods.end(); I != E; ++I) { + if (*I == MD) { + IsInvalidated = true; + return true; + } + } + return false; + } + }; + + typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet; + + /// Statement visitor, which walks the method body and flags the ivars + /// referenced in it (either directly or via property). + class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { + /// The set of Ivars which need to be invalidated. + IvarSet &IVars; + + /// Flag is set as the result of a message send to another + /// invalidation method. + bool &CalledAnotherInvalidationMethod; + + /// Property setter to ivar mapping. + const MethToIvarMapTy &PropertySetterToIvarMap; + + /// Property getter to ivar mapping. + const MethToIvarMapTy &PropertyGetterToIvarMap; + + /// Property to ivar mapping. + const PropToIvarMapTy &PropertyToIvarMap; + + /// The invalidation method being currently processed. + const ObjCMethodDecl *InvalidationMethod; + + ASTContext &Ctx; + + /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr. + const Expr *peel(const Expr *E) const; + + /// Does this expression represent zero: '0'? + bool isZero(const Expr *E) const; + + /// Mark the given ivar as invalidated. + void markInvalidated(const ObjCIvarDecl *Iv); + + /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as + /// invalidated. + void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef); + + /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks + /// it as invalidated. + void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA); + + /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar, + /// if yes, marks it as invalidated. + void checkObjCMessageExpr(const ObjCMessageExpr *ME); + + /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated. + void check(const Expr *E); + + public: + MethodCrawler(IvarSet &InIVars, + bool &InCalledAnotherInvalidationMethod, + const MethToIvarMapTy &InPropertySetterToIvarMap, + const MethToIvarMapTy &InPropertyGetterToIvarMap, + const PropToIvarMapTy &InPropertyToIvarMap, + ASTContext &InCtx) + : IVars(InIVars), + CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod), + PropertySetterToIvarMap(InPropertySetterToIvarMap), + PropertyGetterToIvarMap(InPropertyGetterToIvarMap), + PropertyToIvarMap(InPropertyToIvarMap), + InvalidationMethod(nullptr), + Ctx(InCtx) {} + + void VisitStmt(const Stmt *S) { VisitChildren(S); } + + void VisitBinaryOperator(const BinaryOperator *BO); + + void VisitObjCMessageExpr(const ObjCMessageExpr *ME); + + void VisitChildren(const Stmt *S) { + for (const auto *Child : S->children()) { + if (Child) + this->Visit(Child); + if (CalledAnotherInvalidationMethod) + return; + } + } + }; + + /// Check if the any of the methods inside the interface are annotated with + /// the invalidation annotation, update the IvarInfo accordingly. + /// \param LookForPartial is set when we are searching for partial + /// invalidators. + static void containsInvalidationMethod(const ObjCContainerDecl *D, + InvalidationInfo &Out, + bool LookForPartial); + + /// Check if ivar should be tracked and add to TrackedIvars if positive. + /// Returns true if ivar should be tracked. + static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars, + const ObjCIvarDecl **FirstIvarDecl); + + /// Given the property declaration, and the list of tracked ivars, finds + /// the ivar backing the property when possible. Returns '0' when no such + /// ivar could be found. + static const ObjCIvarDecl *findPropertyBackingIvar( + const ObjCPropertyDecl *Prop, + const ObjCInterfaceDecl *InterfaceD, + IvarSet &TrackedIvars, + const ObjCIvarDecl **FirstIvarDecl); + + /// Print ivar name or the property if the given ivar backs a property. + static void printIvar(llvm::raw_svector_ostream &os, + const ObjCIvarDecl *IvarDecl, + const IvarToPropMapTy &IvarToPopertyMap); + + void reportNoInvalidationMethod(CheckName CheckName, + const ObjCIvarDecl *FirstIvarDecl, + const IvarToPropMapTy &IvarToPopertyMap, + const ObjCInterfaceDecl *InterfaceD, + bool MissingDeclaration) const; + + void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, + const IvarToPropMapTy &IvarToPopertyMap, + const ObjCMethodDecl *MethodD) const; + + AnalysisManager& Mgr; + BugReporter &BR; + /// Filter on the checks performed. + const ChecksFilter &Filter; + +public: + IvarInvalidationCheckerImpl(AnalysisManager& InMgr, + BugReporter &InBR, + const ChecksFilter &InFilter) : + Mgr (InMgr), BR(InBR), Filter(InFilter) {} + + void visit(const ObjCImplementationDecl *D) const; +}; + +static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) { + for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) { + if (!LookForPartial && + Ann->getAnnotation() == "objc_instance_variable_invalidator") + return true; + if (LookForPartial && + Ann->getAnnotation() == "objc_instance_variable_invalidator_partial") + return true; + } + return false; +} + +void IvarInvalidationCheckerImpl::containsInvalidationMethod( + const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) { + + if (!D) + return; + + assert(!isa<ObjCImplementationDecl>(D)); + // TODO: Cache the results. + + // Check all methods. + for (const auto *MDI : D->methods()) + if (isInvalidationMethod(MDI, Partial)) + OutInfo.addInvalidationMethod( + cast<ObjCMethodDecl>(MDI->getCanonicalDecl())); + + // If interface, check all parent protocols and super. + if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) { + + // Visit all protocols. + for (const auto *I : InterfD->protocols()) + containsInvalidationMethod(I->getDefinition(), OutInfo, Partial); + + // Visit all categories in case the invalidation method is declared in + // a category. + for (const auto *Ext : InterfD->visible_extensions()) + containsInvalidationMethod(Ext, OutInfo, Partial); + + containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial); + return; + } + + // If protocol, check all parent protocols. + if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) { + for (const auto *I : ProtD->protocols()) { + containsInvalidationMethod(I->getDefinition(), OutInfo, Partial); + } + return; + } +} + +bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv, + IvarSet &TrackedIvars, + const ObjCIvarDecl **FirstIvarDecl) { + QualType IvQTy = Iv->getType(); + const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>(); + if (!IvTy) + return false; + const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl(); + + InvalidationInfo Info; + containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false); + if (Info.needsInvalidation()) { + const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl()); + TrackedIvars[I] = Info; + if (!*FirstIvarDecl) + *FirstIvarDecl = I; + return true; + } + return false; +} + +const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar( + const ObjCPropertyDecl *Prop, + const ObjCInterfaceDecl *InterfaceD, + IvarSet &TrackedIvars, + const ObjCIvarDecl **FirstIvarDecl) { + const ObjCIvarDecl *IvarD = nullptr; + + // Lookup for the synthesized case. + IvarD = Prop->getPropertyIvarDecl(); + // We only track the ivars/properties that are defined in the current + // class (not the parent). + if (IvarD && IvarD->getContainingInterface() == InterfaceD) { + if (TrackedIvars.count(IvarD)) { + return IvarD; + } + // If the ivar is synthesized we still want to track it. + if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl)) + return IvarD; + } + + // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars. + StringRef PropName = Prop->getIdentifier()->getName(); + for (IvarSet::const_iterator I = TrackedIvars.begin(), + E = TrackedIvars.end(); I != E; ++I) { + const ObjCIvarDecl *Iv = I->first; + StringRef IvarName = Iv->getName(); + + if (IvarName == PropName) + return Iv; + + SmallString<128> PropNameWithUnderscore; + { + llvm::raw_svector_ostream os(PropNameWithUnderscore); + os << '_' << PropName; + } + if (IvarName == PropNameWithUnderscore) + return Iv; + } + + // Note, this is a possible source of false positives. We could look at the + // getter implementation to find the ivar when its name is not derived from + // the property name. + return nullptr; +} + +void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os, + const ObjCIvarDecl *IvarDecl, + const IvarToPropMapTy &IvarToPopertyMap) { + if (IvarDecl->getSynthesize()) { + const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl); + assert(PD &&"Do we synthesize ivars for something other than properties?"); + os << "Property "<< PD->getName() << " "; + } else { + os << "Instance variable "<< IvarDecl->getName() << " "; + } +} + +// Check that the invalidatable interfaces with ivars/properties implement the +// invalidation methods. +void IvarInvalidationCheckerImpl:: +visit(const ObjCImplementationDecl *ImplD) const { + // Collect all ivars that need cleanup. + IvarSet Ivars; + // Record the first Ivar needing invalidation; used in reporting when only + // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure + // deterministic output. + const ObjCIvarDecl *FirstIvarDecl = nullptr; + const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface(); + + // Collect ivars declared in this class, its extensions and its implementation + ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD); + for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv; + Iv= Iv->getNextIvar()) + trackIvar(Iv, Ivars, &FirstIvarDecl); + + // Construct Property/Property Accessor to Ivar maps to assist checking if an + // ivar which is backing a property has been reset. + MethToIvarMapTy PropSetterToIvarMap; + MethToIvarMapTy PropGetterToIvarMap; + PropToIvarMapTy PropertyToIvarMap; + IvarToPropMapTy IvarToPopertyMap; + + ObjCInterfaceDecl::PropertyMap PropMap; + ObjCInterfaceDecl::PropertyDeclOrder PropOrder; + InterfaceD->collectPropertiesToImplement(PropMap, PropOrder); + + for (ObjCInterfaceDecl::PropertyMap::iterator + I = PropMap.begin(), E = PropMap.end(); I != E; ++I) { + const ObjCPropertyDecl *PD = I->second; + if (PD->isClassProperty()) + continue; + + const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars, + &FirstIvarDecl); + if (!ID) + continue; + + // Store the mappings. + PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); + PropertyToIvarMap[PD] = ID; + IvarToPopertyMap[ID] = PD; + + // Find the setter and the getter. + const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl(); + if (SetterD) { + SetterD = SetterD->getCanonicalDecl(); + PropSetterToIvarMap[SetterD] = ID; + } + + const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl(); + if (GetterD) { + GetterD = GetterD->getCanonicalDecl(); + PropGetterToIvarMap[GetterD] = ID; + } + } + + // If no ivars need invalidation, there is nothing to check here. + if (Ivars.empty()) + return; + + // Find all partial invalidation methods. + InvalidationInfo PartialInfo; + containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true); + + // Remove ivars invalidated by the partial invalidation methods. They do not + // need to be invalidated in the regular invalidation methods. + bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false; + for (MethodSet::iterator + I = PartialInfo.InvalidationMethods.begin(), + E = PartialInfo.InvalidationMethods.end(); I != E; ++I) { + const ObjCMethodDecl *InterfD = *I; + + // Get the corresponding method in the @implementation. + const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), + InterfD->isInstanceMethod()); + if (D && D->hasBody()) { + AtImplementationContainsAtLeastOnePartialInvalidationMethod = true; + + bool CalledAnotherInvalidationMethod = false; + // The MethodCrowler is going to remove the invalidated ivars. + MethodCrawler(Ivars, + CalledAnotherInvalidationMethod, + PropSetterToIvarMap, + PropGetterToIvarMap, + PropertyToIvarMap, + BR.getContext()).VisitStmt(D->getBody()); + // If another invalidation method was called, trust that full invalidation + // has occurred. + if (CalledAnotherInvalidationMethod) + Ivars.clear(); + } + } + + // If all ivars have been invalidated by partial invalidators, there is + // nothing to check here. + if (Ivars.empty()) + return; + + // Find all invalidation methods in this @interface declaration and parents. + InvalidationInfo Info; + containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false); + + // Report an error in case none of the invalidation methods are declared. + if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) { + if (Filter.check_MissingInvalidationMethod) + reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod, + FirstIvarDecl, IvarToPopertyMap, InterfaceD, + /*MissingDeclaration*/ true); + // If there are no invalidation methods, there is no ivar validation work + // to be done. + return; + } + + // Only check if Ivars are invalidated when InstanceVariableInvalidation + // has been requested. + if (!Filter.check_InstanceVariableInvalidation) + return; + + // Check that all ivars are invalidated by the invalidation methods. + bool AtImplementationContainsAtLeastOneInvalidationMethod = false; + for (MethodSet::iterator I = Info.InvalidationMethods.begin(), + E = Info.InvalidationMethods.end(); I != E; ++I) { + const ObjCMethodDecl *InterfD = *I; + + // Get the corresponding method in the @implementation. + const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(), + InterfD->isInstanceMethod()); + if (D && D->hasBody()) { + AtImplementationContainsAtLeastOneInvalidationMethod = true; + + // Get a copy of ivars needing invalidation. + IvarSet IvarsI = Ivars; + + bool CalledAnotherInvalidationMethod = false; + MethodCrawler(IvarsI, + CalledAnotherInvalidationMethod, + PropSetterToIvarMap, + PropGetterToIvarMap, + PropertyToIvarMap, + BR.getContext()).VisitStmt(D->getBody()); + // If another invalidation method was called, trust that full invalidation + // has occurred. + if (CalledAnotherInvalidationMethod) + continue; + + // Warn on the ivars that were not invalidated by the method. + for (IvarSet::const_iterator + I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I) + reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D); + } + } + + // Report an error in case none of the invalidation methods are implemented. + if (!AtImplementationContainsAtLeastOneInvalidationMethod) { + if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) { + // Warn on the ivars that were not invalidated by the prrtial + // invalidation methods. + for (IvarSet::const_iterator + I = Ivars.begin(), E = Ivars.end(); I != E; ++I) + reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, nullptr); + } else { + // Otherwise, no invalidation methods were implemented. + reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation, + FirstIvarDecl, IvarToPopertyMap, InterfaceD, + /*MissingDeclaration*/ false); + } + } +} + +void IvarInvalidationCheckerImpl::reportNoInvalidationMethod( + CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl, + const IvarToPropMapTy &IvarToPopertyMap, + const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const { + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + assert(FirstIvarDecl); + printIvar(os, FirstIvarDecl, IvarToPopertyMap); + os << "needs to be invalidated; "; + if (MissingDeclaration) + os << "no invalidation method is declared for "; + else + os << "no invalidation method is defined in the @implementation for "; + os << InterfaceD->getName(); + + PathDiagnosticLocation IvarDecLocation = + PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager()); + + BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation", + categories::CoreFoundationObjectiveC, os.str(), + IvarDecLocation); +} + +void IvarInvalidationCheckerImpl:: +reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD, + const IvarToPropMapTy &IvarToPopertyMap, + const ObjCMethodDecl *MethodD) const { + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + printIvar(os, IvarD, IvarToPopertyMap); + os << "needs to be invalidated or set to nil"; + if (MethodD) { + PathDiagnosticLocation MethodDecLocation = + PathDiagnosticLocation::createEnd(MethodD->getBody(), + BR.getSourceManager(), + Mgr.getAnalysisDeclContext(MethodD)); + BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation, + "Incomplete invalidation", + categories::CoreFoundationObjectiveC, os.str(), + MethodDecLocation); + } else { + BR.EmitBasicReport( + IvarD, Filter.checkName_InstanceVariableInvalidation, + "Incomplete invalidation", categories::CoreFoundationObjectiveC, + os.str(), + PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager())); + } +} + +void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated( + const ObjCIvarDecl *Iv) { + IvarSet::iterator I = IVars.find(Iv); + if (I != IVars.end()) { + // If InvalidationMethod is present, we are processing the message send and + // should ensure we are invalidating with the appropriate method, + // otherwise, we are processing setting to 'nil'. + if (!InvalidationMethod || I->second.hasMethod(InvalidationMethod)) + IVars.erase(I); + } +} + +const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const { + E = E->IgnoreParenCasts(); + if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E)) + E = POE->getSyntacticForm()->IgnoreParenCasts(); + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E)) + E = OVE->getSourceExpr()->IgnoreParenCasts(); + return E; +} + +void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr( + const ObjCIvarRefExpr *IvarRef) { + if (const Decl *D = IvarRef->getDecl()) + markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl())); +} + +void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr( + const ObjCMessageExpr *ME) { + const ObjCMethodDecl *MD = ME->getMethodDecl(); + if (MD) { + MD = MD->getCanonicalDecl(); + MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD); + if (IvI != PropertyGetterToIvarMap.end()) + markInvalidated(IvI->second); + } +} + +void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr( + const ObjCPropertyRefExpr *PA) { + + if (PA->isExplicitProperty()) { + const ObjCPropertyDecl *PD = PA->getExplicitProperty(); + if (PD) { + PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl()); + PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD); + if (IvI != PropertyToIvarMap.end()) + markInvalidated(IvI->second); + return; + } + } + + if (PA->isImplicitProperty()) { + const ObjCMethodDecl *MD = PA->getImplicitPropertySetter(); + if (MD) { + MD = MD->getCanonicalDecl(); + MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD); + if (IvI != PropertyGetterToIvarMap.end()) + markInvalidated(IvI->second); + return; + } + } +} + +bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const { + E = peel(E); + + return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull) + != Expr::NPCK_NotNull); +} + +void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) { + E = peel(E); + + if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { + checkObjCIvarRefExpr(IvarRef); + return; + } + + if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) { + checkObjCPropertyRefExpr(PropRef); + return; + } + + if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) { + checkObjCMessageExpr(MsgExpr); + return; + } +} + +void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator( + const BinaryOperator *BO) { + VisitStmt(BO); + + // Do we assign/compare against zero? If yes, check the variable we are + // assigning to. + BinaryOperatorKind Opcode = BO->getOpcode(); + if (Opcode != BO_Assign && + Opcode != BO_EQ && + Opcode != BO_NE) + return; + + if (isZero(BO->getRHS())) { + check(BO->getLHS()); + return; + } + + if (Opcode != BO_Assign && isZero(BO->getLHS())) { + check(BO->getRHS()); + return; + } +} + +void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr( + const ObjCMessageExpr *ME) { + const ObjCMethodDecl *MD = ME->getMethodDecl(); + const Expr *Receiver = ME->getInstanceReceiver(); + + // Stop if we are calling '[self invalidate]'. + if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false)) + if (Receiver->isObjCSelfExpr()) { + CalledAnotherInvalidationMethod = true; + return; + } + + // Check if we call a setter and set the property to 'nil'. + if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) { + MD = MD->getCanonicalDecl(); + MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD); + if (IvI != PropertySetterToIvarMap.end()) { + markInvalidated(IvI->second); + return; + } + } + + // Check if we call the 'invalidation' routine on the ivar. + if (Receiver) { + InvalidationMethod = MD; + check(Receiver->IgnoreParenCasts()); + InvalidationMethod = nullptr; + } + + VisitStmt(ME); +} +} // end anonymous namespace + +// Register the checkers. +namespace { +class IvarInvalidationChecker : + public Checker<check::ASTDecl<ObjCImplementationDecl> > { +public: + ChecksFilter Filter; +public: + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr, + BugReporter &BR) const { + IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter); + Walker.visit(D); + } +}; +} // end anonymous namespace + +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + IvarInvalidationChecker *checker = \ + mgr.registerChecker<IvarInvalidationChecker>(); \ + checker->Filter.check_##name = true; \ + checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \ + } + +REGISTER_CHECKER(InstanceVariableInvalidation) +REGISTER_CHECKER(MissingInvalidationMethod) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp new file mode 100644 index 000000000000..df238f2b2e45 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp @@ -0,0 +1,316 @@ +//=== LLVMConventionsChecker.cpp - Check LLVM codebase conventions ---*- C++ -*- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines LLVMConventionsChecker, a bunch of small little checks +// for checking specific coding conventions in the LLVM/Clang codebase. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Generic type checking routines. +//===----------------------------------------------------------------------===// + +static bool IsLLVMStringRef(QualType T) { + const RecordType *RT = T->getAs<RecordType>(); + if (!RT) + return false; + + return StringRef(QualType(RT, 0).getAsString()) == "class StringRef"; +} + +/// Check whether the declaration is semantically inside the top-level +/// namespace named by ns. +static bool InNamespace(const Decl *D, StringRef NS) { + const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D->getDeclContext()); + if (!ND) + return false; + const IdentifierInfo *II = ND->getIdentifier(); + if (!II || !II->getName().equals(NS)) + return false; + return isa<TranslationUnitDecl>(ND->getDeclContext()); +} + +static bool IsStdString(QualType T) { + if (const ElaboratedType *QT = T->getAs<ElaboratedType>()) + T = QT->getNamedType(); + + const TypedefType *TT = T->getAs<TypedefType>(); + if (!TT) + return false; + + const TypedefNameDecl *TD = TT->getDecl(); + + if (!TD->isInStdNamespace()) + return false; + + return TD->getName() == "string"; +} + +static bool IsClangType(const RecordDecl *RD) { + return RD->getName() == "Type" && InNamespace(RD, "clang"); +} + +static bool IsClangDecl(const RecordDecl *RD) { + return RD->getName() == "Decl" && InNamespace(RD, "clang"); +} + +static bool IsClangStmt(const RecordDecl *RD) { + return RD->getName() == "Stmt" && InNamespace(RD, "clang"); +} + +static bool IsClangAttr(const RecordDecl *RD) { + return RD->getName() == "Attr" && InNamespace(RD, "clang"); +} + +static bool IsStdVector(QualType T) { + const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>(); + if (!TS) + return false; + + TemplateName TM = TS->getTemplateName(); + TemplateDecl *TD = TM.getAsTemplateDecl(); + + if (!TD || !InNamespace(TD, "std")) + return false; + + return TD->getName() == "vector"; +} + +static bool IsSmallVector(QualType T) { + const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>(); + if (!TS) + return false; + + TemplateName TM = TS->getTemplateName(); + TemplateDecl *TD = TM.getAsTemplateDecl(); + + if (!TD || !InNamespace(TD, "llvm")) + return false; + + return TD->getName() == "SmallVector"; +} + +//===----------------------------------------------------------------------===// +// CHECK: a StringRef should not be bound to a temporary std::string whose +// lifetime is shorter than the StringRef's. +//===----------------------------------------------------------------------===// + +namespace { +class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> { + const Decl *DeclWithIssue; + BugReporter &BR; + const CheckerBase *Checker; + +public: + StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br, + const CheckerBase *checker) + : DeclWithIssue(declWithIssue), BR(br), Checker(checker) {} + void VisitChildren(Stmt *S) { + for (Stmt *Child : S->children()) + if (Child) + Visit(Child); + } + void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitDeclStmt(DeclStmt *DS); +private: + void VisitVarDecl(VarDecl *VD); +}; +} // end anonymous namespace + +static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR, + const CheckerBase *Checker) { + StringRefCheckerVisitor walker(D, BR, Checker); + walker.Visit(D->getBody()); +} + +void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) { + VisitChildren(S); + + for (auto *I : S->decls()) + if (VarDecl *VD = dyn_cast<VarDecl>(I)) + VisitVarDecl(VD); +} + +void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { + Expr *Init = VD->getInit(); + if (!Init) + return; + + // Pattern match for: + // StringRef x = call() (where call returns std::string) + if (!IsLLVMStringRef(VD->getType())) + return; + ExprWithCleanups *Ex1 = dyn_cast<ExprWithCleanups>(Init); + if (!Ex1) + return; + CXXConstructExpr *Ex2 = dyn_cast<CXXConstructExpr>(Ex1->getSubExpr()); + if (!Ex2 || Ex2->getNumArgs() != 1) + return; + ImplicitCastExpr *Ex3 = dyn_cast<ImplicitCastExpr>(Ex2->getArg(0)); + if (!Ex3) + return; + CXXConstructExpr *Ex4 = dyn_cast<CXXConstructExpr>(Ex3->getSubExpr()); + if (!Ex4 || Ex4->getNumArgs() != 1) + return; + ImplicitCastExpr *Ex5 = dyn_cast<ImplicitCastExpr>(Ex4->getArg(0)); + if (!Ex5) + return; + CXXBindTemporaryExpr *Ex6 = dyn_cast<CXXBindTemporaryExpr>(Ex5->getSubExpr()); + if (!Ex6 || !IsStdString(Ex6->getType())) + return; + + // Okay, badness! Report an error. + const char *desc = "StringRef should not be bound to temporary " + "std::string that it outlives"; + PathDiagnosticLocation VDLoc = + PathDiagnosticLocation::createBegin(VD, BR.getSourceManager()); + BR.EmitBasicReport(DeclWithIssue, Checker, desc, "LLVM Conventions", desc, + VDLoc, Init->getSourceRange()); +} + +//===----------------------------------------------------------------------===// +// CHECK: Clang AST nodes should not have fields that can allocate +// memory. +//===----------------------------------------------------------------------===// + +static bool AllocatesMemory(QualType T) { + return IsStdVector(T) || IsStdString(T) || IsSmallVector(T); +} + +// This type checking could be sped up via dynamic programming. +static bool IsPartOfAST(const CXXRecordDecl *R) { + if (IsClangStmt(R) || IsClangType(R) || IsClangDecl(R) || IsClangAttr(R)) + return true; + + for (const auto &BS : R->bases()) { + QualType T = BS.getType(); + if (const RecordType *baseT = T->getAs<RecordType>()) { + CXXRecordDecl *baseD = cast<CXXRecordDecl>(baseT->getDecl()); + if (IsPartOfAST(baseD)) + return true; + } + } + + return false; +} + +namespace { +class ASTFieldVisitor { + SmallVector<FieldDecl*, 10> FieldChain; + const CXXRecordDecl *Root; + BugReporter &BR; + const CheckerBase *Checker; + +public: + ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br, + const CheckerBase *checker) + : Root(root), BR(br), Checker(checker) {} + + void Visit(FieldDecl *D); + void ReportError(QualType T); +}; +} // end anonymous namespace + +static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR, + const CheckerBase *Checker) { + if (!IsPartOfAST(R)) + return; + + for (auto *I : R->fields()) { + ASTFieldVisitor walker(R, BR, Checker); + walker.Visit(I); + } +} + +void ASTFieldVisitor::Visit(FieldDecl *D) { + FieldChain.push_back(D); + + QualType T = D->getType(); + + if (AllocatesMemory(T)) + ReportError(T); + + if (const RecordType *RT = T->getAs<RecordType>()) { + const RecordDecl *RD = RT->getDecl()->getDefinition(); + for (auto *I : RD->fields()) + Visit(I); + } + + FieldChain.pop_back(); +} + +void ASTFieldVisitor::ReportError(QualType T) { + SmallString<1024> buf; + llvm::raw_svector_ostream os(buf); + + os << "AST class '" << Root->getName() << "' has a field '" + << FieldChain.front()->getName() << "' that allocates heap memory"; + if (FieldChain.size() > 1) { + os << " via the following chain: "; + bool isFirst = true; + for (SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(), + E=FieldChain.end(); I!=E; ++I) { + if (!isFirst) + os << '.'; + else + isFirst = false; + os << (*I)->getName(); + } + } + os << " (type " << FieldChain.back()->getType().getAsString() << ")"; + + // Note that this will fire for every translation unit that uses this + // class. This is suboptimal, but at least scan-build will merge + // duplicate HTML reports. In the future we need a unified way of merging + // duplicate reports across translation units. For C++ classes we cannot + // just report warnings when we see an out-of-line method definition for a + // class, as that heuristic doesn't always work (the complete definition of + // the class may be in the header file, for example). + PathDiagnosticLocation L = PathDiagnosticLocation::createBegin( + FieldChain.front(), BR.getSourceManager()); + BR.EmitBasicReport(Root, Checker, "AST node allocates heap memory", + "LLVM Conventions", os.str(), L); +} + +//===----------------------------------------------------------------------===// +// LLVMConventionsChecker +//===----------------------------------------------------------------------===// + +namespace { +class LLVMConventionsChecker : public Checker< + check::ASTDecl<CXXRecordDecl>, + check::ASTCodeBody > { +public: + void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr, + BugReporter &BR) const { + if (R->isCompleteDefinition()) + CheckASTMemory(R, BR, this); + } + + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + CheckStringRefAssignedTemporary(D, BR, this); + } +}; +} + +void ento::registerLLVMConventionsChecker(CheckerManager &mgr) { + mgr.registerChecker<LLVMConventionsChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp new file mode 100644 index 000000000000..eda39efeca17 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp @@ -0,0 +1,1411 @@ +//=- LocalizationChecker.cpp -------------------------------------*- 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 a set of checks for localizability including: +// 1) A checker that warns about uses of non-localized NSStrings passed to +// UI methods expecting localized strings +// 2) A syntactic checker that warns against the bad practice of +// not including a comment in NSLocalizedString macros. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Attr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Lex/Lexer.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "llvm/Support/Unicode.h" + +using namespace clang; +using namespace ento; + +namespace { +struct LocalizedState { +private: + enum Kind { NonLocalized, Localized } K; + LocalizedState(Kind InK) : K(InK) {} + +public: + bool isLocalized() const { return K == Localized; } + bool isNonLocalized() const { return K == NonLocalized; } + + static LocalizedState getLocalized() { return LocalizedState(Localized); } + static LocalizedState getNonLocalized() { + return LocalizedState(NonLocalized); + } + + // Overload the == operator + bool operator==(const LocalizedState &X) const { return K == X.K; } + + // LLVMs equivalent of a hash function + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } +}; + +class NonLocalizedStringChecker + : public Checker<check::PreCall, check::PostCall, check::PreObjCMessage, + check::PostObjCMessage, + check::PostStmt<ObjCStringLiteral>> { + + mutable std::unique_ptr<BugType> BT; + + // Methods that require a localized string + mutable llvm::DenseMap<const IdentifierInfo *, + llvm::DenseMap<Selector, uint8_t>> UIMethods; + // Methods that return a localized string + mutable llvm::SmallSet<std::pair<const IdentifierInfo *, Selector>, 12> LSM; + // C Functions that return a localized string + mutable llvm::SmallSet<const IdentifierInfo *, 5> LSF; + + void initUIMethods(ASTContext &Ctx) const; + void initLocStringsMethods(ASTContext &Ctx) const; + + bool hasNonLocalizedState(SVal S, CheckerContext &C) const; + bool hasLocalizedState(SVal S, CheckerContext &C) const; + void setNonLocalizedState(SVal S, CheckerContext &C) const; + void setLocalizedState(SVal S, CheckerContext &C) const; + + bool isAnnotatedAsReturningLocalized(const Decl *D) const; + bool isAnnotatedAsTakingLocalized(const Decl *D) const; + void reportLocalizationError(SVal S, const CallEvent &M, CheckerContext &C, + int argumentNumber = 0) const; + + int getLocalizedArgumentForSelector(const IdentifierInfo *Receiver, + Selector S) const; + +public: + NonLocalizedStringChecker(); + + // When this parameter is set to true, the checker assumes all + // methods that return NSStrings are unlocalized. Thus, more false + // positives will be reported. + DefaultBool IsAggressive; + + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; + void checkPostStmt(const ObjCStringLiteral *SL, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; +}; + +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(LocalizedMemMap, const MemRegion *, + LocalizedState) + +NonLocalizedStringChecker::NonLocalizedStringChecker() { + BT.reset(new BugType(this, "Unlocalizable string", + "Localizability Issue (Apple)")); +} + +namespace { +class NonLocalizedStringBRVisitor final : public BugReporterVisitor { + + const MemRegion *NonLocalizedString; + bool Satisfied; + +public: + NonLocalizedStringBRVisitor(const MemRegion *NonLocalizedString) + : NonLocalizedString(NonLocalizedString), Satisfied(false) { + assert(NonLocalizedString); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + BugReport &BR) override; + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.Add(NonLocalizedString); + } +}; +} // End anonymous namespace. + +#define NEW_RECEIVER(receiver) \ + llvm::DenseMap<Selector, uint8_t> &receiver##M = \ + UIMethods.insert({&Ctx.Idents.get(#receiver), \ + llvm::DenseMap<Selector, uint8_t>()}) \ + .first->second; +#define ADD_NULLARY_METHOD(receiver, method, argument) \ + receiver##M.insert( \ + {Ctx.Selectors.getNullarySelector(&Ctx.Idents.get(#method)), argument}); +#define ADD_UNARY_METHOD(receiver, method, argument) \ + receiver##M.insert( \ + {Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(#method)), argument}); +#define ADD_METHOD(receiver, method_list, count, argument) \ + receiver##M.insert({Ctx.Selectors.getSelector(count, method_list), argument}); + +/// Initializes a list of methods that require a localized string +/// Format: {"ClassName", {{"selectorName:", LocStringArg#}, ...}, ...} +void NonLocalizedStringChecker::initUIMethods(ASTContext &Ctx) const { + if (!UIMethods.empty()) + return; + + // UI Methods + NEW_RECEIVER(UISearchDisplayController) + ADD_UNARY_METHOD(UISearchDisplayController, setSearchResultsTitle, 0) + + NEW_RECEIVER(UITabBarItem) + IdentifierInfo *initWithTitleUITabBarItemTag[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("image"), + &Ctx.Idents.get("tag")}; + ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemTag, 3, 0) + IdentifierInfo *initWithTitleUITabBarItemImage[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("image"), + &Ctx.Idents.get("selectedImage")}; + ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemImage, 3, 0) + + NEW_RECEIVER(NSDockTile) + ADD_UNARY_METHOD(NSDockTile, setBadgeLabel, 0) + + NEW_RECEIVER(NSStatusItem) + ADD_UNARY_METHOD(NSStatusItem, setTitle, 0) + ADD_UNARY_METHOD(NSStatusItem, setToolTip, 0) + + NEW_RECEIVER(UITableViewRowAction) + IdentifierInfo *rowActionWithStyleUITableViewRowAction[] = { + &Ctx.Idents.get("rowActionWithStyle"), &Ctx.Idents.get("title"), + &Ctx.Idents.get("handler")}; + ADD_METHOD(UITableViewRowAction, rowActionWithStyleUITableViewRowAction, 3, 1) + ADD_UNARY_METHOD(UITableViewRowAction, setTitle, 0) + + NEW_RECEIVER(NSBox) + ADD_UNARY_METHOD(NSBox, setTitle, 0) + + NEW_RECEIVER(NSButton) + ADD_UNARY_METHOD(NSButton, setTitle, 0) + ADD_UNARY_METHOD(NSButton, setAlternateTitle, 0) + IdentifierInfo *radioButtonWithTitleNSButton[] = { + &Ctx.Idents.get("radioButtonWithTitle"), &Ctx.Idents.get("target"), + &Ctx.Idents.get("action")}; + ADD_METHOD(NSButton, radioButtonWithTitleNSButton, 3, 0) + IdentifierInfo *buttonWithTitleNSButtonImage[] = { + &Ctx.Idents.get("buttonWithTitle"), &Ctx.Idents.get("image"), + &Ctx.Idents.get("target"), &Ctx.Idents.get("action")}; + ADD_METHOD(NSButton, buttonWithTitleNSButtonImage, 4, 0) + IdentifierInfo *checkboxWithTitleNSButton[] = { + &Ctx.Idents.get("checkboxWithTitle"), &Ctx.Idents.get("target"), + &Ctx.Idents.get("action")}; + ADD_METHOD(NSButton, checkboxWithTitleNSButton, 3, 0) + IdentifierInfo *buttonWithTitleNSButtonTarget[] = { + &Ctx.Idents.get("buttonWithTitle"), &Ctx.Idents.get("target"), + &Ctx.Idents.get("action")}; + ADD_METHOD(NSButton, buttonWithTitleNSButtonTarget, 3, 0) + + NEW_RECEIVER(NSSavePanel) + ADD_UNARY_METHOD(NSSavePanel, setPrompt, 0) + ADD_UNARY_METHOD(NSSavePanel, setTitle, 0) + ADD_UNARY_METHOD(NSSavePanel, setNameFieldLabel, 0) + ADD_UNARY_METHOD(NSSavePanel, setNameFieldStringValue, 0) + ADD_UNARY_METHOD(NSSavePanel, setMessage, 0) + + NEW_RECEIVER(UIPrintInfo) + ADD_UNARY_METHOD(UIPrintInfo, setJobName, 0) + + NEW_RECEIVER(NSTabViewItem) + ADD_UNARY_METHOD(NSTabViewItem, setLabel, 0) + ADD_UNARY_METHOD(NSTabViewItem, setToolTip, 0) + + NEW_RECEIVER(NSBrowser) + IdentifierInfo *setTitleNSBrowser[] = {&Ctx.Idents.get("setTitle"), + &Ctx.Idents.get("ofColumn")}; + ADD_METHOD(NSBrowser, setTitleNSBrowser, 2, 0) + + NEW_RECEIVER(UIAccessibilityElement) + ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityLabel, 0) + ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityHint, 0) + ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityValue, 0) + + NEW_RECEIVER(UIAlertAction) + IdentifierInfo *actionWithTitleUIAlertAction[] = { + &Ctx.Idents.get("actionWithTitle"), &Ctx.Idents.get("style"), + &Ctx.Idents.get("handler")}; + ADD_METHOD(UIAlertAction, actionWithTitleUIAlertAction, 3, 0) + + NEW_RECEIVER(NSPopUpButton) + ADD_UNARY_METHOD(NSPopUpButton, addItemWithTitle, 0) + IdentifierInfo *insertItemWithTitleNSPopUpButton[] = { + &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("atIndex")}; + ADD_METHOD(NSPopUpButton, insertItemWithTitleNSPopUpButton, 2, 0) + ADD_UNARY_METHOD(NSPopUpButton, removeItemWithTitle, 0) + ADD_UNARY_METHOD(NSPopUpButton, selectItemWithTitle, 0) + ADD_UNARY_METHOD(NSPopUpButton, setTitle, 0) + + NEW_RECEIVER(NSTableViewRowAction) + IdentifierInfo *rowActionWithStyleNSTableViewRowAction[] = { + &Ctx.Idents.get("rowActionWithStyle"), &Ctx.Idents.get("title"), + &Ctx.Idents.get("handler")}; + ADD_METHOD(NSTableViewRowAction, rowActionWithStyleNSTableViewRowAction, 3, 1) + ADD_UNARY_METHOD(NSTableViewRowAction, setTitle, 0) + + NEW_RECEIVER(NSImage) + ADD_UNARY_METHOD(NSImage, setAccessibilityDescription, 0) + + NEW_RECEIVER(NSUserActivity) + ADD_UNARY_METHOD(NSUserActivity, setTitle, 0) + + NEW_RECEIVER(NSPathControlItem) + ADD_UNARY_METHOD(NSPathControlItem, setTitle, 0) + + NEW_RECEIVER(NSCell) + ADD_UNARY_METHOD(NSCell, initTextCell, 0) + ADD_UNARY_METHOD(NSCell, setTitle, 0) + ADD_UNARY_METHOD(NSCell, setStringValue, 0) + + NEW_RECEIVER(NSPathControl) + ADD_UNARY_METHOD(NSPathControl, setPlaceholderString, 0) + + NEW_RECEIVER(UIAccessibility) + ADD_UNARY_METHOD(UIAccessibility, setAccessibilityLabel, 0) + ADD_UNARY_METHOD(UIAccessibility, setAccessibilityHint, 0) + ADD_UNARY_METHOD(UIAccessibility, setAccessibilityValue, 0) + + NEW_RECEIVER(NSTableColumn) + ADD_UNARY_METHOD(NSTableColumn, setTitle, 0) + ADD_UNARY_METHOD(NSTableColumn, setHeaderToolTip, 0) + + NEW_RECEIVER(NSSegmentedControl) + IdentifierInfo *setLabelNSSegmentedControl[] = { + &Ctx.Idents.get("setLabel"), &Ctx.Idents.get("forSegment")}; + ADD_METHOD(NSSegmentedControl, setLabelNSSegmentedControl, 2, 0) + IdentifierInfo *setToolTipNSSegmentedControl[] = { + &Ctx.Idents.get("setToolTip"), &Ctx.Idents.get("forSegment")}; + ADD_METHOD(NSSegmentedControl, setToolTipNSSegmentedControl, 2, 0) + + NEW_RECEIVER(NSButtonCell) + ADD_UNARY_METHOD(NSButtonCell, setTitle, 0) + ADD_UNARY_METHOD(NSButtonCell, setAlternateTitle, 0) + + NEW_RECEIVER(NSDatePickerCell) + ADD_UNARY_METHOD(NSDatePickerCell, initTextCell, 0) + + NEW_RECEIVER(NSSliderCell) + ADD_UNARY_METHOD(NSSliderCell, setTitle, 0) + + NEW_RECEIVER(NSControl) + ADD_UNARY_METHOD(NSControl, setStringValue, 0) + + NEW_RECEIVER(NSAccessibility) + ADD_UNARY_METHOD(NSAccessibility, setAccessibilityValueDescription, 0) + ADD_UNARY_METHOD(NSAccessibility, setAccessibilityLabel, 0) + ADD_UNARY_METHOD(NSAccessibility, setAccessibilityTitle, 0) + ADD_UNARY_METHOD(NSAccessibility, setAccessibilityPlaceholderValue, 0) + ADD_UNARY_METHOD(NSAccessibility, setAccessibilityHelp, 0) + + NEW_RECEIVER(NSMatrix) + IdentifierInfo *setToolTipNSMatrix[] = {&Ctx.Idents.get("setToolTip"), + &Ctx.Idents.get("forCell")}; + ADD_METHOD(NSMatrix, setToolTipNSMatrix, 2, 0) + + NEW_RECEIVER(NSPrintPanel) + ADD_UNARY_METHOD(NSPrintPanel, setDefaultButtonTitle, 0) + + NEW_RECEIVER(UILocalNotification) + ADD_UNARY_METHOD(UILocalNotification, setAlertBody, 0) + ADD_UNARY_METHOD(UILocalNotification, setAlertAction, 0) + ADD_UNARY_METHOD(UILocalNotification, setAlertTitle, 0) + + NEW_RECEIVER(NSSlider) + ADD_UNARY_METHOD(NSSlider, setTitle, 0) + + NEW_RECEIVER(UIMenuItem) + IdentifierInfo *initWithTitleUIMenuItem[] = {&Ctx.Idents.get("initWithTitle"), + &Ctx.Idents.get("action")}; + ADD_METHOD(UIMenuItem, initWithTitleUIMenuItem, 2, 0) + ADD_UNARY_METHOD(UIMenuItem, setTitle, 0) + + NEW_RECEIVER(UIAlertController) + IdentifierInfo *alertControllerWithTitleUIAlertController[] = { + &Ctx.Idents.get("alertControllerWithTitle"), &Ctx.Idents.get("message"), + &Ctx.Idents.get("preferredStyle")}; + ADD_METHOD(UIAlertController, alertControllerWithTitleUIAlertController, 3, 1) + ADD_UNARY_METHOD(UIAlertController, setTitle, 0) + ADD_UNARY_METHOD(UIAlertController, setMessage, 0) + + NEW_RECEIVER(UIApplicationShortcutItem) + IdentifierInfo *initWithTypeUIApplicationShortcutItemIcon[] = { + &Ctx.Idents.get("initWithType"), &Ctx.Idents.get("localizedTitle"), + &Ctx.Idents.get("localizedSubtitle"), &Ctx.Idents.get("icon"), + &Ctx.Idents.get("userInfo")}; + ADD_METHOD(UIApplicationShortcutItem, + initWithTypeUIApplicationShortcutItemIcon, 5, 1) + IdentifierInfo *initWithTypeUIApplicationShortcutItem[] = { + &Ctx.Idents.get("initWithType"), &Ctx.Idents.get("localizedTitle")}; + ADD_METHOD(UIApplicationShortcutItem, initWithTypeUIApplicationShortcutItem, + 2, 1) + + NEW_RECEIVER(UIActionSheet) + IdentifierInfo *initWithTitleUIActionSheet[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("delegate"), + &Ctx.Idents.get("cancelButtonTitle"), + &Ctx.Idents.get("destructiveButtonTitle"), + &Ctx.Idents.get("otherButtonTitles")}; + ADD_METHOD(UIActionSheet, initWithTitleUIActionSheet, 5, 0) + ADD_UNARY_METHOD(UIActionSheet, addButtonWithTitle, 0) + ADD_UNARY_METHOD(UIActionSheet, setTitle, 0) + + NEW_RECEIVER(UIAccessibilityCustomAction) + IdentifierInfo *initWithNameUIAccessibilityCustomAction[] = { + &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("target"), + &Ctx.Idents.get("selector")}; + ADD_METHOD(UIAccessibilityCustomAction, + initWithNameUIAccessibilityCustomAction, 3, 0) + ADD_UNARY_METHOD(UIAccessibilityCustomAction, setName, 0) + + NEW_RECEIVER(UISearchBar) + ADD_UNARY_METHOD(UISearchBar, setText, 0) + ADD_UNARY_METHOD(UISearchBar, setPrompt, 0) + ADD_UNARY_METHOD(UISearchBar, setPlaceholder, 0) + + NEW_RECEIVER(UIBarItem) + ADD_UNARY_METHOD(UIBarItem, setTitle, 0) + + NEW_RECEIVER(UITextView) + ADD_UNARY_METHOD(UITextView, setText, 0) + + NEW_RECEIVER(NSView) + ADD_UNARY_METHOD(NSView, setToolTip, 0) + + NEW_RECEIVER(NSTextField) + ADD_UNARY_METHOD(NSTextField, setPlaceholderString, 0) + ADD_UNARY_METHOD(NSTextField, textFieldWithString, 0) + ADD_UNARY_METHOD(NSTextField, wrappingLabelWithString, 0) + ADD_UNARY_METHOD(NSTextField, labelWithString, 0) + + NEW_RECEIVER(NSAttributedString) + ADD_UNARY_METHOD(NSAttributedString, initWithString, 0) + IdentifierInfo *initWithStringNSAttributedString[] = { + &Ctx.Idents.get("initWithString"), &Ctx.Idents.get("attributes")}; + ADD_METHOD(NSAttributedString, initWithStringNSAttributedString, 2, 0) + + NEW_RECEIVER(NSText) + ADD_UNARY_METHOD(NSText, setString, 0) + + NEW_RECEIVER(UIKeyCommand) + IdentifierInfo *keyCommandWithInputUIKeyCommand[] = { + &Ctx.Idents.get("keyCommandWithInput"), &Ctx.Idents.get("modifierFlags"), + &Ctx.Idents.get("action"), &Ctx.Idents.get("discoverabilityTitle")}; + ADD_METHOD(UIKeyCommand, keyCommandWithInputUIKeyCommand, 4, 3) + ADD_UNARY_METHOD(UIKeyCommand, setDiscoverabilityTitle, 0) + + NEW_RECEIVER(UILabel) + ADD_UNARY_METHOD(UILabel, setText, 0) + + NEW_RECEIVER(NSAlert) + IdentifierInfo *alertWithMessageTextNSAlert[] = { + &Ctx.Idents.get("alertWithMessageText"), &Ctx.Idents.get("defaultButton"), + &Ctx.Idents.get("alternateButton"), &Ctx.Idents.get("otherButton"), + &Ctx.Idents.get("informativeTextWithFormat")}; + ADD_METHOD(NSAlert, alertWithMessageTextNSAlert, 5, 0) + ADD_UNARY_METHOD(NSAlert, addButtonWithTitle, 0) + ADD_UNARY_METHOD(NSAlert, setMessageText, 0) + ADD_UNARY_METHOD(NSAlert, setInformativeText, 0) + ADD_UNARY_METHOD(NSAlert, setHelpAnchor, 0) + + NEW_RECEIVER(UIMutableApplicationShortcutItem) + ADD_UNARY_METHOD(UIMutableApplicationShortcutItem, setLocalizedTitle, 0) + ADD_UNARY_METHOD(UIMutableApplicationShortcutItem, setLocalizedSubtitle, 0) + + NEW_RECEIVER(UIButton) + IdentifierInfo *setTitleUIButton[] = {&Ctx.Idents.get("setTitle"), + &Ctx.Idents.get("forState")}; + ADD_METHOD(UIButton, setTitleUIButton, 2, 0) + + NEW_RECEIVER(NSWindow) + ADD_UNARY_METHOD(NSWindow, setTitle, 0) + IdentifierInfo *minFrameWidthWithTitleNSWindow[] = { + &Ctx.Idents.get("minFrameWidthWithTitle"), &Ctx.Idents.get("styleMask")}; + ADD_METHOD(NSWindow, minFrameWidthWithTitleNSWindow, 2, 0) + ADD_UNARY_METHOD(NSWindow, setMiniwindowTitle, 0) + + NEW_RECEIVER(NSPathCell) + ADD_UNARY_METHOD(NSPathCell, setPlaceholderString, 0) + + NEW_RECEIVER(UIDocumentMenuViewController) + IdentifierInfo *addOptionWithTitleUIDocumentMenuViewController[] = { + &Ctx.Idents.get("addOptionWithTitle"), &Ctx.Idents.get("image"), + &Ctx.Idents.get("order"), &Ctx.Idents.get("handler")}; + ADD_METHOD(UIDocumentMenuViewController, + addOptionWithTitleUIDocumentMenuViewController, 4, 0) + + NEW_RECEIVER(UINavigationItem) + ADD_UNARY_METHOD(UINavigationItem, initWithTitle, 0) + ADD_UNARY_METHOD(UINavigationItem, setTitle, 0) + ADD_UNARY_METHOD(UINavigationItem, setPrompt, 0) + + NEW_RECEIVER(UIAlertView) + IdentifierInfo *initWithTitleUIAlertView[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("message"), + &Ctx.Idents.get("delegate"), &Ctx.Idents.get("cancelButtonTitle"), + &Ctx.Idents.get("otherButtonTitles")}; + ADD_METHOD(UIAlertView, initWithTitleUIAlertView, 5, 0) + ADD_UNARY_METHOD(UIAlertView, addButtonWithTitle, 0) + ADD_UNARY_METHOD(UIAlertView, setTitle, 0) + ADD_UNARY_METHOD(UIAlertView, setMessage, 0) + + NEW_RECEIVER(NSFormCell) + ADD_UNARY_METHOD(NSFormCell, initTextCell, 0) + ADD_UNARY_METHOD(NSFormCell, setTitle, 0) + ADD_UNARY_METHOD(NSFormCell, setPlaceholderString, 0) + + NEW_RECEIVER(NSUserNotification) + ADD_UNARY_METHOD(NSUserNotification, setTitle, 0) + ADD_UNARY_METHOD(NSUserNotification, setSubtitle, 0) + ADD_UNARY_METHOD(NSUserNotification, setInformativeText, 0) + ADD_UNARY_METHOD(NSUserNotification, setActionButtonTitle, 0) + ADD_UNARY_METHOD(NSUserNotification, setOtherButtonTitle, 0) + ADD_UNARY_METHOD(NSUserNotification, setResponsePlaceholder, 0) + + NEW_RECEIVER(NSToolbarItem) + ADD_UNARY_METHOD(NSToolbarItem, setLabel, 0) + ADD_UNARY_METHOD(NSToolbarItem, setPaletteLabel, 0) + ADD_UNARY_METHOD(NSToolbarItem, setToolTip, 0) + + NEW_RECEIVER(NSProgress) + ADD_UNARY_METHOD(NSProgress, setLocalizedDescription, 0) + ADD_UNARY_METHOD(NSProgress, setLocalizedAdditionalDescription, 0) + + NEW_RECEIVER(NSSegmentedCell) + IdentifierInfo *setLabelNSSegmentedCell[] = {&Ctx.Idents.get("setLabel"), + &Ctx.Idents.get("forSegment")}; + ADD_METHOD(NSSegmentedCell, setLabelNSSegmentedCell, 2, 0) + IdentifierInfo *setToolTipNSSegmentedCell[] = {&Ctx.Idents.get("setToolTip"), + &Ctx.Idents.get("forSegment")}; + ADD_METHOD(NSSegmentedCell, setToolTipNSSegmentedCell, 2, 0) + + NEW_RECEIVER(NSUndoManager) + ADD_UNARY_METHOD(NSUndoManager, setActionName, 0) + ADD_UNARY_METHOD(NSUndoManager, undoMenuTitleForUndoActionName, 0) + ADD_UNARY_METHOD(NSUndoManager, redoMenuTitleForUndoActionName, 0) + + NEW_RECEIVER(NSMenuItem) + IdentifierInfo *initWithTitleNSMenuItem[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("action"), + &Ctx.Idents.get("keyEquivalent")}; + ADD_METHOD(NSMenuItem, initWithTitleNSMenuItem, 3, 0) + ADD_UNARY_METHOD(NSMenuItem, setTitle, 0) + ADD_UNARY_METHOD(NSMenuItem, setToolTip, 0) + + NEW_RECEIVER(NSPopUpButtonCell) + IdentifierInfo *initTextCellNSPopUpButtonCell[] = { + &Ctx.Idents.get("initTextCell"), &Ctx.Idents.get("pullsDown")}; + ADD_METHOD(NSPopUpButtonCell, initTextCellNSPopUpButtonCell, 2, 0) + ADD_UNARY_METHOD(NSPopUpButtonCell, addItemWithTitle, 0) + IdentifierInfo *insertItemWithTitleNSPopUpButtonCell[] = { + &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("atIndex")}; + ADD_METHOD(NSPopUpButtonCell, insertItemWithTitleNSPopUpButtonCell, 2, 0) + ADD_UNARY_METHOD(NSPopUpButtonCell, removeItemWithTitle, 0) + ADD_UNARY_METHOD(NSPopUpButtonCell, selectItemWithTitle, 0) + ADD_UNARY_METHOD(NSPopUpButtonCell, setTitle, 0) + + NEW_RECEIVER(NSViewController) + ADD_UNARY_METHOD(NSViewController, setTitle, 0) + + NEW_RECEIVER(NSMenu) + ADD_UNARY_METHOD(NSMenu, initWithTitle, 0) + IdentifierInfo *insertItemWithTitleNSMenu[] = { + &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("action"), + &Ctx.Idents.get("keyEquivalent"), &Ctx.Idents.get("atIndex")}; + ADD_METHOD(NSMenu, insertItemWithTitleNSMenu, 4, 0) + IdentifierInfo *addItemWithTitleNSMenu[] = { + &Ctx.Idents.get("addItemWithTitle"), &Ctx.Idents.get("action"), + &Ctx.Idents.get("keyEquivalent")}; + ADD_METHOD(NSMenu, addItemWithTitleNSMenu, 3, 0) + ADD_UNARY_METHOD(NSMenu, setTitle, 0) + + NEW_RECEIVER(UIMutableUserNotificationAction) + ADD_UNARY_METHOD(UIMutableUserNotificationAction, setTitle, 0) + + NEW_RECEIVER(NSForm) + ADD_UNARY_METHOD(NSForm, addEntry, 0) + IdentifierInfo *insertEntryNSForm[] = {&Ctx.Idents.get("insertEntry"), + &Ctx.Idents.get("atIndex")}; + ADD_METHOD(NSForm, insertEntryNSForm, 2, 0) + + NEW_RECEIVER(NSTextFieldCell) + ADD_UNARY_METHOD(NSTextFieldCell, setPlaceholderString, 0) + + NEW_RECEIVER(NSUserNotificationAction) + IdentifierInfo *actionWithIdentifierNSUserNotificationAction[] = { + &Ctx.Idents.get("actionWithIdentifier"), &Ctx.Idents.get("title")}; + ADD_METHOD(NSUserNotificationAction, + actionWithIdentifierNSUserNotificationAction, 2, 1) + + NEW_RECEIVER(UITextField) + ADD_UNARY_METHOD(UITextField, setText, 0) + ADD_UNARY_METHOD(UITextField, setPlaceholder, 0) + + NEW_RECEIVER(UIBarButtonItem) + IdentifierInfo *initWithTitleUIBarButtonItem[] = { + &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("style"), + &Ctx.Idents.get("target"), &Ctx.Idents.get("action")}; + ADD_METHOD(UIBarButtonItem, initWithTitleUIBarButtonItem, 4, 0) + + NEW_RECEIVER(UIViewController) + ADD_UNARY_METHOD(UIViewController, setTitle, 0) + + NEW_RECEIVER(UISegmentedControl) + IdentifierInfo *insertSegmentWithTitleUISegmentedControl[] = { + &Ctx.Idents.get("insertSegmentWithTitle"), &Ctx.Idents.get("atIndex"), + &Ctx.Idents.get("animated")}; + ADD_METHOD(UISegmentedControl, insertSegmentWithTitleUISegmentedControl, 3, 0) + IdentifierInfo *setTitleUISegmentedControl[] = { + &Ctx.Idents.get("setTitle"), &Ctx.Idents.get("forSegmentAtIndex")}; + ADD_METHOD(UISegmentedControl, setTitleUISegmentedControl, 2, 0) + + NEW_RECEIVER(NSAccessibilityCustomRotorItemResult) + IdentifierInfo + *initWithItemLoadingTokenNSAccessibilityCustomRotorItemResult[] = { + &Ctx.Idents.get("initWithItemLoadingToken"), + &Ctx.Idents.get("customLabel")}; + ADD_METHOD(NSAccessibilityCustomRotorItemResult, + initWithItemLoadingTokenNSAccessibilityCustomRotorItemResult, 2, 1) + ADD_UNARY_METHOD(NSAccessibilityCustomRotorItemResult, setCustomLabel, 0) + + NEW_RECEIVER(UIContextualAction) + IdentifierInfo *contextualActionWithStyleUIContextualAction[] = { + &Ctx.Idents.get("contextualActionWithStyle"), &Ctx.Idents.get("title"), + &Ctx.Idents.get("handler")}; + ADD_METHOD(UIContextualAction, contextualActionWithStyleUIContextualAction, 3, + 1) + ADD_UNARY_METHOD(UIContextualAction, setTitle, 0) + + NEW_RECEIVER(NSAccessibilityCustomRotor) + IdentifierInfo *initWithLabelNSAccessibilityCustomRotor[] = { + &Ctx.Idents.get("initWithLabel"), &Ctx.Idents.get("itemSearchDelegate")}; + ADD_METHOD(NSAccessibilityCustomRotor, + initWithLabelNSAccessibilityCustomRotor, 2, 0) + ADD_UNARY_METHOD(NSAccessibilityCustomRotor, setLabel, 0) + + NEW_RECEIVER(NSWindowTab) + ADD_UNARY_METHOD(NSWindowTab, setTitle, 0) + ADD_UNARY_METHOD(NSWindowTab, setToolTip, 0) + + NEW_RECEIVER(NSAccessibilityCustomAction) + IdentifierInfo *initWithNameNSAccessibilityCustomAction[] = { + &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("handler")}; + ADD_METHOD(NSAccessibilityCustomAction, + initWithNameNSAccessibilityCustomAction, 2, 0) + IdentifierInfo *initWithNameTargetNSAccessibilityCustomAction[] = { + &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("target"), + &Ctx.Idents.get("selector")}; + ADD_METHOD(NSAccessibilityCustomAction, + initWithNameTargetNSAccessibilityCustomAction, 3, 0) + ADD_UNARY_METHOD(NSAccessibilityCustomAction, setName, 0) +} + +#define LSF_INSERT(function_name) LSF.insert(&Ctx.Idents.get(function_name)); +#define LSM_INSERT_NULLARY(receiver, method_name) \ + LSM.insert({&Ctx.Idents.get(receiver), Ctx.Selectors.getNullarySelector( \ + &Ctx.Idents.get(method_name))}); +#define LSM_INSERT_UNARY(receiver, method_name) \ + LSM.insert({&Ctx.Idents.get(receiver), \ + Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(method_name))}); +#define LSM_INSERT_SELECTOR(receiver, method_list, arguments) \ + LSM.insert({&Ctx.Idents.get(receiver), \ + Ctx.Selectors.getSelector(arguments, method_list)}); + +/// Initializes a list of methods and C functions that return a localized string +void NonLocalizedStringChecker::initLocStringsMethods(ASTContext &Ctx) const { + if (!LSM.empty()) + return; + + IdentifierInfo *LocalizedStringMacro[] = { + &Ctx.Idents.get("localizedStringForKey"), &Ctx.Idents.get("value"), + &Ctx.Idents.get("table")}; + LSM_INSERT_SELECTOR("NSBundle", LocalizedStringMacro, 3) + LSM_INSERT_UNARY("NSDateFormatter", "stringFromDate") + IdentifierInfo *LocalizedStringFromDate[] = { + &Ctx.Idents.get("localizedStringFromDate"), &Ctx.Idents.get("dateStyle"), + &Ctx.Idents.get("timeStyle")}; + LSM_INSERT_SELECTOR("NSDateFormatter", LocalizedStringFromDate, 3) + LSM_INSERT_UNARY("NSNumberFormatter", "stringFromNumber") + LSM_INSERT_NULLARY("UITextField", "text") + LSM_INSERT_NULLARY("UITextView", "text") + LSM_INSERT_NULLARY("UILabel", "text") + + LSF_INSERT("CFDateFormatterCreateStringWithDate"); + LSF_INSERT("CFDateFormatterCreateStringWithAbsoluteTime"); + LSF_INSERT("CFNumberFormatterCreateStringWithNumber"); +} + +/// Checks to see if the method / function declaration includes +/// __attribute__((annotate("returns_localized_nsstring"))) +bool NonLocalizedStringChecker::isAnnotatedAsReturningLocalized( + const Decl *D) const { + if (!D) + return false; + return std::any_of( + D->specific_attr_begin<AnnotateAttr>(), + D->specific_attr_end<AnnotateAttr>(), [](const AnnotateAttr *Ann) { + return Ann->getAnnotation() == "returns_localized_nsstring"; + }); +} + +/// Checks to see if the method / function declaration includes +/// __attribute__((annotate("takes_localized_nsstring"))) +bool NonLocalizedStringChecker::isAnnotatedAsTakingLocalized( + const Decl *D) const { + if (!D) + return false; + return std::any_of( + D->specific_attr_begin<AnnotateAttr>(), + D->specific_attr_end<AnnotateAttr>(), [](const AnnotateAttr *Ann) { + return Ann->getAnnotation() == "takes_localized_nsstring"; + }); +} + +/// Returns true if the given SVal is marked as Localized in the program state +bool NonLocalizedStringChecker::hasLocalizedState(SVal S, + CheckerContext &C) const { + const MemRegion *mt = S.getAsRegion(); + if (mt) { + const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt); + if (LS && LS->isLocalized()) + return true; + } + return false; +} + +/// Returns true if the given SVal is marked as NonLocalized in the program +/// state +bool NonLocalizedStringChecker::hasNonLocalizedState(SVal S, + CheckerContext &C) const { + const MemRegion *mt = S.getAsRegion(); + if (mt) { + const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt); + if (LS && LS->isNonLocalized()) + return true; + } + return false; +} + +/// Marks the given SVal as Localized in the program state +void NonLocalizedStringChecker::setLocalizedState(const SVal S, + CheckerContext &C) const { + const MemRegion *mt = S.getAsRegion(); + if (mt) { + ProgramStateRef State = + C.getState()->set<LocalizedMemMap>(mt, LocalizedState::getLocalized()); + C.addTransition(State); + } +} + +/// Marks the given SVal as NonLocalized in the program state +void NonLocalizedStringChecker::setNonLocalizedState(const SVal S, + CheckerContext &C) const { + const MemRegion *mt = S.getAsRegion(); + if (mt) { + ProgramStateRef State = C.getState()->set<LocalizedMemMap>( + mt, LocalizedState::getNonLocalized()); + C.addTransition(State); + } +} + + +static bool isDebuggingName(std::string name) { + return StringRef(name).lower().find("debug") != StringRef::npos; +} + +/// Returns true when, heuristically, the analyzer may be analyzing debugging +/// code. We use this to suppress localization diagnostics in un-localized user +/// interfaces that are only used for debugging and are therefore not user +/// facing. +static bool isDebuggingContext(CheckerContext &C) { + const Decl *D = C.getCurrentAnalysisDeclContext()->getDecl(); + if (!D) + return false; + + if (auto *ND = dyn_cast<NamedDecl>(D)) { + if (isDebuggingName(ND->getNameAsString())) + return true; + } + + const DeclContext *DC = D->getDeclContext(); + + if (auto *CD = dyn_cast<ObjCContainerDecl>(DC)) { + if (isDebuggingName(CD->getNameAsString())) + return true; + } + + return false; +} + + +/// Reports a localization error for the passed in method call and SVal +void NonLocalizedStringChecker::reportLocalizationError( + SVal S, const CallEvent &M, CheckerContext &C, int argumentNumber) const { + + // Don't warn about localization errors in classes and methods that + // may be debug code. + if (isDebuggingContext(C)) + return; + + ExplodedNode *ErrNode = C.getPredecessor(); + static CheckerProgramPointTag Tag("NonLocalizedStringChecker", + "UnlocalizedString"); + ErrNode = C.addTransition(C.getState(), C.getPredecessor(), &Tag); + + if (!ErrNode) + return; + + // Generate the bug report. + std::unique_ptr<BugReport> R(new BugReport( + *BT, "User-facing text should use localized string macro", ErrNode)); + if (argumentNumber) { + R->addRange(M.getArgExpr(argumentNumber - 1)->getSourceRange()); + } else { + R->addRange(M.getSourceRange()); + } + R->markInteresting(S); + + const MemRegion *StringRegion = S.getAsRegion(); + if (StringRegion) + R->addVisitor(llvm::make_unique<NonLocalizedStringBRVisitor>(StringRegion)); + + C.emitReport(std::move(R)); +} + +/// Returns the argument number requiring localized string if it exists +/// otherwise, returns -1 +int NonLocalizedStringChecker::getLocalizedArgumentForSelector( + const IdentifierInfo *Receiver, Selector S) const { + auto method = UIMethods.find(Receiver); + + if (method == UIMethods.end()) + return -1; + + auto argumentIterator = method->getSecond().find(S); + + if (argumentIterator == method->getSecond().end()) + return -1; + + int argumentNumber = argumentIterator->getSecond(); + return argumentNumber; +} + +/// Check if the string being passed in has NonLocalized state +void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg, + CheckerContext &C) const { + initUIMethods(C.getASTContext()); + + const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); + if (!OD) + return; + const IdentifierInfo *odInfo = OD->getIdentifier(); + + Selector S = msg.getSelector(); + + std::string SelectorString = S.getAsString(); + StringRef SelectorName = SelectorString; + assert(!SelectorName.empty()); + + if (odInfo->isStr("NSString")) { + // Handle the case where the receiver is an NSString + // These special NSString methods draw to the screen + + if (!(SelectorName.startswith("drawAtPoint") || + SelectorName.startswith("drawInRect") || + SelectorName.startswith("drawWithRect"))) + return; + + SVal svTitle = msg.getReceiverSVal(); + + bool isNonLocalized = hasNonLocalizedState(svTitle, C); + + if (isNonLocalized) { + reportLocalizationError(svTitle, msg, C); + } + } + + int argumentNumber = getLocalizedArgumentForSelector(odInfo, S); + // Go up each hierarchy of superclasses and their protocols + while (argumentNumber < 0 && OD->getSuperClass() != nullptr) { + for (const auto *P : OD->all_referenced_protocols()) { + argumentNumber = getLocalizedArgumentForSelector(P->getIdentifier(), S); + if (argumentNumber >= 0) + break; + } + if (argumentNumber < 0) { + OD = OD->getSuperClass(); + argumentNumber = getLocalizedArgumentForSelector(OD->getIdentifier(), S); + } + } + + if (argumentNumber < 0) { // There was no match in UIMethods + if (const Decl *D = msg.getDecl()) { + if (const ObjCMethodDecl *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) { + auto formals = OMD->parameters(); + for (unsigned i = 0, ei = formals.size(); i != ei; ++i) { + if (isAnnotatedAsTakingLocalized(formals[i])) { + argumentNumber = i; + break; + } + } + } + } + } + + if (argumentNumber < 0) // Still no match + return; + + SVal svTitle = msg.getArgSVal(argumentNumber); + + if (const ObjCStringRegion *SR = + dyn_cast_or_null<ObjCStringRegion>(svTitle.getAsRegion())) { + StringRef stringValue = + SR->getObjCStringLiteral()->getString()->getString(); + if ((stringValue.trim().size() == 0 && stringValue.size() > 0) || + stringValue.empty()) + return; + if (!IsAggressive && llvm::sys::unicode::columnWidthUTF8(stringValue) < 2) + return; + } + + bool isNonLocalized = hasNonLocalizedState(svTitle, C); + + if (isNonLocalized) { + reportLocalizationError(svTitle, msg, C, argumentNumber + 1); + } +} + +void NonLocalizedStringChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const Decl *D = Call.getDecl(); + if (D && isa<FunctionDecl>(D)) { + const FunctionDecl *FD = dyn_cast<FunctionDecl>(D); + auto formals = FD->parameters(); + for (unsigned i = 0, + ei = std::min(unsigned(formals.size()), Call.getNumArgs()); + i != ei; ++i) { + if (isAnnotatedAsTakingLocalized(formals[i])) { + auto actual = Call.getArgSVal(i); + if (hasNonLocalizedState(actual, C)) { + reportLocalizationError(actual, Call, C, i + 1); + } + } + } + } +} + +static inline bool isNSStringType(QualType T, ASTContext &Ctx) { + + const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>(); + if (!PT) + return false; + + ObjCInterfaceDecl *Cls = PT->getObjectType()->getInterface(); + if (!Cls) + return false; + + IdentifierInfo *ClsName = Cls->getIdentifier(); + + // FIXME: Should we walk the chain of classes? + return ClsName == &Ctx.Idents.get("NSString") || + ClsName == &Ctx.Idents.get("NSMutableString"); +} + +/// Marks a string being returned by any call as localized +/// if it is in LocStringFunctions (LSF) or the function is annotated. +/// Otherwise, we mark it as NonLocalized (Aggressive) or +/// NonLocalized only if it is not backed by a SymRegion (Non-Aggressive), +/// basically leaving only string literals as NonLocalized. +void NonLocalizedStringChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + initLocStringsMethods(C.getASTContext()); + + if (!Call.getOriginExpr()) + return; + + // Anything that takes in a localized NSString as an argument + // and returns an NSString will be assumed to be returning a + // localized NSString. (Counter: Incorrectly combining two LocalizedStrings) + const QualType RT = Call.getResultType(); + if (isNSStringType(RT, C.getASTContext())) { + for (unsigned i = 0; i < Call.getNumArgs(); ++i) { + SVal argValue = Call.getArgSVal(i); + if (hasLocalizedState(argValue, C)) { + SVal sv = Call.getReturnValue(); + setLocalizedState(sv, C); + return; + } + } + } + + const Decl *D = Call.getDecl(); + if (!D) + return; + + const IdentifierInfo *Identifier = Call.getCalleeIdentifier(); + + SVal sv = Call.getReturnValue(); + if (isAnnotatedAsReturningLocalized(D) || LSF.count(Identifier) != 0) { + setLocalizedState(sv, C); + } else if (isNSStringType(RT, C.getASTContext()) && + !hasLocalizedState(sv, C)) { + if (IsAggressive) { + setNonLocalizedState(sv, C); + } else { + const SymbolicRegion *SymReg = + dyn_cast_or_null<SymbolicRegion>(sv.getAsRegion()); + if (!SymReg) + setNonLocalizedState(sv, C); + } + } +} + +/// Marks a string being returned by an ObjC method as localized +/// if it is in LocStringMethods or the method is annotated +void NonLocalizedStringChecker::checkPostObjCMessage(const ObjCMethodCall &msg, + CheckerContext &C) const { + initLocStringsMethods(C.getASTContext()); + + if (!msg.isInstanceMessage()) + return; + + const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); + if (!OD) + return; + const IdentifierInfo *odInfo = OD->getIdentifier(); + + Selector S = msg.getSelector(); + std::string SelectorName = S.getAsString(); + + std::pair<const IdentifierInfo *, Selector> MethodDescription = {odInfo, S}; + + if (LSM.count(MethodDescription) || + isAnnotatedAsReturningLocalized(msg.getDecl())) { + SVal sv = msg.getReturnValue(); + setLocalizedState(sv, C); + } +} + +/// Marks all empty string literals as localized +void NonLocalizedStringChecker::checkPostStmt(const ObjCStringLiteral *SL, + CheckerContext &C) const { + SVal sv = C.getSVal(SL); + setNonLocalizedState(sv, C); +} + +std::shared_ptr<PathDiagnosticPiece> +NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, BugReport &BR) { + if (Satisfied) + return nullptr; + + Optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>(); + if (!Point.hasValue()) + return nullptr; + + auto *LiteralExpr = dyn_cast<ObjCStringLiteral>(Point->getStmt()); + if (!LiteralExpr) + return nullptr; + + SVal LiteralSVal = Succ->getSVal(LiteralExpr); + if (LiteralSVal.getAsRegion() != NonLocalizedString) + return nullptr; + + Satisfied = true; + + PathDiagnosticLocation L = + PathDiagnosticLocation::create(*Point, BRC.getSourceManager()); + + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + auto Piece = std::make_shared<PathDiagnosticEventPiece>( + L, "Non-localized string literal here"); + Piece->addRange(LiteralExpr->getSourceRange()); + + return std::move(Piece); +} + +namespace { +class EmptyLocalizationContextChecker + : public Checker<check::ASTDecl<ObjCImplementationDecl>> { + + // A helper class, which walks the AST + class MethodCrawler : public ConstStmtVisitor<MethodCrawler> { + const ObjCMethodDecl *MD; + BugReporter &BR; + AnalysisManager &Mgr; + const CheckerBase *Checker; + LocationOrAnalysisDeclContext DCtx; + + public: + MethodCrawler(const ObjCMethodDecl *InMD, BugReporter &InBR, + const CheckerBase *Checker, AnalysisManager &InMgr, + AnalysisDeclContext *InDCtx) + : MD(InMD), BR(InBR), Mgr(InMgr), Checker(Checker), DCtx(InDCtx) {} + + void VisitStmt(const Stmt *S) { VisitChildren(S); } + + void VisitObjCMessageExpr(const ObjCMessageExpr *ME); + + void reportEmptyContextError(const ObjCMessageExpr *M) const; + + void VisitChildren(const Stmt *S) { + for (const Stmt *Child : S->children()) { + if (Child) + this->Visit(Child); + } + } + }; + +public: + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, + BugReporter &BR) const; +}; +} // end anonymous namespace + +void EmptyLocalizationContextChecker::checkASTDecl( + const ObjCImplementationDecl *D, AnalysisManager &Mgr, + BugReporter &BR) const { + + for (const ObjCMethodDecl *M : D->methods()) { + AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M); + + const Stmt *Body = M->getBody(); + assert(Body); + + MethodCrawler MC(M->getCanonicalDecl(), BR, this, Mgr, DCtx); + MC.VisitStmt(Body); + } +} + +/// This check attempts to match these macros, assuming they are defined as +/// follows: +/// +/// #define NSLocalizedString(key, comment) \ +/// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil] +/// #define NSLocalizedStringFromTable(key, tbl, comment) \ +/// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)] +/// #define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \ +/// [bundle localizedStringForKey:(key) value:@"" table:(tbl)] +/// #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment) +/// +/// We cannot use the path sensitive check because the macro argument we are +/// checking for (comment) is not used and thus not present in the AST, +/// so we use Lexer on the original macro call and retrieve the value of +/// the comment. If it's empty or nil, we raise a warning. +void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr( + const ObjCMessageExpr *ME) { + + // FIXME: We may be able to use PPCallbacks to check for empty context + // comments as part of preprocessing and avoid this re-lexing hack. + const ObjCInterfaceDecl *OD = ME->getReceiverInterface(); + if (!OD) + return; + + const IdentifierInfo *odInfo = OD->getIdentifier(); + + if (!(odInfo->isStr("NSBundle") && + ME->getSelector().getAsString() == + "localizedStringForKey:value:table:")) { + return; + } + + SourceRange R = ME->getSourceRange(); + if (!R.getBegin().isMacroID()) + return; + + // getImmediateMacroCallerLoc gets the location of the immediate macro + // caller, one level up the stack toward the initial macro typed into the + // source, so SL should point to the NSLocalizedString macro. + SourceLocation SL = + Mgr.getSourceManager().getImmediateMacroCallerLoc(R.getBegin()); + std::pair<FileID, unsigned> SLInfo = + Mgr.getSourceManager().getDecomposedLoc(SL); + + SrcMgr::SLocEntry SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); + + // If NSLocalizedString macro is wrapped in another macro, we need to + // unwrap the expansion until we get to the NSLocalizedStringMacro. + while (SE.isExpansion()) { + SL = SE.getExpansion().getSpellingLoc(); + SLInfo = Mgr.getSourceManager().getDecomposedLoc(SL); + SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first); + } + + bool Invalid = false; + llvm::MemoryBuffer *BF = + Mgr.getSourceManager().getBuffer(SLInfo.first, SL, &Invalid); + if (Invalid) + return; + + Lexer TheLexer(SL, LangOptions(), BF->getBufferStart(), + BF->getBufferStart() + SLInfo.second, BF->getBufferEnd()); + + Token I; + Token Result; // This will hold the token just before the last ')' + int p_count = 0; // This is for parenthesis matching + while (!TheLexer.LexFromRawLexer(I)) { + if (I.getKind() == tok::l_paren) + ++p_count; + if (I.getKind() == tok::r_paren) { + if (p_count == 1) + break; + --p_count; + } + Result = I; + } + + if (isAnyIdentifier(Result.getKind())) { + if (Result.getRawIdentifier().equals("nil")) { + reportEmptyContextError(ME); + return; + } + } + + if (!isStringLiteral(Result.getKind())) + return; + + StringRef Comment = + StringRef(Result.getLiteralData(), Result.getLength()).trim('"'); + + if ((Comment.trim().size() == 0 && Comment.size() > 0) || // Is Whitespace + Comment.empty()) { + reportEmptyContextError(ME); + } +} + +void EmptyLocalizationContextChecker::MethodCrawler::reportEmptyContextError( + const ObjCMessageExpr *ME) const { + // Generate the bug report. + BR.EmitBasicReport(MD, Checker, "Context Missing", + "Localizability Issue (Apple)", + "Localized string macro should include a non-empty " + "comment for translators", + PathDiagnosticLocation(ME, BR.getSourceManager(), DCtx)); +} + +namespace { +class PluralMisuseChecker : public Checker<check::ASTCodeBody> { + + // A helper class, which walks the AST + class MethodCrawler : public RecursiveASTVisitor<MethodCrawler> { + BugReporter &BR; + const CheckerBase *Checker; + AnalysisDeclContext *AC; + + // This functions like a stack. We push on any IfStmt or + // ConditionalOperator that matches the condition + // and pop it off when we leave that statement + llvm::SmallVector<const clang::Stmt *, 8> MatchingStatements; + // This is true when we are the direct-child of a + // matching statement + bool InMatchingStatement = false; + + public: + explicit MethodCrawler(BugReporter &InBR, const CheckerBase *Checker, + AnalysisDeclContext *InAC) + : BR(InBR), Checker(Checker), AC(InAC) {} + + bool VisitIfStmt(const IfStmt *I); + bool EndVisitIfStmt(IfStmt *I); + bool TraverseIfStmt(IfStmt *x); + bool VisitConditionalOperator(const ConditionalOperator *C); + bool TraverseConditionalOperator(ConditionalOperator *C); + bool VisitCallExpr(const CallExpr *CE); + bool VisitObjCMessageExpr(const ObjCMessageExpr *ME); + + private: + void reportPluralMisuseError(const Stmt *S) const; + bool isCheckingPlurality(const Expr *E) const; + }; + +public: + void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr, + BugReporter &BR) const { + MethodCrawler Visitor(BR, this, Mgr.getAnalysisDeclContext(D)); + Visitor.TraverseDecl(const_cast<Decl *>(D)); + } +}; +} // end anonymous namespace + +// Checks the condition of the IfStmt and returns true if one +// of the following heuristics are met: +// 1) The conidtion is a variable with "singular" or "plural" in the name +// 2) The condition is a binary operator with 1 or 2 on the right-hand side +bool PluralMisuseChecker::MethodCrawler::isCheckingPlurality( + const Expr *Condition) const { + const BinaryOperator *BO = nullptr; + // Accounts for when a VarDecl represents a BinaryOperator + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Condition)) { + if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) { + const Expr *InitExpr = VD->getInit(); + if (InitExpr) { + if (const BinaryOperator *B = + dyn_cast<BinaryOperator>(InitExpr->IgnoreParenImpCasts())) { + BO = B; + } + } + if (VD->getName().lower().find("plural") != StringRef::npos || + VD->getName().lower().find("singular") != StringRef::npos) { + return true; + } + } + } else if (const BinaryOperator *B = dyn_cast<BinaryOperator>(Condition)) { + BO = B; + } + + if (BO == nullptr) + return false; + + if (IntegerLiteral *IL = dyn_cast_or_null<IntegerLiteral>( + BO->getRHS()->IgnoreParenImpCasts())) { + llvm::APInt Value = IL->getValue(); + if (Value == 1 || Value == 2) { + return true; + } + } + return false; +} + +// A CallExpr with "LOC" in its identifier that takes in a string literal +// has been shown to almost always be a function that returns a localized +// string. Raise a diagnostic when this is in a statement that matches +// the condition. +bool PluralMisuseChecker::MethodCrawler::VisitCallExpr(const CallExpr *CE) { + if (InMatchingStatement) { + if (const FunctionDecl *FD = CE->getDirectCallee()) { + std::string NormalizedName = + StringRef(FD->getNameInfo().getAsString()).lower(); + if (NormalizedName.find("loc") != std::string::npos) { + for (const Expr *Arg : CE->arguments()) { + if (isa<ObjCStringLiteral>(Arg)) + reportPluralMisuseError(CE); + } + } + } + } + return true; +} + +// The other case is for NSLocalizedString which also returns +// a localized string. It's a macro for the ObjCMessageExpr +// [NSBundle localizedStringForKey:value:table:] Raise a +// diagnostic when this is in a statement that matches +// the condition. +bool PluralMisuseChecker::MethodCrawler::VisitObjCMessageExpr( + const ObjCMessageExpr *ME) { + const ObjCInterfaceDecl *OD = ME->getReceiverInterface(); + if (!OD) + return true; + + const IdentifierInfo *odInfo = OD->getIdentifier(); + + if (odInfo->isStr("NSBundle") && + ME->getSelector().getAsString() == "localizedStringForKey:value:table:") { + if (InMatchingStatement) { + reportPluralMisuseError(ME); + } + } + return true; +} + +/// Override TraverseIfStmt so we know when we are done traversing an IfStmt +bool PluralMisuseChecker::MethodCrawler::TraverseIfStmt(IfStmt *I) { + RecursiveASTVisitor<MethodCrawler>::TraverseIfStmt(I); + return EndVisitIfStmt(I); +} + +// EndVisit callbacks are not provided by the RecursiveASTVisitor +// so we override TraverseIfStmt and make a call to EndVisitIfStmt +// after traversing the IfStmt +bool PluralMisuseChecker::MethodCrawler::EndVisitIfStmt(IfStmt *I) { + MatchingStatements.pop_back(); + if (!MatchingStatements.empty()) { + if (MatchingStatements.back() != nullptr) { + InMatchingStatement = true; + return true; + } + } + InMatchingStatement = false; + return true; +} + +bool PluralMisuseChecker::MethodCrawler::VisitIfStmt(const IfStmt *I) { + const Expr *Condition = I->getCond()->IgnoreParenImpCasts(); + if (isCheckingPlurality(Condition)) { + MatchingStatements.push_back(I); + InMatchingStatement = true; + } else { + MatchingStatements.push_back(nullptr); + InMatchingStatement = false; + } + + return true; +} + +// Preliminary support for conditional operators. +bool PluralMisuseChecker::MethodCrawler::TraverseConditionalOperator( + ConditionalOperator *C) { + RecursiveASTVisitor<MethodCrawler>::TraverseConditionalOperator(C); + MatchingStatements.pop_back(); + if (!MatchingStatements.empty()) { + if (MatchingStatements.back() != nullptr) + InMatchingStatement = true; + else + InMatchingStatement = false; + } else { + InMatchingStatement = false; + } + return true; +} + +bool PluralMisuseChecker::MethodCrawler::VisitConditionalOperator( + const ConditionalOperator *C) { + const Expr *Condition = C->getCond()->IgnoreParenImpCasts(); + if (isCheckingPlurality(Condition)) { + MatchingStatements.push_back(C); + InMatchingStatement = true; + } else { + MatchingStatements.push_back(nullptr); + InMatchingStatement = false; + } + return true; +} + +void PluralMisuseChecker::MethodCrawler::reportPluralMisuseError( + const Stmt *S) const { + // Generate the bug report. + BR.EmitBasicReport(AC->getDecl(), Checker, "Plural Misuse", + "Localizability Issue (Apple)", + "Plural cases are not supported across all languages. " + "Use a .stringsdict file instead", + PathDiagnosticLocation(S, BR.getSourceManager(), AC)); +} + +//===----------------------------------------------------------------------===// +// Checker registration. +//===----------------------------------------------------------------------===// + +void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) { + NonLocalizedStringChecker *checker = + mgr.registerChecker<NonLocalizedStringChecker>(); + checker->IsAggressive = + mgr.getAnalyzerOptions().getCheckerBooleanOption("AggressiveReport", + false, checker); +} + +void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) { + mgr.registerChecker<EmptyLocalizationContextChecker>(); +} + +void ento::registerPluralMisuseChecker(CheckerManager &mgr) { + mgr.registerChecker<PluralMisuseChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp new file mode 100644 index 000000000000..fb9bccebe465 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp @@ -0,0 +1,116 @@ +//===-- MPIBugReporter.cpp - bug reporter -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines prefabricated reports which are emitted in +/// case of MPI related bugs, detected by path-sensitive analysis. +/// +//===----------------------------------------------------------------------===// + +#include "MPIBugReporter.h" +#include "MPIChecker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" + +namespace clang { +namespace ento { +namespace mpi { + +void MPIBugReporter::reportDoubleNonblocking( + const CallEvent &MPICallEvent, const ento::mpi::Request &Req, + const MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const { + + std::string ErrorText; + ErrorText = "Double nonblocking on request " + + RequestRegion->getDescriptiveName() + ". "; + + auto Report = llvm::make_unique<BugReport>(*DoubleNonblockingBugType, + ErrorText, ExplNode); + + Report->addRange(MPICallEvent.getSourceRange()); + SourceRange Range = RequestRegion->sourceRange(); + + if (Range.isValid()) + Report->addRange(Range); + + Report->addVisitor(llvm::make_unique<RequestNodeVisitor>( + RequestRegion, "Request is previously used by nonblocking call here. ")); + Report->markInteresting(RequestRegion); + + BReporter.emitReport(std::move(Report)); +} + +void MPIBugReporter::reportMissingWait( + const ento::mpi::Request &Req, const MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const { + std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + + " has no matching wait. "}; + + auto Report = + llvm::make_unique<BugReport>(*MissingWaitBugType, ErrorText, ExplNode); + + SourceRange Range = RequestRegion->sourceRange(); + if (Range.isValid()) + Report->addRange(Range); + Report->addVisitor(llvm::make_unique<RequestNodeVisitor>( + RequestRegion, "Request is previously used by nonblocking call here. ")); + Report->markInteresting(RequestRegion); + + BReporter.emitReport(std::move(Report)); +} + +void MPIBugReporter::reportUnmatchedWait( + const CallEvent &CE, const clang::ento::MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const { + std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() + + " has no matching nonblocking call. "}; + + auto Report = + llvm::make_unique<BugReport>(*UnmatchedWaitBugType, ErrorText, ExplNode); + + Report->addRange(CE.getSourceRange()); + SourceRange Range = RequestRegion->sourceRange(); + if (Range.isValid()) + Report->addRange(Range); + + BReporter.emitReport(std::move(Report)); +} + +std::shared_ptr<PathDiagnosticPiece> +MPIBugReporter::RequestNodeVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) { + + if (IsNodeFound) + return nullptr; + + const Request *const Req = N->getState()->get<RequestMap>(RequestRegion); + const Request *const PrevReq = + N->getFirstPred()->getState()->get<RequestMap>(RequestRegion); + + // Check if request was previously unused or in a different state. + if ((Req && !PrevReq) || (Req->CurrentState != PrevReq->CurrentState)) { + IsNodeFound = true; + + ProgramPoint P = N->getFirstPred()->getLocation(); + PathDiagnosticLocation L = + PathDiagnosticLocation::create(P, BRC.getSourceManager()); + + return std::make_shared<PathDiagnosticEventPiece>(L, ErrorText); + } + + return nullptr; +} + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h new file mode 100644 index 000000000000..32fcb07e3371 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h @@ -0,0 +1,108 @@ +//===-- MPIBugReporter.h - bug reporter -----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines prefabricated reports which are emitted in +/// case of MPI related bugs, detected by path-sensitive analysis. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H + +#include "MPITypes.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" + +namespace clang { +namespace ento { +namespace mpi { + +class MPIBugReporter { +public: + MPIBugReporter(const CheckerBase &CB) { + UnmatchedWaitBugType.reset(new BugType(&CB, "Unmatched wait", MPIError)); + DoubleNonblockingBugType.reset( + new BugType(&CB, "Double nonblocking", MPIError)); + MissingWaitBugType.reset(new BugType(&CB, "Missing wait", MPIError)); + } + + /// Report duplicate request use by nonblocking calls without intermediate + /// wait. + /// + /// \param MPICallEvent MPI call that caused the double nonblocking + /// \param Req request that was used by two nonblocking calls in sequence + /// \param RequestRegion memory region of the request + /// \param ExplNode node in the graph the bug appeared at + /// \param BReporter bug reporter for current context + void reportDoubleNonblocking(const CallEvent &MPICallEvent, + const Request &Req, + const MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const; + + /// Report a missing wait for a nonblocking call. + /// + /// \param Req request that is not matched by a wait + /// \param RequestRegion memory region of the request + /// \param ExplNode node in the graph the bug appeared at + /// \param BReporter bug reporter for current context + void reportMissingWait(const Request &Req, + const MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const; + + /// Report a wait on a request that has not been used at all before. + /// + /// \param CE wait call that uses the request + /// \param RequestRegion memory region of the request + /// \param ExplNode node in the graph the bug appeared at + /// \param BReporter bug reporter for current context + void reportUnmatchedWait(const CallEvent &CE, + const MemRegion *const RequestRegion, + const ExplodedNode *const ExplNode, + BugReporter &BReporter) const; + +private: + const std::string MPIError = "MPI Error"; + + // path-sensitive bug types + std::unique_ptr<BugType> UnmatchedWaitBugType; + std::unique_ptr<BugType> MissingWaitBugType; + std::unique_ptr<BugType> DoubleNonblockingBugType; + + /// Bug visitor class to find the node where the request region was previously + /// used in order to include it into the BugReport path. + class RequestNodeVisitor : public BugReporterVisitor { + public: + RequestNodeVisitor(const MemRegion *const MemoryRegion, + const std::string &ErrText) + : RequestRegion(MemoryRegion), ErrorText(ErrText) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(RequestRegion); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + const MemRegion *const RequestRegion; + bool IsNodeFound = false; + std::string ErrorText; + }; +}; + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang + +#endif diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp new file mode 100644 index 000000000000..28c6898f7947 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp @@ -0,0 +1,190 @@ +//===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the main class of MPI-Checker which serves as an entry +/// point. It is created once for each translation unit analysed. +/// The checker defines path-sensitive checks, to verify correct usage of the +/// MPI API. +/// +//===----------------------------------------------------------------------===// + +#include "MPIChecker.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" + +namespace clang { +namespace ento { +namespace mpi { + +void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent, + CheckerContext &Ctx) const { + if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) { + return; + } + const MemRegion *const MR = + PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion(); + if (!MR) + return; + const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); + + // The region must be typed, in order to reason about it. + if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) + return; + + ProgramStateRef State = Ctx.getState(); + const Request *const Req = State->get<RequestMap>(MR); + + // double nonblocking detected + if (Req && Req->CurrentState == Request::State::Nonblocking) { + ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode(); + BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode, + Ctx.getBugReporter()); + Ctx.addTransition(ErrorNode->getState(), ErrorNode); + } + // no error + else { + State = State->set<RequestMap>(MR, Request::State::Nonblocking); + Ctx.addTransition(State); + } +} + +void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent, + CheckerContext &Ctx) const { + if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier())) + return; + const MemRegion *const MR = topRegionUsedByWait(PreCallEvent); + if (!MR) + return; + const ElementRegion *const ER = dyn_cast<ElementRegion>(MR); + + // The region must be typed, in order to reason about it. + if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion()))) + return; + + llvm::SmallVector<const MemRegion *, 2> ReqRegions; + allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx); + if (ReqRegions.empty()) + return; + + ProgramStateRef State = Ctx.getState(); + static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait"); + ExplodedNode *ErrorNode{nullptr}; + + // Check all request regions used by the wait function. + for (const auto &ReqRegion : ReqRegions) { + const Request *const Req = State->get<RequestMap>(ReqRegion); + State = State->set<RequestMap>(ReqRegion, Request::State::Wait); + if (!Req) { + if (!ErrorNode) { + ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); + State = ErrorNode->getState(); + } + // A wait has no matching nonblocking call. + BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode, + Ctx.getBugReporter()); + } + } + + if (!ErrorNode) { + Ctx.addTransition(State); + } else { + Ctx.addTransition(State, ErrorNode); + } +} + +void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper, + CheckerContext &Ctx) const { + ProgramStateRef State = Ctx.getState(); + const auto &Requests = State->get<RequestMap>(); + if (Requests.isEmpty()) + return; + + static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait"); + ExplodedNode *ErrorNode{nullptr}; + + auto ReqMap = State->get<RequestMap>(); + for (const auto &Req : ReqMap) { + if (!SymReaper.isLiveRegion(Req.first)) { + if (Req.second.CurrentState == Request::State::Nonblocking) { + + if (!ErrorNode) { + ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag); + State = ErrorNode->getState(); + } + BReporter.reportMissingWait(Req.second, Req.first, ErrorNode, + Ctx.getBugReporter()); + } + State = State->remove<RequestMap>(Req.first); + } + } + + // Transition to update the state regarding removed requests. + if (!ErrorNode) { + Ctx.addTransition(State); + } else { + Ctx.addTransition(State, ErrorNode); + } +} + +const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const { + + if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { + return CE.getArgSVal(0).getAsRegion(); + } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { + return CE.getArgSVal(1).getAsRegion(); + } else { + return (const MemRegion *)nullptr; + } +} + +void MPIChecker::allRegionsUsedByWait( + llvm::SmallVector<const MemRegion *, 2> &ReqRegions, + const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const { + + MemRegionManager *const RegionManager = MR->getMemRegionManager(); + + if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) { + const SubRegion *SuperRegion{nullptr}; + if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) { + SuperRegion = cast<SubRegion>(ER->getSuperRegion()); + } + + // A single request is passed to MPI_Waitall. + if (!SuperRegion) { + ReqRegions.push_back(MR); + return; + } + + const auto &Size = Ctx.getStoreManager().getSizeInElements( + Ctx.getState(), SuperRegion, + CE.getArgExpr(1)->getType()->getPointeeType()); + const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue(); + + for (size_t i = 0; i < ArrSize; ++i) { + const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i); + + const ElementRegion *const ER = RegionManager->getElementRegion( + CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion, + Ctx.getASTContext()); + + ReqRegions.push_back(ER->getAs<MemRegion>()); + } + } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) { + ReqRegions.push_back(MR); + } +} + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang + +// Registers the checker for static analysis. +void clang::ento::registerMPIChecker(CheckerManager &MGR) { + MGR.registerChecker<clang::ento::mpi::MPIChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h new file mode 100644 index 000000000000..6b1c062ef3d5 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h @@ -0,0 +1,105 @@ +//===-- MPIChecker.h - Verify MPI API usage- --------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the main class of MPI-Checker which serves as an entry +/// point. It is created once for each translation unit analysed. +/// The checker defines path-sensitive checks, to verify correct usage of the +/// MPI API. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKER_H + +#include "MPIBugReporter.h" +#include "MPITypes.h" +#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +namespace clang { +namespace ento { +namespace mpi { + +class MPIChecker : public Checker<check::PreCall, check::DeadSymbols> { +public: + MPIChecker() : BReporter(*this) {} + + // path-sensitive callbacks + void checkPreCall(const CallEvent &CE, CheckerContext &Ctx) const { + dynamicInit(Ctx); + checkUnmatchedWaits(CE, Ctx); + checkDoubleNonblocking(CE, Ctx); + } + + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &Ctx) const { + dynamicInit(Ctx); + checkMissingWaits(SymReaper, Ctx); + } + + void dynamicInit(CheckerContext &Ctx) const { + if (FuncClassifier) + return; + const_cast<std::unique_ptr<MPIFunctionClassifier> &>(FuncClassifier) + .reset(new MPIFunctionClassifier{Ctx.getASTContext()}); + } + + /// Checks if a request is used by nonblocking calls multiple times + /// in sequence without intermediate wait. The check contains a guard, + /// in order to only inspect nonblocking functions. + /// + /// \param PreCallEvent MPI call to verify + void checkDoubleNonblocking(const clang::ento::CallEvent &PreCallEvent, + clang::ento::CheckerContext &Ctx) const; + + /// Checks if the request used by the wait function was not used at all + /// before. The check contains a guard, in order to only inspect wait + /// functions. + /// + /// \param PreCallEvent MPI call to verify + void checkUnmatchedWaits(const clang::ento::CallEvent &PreCallEvent, + clang::ento::CheckerContext &Ctx) const; + + /// Check if a nonblocking call is not matched by a wait. + /// If a memory region is not alive and the last function using the + /// request was a nonblocking call, this is rated as a missing wait. + void checkMissingWaits(clang::ento::SymbolReaper &SymReaper, + clang::ento::CheckerContext &Ctx) const; + +private: + /// Collects all memory regions of a request(array) used by a wait + /// function. If the wait function uses a single request, this is a single + /// region. For wait functions using multiple requests, multiple regions + /// representing elements in the array are collected. + /// + /// \param ReqRegions vector the regions get pushed into + /// \param MR top most region to iterate + /// \param CE MPI wait call using the request(s) + void allRegionsUsedByWait( + llvm::SmallVector<const clang::ento::MemRegion *, 2> &ReqRegions, + const clang::ento::MemRegion *const MR, const clang::ento::CallEvent &CE, + clang::ento::CheckerContext &Ctx) const; + + /// Returns the memory region used by a wait function. + /// Distinguishes between MPI_Wait and MPI_Waitall. + /// + /// \param CE MPI wait call + const clang::ento::MemRegion * + topRegionUsedByWait(const clang::ento::CallEvent &CE) const; + + const std::unique_ptr<MPIFunctionClassifier> FuncClassifier; + MPIBugReporter BReporter; +}; + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang + +#endif diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp new file mode 100644 index 000000000000..12760abaeeff --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp @@ -0,0 +1,284 @@ +//===-- MPIFunctionClassifier.cpp - classifies MPI functions ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines functionality to identify and classify MPI functions. +/// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h" +#include "llvm/ADT/STLExtras.h" + +namespace clang { +namespace ento { +namespace mpi { + +void MPIFunctionClassifier::identifierInit(ASTContext &ASTCtx) { + // Initialize function identifiers. + initPointToPointIdentifiers(ASTCtx); + initCollectiveIdentifiers(ASTCtx); + initAdditionalIdentifiers(ASTCtx); +} + +void MPIFunctionClassifier::initPointToPointIdentifiers(ASTContext &ASTCtx) { + // Copy identifiers into the correct classification containers. + IdentInfo_MPI_Send = &ASTCtx.Idents.get("MPI_Send"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Send); + MPIType.push_back(IdentInfo_MPI_Send); + assert(IdentInfo_MPI_Send); + + IdentInfo_MPI_Isend = &ASTCtx.Idents.get("MPI_Isend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Isend); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Isend); + MPIType.push_back(IdentInfo_MPI_Isend); + assert(IdentInfo_MPI_Isend); + + IdentInfo_MPI_Ssend = &ASTCtx.Idents.get("MPI_Ssend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Ssend); + MPIType.push_back(IdentInfo_MPI_Ssend); + assert(IdentInfo_MPI_Ssend); + + IdentInfo_MPI_Issend = &ASTCtx.Idents.get("MPI_Issend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Issend); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Issend); + MPIType.push_back(IdentInfo_MPI_Issend); + assert(IdentInfo_MPI_Issend); + + IdentInfo_MPI_Bsend = &ASTCtx.Idents.get("MPI_Bsend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Bsend); + MPIType.push_back(IdentInfo_MPI_Bsend); + assert(IdentInfo_MPI_Bsend); + + IdentInfo_MPI_Ibsend = &ASTCtx.Idents.get("MPI_Ibsend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Ibsend); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Ibsend); + MPIType.push_back(IdentInfo_MPI_Ibsend); + assert(IdentInfo_MPI_Ibsend); + + IdentInfo_MPI_Rsend = &ASTCtx.Idents.get("MPI_Rsend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Rsend); + MPIType.push_back(IdentInfo_MPI_Rsend); + assert(IdentInfo_MPI_Rsend); + + IdentInfo_MPI_Irsend = &ASTCtx.Idents.get("MPI_Irsend"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Irsend); + MPIType.push_back(IdentInfo_MPI_Irsend); + assert(IdentInfo_MPI_Irsend); + + IdentInfo_MPI_Recv = &ASTCtx.Idents.get("MPI_Recv"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Recv); + MPIType.push_back(IdentInfo_MPI_Recv); + assert(IdentInfo_MPI_Recv); + + IdentInfo_MPI_Irecv = &ASTCtx.Idents.get("MPI_Irecv"); + MPIPointToPointTypes.push_back(IdentInfo_MPI_Irecv); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Irecv); + MPIType.push_back(IdentInfo_MPI_Irecv); + assert(IdentInfo_MPI_Irecv); +} + +void MPIFunctionClassifier::initCollectiveIdentifiers(ASTContext &ASTCtx) { + // Copy identifiers into the correct classification containers. + IdentInfo_MPI_Scatter = &ASTCtx.Idents.get("MPI_Scatter"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Scatter); + MPIPointToCollTypes.push_back(IdentInfo_MPI_Scatter); + MPIType.push_back(IdentInfo_MPI_Scatter); + assert(IdentInfo_MPI_Scatter); + + IdentInfo_MPI_Iscatter = &ASTCtx.Idents.get("MPI_Iscatter"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Iscatter); + MPIPointToCollTypes.push_back(IdentInfo_MPI_Iscatter); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Iscatter); + MPIType.push_back(IdentInfo_MPI_Iscatter); + assert(IdentInfo_MPI_Iscatter); + + IdentInfo_MPI_Gather = &ASTCtx.Idents.get("MPI_Gather"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Gather); + MPICollToPointTypes.push_back(IdentInfo_MPI_Gather); + MPIType.push_back(IdentInfo_MPI_Gather); + assert(IdentInfo_MPI_Gather); + + IdentInfo_MPI_Igather = &ASTCtx.Idents.get("MPI_Igather"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Igather); + MPICollToPointTypes.push_back(IdentInfo_MPI_Igather); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Igather); + MPIType.push_back(IdentInfo_MPI_Igather); + assert(IdentInfo_MPI_Igather); + + IdentInfo_MPI_Allgather = &ASTCtx.Idents.get("MPI_Allgather"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Allgather); + MPICollToCollTypes.push_back(IdentInfo_MPI_Allgather); + MPIType.push_back(IdentInfo_MPI_Allgather); + assert(IdentInfo_MPI_Allgather); + + IdentInfo_MPI_Iallgather = &ASTCtx.Idents.get("MPI_Iallgather"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Iallgather); + MPICollToCollTypes.push_back(IdentInfo_MPI_Iallgather); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Iallgather); + MPIType.push_back(IdentInfo_MPI_Iallgather); + assert(IdentInfo_MPI_Iallgather); + + IdentInfo_MPI_Bcast = &ASTCtx.Idents.get("MPI_Bcast"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Bcast); + MPIPointToCollTypes.push_back(IdentInfo_MPI_Bcast); + MPIType.push_back(IdentInfo_MPI_Bcast); + assert(IdentInfo_MPI_Bcast); + + IdentInfo_MPI_Ibcast = &ASTCtx.Idents.get("MPI_Ibcast"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Ibcast); + MPIPointToCollTypes.push_back(IdentInfo_MPI_Ibcast); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Ibcast); + MPIType.push_back(IdentInfo_MPI_Ibcast); + assert(IdentInfo_MPI_Ibcast); + + IdentInfo_MPI_Reduce = &ASTCtx.Idents.get("MPI_Reduce"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Reduce); + MPICollToPointTypes.push_back(IdentInfo_MPI_Reduce); + MPIType.push_back(IdentInfo_MPI_Reduce); + assert(IdentInfo_MPI_Reduce); + + IdentInfo_MPI_Ireduce = &ASTCtx.Idents.get("MPI_Ireduce"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Ireduce); + MPICollToPointTypes.push_back(IdentInfo_MPI_Ireduce); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Ireduce); + MPIType.push_back(IdentInfo_MPI_Ireduce); + assert(IdentInfo_MPI_Ireduce); + + IdentInfo_MPI_Allreduce = &ASTCtx.Idents.get("MPI_Allreduce"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Allreduce); + MPICollToCollTypes.push_back(IdentInfo_MPI_Allreduce); + MPIType.push_back(IdentInfo_MPI_Allreduce); + assert(IdentInfo_MPI_Allreduce); + + IdentInfo_MPI_Iallreduce = &ASTCtx.Idents.get("MPI_Iallreduce"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Iallreduce); + MPICollToCollTypes.push_back(IdentInfo_MPI_Iallreduce); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Iallreduce); + MPIType.push_back(IdentInfo_MPI_Iallreduce); + assert(IdentInfo_MPI_Iallreduce); + + IdentInfo_MPI_Alltoall = &ASTCtx.Idents.get("MPI_Alltoall"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Alltoall); + MPICollToCollTypes.push_back(IdentInfo_MPI_Alltoall); + MPIType.push_back(IdentInfo_MPI_Alltoall); + assert(IdentInfo_MPI_Alltoall); + + IdentInfo_MPI_Ialltoall = &ASTCtx.Idents.get("MPI_Ialltoall"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Ialltoall); + MPICollToCollTypes.push_back(IdentInfo_MPI_Ialltoall); + MPINonBlockingTypes.push_back(IdentInfo_MPI_Ialltoall); + MPIType.push_back(IdentInfo_MPI_Ialltoall); + assert(IdentInfo_MPI_Ialltoall); +} + +void MPIFunctionClassifier::initAdditionalIdentifiers(ASTContext &ASTCtx) { + IdentInfo_MPI_Comm_rank = &ASTCtx.Idents.get("MPI_Comm_rank"); + MPIType.push_back(IdentInfo_MPI_Comm_rank); + assert(IdentInfo_MPI_Comm_rank); + + IdentInfo_MPI_Comm_size = &ASTCtx.Idents.get("MPI_Comm_size"); + MPIType.push_back(IdentInfo_MPI_Comm_size); + assert(IdentInfo_MPI_Comm_size); + + IdentInfo_MPI_Wait = &ASTCtx.Idents.get("MPI_Wait"); + MPIType.push_back(IdentInfo_MPI_Wait); + assert(IdentInfo_MPI_Wait); + + IdentInfo_MPI_Waitall = &ASTCtx.Idents.get("MPI_Waitall"); + MPIType.push_back(IdentInfo_MPI_Waitall); + assert(IdentInfo_MPI_Waitall); + + IdentInfo_MPI_Barrier = &ASTCtx.Idents.get("MPI_Barrier"); + MPICollectiveTypes.push_back(IdentInfo_MPI_Barrier); + MPIType.push_back(IdentInfo_MPI_Barrier); + assert(IdentInfo_MPI_Barrier); +} + +// general identifiers +bool MPIFunctionClassifier::isMPIType(const IdentifierInfo *IdentInfo) const { + return llvm::is_contained(MPIType, IdentInfo); +} + +bool MPIFunctionClassifier::isNonBlockingType( + const IdentifierInfo *IdentInfo) const { + return llvm::is_contained(MPINonBlockingTypes, IdentInfo); +} + +// point-to-point identifiers +bool MPIFunctionClassifier::isPointToPointType( + const IdentifierInfo *IdentInfo) const { + return llvm::is_contained(MPIPointToPointTypes, IdentInfo); +} + +// collective identifiers +bool MPIFunctionClassifier::isCollectiveType( + const IdentifierInfo *IdentInfo) const { + return llvm::is_contained(MPICollectiveTypes, IdentInfo); +} + +bool MPIFunctionClassifier::isCollToColl( + const IdentifierInfo *IdentInfo) const { + return llvm::is_contained(MPICollToCollTypes, IdentInfo); +} + +bool MPIFunctionClassifier::isScatterType( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Scatter || + IdentInfo == IdentInfo_MPI_Iscatter; +} + +bool MPIFunctionClassifier::isGatherType( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Gather || + IdentInfo == IdentInfo_MPI_Igather || + IdentInfo == IdentInfo_MPI_Allgather || + IdentInfo == IdentInfo_MPI_Iallgather; +} + +bool MPIFunctionClassifier::isAllgatherType( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Allgather || + IdentInfo == IdentInfo_MPI_Iallgather; +} + +bool MPIFunctionClassifier::isAlltoallType( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Alltoall || + IdentInfo == IdentInfo_MPI_Ialltoall; +} + +bool MPIFunctionClassifier::isBcastType(const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Bcast || IdentInfo == IdentInfo_MPI_Ibcast; +} + +bool MPIFunctionClassifier::isReduceType( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Reduce || + IdentInfo == IdentInfo_MPI_Ireduce || + IdentInfo == IdentInfo_MPI_Allreduce || + IdentInfo == IdentInfo_MPI_Iallreduce; +} + +// additional identifiers +bool MPIFunctionClassifier::isMPI_Wait(const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Wait; +} + +bool MPIFunctionClassifier::isMPI_Waitall( + const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Waitall; +} + +bool MPIFunctionClassifier::isWaitType(const IdentifierInfo *IdentInfo) const { + return IdentInfo == IdentInfo_MPI_Wait || IdentInfo == IdentInfo_MPI_Waitall; +} + +} // end of namespace: mpi +} // end of namespace: ento +} // end of namespace: clang diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h new file mode 100644 index 000000000000..2e7140cd771e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h @@ -0,0 +1,67 @@ +//===-- MPITypes.h - Functionality to model MPI concepts --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file provides definitions to model concepts of MPI. The mpi::Request +/// class defines a wrapper class, in order to make MPI requests trackable for +/// path-sensitive analysis. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPITYPES_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPITYPES_H + +#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "llvm/ADT/SmallSet.h" + +namespace clang { +namespace ento { +namespace mpi { + +class Request { +public: + enum State : unsigned char { Nonblocking, Wait }; + + Request(State S) : CurrentState{S} {} + + void Profile(llvm::FoldingSetNodeID &Id) const { + Id.AddInteger(CurrentState); + } + + bool operator==(const Request &ToCompare) const { + return CurrentState == ToCompare.CurrentState; + } + + const State CurrentState; +}; + +// The RequestMap stores MPI requests which are identified by their memory +// region. Requests are used in MPI to complete nonblocking operations with wait +// operations. A custom map implementation is used, in order to make it +// available in an arbitrary amount of translation units. +struct RequestMap {}; +typedef llvm::ImmutableMap<const clang::ento::MemRegion *, + clang::ento::mpi::Request> + RequestMapImpl; + +} // end of namespace: mpi + +template <> +struct ProgramStateTrait<mpi::RequestMap> + : public ProgramStatePartialTrait<mpi::RequestMapImpl> { + static void *GDMIndex() { + static int index = 0; + return &index; + } +}; + +} // end of namespace: ento +} // end of namespace: clang +#endif diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp new file mode 100644 index 000000000000..06e27fc5718d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp @@ -0,0 +1,664 @@ +//==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This checker flags misuses of KeyChainAPI. In particular, the password data +// allocated/returned by SecKeychainItemCopyContent, +// SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has +// to be freed using a call to SecKeychainItemFreeContent. +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>, + check::PostStmt<CallExpr>, + check::DeadSymbols, + check::PointerEscape, + eval::Assume> { + mutable std::unique_ptr<BugType> BT; + +public: + /// AllocationState is a part of the checker specific state together with the + /// MemRegion corresponding to the allocated data. + struct AllocationState { + /// The index of the allocator function. + unsigned int AllocatorIdx; + SymbolRef Region; + + AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) : + AllocatorIdx(Idx), + Region(R) {} + + bool operator==(const AllocationState &X) const { + return (AllocatorIdx == X.AllocatorIdx && + Region == X.Region); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(AllocatorIdx); + ID.AddPointer(Region); + } + }; + + void checkPreStmt(const CallExpr *S, CheckerContext &C) const; + void checkPostStmt(const CallExpr *S, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; + ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, + bool Assumption) const; + void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const; + +private: + typedef std::pair<SymbolRef, const AllocationState*> AllocationPair; + typedef SmallVector<AllocationPair, 2> AllocationPairVec; + + enum APIKind { + /// Denotes functions tracked by this checker. + ValidAPI = 0, + /// The functions commonly/mistakenly used in place of the given API. + ErrorAPI = 1, + /// The functions which may allocate the data. These are tracked to reduce + /// the false alarm rate. + PossibleAPI = 2 + }; + /// Stores the information about the allocator and deallocator functions - + /// these are the functions the checker is tracking. + struct ADFunctionInfo { + const char* Name; + unsigned int Param; + unsigned int DeallocatorIdx; + APIKind Kind; + }; + static const unsigned InvalidIdx = 100000; + static const unsigned FunctionsToTrackSize = 8; + static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize]; + /// The value, which represents no error return value for allocator functions. + static const unsigned NoErr = 0; + + /// Given the function name, returns the index of the allocator/deallocator + /// function. + static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator); + + inline void initBugType() const { + if (!BT) + BT.reset(new BugType(this, "Improper use of SecKeychain API", + "API Misuse (Apple)")); + } + + void generateDeallocatorMismatchReport(const AllocationPair &AP, + const Expr *ArgExpr, + CheckerContext &C) const; + + /// Find the allocation site for Sym on the path leading to the node N. + const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym, + CheckerContext &C) const; + + std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport( + const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const; + + /// Mark an AllocationPair interesting for diagnostic reporting. + void markInteresting(BugReport *R, const AllocationPair &AP) const { + R->markInteresting(AP.first); + R->markInteresting(AP.second->Region); + } + + /// The bug visitor which allows us to print extra diagnostics along the + /// BugReport path. For example, showing the allocation site of the leaked + /// region. + class SecKeychainBugVisitor : public BugReporterVisitor { + protected: + // The allocated region symbol tracked by the main analysis. + SymbolRef Sym; + + public: + SecKeychainBugVisitor(SymbolRef S) : Sym(S) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Sym); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + }; +}; +} + +/// ProgramState traits to store the currently allocated (and not yet freed) +/// symbols. This is a map from the allocated content symbol to the +/// corresponding AllocationState. +REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData, + SymbolRef, + MacOSKeychainAPIChecker::AllocationState) + +static bool isEnclosingFunctionParam(const Expr *E) { + E = E->IgnoreParenCasts(); + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) { + const ValueDecl *VD = DRE->getDecl(); + if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD)) + return true; + } + return false; +} + +const MacOSKeychainAPIChecker::ADFunctionInfo + MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = { + {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0 + {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1 + {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2 + {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3 + {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4 + {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5 + {"free", 0, InvalidIdx, ErrorAPI}, // 6 + {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7 +}; + +unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name, + bool IsAllocator) { + for (unsigned I = 0; I < FunctionsToTrackSize; ++I) { + ADFunctionInfo FI = FunctionsToTrack[I]; + if (FI.Name != Name) + continue; + // Make sure the function is of the right type (allocator vs deallocator). + if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx)) + return InvalidIdx; + if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx)) + return InvalidIdx; + + return I; + } + // The function is not tracked. + return InvalidIdx; +} + +static bool isBadDeallocationArgument(const MemRegion *Arg) { + if (!Arg) + return false; + return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) || + isa<TypedRegion>(Arg); +} + +/// Given the address expression, retrieve the value it's pointing to. Assume +/// that value is itself an address, and return the corresponding symbol. +static SymbolRef getAsPointeeSymbol(const Expr *Expr, + CheckerContext &C) { + ProgramStateRef State = C.getState(); + SVal ArgV = C.getSVal(Expr); + + if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) { + StoreManager& SM = C.getStoreManager(); + SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol(); + if (sym) + return sym; + } + return nullptr; +} + +// Report deallocator mismatch. Remove the region from tracking - reporting a +// missing free error after this one is redundant. +void MacOSKeychainAPIChecker:: + generateDeallocatorMismatchReport(const AllocationPair &AP, + const Expr *ArgExpr, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + State = State->remove<AllocatedData>(AP.first); + ExplodedNode *N = C.generateNonFatalErrorNode(State); + + if (!N) + return; + initBugType(); + SmallString<80> sbuf; + llvm::raw_svector_ostream os(sbuf); + unsigned int PDeallocIdx = + FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx; + + os << "Deallocator doesn't match the allocator: '" + << FunctionsToTrack[PDeallocIdx].Name << "' should be used."; + auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); + Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); + Report->addRange(ArgExpr->getSourceRange()); + markInteresting(Report.get(), AP); + C.emitReport(std::move(Report)); +} + +void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + unsigned idx = InvalidIdx; + ProgramStateRef State = C.getState(); + + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + StringRef funName = C.getCalleeName(FD); + if (funName.empty()) + return; + + // If it is a call to an allocator function, it could be a double allocation. + idx = getTrackedFunctionIndex(funName, true); + if (idx != InvalidIdx) { + unsigned paramIdx = FunctionsToTrack[idx].Param; + if (CE->getNumArgs() <= paramIdx) + return; + + const Expr *ArgExpr = CE->getArg(paramIdx); + if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) + if (const AllocationState *AS = State->get<AllocatedData>(V)) { + // Remove the value from the state. The new symbol will be added for + // tracking when the second allocator is processed in checkPostStmt(). + State = State->remove<AllocatedData>(V); + ExplodedNode *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + initBugType(); + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; + os << "Allocated data should be released before another call to " + << "the allocator: missing a call to '" + << FunctionsToTrack[DIdx].Name + << "'."; + auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); + Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V)); + Report->addRange(ArgExpr->getSourceRange()); + Report->markInteresting(AS->Region); + C.emitReport(std::move(Report)); + } + return; + } + + // Is it a call to one of deallocator functions? + idx = getTrackedFunctionIndex(funName, false); + if (idx == InvalidIdx) + return; + + unsigned paramIdx = FunctionsToTrack[idx].Param; + if (CE->getNumArgs() <= paramIdx) + return; + + // Check the argument to the deallocator. + const Expr *ArgExpr = CE->getArg(paramIdx); + SVal ArgSVal = C.getSVal(ArgExpr); + + // Undef is reported by another checker. + if (ArgSVal.isUndef()) + return; + + SymbolRef ArgSM = ArgSVal.getAsLocSymbol(); + + // If the argument is coming from the heap, globals, or unknown, do not + // report it. + bool RegionArgIsBad = false; + if (!ArgSM) { + if (!isBadDeallocationArgument(ArgSVal.getAsRegion())) + return; + RegionArgIsBad = true; + } + + // Is the argument to the call being tracked? + const AllocationState *AS = State->get<AllocatedData>(ArgSM); + if (!AS) + return; + + // TODO: We might want to report double free here. + // (that would involve tracking all the freed symbols in the checker state). + if (RegionArgIsBad) { + // It is possible that this is a false positive - the argument might + // have entered as an enclosing function parameter. + if (isEnclosingFunctionParam(ArgExpr)) + return; + + ExplodedNode *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + initBugType(); + auto Report = llvm::make_unique<BugReport>( + *BT, "Trying to free data which has not been allocated.", N); + Report->addRange(ArgExpr->getSourceRange()); + if (AS) + Report->markInteresting(AS->Region); + C.emitReport(std::move(Report)); + return; + } + + // Process functions which might deallocate. + if (FunctionsToTrack[idx].Kind == PossibleAPI) { + + if (funName == "CFStringCreateWithBytesNoCopy") { + const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts(); + // NULL ~ default deallocator, so warn. + if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(), + Expr::NPC_ValueDependentIsNotNull)) { + const AllocationPair AP = std::make_pair(ArgSM, AS); + generateDeallocatorMismatchReport(AP, ArgExpr, C); + return; + } + // One of the default allocators, so warn. + if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) { + StringRef DeallocatorName = DE->getFoundDecl()->getName(); + if (DeallocatorName == "kCFAllocatorDefault" || + DeallocatorName == "kCFAllocatorSystemDefault" || + DeallocatorName == "kCFAllocatorMalloc") { + const AllocationPair AP = std::make_pair(ArgSM, AS); + generateDeallocatorMismatchReport(AP, ArgExpr, C); + return; + } + // If kCFAllocatorNull, which does not deallocate, we still have to + // find the deallocator. + if (DE->getFoundDecl()->getName() == "kCFAllocatorNull") + return; + } + // In all other cases, assume the user supplied a correct deallocator + // that will free memory so stop tracking. + State = State->remove<AllocatedData>(ArgSM); + C.addTransition(State); + return; + } + + llvm_unreachable("We know of no other possible APIs."); + } + + // The call is deallocating a value we previously allocated, so remove it + // from the next state. + State = State->remove<AllocatedData>(ArgSM); + + // Check if the proper deallocator is used. + unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx; + if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) { + const AllocationPair AP = std::make_pair(ArgSM, AS); + generateDeallocatorMismatchReport(AP, ArgExpr, C); + return; + } + + C.addTransition(State); +} + +void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + StringRef funName = C.getCalleeName(FD); + + // If a value has been allocated, add it to the set for tracking. + unsigned idx = getTrackedFunctionIndex(funName, true); + if (idx == InvalidIdx) + return; + + const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param); + // If the argument entered as an enclosing function parameter, skip it to + // avoid false positives. + if (isEnclosingFunctionParam(ArgExpr) && + C.getLocationContext()->getParent() == nullptr) + return; + + if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) { + // If the argument points to something that's not a symbolic region, it + // can be: + // - unknown (cannot reason about it) + // - undefined (already reported by other checker) + // - constant (null - should not be tracked, + // other constant will generate a compiler warning) + // - goto (should be reported by other checker) + + // The call return value symbol should stay alive for as long as the + // allocated value symbol, since our diagnostics depend on the value + // returned by the call. Ex: Data should only be freed if noErr was + // returned during allocation.) + SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol(); + C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol); + + // Track the allocated value in the checker state. + State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx, + RetStatusSymbol)); + assert(State); + C.addTransition(State); + } +} + +// TODO: This logic is the same as in Malloc checker. +const ExplodedNode * +MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N, + SymbolRef Sym, + CheckerContext &C) const { + const LocationContext *LeakContext = N->getLocationContext(); + // Walk the ExplodedGraph backwards and find the first node that referred to + // the tracked symbol. + const ExplodedNode *AllocNode = N; + + while (N) { + if (!N->getState()->get<AllocatedData>(Sym)) + break; + // Allocation node, is the last node in the current or parent context in + // which the symbol was tracked. + const LocationContext *NContext = N->getLocationContext(); + if (NContext == LeakContext || + NContext->isParentOf(LeakContext)) + AllocNode = N; + N = N->pred_empty() ? nullptr : *(N->pred_begin()); + } + + return AllocNode; +} + +std::unique_ptr<BugReport> +MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport( + const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const { + const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx]; + initBugType(); + SmallString<70> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << "Allocated data is not released: missing a call to '" + << FunctionsToTrack[FI.DeallocatorIdx].Name << "'."; + + // Most bug reports are cached at the location where they occurred. + // With leaks, we want to unique them by the location where they were + // allocated, and only report a single path. + PathDiagnosticLocation LocUsedForUniqueing; + const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C); + const Stmt *AllocStmt = PathDiagnosticLocation::getStmt(AllocNode); + + if (AllocStmt) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt, + C.getSourceManager(), + AllocNode->getLocationContext()); + + auto Report = + llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing, + AllocNode->getLocationContext()->getDecl()); + + Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first)); + markInteresting(Report.get(), AP); + return Report; +} + +/// If the return symbol is assumed to be error, remove the allocated info +/// from consideration. +ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State, + SVal Cond, + bool Assumption) const { + AllocatedDataTy AMap = State->get<AllocatedData>(); + if (AMap.isEmpty()) + return State; + + auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr()); + if (!CondBSE) + return State; + BinaryOperator::Opcode OpCode = CondBSE->getOpcode(); + if (OpCode != BO_EQ && OpCode != BO_NE) + return State; + + // Match for a restricted set of patterns for cmparison of error codes. + // Note, the comparisons of type '0 == st' are transformed into SymIntExpr. + SymbolRef ReturnSymbol = nullptr; + if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) { + const llvm::APInt &RHS = SIE->getRHS(); + bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) || + (OpCode == BO_NE && RHS == NoErr); + if (!Assumption) + ErrorIsReturned = !ErrorIsReturned; + if (ErrorIsReturned) + ReturnSymbol = SIE->getLHS(); + } + + if (ReturnSymbol) + for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { + if (ReturnSymbol == I->second.Region) + State = State->remove<AllocatedData>(I->first); + } + + return State; +} + +void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + AllocatedDataTy AMap = State->get<AllocatedData>(); + if (AMap.isEmpty()) + return; + + bool Changed = false; + AllocationPairVec Errors; + for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { + if (!SR.isDead(I->first)) + continue; + + Changed = true; + State = State->remove<AllocatedData>(I->first); + // If the allocated symbol is null do not report. + ConstraintManager &CMgr = State->getConstraintManager(); + ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey()); + if (AllocFailed.isConstrainedTrue()) + continue; + Errors.push_back(std::make_pair(I->first, &I->second)); + } + if (!Changed) { + // Generate the new, cleaned up state. + C.addTransition(State); + return; + } + + static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak"); + ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag); + if (!N) + return; + + // Generate the error reports. + for (const auto &P : Errors) + C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C)); + + // Generate the new, cleaned up state. + C.addTransition(State, N); +} + +ProgramStateRef MacOSKeychainAPIChecker::checkPointerEscape( + ProgramStateRef State, const InvalidatedSymbols &Escaped, + const CallEvent *Call, PointerEscapeKind Kind) const { + // FIXME: This branch doesn't make any sense at all, but it is an overfitted + // replacement for a previous overfitted code that was making even less sense. + if (!Call || Call->getDecl()) + return State; + + for (auto I : State->get<AllocatedData>()) { + SymbolRef Sym = I.first; + if (Escaped.count(Sym)) + State = State->remove<AllocatedData>(Sym); + + // This checker is special. Most checkers in fact only track symbols of + // SymbolConjured type, eg. symbols returned from functions such as + // malloc(). This checker tracks symbols returned as out-parameters. + // + // When a function is evaluated conservatively, the out-parameter's pointee + // base region gets invalidated with a SymbolConjured. If the base region is + // larger than the region we're interested in, the value we're interested in + // would be SymbolDerived based on that SymbolConjured. However, such + // SymbolDerived will never be listed in the Escaped set when the base + // region is invalidated because ExprEngine doesn't know which symbols + // were derived from a given symbol, while there can be infinitely many + // valid symbols derived from any given symbol. + // + // Hence the extra boilerplate: remove the derived symbol when its parent + // symbol escapes. + // + if (const auto *SD = dyn_cast<SymbolDerived>(Sym)) { + SymbolRef ParentSym = SD->getParentSymbol(); + if (Escaped.count(ParentSym)) + State = State->remove<AllocatedData>(Sym); + } + } + return State; +} + +std::shared_ptr<PathDiagnosticPiece> +MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { + const AllocationState *AS = N->getState()->get<AllocatedData>(Sym); + if (!AS) + return nullptr; + const AllocationState *ASPrev = + N->getFirstPred()->getState()->get<AllocatedData>(Sym); + if (ASPrev) + return nullptr; + + // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the + // allocation site. + const CallExpr *CE = + cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt()); + const FunctionDecl *funDecl = CE->getDirectCallee(); + assert(funDecl && "We do not support indirect function calls as of now."); + StringRef funName = funDecl->getName(); + + // Get the expression of the corresponding argument. + unsigned Idx = getTrackedFunctionIndex(funName, true); + assert(Idx != InvalidIdx && "This should be a call to an allocator."); + const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param); + PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, + "Data is allocated here."); +} + +void MacOSKeychainAPIChecker::printState(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + const char *Sep) const { + + AllocatedDataTy AMap = State->get<AllocatedData>(); + + if (!AMap.isEmpty()) { + Out << Sep << "KeychainAPIChecker :" << NL; + for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) { + I.getKey()->dumpToStream(Out); + } + } +} + + +void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) { + mgr.registerChecker<MacOSKeychainAPIChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp new file mode 100644 index 000000000000..f5976d7da4c1 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -0,0 +1,176 @@ +// MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines MacOSXAPIChecker, which is an assortment of checks on calls +// to various, widely used Apple APIs. +// +// FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated +// to here, using the new Checker interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > { + mutable std::unique_ptr<BugType> BT_dispatchOnce; + + static const ObjCIvarRegion *getParentIvarRegion(const MemRegion *R); + +public: + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + + void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, + StringRef FName) const; + + typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &, + const CallExpr *, + StringRef FName) const; +}; +} //end anonymous namespace + +//===----------------------------------------------------------------------===// +// dispatch_once and dispatch_once_f +//===----------------------------------------------------------------------===// + +const ObjCIvarRegion * +MacOSXAPIChecker::getParentIvarRegion(const MemRegion *R) { + const SubRegion *SR = dyn_cast<SubRegion>(R); + while (SR) { + if (const ObjCIvarRegion *IR = dyn_cast<ObjCIvarRegion>(SR)) + return IR; + SR = dyn_cast<SubRegion>(SR->getSuperRegion()); + } + return nullptr; +} + +void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, + StringRef FName) const { + if (CE->getNumArgs() < 1) + return; + + // Check if the first argument is improperly allocated. If so, issue a + // warning because that's likely to be bad news. + const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion(); + if (!R) + return; + + // Global variables are fine. + const MemRegion *RB = R->getBaseRegion(); + const MemSpaceRegion *RS = RB->getMemorySpace(); + if (isa<GlobalsSpaceRegion>(RS)) + return; + + // Handle _dispatch_once. In some versions of the OS X SDK we have the case + // that dispatch_once is a macro that wraps a call to _dispatch_once. + // _dispatch_once is then a function which then calls the real dispatch_once. + // Users do not care; they just want the warning at the top-level call. + if (CE->getBeginLoc().isMacroID()) { + StringRef TrimmedFName = FName.ltrim('_'); + if (TrimmedFName != FName) + FName = TrimmedFName; + } + + SmallString<256> S; + llvm::raw_svector_ostream os(S); + bool SuggestStatic = false; + os << "Call to '" << FName << "' uses"; + if (const VarRegion *VR = dyn_cast<VarRegion>(RB)) { + const VarDecl *VD = VR->getDecl(); + // FIXME: These should have correct memory space and thus should be filtered + // out earlier. This branch only fires when we're looking from a block, + // which we analyze as a top-level declaration, onto a static local + // in a function that contains the block. + if (VD->isStaticLocal()) + return; + // We filtered out globals earlier, so it must be a local variable + // or a block variable which is under UnknownSpaceRegion. + if (VR != R) + os << " memory within"; + if (VD->hasAttr<BlocksAttr>()) + os << " the block variable '"; + else + os << " the local variable '"; + os << VR->getDecl()->getName() << '\''; + SuggestStatic = true; + } else if (const ObjCIvarRegion *IVR = getParentIvarRegion(R)) { + if (IVR != R) + os << " memory within"; + os << " the instance variable '" << IVR->getDecl()->getName() << '\''; + } else if (isa<HeapSpaceRegion>(RS)) { + os << " heap-allocated memory"; + } else if (isa<UnknownSpaceRegion>(RS)) { + // Presence of an IVar superregion has priority over this branch, because + // ObjC objects are on the heap even if the core doesn't realize this. + // Presence of a block variable base region has priority over this branch, + // because block variables are known to be either on stack or on heap + // (might actually move between the two, hence UnknownSpace). + return; + } else { + os << " stack allocated memory"; + } + os << " for the predicate value. Using such transient memory for " + "the predicate is potentially dangerous."; + if (SuggestStatic) + os << " Perhaps you intended to declare the variable as 'static'?"; + + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + if (!BT_dispatchOnce) + BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'", + "API Misuse (Apple)")); + + auto report = llvm::make_unique<BugReport>(*BT_dispatchOnce, os.str(), N); + report->addRange(CE->getArg(0)->getSourceRange()); + C.emitReport(std::move(report)); +} + +//===----------------------------------------------------------------------===// +// Central dispatch function. +//===----------------------------------------------------------------------===// + +void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + StringRef Name = C.getCalleeName(CE); + if (Name.empty()) + return; + + SubChecker SC = + llvm::StringSwitch<SubChecker>(Name) + .Cases("dispatch_once", + "_dispatch_once", + "dispatch_once_f", + &MacOSXAPIChecker::CheckDispatchOnce) + .Default(nullptr); + + if (SC) + (this->*SC)(C, CE, Name); +} + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +void ento::registerMacOSXAPIChecker(CheckerManager &mgr) { + mgr.registerChecker<MacOSXAPIChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp new file mode 100644 index 000000000000..ae1b1fc837be --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -0,0 +1,3133 @@ +//=== MallocChecker.cpp - A malloc/free checker -------------------*- 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 malloc/free checker, which checks for potential memory +// leaks, double free, and use-after-free problems. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "InterCheckerAPI.h" +#include "clang/AST/Attr.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "AllocationState.h" +#include <climits> +#include <utility> + +using namespace clang; +using namespace ento; + +namespace { + +// Used to check correspondence between allocators and deallocators. +enum AllocationFamily { + AF_None, + AF_Malloc, + AF_CXXNew, + AF_CXXNewArray, + AF_IfNameIndex, + AF_Alloca, + AF_InnerBuffer +}; + +class RefState { + enum Kind { // Reference to allocated memory. + Allocated, + // Reference to zero-allocated memory. + AllocatedOfSizeZero, + // Reference to released/freed memory. + Released, + // The responsibility for freeing resources has transferred from + // this reference. A relinquished symbol should not be freed. + Relinquished, + // We are no longer guaranteed to have observed all manipulations + // of this pointer/memory. For example, it could have been + // passed as a parameter to an opaque function. + Escaped + }; + + const Stmt *S; + unsigned K : 3; // Kind enum, but stored as a bitfield. + unsigned Family : 29; // Rest of 32-bit word, currently just an allocation + // family. + + RefState(Kind k, const Stmt *s, unsigned family) + : S(s), K(k), Family(family) { + assert(family != AF_None); + } +public: + bool isAllocated() const { return K == Allocated; } + bool isAllocatedOfSizeZero() const { return K == AllocatedOfSizeZero; } + bool isReleased() const { return K == Released; } + bool isRelinquished() const { return K == Relinquished; } + bool isEscaped() const { return K == Escaped; } + AllocationFamily getAllocationFamily() const { + return (AllocationFamily)Family; + } + const Stmt *getStmt() const { return S; } + + bool operator==(const RefState &X) const { + return K == X.K && S == X.S && Family == X.Family; + } + + static RefState getAllocated(unsigned family, const Stmt *s) { + return RefState(Allocated, s, family); + } + static RefState getAllocatedOfSizeZero(const RefState *RS) { + return RefState(AllocatedOfSizeZero, RS->getStmt(), + RS->getAllocationFamily()); + } + static RefState getReleased(unsigned family, const Stmt *s) { + return RefState(Released, s, family); + } + static RefState getRelinquished(unsigned family, const Stmt *s) { + return RefState(Relinquished, s, family); + } + static RefState getEscaped(const RefState *RS) { + return RefState(Escaped, RS->getStmt(), RS->getAllocationFamily()); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + ID.AddPointer(S); + ID.AddInteger(Family); + } + + void dump(raw_ostream &OS) const { + switch (static_cast<Kind>(K)) { +#define CASE(ID) case ID: OS << #ID; break; + CASE(Allocated) + CASE(AllocatedOfSizeZero) + CASE(Released) + CASE(Relinquished) + CASE(Escaped) + } + } + + LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); } +}; + +enum ReallocPairKind { + RPToBeFreedAfterFailure, + // The symbol has been freed when reallocation failed. + RPIsFreeOnFailure, + // The symbol does not need to be freed after reallocation fails. + RPDoNotTrackAfterFailure +}; + +/// \class ReallocPair +/// Stores information about the symbol being reallocated by a call to +/// 'realloc' to allow modeling failed reallocation later in the path. +struct ReallocPair { + // The symbol which realloc reallocated. + SymbolRef ReallocatedSym; + ReallocPairKind Kind; + + ReallocPair(SymbolRef S, ReallocPairKind K) : + ReallocatedSym(S), Kind(K) {} + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(Kind); + ID.AddPointer(ReallocatedSym); + } + bool operator==(const ReallocPair &X) const { + return ReallocatedSym == X.ReallocatedSym && + Kind == X.Kind; + } +}; + +typedef std::pair<const ExplodedNode*, const MemRegion*> LeakInfo; + +class MallocChecker : public Checker<check::DeadSymbols, + check::PointerEscape, + check::ConstPointerEscape, + check::PreStmt<ReturnStmt>, + check::EndFunction, + check::PreCall, + check::PostStmt<CallExpr>, + check::PostStmt<CXXNewExpr>, + check::NewAllocator, + check::PreStmt<CXXDeleteExpr>, + check::PostStmt<BlockExpr>, + check::PostObjCMessage, + check::Location, + eval::Assume> +{ +public: + MallocChecker() + : II_alloca(nullptr), II_win_alloca(nullptr), II_malloc(nullptr), + II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr), + II_valloc(nullptr), II_reallocf(nullptr), II_strndup(nullptr), + II_strdup(nullptr), II_win_strdup(nullptr), II_kmalloc(nullptr), + II_if_nameindex(nullptr), II_if_freenameindex(nullptr), + II_wcsdup(nullptr), II_win_wcsdup(nullptr), II_g_malloc(nullptr), + II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr), + II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr), + II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr), + II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr), + II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr), + II_g_try_realloc_n(nullptr) {} + + /// In pessimistic mode, the checker assumes that it does not know which + /// functions might free the memory. + enum CheckKind { + CK_MallocChecker, + CK_NewDeleteChecker, + CK_NewDeleteLeaksChecker, + CK_MismatchedDeallocatorChecker, + CK_InnerPointerChecker, + CK_NumCheckKinds + }; + + enum class MemoryOperationKind { + MOK_Allocate, + MOK_Free, + MOK_Any + }; + + DefaultBool IsOptimistic; + + DefaultBool ChecksEnabled[CK_NumCheckKinds]; + CheckName CheckNames[CK_NumCheckKinds]; + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const; + void checkNewAllocator(const CXXNewExpr *NE, SVal Target, + CheckerContext &C) const; + void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const; + void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; + void checkEndFunction(const ReturnStmt *S, CheckerContext &C) const; + ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, + bool Assumption) const; + void checkLocation(SVal l, bool isLoad, const Stmt *S, + CheckerContext &C) const; + + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; + ProgramStateRef checkConstPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; + + void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const override; + +private: + mutable std::unique_ptr<BugType> BT_DoubleFree[CK_NumCheckKinds]; + mutable std::unique_ptr<BugType> BT_DoubleDelete; + mutable std::unique_ptr<BugType> BT_Leak[CK_NumCheckKinds]; + mutable std::unique_ptr<BugType> BT_UseFree[CK_NumCheckKinds]; + mutable std::unique_ptr<BugType> BT_BadFree[CK_NumCheckKinds]; + mutable std::unique_ptr<BugType> BT_FreeAlloca[CK_NumCheckKinds]; + mutable std::unique_ptr<BugType> BT_MismatchedDealloc; + mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds]; + mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds]; + mutable IdentifierInfo *II_alloca, *II_win_alloca, *II_malloc, *II_free, + *II_realloc, *II_calloc, *II_valloc, *II_reallocf, + *II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc, + *II_if_nameindex, *II_if_freenameindex, *II_wcsdup, + *II_win_wcsdup, *II_g_malloc, *II_g_malloc0, + *II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0, + *II_g_try_realloc, *II_g_free, *II_g_memdup, + *II_g_malloc_n, *II_g_malloc0_n, *II_g_realloc_n, + *II_g_try_malloc_n, *II_g_try_malloc0_n, + *II_g_try_realloc_n; + mutable Optional<uint64_t> KernelZeroFlagVal; + + void initIdentifierInfo(ASTContext &C) const; + + /// Determine family of a deallocation expression. + AllocationFamily getAllocationFamily(CheckerContext &C, const Stmt *S) const; + + /// Print names of allocators and deallocators. + /// + /// \returns true on success. + bool printAllocDeallocName(raw_ostream &os, CheckerContext &C, + const Expr *E) const; + + /// Print expected name of an allocator based on the deallocator's + /// family derived from the DeallocExpr. + void printExpectedAllocName(raw_ostream &os, CheckerContext &C, + const Expr *DeallocExpr) const; + /// Print expected name of a deallocator based on the allocator's + /// family. + void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const; + + ///@{ + /// Check if this is one of the functions which can allocate/reallocate memory + /// pointed to by one of its arguments. + bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const; + bool isCMemFunction(const FunctionDecl *FD, + ASTContext &C, + AllocationFamily Family, + MemoryOperationKind MemKind) const; + bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const; + ///@} + + /// Process C++ operator new()'s allocation, which is the part of C++ + /// new-expression that goes before the constructor. + void processNewAllocation(const CXXNewExpr *NE, CheckerContext &C, + SVal Target) const; + + /// Perform a zero-allocation check. + /// The optional \p RetVal parameter specifies the newly allocated pointer + /// value; if unspecified, the value of expression \p E is used. + ProgramStateRef ProcessZeroAllocation(CheckerContext &C, const Expr *E, + const unsigned AllocationSizeArg, + ProgramStateRef State, + Optional<SVal> RetVal = None) const; + + ProgramStateRef MallocMemReturnsAttr(CheckerContext &C, + const CallExpr *CE, + const OwnershipAttr* Att, + ProgramStateRef State) const; + static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, + const Expr *SizeEx, SVal Init, + ProgramStateRef State, + AllocationFamily Family = AF_Malloc); + static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE, + SVal SizeEx, SVal Init, + ProgramStateRef State, + AllocationFamily Family = AF_Malloc); + + static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE, + ProgramStateRef State, SVal Target); + + // Check if this malloc() for special flags. At present that means M_ZERO or + // __GFP_ZERO (in which case, treat it like calloc). + llvm::Optional<ProgramStateRef> + performKernelMalloc(const CallExpr *CE, CheckerContext &C, + const ProgramStateRef &State) const; + + /// Update the RefState to reflect the new memory allocation. + /// The optional \p RetVal parameter specifies the newly allocated pointer + /// value; if unspecified, the value of expression \p E is used. + static ProgramStateRef + MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State, + AllocationFamily Family = AF_Malloc, + Optional<SVal> RetVal = None); + + ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att, + ProgramStateRef State) const; + ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE, + ProgramStateRef state, unsigned Num, + bool Hold, + bool &ReleasedAllocated, + bool ReturnsNullOnFailure = false) const; + ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg, + const Expr *ParentExpr, + ProgramStateRef State, + bool Hold, + bool &ReleasedAllocated, + bool ReturnsNullOnFailure = false) const; + + ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE, + bool FreesMemOnFailure, + ProgramStateRef State, + bool SuffixWithN = false) const; + static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, + const Expr *BlockBytes); + static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE, + ProgramStateRef State); + + /// Check if the memory associated with this symbol was released. + bool isReleased(SymbolRef Sym, CheckerContext &C) const; + + bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const; + + void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C, + const Stmt *S) const; + + bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const; + + /// Check if the function is known free memory, or if it is + /// "interesting" and should be modeled explicitly. + /// + /// \param [out] EscapingSymbol A function might not free memory in general, + /// but could be known to free a particular symbol. In this case, false is + /// returned and the single escaping symbol is returned through the out + /// parameter. + /// + /// We assume that pointers do not escape through calls to system functions + /// not handled by this checker. + bool mayFreeAnyEscapedMemoryOrIsModeledExplicitly(const CallEvent *Call, + ProgramStateRef State, + SymbolRef &EscapingSymbol) const; + + // Implementation of the checkPointerEscape callbacks. + ProgramStateRef checkPointerEscapeAux(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + bool(*CheckRefState)(const RefState*)) const; + + // Implementation of the checkPreStmt and checkEndFunction callbacks. + void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext &C) const; + + ///@{ + /// Tells if a given family/call/symbol is tracked by the current checker. + /// Sets CheckKind to the kind of the checker responsible for this + /// family/call/symbol. + Optional<CheckKind> getCheckIfTracked(AllocationFamily Family, + bool IsALeakCheck = false) const; + Optional<CheckKind> getCheckIfTracked(CheckerContext &C, + const Stmt *AllocDeallocStmt, + bool IsALeakCheck = false) const; + Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym, + bool IsALeakCheck = false) const; + ///@} + static bool SummarizeValue(raw_ostream &os, SVal V); + static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR); + void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range, + const Expr *DeallocExpr) const; + void ReportFreeAlloca(CheckerContext &C, SVal ArgVal, + SourceRange Range) const; + void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range, + const Expr *DeallocExpr, const RefState *RS, + SymbolRef Sym, bool OwnershipTransferred) const; + void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range, + const Expr *DeallocExpr, + const Expr *AllocExpr = nullptr) const; + void ReportUseAfterFree(CheckerContext &C, SourceRange Range, + SymbolRef Sym) const; + void ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released, + SymbolRef Sym, SymbolRef PrevSym) const; + + void ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const; + + void ReportUseZeroAllocated(CheckerContext &C, SourceRange Range, + SymbolRef Sym) const; + + void ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, + SourceRange Range, const Expr *FreeExpr) const; + + /// Find the location of the allocation for Sym on the path leading to the + /// exploded node N. + LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym, + CheckerContext &C) const; + + void reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const; + + /// The bug visitor which allows us to print extra diagnostics along the + /// BugReport path. For example, showing the allocation site of the leaked + /// region. + class MallocBugVisitor final : public BugReporterVisitor { + protected: + enum NotificationMode { + Normal, + ReallocationFailed + }; + + // The allocated region symbol tracked by the main analysis. + SymbolRef Sym; + + // The mode we are in, i.e. what kind of diagnostics will be emitted. + NotificationMode Mode; + + // A symbol from when the primary region should have been reallocated. + SymbolRef FailedReallocSymbol; + + // A C++ destructor stack frame in which memory was released. Used for + // miscellaneous false positive suppression. + const StackFrameContext *ReleaseDestructorLC; + + bool IsLeak; + + public: + MallocBugVisitor(SymbolRef S, bool isLeak = false) + : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr), + ReleaseDestructorLC(nullptr), IsLeak(isLeak) {} + + static void *getTag() { + static int Tag = 0; + return &Tag; + } + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.AddPointer(getTag()); + ID.AddPointer(Sym); + } + + inline bool isAllocated(const RefState *S, const RefState *SPrev, + const Stmt *Stmt) { + // Did not track -> allocated. Other state (released) -> allocated. + return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) && + (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) && + (!SPrev || !(SPrev->isAllocated() || + SPrev->isAllocatedOfSizeZero()))); + } + + inline bool isReleased(const RefState *S, const RefState *SPrev, + const Stmt *Stmt) { + // Did not track -> released. Other state (allocated) -> released. + // The statement associated with the release might be missing. + bool IsReleased = (S && S->isReleased()) && + (!SPrev || !SPrev->isReleased()); + assert(!IsReleased || + (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt))) || + (!Stmt && S->getAllocationFamily() == AF_InnerBuffer)); + return IsReleased; + } + + inline bool isRelinquished(const RefState *S, const RefState *SPrev, + const Stmt *Stmt) { + // Did not track -> relinquished. Other state (allocated) -> relinquished. + return (Stmt && (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) || + isa<ObjCPropertyRefExpr>(Stmt)) && + (S && S->isRelinquished()) && + (!SPrev || !SPrev->isRelinquished())); + } + + inline bool isReallocFailedCheck(const RefState *S, const RefState *SPrev, + const Stmt *Stmt) { + // If the expression is not a call, and the state change is + // released -> allocated, it must be the realloc return value + // check. If we have to handle more cases here, it might be cleaner just + // to track this extra bit in the state itself. + return ((!Stmt || !isa<CallExpr>(Stmt)) && + (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) && + (SPrev && !(SPrev->isAllocated() || + SPrev->isAllocatedOfSizeZero()))); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + std::shared_ptr<PathDiagnosticPiece> + getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode, + BugReport &BR) override { + if (!IsLeak) + return nullptr; + + PathDiagnosticLocation L = + PathDiagnosticLocation::createEndOfPath(EndPathNode, + BRC.getSourceManager()); + // Do not add the statement itself as a range in case of leak. + return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(), + false); + } + + private: + class StackHintGeneratorForReallocationFailed + : public StackHintGeneratorForSymbol { + public: + StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M) + : StackHintGeneratorForSymbol(S, M) {} + + std::string getMessageForArg(const Expr *ArgE, + unsigned ArgIndex) override { + // Printed parameters start at 1, not 0. + ++ArgIndex; + + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex) + << " parameter failed"; + + return os.str(); + } + + std::string getMessageForReturn(const CallExpr *CallExpr) override { + return "Reallocation of returned value failed"; + } + }; + }; +}; +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState) +REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair) +REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef) + +// A map from the freed symbol to the symbol representing the return value of +// the free function. +REGISTER_MAP_WITH_PROGRAMSTATE(FreeReturnValue, SymbolRef, SymbolRef) + +namespace { +class StopTrackingCallback final : public SymbolVisitor { + ProgramStateRef state; +public: + StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {} + ProgramStateRef getState() const { return state; } + + bool VisitSymbol(SymbolRef sym) override { + state = state->remove<RegionState>(sym); + return true; + } +}; +} // end anonymous namespace + +void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const { + if (II_malloc) + return; + II_alloca = &Ctx.Idents.get("alloca"); + II_malloc = &Ctx.Idents.get("malloc"); + II_free = &Ctx.Idents.get("free"); + II_realloc = &Ctx.Idents.get("realloc"); + II_reallocf = &Ctx.Idents.get("reallocf"); + II_calloc = &Ctx.Idents.get("calloc"); + II_valloc = &Ctx.Idents.get("valloc"); + II_strdup = &Ctx.Idents.get("strdup"); + II_strndup = &Ctx.Idents.get("strndup"); + II_wcsdup = &Ctx.Idents.get("wcsdup"); + II_kmalloc = &Ctx.Idents.get("kmalloc"); + II_if_nameindex = &Ctx.Idents.get("if_nameindex"); + II_if_freenameindex = &Ctx.Idents.get("if_freenameindex"); + + //MSVC uses `_`-prefixed instead, so we check for them too. + II_win_strdup = &Ctx.Idents.get("_strdup"); + II_win_wcsdup = &Ctx.Idents.get("_wcsdup"); + II_win_alloca = &Ctx.Idents.get("_alloca"); + + // Glib + II_g_malloc = &Ctx.Idents.get("g_malloc"); + II_g_malloc0 = &Ctx.Idents.get("g_malloc0"); + II_g_realloc = &Ctx.Idents.get("g_realloc"); + II_g_try_malloc = &Ctx.Idents.get("g_try_malloc"); + II_g_try_malloc0 = &Ctx.Idents.get("g_try_malloc0"); + II_g_try_realloc = &Ctx.Idents.get("g_try_realloc"); + II_g_free = &Ctx.Idents.get("g_free"); + II_g_memdup = &Ctx.Idents.get("g_memdup"); + II_g_malloc_n = &Ctx.Idents.get("g_malloc_n"); + II_g_malloc0_n = &Ctx.Idents.get("g_malloc0_n"); + II_g_realloc_n = &Ctx.Idents.get("g_realloc_n"); + II_g_try_malloc_n = &Ctx.Idents.get("g_try_malloc_n"); + II_g_try_malloc0_n = &Ctx.Idents.get("g_try_malloc0_n"); + II_g_try_realloc_n = &Ctx.Idents.get("g_try_realloc_n"); +} + +bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const { + if (isCMemFunction(FD, C, AF_Malloc, MemoryOperationKind::MOK_Any)) + return true; + + if (isCMemFunction(FD, C, AF_IfNameIndex, MemoryOperationKind::MOK_Any)) + return true; + + if (isCMemFunction(FD, C, AF_Alloca, MemoryOperationKind::MOK_Any)) + return true; + + if (isStandardNewDelete(FD, C)) + return true; + + return false; +} + +bool MallocChecker::isCMemFunction(const FunctionDecl *FD, + ASTContext &C, + AllocationFamily Family, + MemoryOperationKind MemKind) const { + if (!FD) + return false; + + bool CheckFree = (MemKind == MemoryOperationKind::MOK_Any || + MemKind == MemoryOperationKind::MOK_Free); + bool CheckAlloc = (MemKind == MemoryOperationKind::MOK_Any || + MemKind == MemoryOperationKind::MOK_Allocate); + + if (FD->getKind() == Decl::Function) { + const IdentifierInfo *FunI = FD->getIdentifier(); + initIdentifierInfo(C); + + if (Family == AF_Malloc && CheckFree) { + if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf || + FunI == II_g_free) + return true; + } + + if (Family == AF_Malloc && CheckAlloc) { + if (FunI == II_malloc || FunI == II_realloc || FunI == II_reallocf || + FunI == II_calloc || FunI == II_valloc || FunI == II_strdup || + FunI == II_win_strdup || FunI == II_strndup || FunI == II_wcsdup || + FunI == II_win_wcsdup || FunI == II_kmalloc || + FunI == II_g_malloc || FunI == II_g_malloc0 || + FunI == II_g_realloc || FunI == II_g_try_malloc || + FunI == II_g_try_malloc0 || FunI == II_g_try_realloc || + FunI == II_g_memdup || FunI == II_g_malloc_n || + FunI == II_g_malloc0_n || FunI == II_g_realloc_n || + FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n || + FunI == II_g_try_realloc_n) + return true; + } + + if (Family == AF_IfNameIndex && CheckFree) { + if (FunI == II_if_freenameindex) + return true; + } + + if (Family == AF_IfNameIndex && CheckAlloc) { + if (FunI == II_if_nameindex) + return true; + } + + if (Family == AF_Alloca && CheckAlloc) { + if (FunI == II_alloca || FunI == II_win_alloca) + return true; + } + } + + if (Family != AF_Malloc) + return false; + + if (IsOptimistic && FD->hasAttrs()) { + for (const auto *I : FD->specific_attrs<OwnershipAttr>()) { + OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind(); + if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) { + if (CheckFree) + return true; + } else if (OwnKind == OwnershipAttr::Returns) { + if (CheckAlloc) + return true; + } + } + } + + return false; +} + +// Tells if the callee is one of the builtin new/delete operators, including +// placement operators and other standard overloads. +bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD, + ASTContext &C) const { + if (!FD) + return false; + + OverloadedOperatorKind Kind = FD->getOverloadedOperator(); + if (Kind != OO_New && Kind != OO_Array_New && + Kind != OO_Delete && Kind != OO_Array_Delete) + return false; + + // This is standard if and only if it's not defined in a user file. + SourceLocation L = FD->getLocation(); + // If the header for operator delete is not included, it's still defined + // in an invalid source location. Check to make sure we don't crash. + return !L.isValid() || C.getSourceManager().isInSystemHeader(L); +} + +llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc( + const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const { + // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels: + // + // void *malloc(unsigned long size, struct malloc_type *mtp, int flags); + // + // One of the possible flags is M_ZERO, which means 'give me back an + // allocation which is already zeroed', like calloc. + + // 2-argument kmalloc(), as used in the Linux kernel: + // + // void *kmalloc(size_t size, gfp_t flags); + // + // Has the similar flag value __GFP_ZERO. + + // This logic is largely cloned from O_CREAT in UnixAPIChecker, maybe some + // code could be shared. + + ASTContext &Ctx = C.getASTContext(); + llvm::Triple::OSType OS = Ctx.getTargetInfo().getTriple().getOS(); + + if (!KernelZeroFlagVal.hasValue()) { + if (OS == llvm::Triple::FreeBSD) + KernelZeroFlagVal = 0x0100; + else if (OS == llvm::Triple::NetBSD) + KernelZeroFlagVal = 0x0002; + else if (OS == llvm::Triple::OpenBSD) + KernelZeroFlagVal = 0x0008; + else if (OS == llvm::Triple::Linux) + // __GFP_ZERO + KernelZeroFlagVal = 0x8000; + else + // FIXME: We need a more general way of getting the M_ZERO value. + // See also: O_CREAT in UnixAPIChecker.cpp. + + // Fall back to normal malloc behavior on platforms where we don't + // know M_ZERO. + return None; + } + + // We treat the last argument as the flags argument, and callers fall-back to + // normal malloc on a None return. This works for the FreeBSD kernel malloc + // as well as Linux kmalloc. + if (CE->getNumArgs() < 2) + return None; + + const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1); + const SVal V = C.getSVal(FlagsEx); + if (!V.getAs<NonLoc>()) { + // The case where 'V' can be a location can only be due to a bad header, + // so in this case bail out. + return None; + } + + NonLoc Flags = V.castAs<NonLoc>(); + NonLoc ZeroFlag = C.getSValBuilder() + .makeIntVal(KernelZeroFlagVal.getValue(), FlagsEx->getType()) + .castAs<NonLoc>(); + SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And, + Flags, ZeroFlag, + FlagsEx->getType()); + if (MaskedFlagsUC.isUnknownOrUndef()) + return None; + DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>(); + + // Check if maskedFlags is non-zero. + ProgramStateRef TrueState, FalseState; + std::tie(TrueState, FalseState) = State->assume(MaskedFlags); + + // If M_ZERO is set, treat this like calloc (initialized). + if (TrueState && !FalseState) { + SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy); + return MallocMemAux(C, CE, CE->getArg(0), ZeroVal, TrueState); + } + + return None; +} + +SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks, + const Expr *BlockBytes) { + SValBuilder &SB = C.getSValBuilder(); + SVal BlocksVal = C.getSVal(Blocks); + SVal BlockBytesVal = C.getSVal(BlockBytes); + ProgramStateRef State = C.getState(); + SVal TotalSize = SB.evalBinOp(State, BO_Mul, BlocksVal, BlockBytesVal, + SB.getContext().getSizeType()); + return TotalSize; +} + +void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const { + if (C.wasInlined) + return; + + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD) + return; + + ProgramStateRef State = C.getState(); + bool ReleasedAllocatedMemory = false; + + if (FD->getKind() == Decl::Function) { + initIdentifierInfo(C.getASTContext()); + IdentifierInfo *FunI = FD->getIdentifier(); + + if (FunI == II_malloc || FunI == II_g_malloc || FunI == II_g_try_malloc) { + if (CE->getNumArgs() < 1) + return; + if (CE->getNumArgs() < 3) { + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + if (CE->getNumArgs() == 1) + State = ProcessZeroAllocation(C, CE, 0, State); + } else if (CE->getNumArgs() == 3) { + llvm::Optional<ProgramStateRef> MaybeState = + performKernelMalloc(CE, C, State); + if (MaybeState.hasValue()) + State = MaybeState.getValue(); + else + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + } + } else if (FunI == II_kmalloc) { + if (CE->getNumArgs() < 1) + return; + llvm::Optional<ProgramStateRef> MaybeState = + performKernelMalloc(CE, C, State); + if (MaybeState.hasValue()) + State = MaybeState.getValue(); + else + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + } else if (FunI == II_valloc) { + if (CE->getNumArgs() < 1) + return; + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State); + State = ProcessZeroAllocation(C, CE, 0, State); + } else if (FunI == II_realloc || FunI == II_g_realloc || + FunI == II_g_try_realloc) { + State = ReallocMemAux(C, CE, false, State); + State = ProcessZeroAllocation(C, CE, 1, State); + } else if (FunI == II_reallocf) { + State = ReallocMemAux(C, CE, true, State); + State = ProcessZeroAllocation(C, CE, 1, State); + } else if (FunI == II_calloc) { + State = CallocMem(C, CE, State); + State = ProcessZeroAllocation(C, CE, 0, State); + State = ProcessZeroAllocation(C, CE, 1, State); + } else if (FunI == II_free || FunI == II_g_free) { + State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); + } else if (FunI == II_strdup || FunI == II_win_strdup || + FunI == II_wcsdup || FunI == II_win_wcsdup) { + State = MallocUpdateRefState(C, CE, State); + } else if (FunI == II_strndup) { + State = MallocUpdateRefState(C, CE, State); + } else if (FunI == II_alloca || FunI == II_win_alloca) { + if (CE->getNumArgs() < 1) + return; + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, + AF_Alloca); + State = ProcessZeroAllocation(C, CE, 0, State); + } else if (isStandardNewDelete(FD, C.getASTContext())) { + // Process direct calls to operator new/new[]/delete/delete[] functions + // as distinct from new/new[]/delete/delete[] expressions that are + // processed by the checkPostStmt callbacks for CXXNewExpr and + // CXXDeleteExpr. + OverloadedOperatorKind K = FD->getOverloadedOperator(); + if (K == OO_New) { + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, + AF_CXXNew); + State = ProcessZeroAllocation(C, CE, 0, State); + } + else if (K == OO_Array_New) { + State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State, + AF_CXXNewArray); + State = ProcessZeroAllocation(C, CE, 0, State); + } + else if (K == OO_Delete || K == OO_Array_Delete) + State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); + else + llvm_unreachable("not a new/delete operator"); + } else if (FunI == II_if_nameindex) { + // Should we model this differently? We can allocate a fixed number of + // elements with zeros in the last one. + State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State, + AF_IfNameIndex); + } else if (FunI == II_if_freenameindex) { + State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory); + } else if (FunI == II_g_malloc0 || FunI == II_g_try_malloc0) { + if (CE->getNumArgs() < 1) + return; + SValBuilder &svalBuilder = C.getSValBuilder(); + SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); + State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State); + State = ProcessZeroAllocation(C, CE, 0, State); + } else if (FunI == II_g_memdup) { + if (CE->getNumArgs() < 2) + return; + State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State); + State = ProcessZeroAllocation(C, CE, 1, State); + } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n || + FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) { + if (CE->getNumArgs() < 2) + return; + SVal Init = UndefinedVal(); + if (FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) { + SValBuilder &SB = C.getSValBuilder(); + Init = SB.makeZeroVal(SB.getContext().CharTy); + } + SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); + State = MallocMemAux(C, CE, TotalSize, Init, State); + State = ProcessZeroAllocation(C, CE, 0, State); + State = ProcessZeroAllocation(C, CE, 1, State); + } else if (FunI == II_g_realloc_n || FunI == II_g_try_realloc_n) { + if (CE->getNumArgs() < 3) + return; + State = ReallocMemAux(C, CE, false, State, true); + State = ProcessZeroAllocation(C, CE, 1, State); + State = ProcessZeroAllocation(C, CE, 2, State); + } + } + + if (IsOptimistic || ChecksEnabled[CK_MismatchedDeallocatorChecker]) { + // Check all the attributes, if there are any. + // There can be multiple of these attributes. + if (FD->hasAttrs()) + for (const auto *I : FD->specific_attrs<OwnershipAttr>()) { + switch (I->getOwnKind()) { + case OwnershipAttr::Returns: + State = MallocMemReturnsAttr(C, CE, I, State); + break; + case OwnershipAttr::Takes: + case OwnershipAttr::Holds: + State = FreeMemAttr(C, CE, I, State); + break; + } + } + } + C.addTransition(State); +} + +// Performs a 0-sized allocations check. +ProgramStateRef MallocChecker::ProcessZeroAllocation( + CheckerContext &C, const Expr *E, const unsigned AllocationSizeArg, + ProgramStateRef State, Optional<SVal> RetVal) const { + if (!State) + return nullptr; + + if (!RetVal) + RetVal = C.getSVal(E); + + const Expr *Arg = nullptr; + + if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { + Arg = CE->getArg(AllocationSizeArg); + } + else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) { + if (NE->isArray()) + Arg = NE->getArraySize(); + else + return State; + } + else + llvm_unreachable("not a CallExpr or CXXNewExpr"); + + assert(Arg); + + Optional<DefinedSVal> DefArgVal = C.getSVal(Arg).getAs<DefinedSVal>(); + + if (!DefArgVal) + return State; + + // Check if the allocation size is 0. + ProgramStateRef TrueState, FalseState; + SValBuilder &SvalBuilder = C.getSValBuilder(); + DefinedSVal Zero = + SvalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>(); + + std::tie(TrueState, FalseState) = + State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero)); + + if (TrueState && !FalseState) { + SymbolRef Sym = RetVal->getAsLocSymbol(); + if (!Sym) + return State; + + const RefState *RS = State->get<RegionState>(Sym); + if (RS) { + if (RS->isAllocated()) + return TrueState->set<RegionState>(Sym, + RefState::getAllocatedOfSizeZero(RS)); + else + return State; + } else { + // Case of zero-size realloc. Historically 'realloc(ptr, 0)' is treated as + // 'free(ptr)' and the returned value from 'realloc(ptr, 0)' is not + // tracked. Add zero-reallocated Sym to the state to catch references + // to zero-allocated memory. + return TrueState->add<ReallocSizeZeroSymbols>(Sym); + } + } + + // Assume the value is non-zero going forward. + assert(FalseState); + return FalseState; +} + +static QualType getDeepPointeeType(QualType T) { + QualType Result = T, PointeeType = T->getPointeeType(); + while (!PointeeType.isNull()) { + Result = PointeeType; + PointeeType = PointeeType->getPointeeType(); + } + return Result; +} + +static bool treatUnusedNewEscaped(const CXXNewExpr *NE) { + + const CXXConstructExpr *ConstructE = NE->getConstructExpr(); + if (!ConstructE) + return false; + + if (!NE->getAllocatedType()->getAsCXXRecordDecl()) + return false; + + const CXXConstructorDecl *CtorD = ConstructE->getConstructor(); + + // Iterate over the constructor parameters. + for (const auto *CtorParam : CtorD->parameters()) { + + QualType CtorParamPointeeT = CtorParam->getType()->getPointeeType(); + if (CtorParamPointeeT.isNull()) + continue; + + CtorParamPointeeT = getDeepPointeeType(CtorParamPointeeT); + + if (CtorParamPointeeT->getAsCXXRecordDecl()) + return true; + } + + return false; +} + +void MallocChecker::processNewAllocation(const CXXNewExpr *NE, + CheckerContext &C, + SVal Target) const { + if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext())) + return; + + ParentMap &PM = C.getLocationContext()->getParentMap(); + if (!PM.isConsumedExpr(NE) && treatUnusedNewEscaped(NE)) + return; + + ProgramStateRef State = C.getState(); + // The return value from operator new is bound to a specified initialization + // value (if any) and we don't want to loose this value. So we call + // MallocUpdateRefState() instead of MallocMemAux() which breaks the + // existing binding. + State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray + : AF_CXXNew, Target); + State = addExtentSize(C, NE, State, Target); + State = ProcessZeroAllocation(C, NE, 0, State, Target); + C.addTransition(State); +} + +void MallocChecker::checkPostStmt(const CXXNewExpr *NE, + CheckerContext &C) const { + if (!C.getAnalysisManager().getAnalyzerOptions().MayInlineCXXAllocator) + processNewAllocation(NE, C, C.getSVal(NE)); +} + +void MallocChecker::checkNewAllocator(const CXXNewExpr *NE, SVal Target, + CheckerContext &C) const { + if (!C.wasInlined) + processNewAllocation(NE, C, Target); +} + +// Sets the extent value of the MemRegion allocated by +// new expression NE to its size in Bytes. +// +ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C, + const CXXNewExpr *NE, + ProgramStateRef State, + SVal Target) { + if (!State) + return nullptr; + SValBuilder &svalBuilder = C.getSValBuilder(); + SVal ElementCount; + const SubRegion *Region; + if (NE->isArray()) { + const Expr *SizeExpr = NE->getArraySize(); + ElementCount = C.getSVal(SizeExpr); + // Store the extent size for the (symbolic)region + // containing the elements. + Region = Target.getAsRegion() + ->getAs<SubRegion>() + ->StripCasts() + ->getAs<SubRegion>(); + } else { + ElementCount = svalBuilder.makeIntVal(1, true); + Region = Target.getAsRegion()->getAs<SubRegion>(); + } + assert(Region); + + // Set the region's extent equal to the Size in Bytes. + QualType ElementType = NE->getAllocatedType(); + ASTContext &AstContext = C.getASTContext(); + CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType); + + if (ElementCount.getAs<NonLoc>()) { + DefinedOrUnknownSVal Extent = Region->getExtent(svalBuilder); + // size in Bytes = ElementCount*TypeSize + SVal SizeInBytes = svalBuilder.evalBinOpNN( + State, BO_Mul, ElementCount.castAs<NonLoc>(), + svalBuilder.makeArrayIndex(TypeSize.getQuantity()), + svalBuilder.getArrayIndexType()); + DefinedOrUnknownSVal extentMatchesSize = svalBuilder.evalEQ( + State, Extent, SizeInBytes.castAs<DefinedOrUnknownSVal>()); + State = State->assume(extentMatchesSize, true); + } + return State; +} + +void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE, + CheckerContext &C) const { + + if (!ChecksEnabled[CK_NewDeleteChecker]) + if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol()) + checkUseAfterFree(Sym, C, DE->getArgument()); + + if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext())) + return; + + ProgramStateRef State = C.getState(); + bool ReleasedAllocated; + State = FreeMemAux(C, DE->getArgument(), DE, State, + /*Hold*/false, ReleasedAllocated); + + C.addTransition(State); +} + +static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) { + // If the first selector piece is one of the names below, assume that the + // object takes ownership of the memory, promising to eventually deallocate it + // with free(). + // Ex: [NSData dataWithBytesNoCopy:bytes length:10]; + // (...unless a 'freeWhenDone' parameter is false, but that's checked later.) + StringRef FirstSlot = Call.getSelector().getNameForSlot(0); + return FirstSlot == "dataWithBytesNoCopy" || + FirstSlot == "initWithBytesNoCopy" || + FirstSlot == "initWithCharactersNoCopy"; +} + +static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) { + Selector S = Call.getSelector(); + + // FIXME: We should not rely on fully-constrained symbols being folded. + for (unsigned i = 1; i < S.getNumArgs(); ++i) + if (S.getNameForSlot(i).equals("freeWhenDone")) + return !Call.getArgSVal(i).isZeroConstant(); + + return None; +} + +void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call, + CheckerContext &C) const { + if (C.wasInlined) + return; + + if (!isKnownDeallocObjCMethodName(Call)) + return; + + if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call)) + if (!*FreeWhenDone) + return; + + bool ReleasedAllocatedMemory; + ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0), + Call.getOriginExpr(), C.getState(), + /*Hold=*/true, ReleasedAllocatedMemory, + /*RetNullOnFailure=*/true); + + C.addTransition(State); +} + +ProgramStateRef +MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr *Att, + ProgramStateRef State) const { + if (!State) + return nullptr; + + if (Att->getModule() != II_malloc) + return nullptr; + + OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); + if (I != E) { + return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), UndefinedVal(), + State); + } + return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State); +} + +ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, + const CallExpr *CE, + const Expr *SizeEx, SVal Init, + ProgramStateRef State, + AllocationFamily Family) { + if (!State) + return nullptr; + + return MallocMemAux(C, CE, C.getSVal(SizeEx), Init, State, Family); +} + +ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C, + const CallExpr *CE, + SVal Size, SVal Init, + ProgramStateRef State, + AllocationFamily Family) { + if (!State) + return nullptr; + + // We expect the malloc functions to return a pointer. + if (!Loc::isLocType(CE->getType())) + return nullptr; + + // Bind the return value to the symbolic value from the heap region. + // TODO: We could rewrite post visit to eval call; 'malloc' does not have + // side effects other than what we model here. + unsigned Count = C.blockCount(); + SValBuilder &svalBuilder = C.getSValBuilder(); + const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); + DefinedSVal RetVal = svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count) + .castAs<DefinedSVal>(); + State = State->BindExpr(CE, C.getLocationContext(), RetVal); + + // Fill the region with the initialization value. + State = State->bindDefaultInitial(RetVal, Init, LCtx); + + // Set the region's extent equal to the Size parameter. + const SymbolicRegion *R = + dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion()); + if (!R) + return nullptr; + if (Optional<DefinedOrUnknownSVal> DefinedSize = + Size.getAs<DefinedOrUnknownSVal>()) { + SValBuilder &svalBuilder = C.getSValBuilder(); + DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); + DefinedOrUnknownSVal extentMatchesSize = + svalBuilder.evalEQ(State, Extent, *DefinedSize); + + State = State->assume(extentMatchesSize, true); + assert(State); + } + + return MallocUpdateRefState(C, CE, State, Family); +} + +ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C, + const Expr *E, + ProgramStateRef State, + AllocationFamily Family, + Optional<SVal> RetVal) { + if (!State) + return nullptr; + + // Get the return value. + if (!RetVal) + RetVal = C.getSVal(E); + + // We expect the malloc functions to return a pointer. + if (!RetVal->getAs<Loc>()) + return nullptr; + + SymbolRef Sym = RetVal->getAsLocSymbol(); + // This is a return value of a function that was not inlined, such as malloc() + // or new(). We've checked that in the caller. Therefore, it must be a symbol. + assert(Sym); + + // Set the symbol's state to Allocated. + return State->set<RegionState>(Sym, RefState::getAllocated(Family, E)); +} + +ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C, + const CallExpr *CE, + const OwnershipAttr *Att, + ProgramStateRef State) const { + if (!State) + return nullptr; + + if (Att->getModule() != II_malloc) + return nullptr; + + bool ReleasedAllocated = false; + + for (const auto &Arg : Att->args()) { + ProgramStateRef StateI = FreeMemAux( + C, CE, State, Arg.getASTIndex(), + Att->getOwnKind() == OwnershipAttr::Holds, ReleasedAllocated); + if (StateI) + State = StateI; + } + return State; +} + +ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, + const CallExpr *CE, + ProgramStateRef State, + unsigned Num, + bool Hold, + bool &ReleasedAllocated, + bool ReturnsNullOnFailure) const { + if (!State) + return nullptr; + + if (CE->getNumArgs() < (Num + 1)) + return nullptr; + + return FreeMemAux(C, CE->getArg(Num), CE, State, Hold, + ReleasedAllocated, ReturnsNullOnFailure); +} + +/// Checks if the previous call to free on the given symbol failed - if free +/// failed, returns true. Also, returns the corresponding return value symbol. +static bool didPreviousFreeFail(ProgramStateRef State, + SymbolRef Sym, SymbolRef &RetStatusSymbol) { + const SymbolRef *Ret = State->get<FreeReturnValue>(Sym); + if (Ret) { + assert(*Ret && "We should not store the null return symbol"); + ConstraintManager &CMgr = State->getConstraintManager(); + ConditionTruthVal FreeFailed = CMgr.isNull(State, *Ret); + RetStatusSymbol = *Ret; + return FreeFailed.isConstrainedTrue(); + } + return false; +} + +AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C, + const Stmt *S) const { + if (!S) + return AF_None; + + if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { + const FunctionDecl *FD = C.getCalleeDecl(CE); + + if (!FD) + FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl()); + + ASTContext &Ctx = C.getASTContext(); + + if (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Any)) + return AF_Malloc; + + if (isStandardNewDelete(FD, Ctx)) { + OverloadedOperatorKind Kind = FD->getOverloadedOperator(); + if (Kind == OO_New || Kind == OO_Delete) + return AF_CXXNew; + else if (Kind == OO_Array_New || Kind == OO_Array_Delete) + return AF_CXXNewArray; + } + + if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any)) + return AF_IfNameIndex; + + if (isCMemFunction(FD, Ctx, AF_Alloca, MemoryOperationKind::MOK_Any)) + return AF_Alloca; + + return AF_None; + } + + if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(S)) + return NE->isArray() ? AF_CXXNewArray : AF_CXXNew; + + if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(S)) + return DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew; + + if (isa<ObjCMessageExpr>(S)) + return AF_Malloc; + + return AF_None; +} + +bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C, + const Expr *E) const { + if (const CallExpr *CE = dyn_cast<CallExpr>(E)) { + // FIXME: This doesn't handle indirect calls. + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return false; + + os << *FD; + if (!FD->isOverloadedOperator()) + os << "()"; + return true; + } + + if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) { + if (Msg->isInstanceMessage()) + os << "-"; + else + os << "+"; + Msg->getSelector().print(os); + return true; + } + + if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) { + os << "'" + << getOperatorSpelling(NE->getOperatorNew()->getOverloadedOperator()) + << "'"; + return true; + } + + if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E)) { + os << "'" + << getOperatorSpelling(DE->getOperatorDelete()->getOverloadedOperator()) + << "'"; + return true; + } + + return false; +} + +void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C, + const Expr *E) const { + AllocationFamily Family = getAllocationFamily(C, E); + + switch(Family) { + case AF_Malloc: os << "malloc()"; return; + case AF_CXXNew: os << "'new'"; return; + case AF_CXXNewArray: os << "'new[]'"; return; + case AF_IfNameIndex: os << "'if_nameindex()'"; return; + case AF_InnerBuffer: os << "container-specific allocator"; return; + case AF_Alloca: + case AF_None: llvm_unreachable("not a deallocation expression"); + } +} + +void MallocChecker::printExpectedDeallocName(raw_ostream &os, + AllocationFamily Family) const { + switch(Family) { + case AF_Malloc: os << "free()"; return; + case AF_CXXNew: os << "'delete'"; return; + case AF_CXXNewArray: os << "'delete[]'"; return; + case AF_IfNameIndex: os << "'if_freenameindex()'"; return; + case AF_InnerBuffer: os << "container-specific deallocator"; return; + case AF_Alloca: + case AF_None: llvm_unreachable("suspicious argument"); + } +} + +ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C, + const Expr *ArgExpr, + const Expr *ParentExpr, + ProgramStateRef State, + bool Hold, + bool &ReleasedAllocated, + bool ReturnsNullOnFailure) const { + + if (!State) + return nullptr; + + SVal ArgVal = C.getSVal(ArgExpr); + if (!ArgVal.getAs<DefinedOrUnknownSVal>()) + return nullptr; + DefinedOrUnknownSVal location = ArgVal.castAs<DefinedOrUnknownSVal>(); + + // Check for null dereferences. + if (!location.getAs<Loc>()) + return nullptr; + + // The explicit NULL case, no operation is performed. + ProgramStateRef notNullState, nullState; + std::tie(notNullState, nullState) = State->assume(location); + if (nullState && !notNullState) + return nullptr; + + // Unknown values could easily be okay + // Undefined values are handled elsewhere + if (ArgVal.isUnknownOrUndef()) + return nullptr; + + const MemRegion *R = ArgVal.getAsRegion(); + + // Nonlocs can't be freed, of course. + // Non-region locations (labels and fixed addresses) also shouldn't be freed. + if (!R) { + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); + return nullptr; + } + + R = R->StripCasts(); + + // Blocks might show up as heap data, but should not be free()d + if (isa<BlockDataRegion>(R)) { + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); + return nullptr; + } + + const MemSpaceRegion *MS = R->getMemorySpace(); + + // Parameters, locals, statics, globals, and memory returned by + // __builtin_alloca() shouldn't be freed. + if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) { + // FIXME: at the time this code was written, malloc() regions were + // represented by conjured symbols, which are all in UnknownSpaceRegion. + // This means that there isn't actually anything from HeapSpaceRegion + // that should be freed, even though we allow it here. + // Of course, free() can work on memory allocated outside the current + // function, so UnknownSpaceRegion is always a possibility. + // False negatives are better than false positives. + + if (isa<AllocaRegion>(R)) + ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); + else + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); + + return nullptr; + } + + const SymbolicRegion *SrBase = dyn_cast<SymbolicRegion>(R->getBaseRegion()); + // Various cases could lead to non-symbol values here. + // For now, ignore them. + if (!SrBase) + return nullptr; + + SymbolRef SymBase = SrBase->getSymbol(); + const RefState *RsBase = State->get<RegionState>(SymBase); + SymbolRef PreviousRetStatusSymbol = nullptr; + + if (RsBase) { + + // Memory returned by alloca() shouldn't be freed. + if (RsBase->getAllocationFamily() == AF_Alloca) { + ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange()); + return nullptr; + } + + // Check for double free first. + if ((RsBase->isReleased() || RsBase->isRelinquished()) && + !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) { + ReportDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(), + SymBase, PreviousRetStatusSymbol); + return nullptr; + + // If the pointer is allocated or escaped, but we are now trying to free it, + // check that the call to free is proper. + } else if (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero() || + RsBase->isEscaped()) { + + // Check if an expected deallocation function matches the real one. + bool DeallocMatchesAlloc = + RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr); + if (!DeallocMatchesAlloc) { + ReportMismatchedDealloc(C, ArgExpr->getSourceRange(), + ParentExpr, RsBase, SymBase, Hold); + return nullptr; + } + + // Check if the memory location being freed is the actual location + // allocated, or an offset. + RegionOffset Offset = R->getAsOffset(); + if (Offset.isValid() && + !Offset.hasSymbolicOffset() && + Offset.getOffset() != 0) { + const Expr *AllocExpr = cast<Expr>(RsBase->getStmt()); + ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr, + AllocExpr); + return nullptr; + } + } + } + + if (SymBase->getType()->isFunctionPointerType()) { + ReportFunctionPointerFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr); + return nullptr; + } + + ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() || + RsBase->isAllocatedOfSizeZero()); + + // Clean out the info on previous call to free return info. + State = State->remove<FreeReturnValue>(SymBase); + + // Keep track of the return value. If it is NULL, we will know that free + // failed. + if (ReturnsNullOnFailure) { + SVal RetVal = C.getSVal(ParentExpr); + SymbolRef RetStatusSymbol = RetVal.getAsSymbol(); + if (RetStatusSymbol) { + C.getSymbolManager().addSymbolDependency(SymBase, RetStatusSymbol); + State = State->set<FreeReturnValue>(SymBase, RetStatusSymbol); + } + } + + AllocationFamily Family = RsBase ? RsBase->getAllocationFamily() + : getAllocationFamily(C, ParentExpr); + // Normal free. + if (Hold) + return State->set<RegionState>(SymBase, + RefState::getRelinquished(Family, + ParentExpr)); + + return State->set<RegionState>(SymBase, + RefState::getReleased(Family, ParentExpr)); +} + +Optional<MallocChecker::CheckKind> +MallocChecker::getCheckIfTracked(AllocationFamily Family, + bool IsALeakCheck) const { + switch (Family) { + case AF_Malloc: + case AF_Alloca: + case AF_IfNameIndex: { + if (ChecksEnabled[CK_MallocChecker]) + return CK_MallocChecker; + return None; + } + case AF_CXXNew: + case AF_CXXNewArray: { + if (IsALeakCheck) { + if (ChecksEnabled[CK_NewDeleteLeaksChecker]) + return CK_NewDeleteLeaksChecker; + } + else { + if (ChecksEnabled[CK_NewDeleteChecker]) + return CK_NewDeleteChecker; + } + return None; + } + case AF_InnerBuffer: { + if (ChecksEnabled[CK_InnerPointerChecker]) + return CK_InnerPointerChecker; + return None; + } + case AF_None: { + llvm_unreachable("no family"); + } + } + llvm_unreachable("unhandled family"); +} + +Optional<MallocChecker::CheckKind> +MallocChecker::getCheckIfTracked(CheckerContext &C, + const Stmt *AllocDeallocStmt, + bool IsALeakCheck) const { + return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt), + IsALeakCheck); +} + +Optional<MallocChecker::CheckKind> +MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym, + bool IsALeakCheck) const { + if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) + return CK_MallocChecker; + + const RefState *RS = C.getState()->get<RegionState>(Sym); + assert(RS); + return getCheckIfTracked(RS->getAllocationFamily(), IsALeakCheck); +} + +bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) { + if (Optional<nonloc::ConcreteInt> IntVal = V.getAs<nonloc::ConcreteInt>()) + os << "an integer (" << IntVal->getValue() << ")"; + else if (Optional<loc::ConcreteInt> ConstAddr = V.getAs<loc::ConcreteInt>()) + os << "a constant address (" << ConstAddr->getValue() << ")"; + else if (Optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>()) + os << "the address of the label '" << Label->getLabel()->getName() << "'"; + else + return false; + + return true; +} + +bool MallocChecker::SummarizeRegion(raw_ostream &os, + const MemRegion *MR) { + switch (MR->getKind()) { + case MemRegion::FunctionCodeRegionKind: { + const NamedDecl *FD = cast<FunctionCodeRegion>(MR)->getDecl(); + if (FD) + os << "the address of the function '" << *FD << '\''; + else + os << "the address of a function"; + return true; + } + case MemRegion::BlockCodeRegionKind: + os << "block text"; + return true; + case MemRegion::BlockDataRegionKind: + // FIXME: where the block came from? + os << "a block"; + return true; + default: { + const MemSpaceRegion *MS = MR->getMemorySpace(); + + if (isa<StackLocalsSpaceRegion>(MS)) { + const VarRegion *VR = dyn_cast<VarRegion>(MR); + const VarDecl *VD; + if (VR) + VD = VR->getDecl(); + else + VD = nullptr; + + if (VD) + os << "the address of the local variable '" << VD->getName() << "'"; + else + os << "the address of a local stack variable"; + return true; + } + + if (isa<StackArgumentsSpaceRegion>(MS)) { + const VarRegion *VR = dyn_cast<VarRegion>(MR); + const VarDecl *VD; + if (VR) + VD = VR->getDecl(); + else + VD = nullptr; + + if (VD) + os << "the address of the parameter '" << VD->getName() << "'"; + else + os << "the address of a parameter"; + return true; + } + + if (isa<GlobalsSpaceRegion>(MS)) { + const VarRegion *VR = dyn_cast<VarRegion>(MR); + const VarDecl *VD; + if (VR) + VD = VR->getDecl(); + else + VD = nullptr; + + if (VD) { + if (VD->isStaticLocal()) + os << "the address of the static variable '" << VD->getName() << "'"; + else + os << "the address of the global variable '" << VD->getName() << "'"; + } else + os << "the address of a global variable"; + return true; + } + + return false; + } + } +} + +void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, + SourceRange Range, + const Expr *DeallocExpr) const { + + if (!ChecksEnabled[CK_MallocChecker] && + !ChecksEnabled[CK_NewDeleteChecker]) + return; + + Optional<MallocChecker::CheckKind> CheckKind = + getCheckIfTracked(C, DeallocExpr); + if (!CheckKind.hasValue()) + return; + + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_BadFree[*CheckKind]) + BT_BadFree[*CheckKind].reset(new BugType( + CheckNames[*CheckKind], "Bad free", categories::MemoryError)); + + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + + const MemRegion *MR = ArgVal.getAsRegion(); + while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR)) + MR = ER->getSuperRegion(); + + os << "Argument to "; + if (!printAllocDeallocName(os, C, DeallocExpr)) + os << "deallocator"; + + os << " is "; + bool Summarized = MR ? SummarizeRegion(os, MR) + : SummarizeValue(os, ArgVal); + if (Summarized) + os << ", which is not memory allocated by "; + else + os << "not memory allocated by "; + + printExpectedAllocName(os, C, DeallocExpr); + + auto R = llvm::make_unique<BugReport>(*BT_BadFree[*CheckKind], os.str(), N); + R->markInteresting(MR); + R->addRange(Range); + C.emitReport(std::move(R)); + } +} + +void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal, + SourceRange Range) const { + + Optional<MallocChecker::CheckKind> CheckKind; + + if (ChecksEnabled[CK_MallocChecker]) + CheckKind = CK_MallocChecker; + else if (ChecksEnabled[CK_MismatchedDeallocatorChecker]) + CheckKind = CK_MismatchedDeallocatorChecker; + else + return; + + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_FreeAlloca[*CheckKind]) + BT_FreeAlloca[*CheckKind].reset(new BugType( + CheckNames[*CheckKind], "Free alloca()", categories::MemoryError)); + + auto R = llvm::make_unique<BugReport>( + *BT_FreeAlloca[*CheckKind], + "Memory allocated by alloca() should not be deallocated", N); + R->markInteresting(ArgVal.getAsRegion()); + R->addRange(Range); + C.emitReport(std::move(R)); + } +} + +void MallocChecker::ReportMismatchedDealloc(CheckerContext &C, + SourceRange Range, + const Expr *DeallocExpr, + const RefState *RS, + SymbolRef Sym, + bool OwnershipTransferred) const { + + if (!ChecksEnabled[CK_MismatchedDeallocatorChecker]) + return; + + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_MismatchedDealloc) + BT_MismatchedDealloc.reset( + new BugType(CheckNames[CK_MismatchedDeallocatorChecker], + "Bad deallocator", categories::MemoryError)); + + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + + const Expr *AllocExpr = cast<Expr>(RS->getStmt()); + SmallString<20> AllocBuf; + llvm::raw_svector_ostream AllocOs(AllocBuf); + SmallString<20> DeallocBuf; + llvm::raw_svector_ostream DeallocOs(DeallocBuf); + + if (OwnershipTransferred) { + if (printAllocDeallocName(DeallocOs, C, DeallocExpr)) + os << DeallocOs.str() << " cannot"; + else + os << "Cannot"; + + os << " take ownership of memory"; + + if (printAllocDeallocName(AllocOs, C, AllocExpr)) + os << " allocated by " << AllocOs.str(); + } else { + os << "Memory"; + if (printAllocDeallocName(AllocOs, C, AllocExpr)) + os << " allocated by " << AllocOs.str(); + + os << " should be deallocated by "; + printExpectedDeallocName(os, RS->getAllocationFamily()); + + if (printAllocDeallocName(DeallocOs, C, DeallocExpr)) + os << ", not " << DeallocOs.str(); + } + + auto R = llvm::make_unique<BugReport>(*BT_MismatchedDealloc, os.str(), N); + R->markInteresting(Sym); + R->addRange(Range); + R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); + C.emitReport(std::move(R)); + } +} + +void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal, + SourceRange Range, const Expr *DeallocExpr, + const Expr *AllocExpr) const { + + + if (!ChecksEnabled[CK_MallocChecker] && + !ChecksEnabled[CK_NewDeleteChecker]) + return; + + Optional<MallocChecker::CheckKind> CheckKind = + getCheckIfTracked(C, AllocExpr); + if (!CheckKind.hasValue()) + return; + + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + if (!BT_OffsetFree[*CheckKind]) + BT_OffsetFree[*CheckKind].reset(new BugType( + CheckNames[*CheckKind], "Offset free", categories::MemoryError)); + + SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + SmallString<20> AllocNameBuf; + llvm::raw_svector_ostream AllocNameOs(AllocNameBuf); + + const MemRegion *MR = ArgVal.getAsRegion(); + assert(MR && "Only MemRegion based symbols can have offset free errors"); + + RegionOffset Offset = MR->getAsOffset(); + assert((Offset.isValid() && + !Offset.hasSymbolicOffset() && + Offset.getOffset() != 0) && + "Only symbols with a valid offset can have offset free errors"); + + int offsetBytes = Offset.getOffset() / C.getASTContext().getCharWidth(); + + os << "Argument to "; + if (!printAllocDeallocName(os, C, DeallocExpr)) + os << "deallocator"; + os << " is offset by " + << offsetBytes + << " " + << ((abs(offsetBytes) > 1) ? "bytes" : "byte") + << " from the start of "; + if (AllocExpr && printAllocDeallocName(AllocNameOs, C, AllocExpr)) + os << "memory allocated by " << AllocNameOs.str(); + else + os << "allocated memory"; + + auto R = llvm::make_unique<BugReport>(*BT_OffsetFree[*CheckKind], os.str(), N); + R->markInteresting(MR->getBaseRegion()); + R->addRange(Range); + C.emitReport(std::move(R)); +} + +void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range, + SymbolRef Sym) const { + + if (!ChecksEnabled[CK_MallocChecker] && + !ChecksEnabled[CK_NewDeleteChecker] && + !ChecksEnabled[CK_InnerPointerChecker]) + return; + + Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind.hasValue()) + return; + + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_UseFree[*CheckKind]) + BT_UseFree[*CheckKind].reset(new BugType( + CheckNames[*CheckKind], "Use-after-free", categories::MemoryError)); + + AllocationFamily AF = + C.getState()->get<RegionState>(Sym)->getAllocationFamily(); + + auto R = llvm::make_unique<BugReport>(*BT_UseFree[*CheckKind], + AF == AF_InnerBuffer + ? "Inner pointer of container used after re/deallocation" + : "Use of memory after it is freed", + N); + + R->markInteresting(Sym); + R->addRange(Range); + R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); + + if (AF == AF_InnerBuffer) + R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym)); + + C.emitReport(std::move(R)); + } +} + +void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range, + bool Released, SymbolRef Sym, + SymbolRef PrevSym) const { + + if (!ChecksEnabled[CK_MallocChecker] && + !ChecksEnabled[CK_NewDeleteChecker]) + return; + + Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind.hasValue()) + return; + + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_DoubleFree[*CheckKind]) + BT_DoubleFree[*CheckKind].reset(new BugType( + CheckNames[*CheckKind], "Double free", categories::MemoryError)); + + auto R = llvm::make_unique<BugReport>( + *BT_DoubleFree[*CheckKind], + (Released ? "Attempt to free released memory" + : "Attempt to free non-owned memory"), + N); + R->addRange(Range); + R->markInteresting(Sym); + if (PrevSym) + R->markInteresting(PrevSym); + R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); + C.emitReport(std::move(R)); + } +} + +void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const { + + if (!ChecksEnabled[CK_NewDeleteChecker]) + return; + + Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + if (!CheckKind.hasValue()) + return; + + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_DoubleDelete) + BT_DoubleDelete.reset(new BugType(CheckNames[CK_NewDeleteChecker], + "Double delete", + categories::MemoryError)); + + auto R = llvm::make_unique<BugReport>( + *BT_DoubleDelete, "Attempt to delete released memory", N); + + R->markInteresting(Sym); + R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); + C.emitReport(std::move(R)); + } +} + +void MallocChecker::ReportUseZeroAllocated(CheckerContext &C, + SourceRange Range, + SymbolRef Sym) const { + + if (!ChecksEnabled[CK_MallocChecker] && + !ChecksEnabled[CK_NewDeleteChecker]) + return; + + Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym); + + if (!CheckKind.hasValue()) + return; + + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_UseZerroAllocated[*CheckKind]) + BT_UseZerroAllocated[*CheckKind].reset( + new BugType(CheckNames[*CheckKind], "Use of zero allocated", + categories::MemoryError)); + + auto R = llvm::make_unique<BugReport>(*BT_UseZerroAllocated[*CheckKind], + "Use of zero-allocated memory", N); + + R->addRange(Range); + if (Sym) { + R->markInteresting(Sym); + R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym)); + } + C.emitReport(std::move(R)); + } +} + +void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal, + SourceRange Range, + const Expr *FreeExpr) const { + if (!ChecksEnabled[CK_MallocChecker]) + return; + + Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, FreeExpr); + if (!CheckKind.hasValue()) + return; + + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_BadFree[*CheckKind]) + BT_BadFree[*CheckKind].reset(new BugType( + CheckNames[*CheckKind], "Bad free", categories::MemoryError)); + + SmallString<100> Buf; + llvm::raw_svector_ostream Os(Buf); + + const MemRegion *MR = ArgVal.getAsRegion(); + while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR)) + MR = ER->getSuperRegion(); + + Os << "Argument to "; + if (!printAllocDeallocName(Os, C, FreeExpr)) + Os << "deallocator"; + + Os << " is a function pointer"; + + auto R = llvm::make_unique<BugReport>(*BT_BadFree[*CheckKind], Os.str(), N); + R->markInteresting(MR); + R->addRange(Range); + C.emitReport(std::move(R)); + } +} + +ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C, + const CallExpr *CE, + bool FreesOnFail, + ProgramStateRef State, + bool SuffixWithN) const { + if (!State) + return nullptr; + + if (SuffixWithN && CE->getNumArgs() < 3) + return nullptr; + else if (CE->getNumArgs() < 2) + return nullptr; + + const Expr *arg0Expr = CE->getArg(0); + SVal Arg0Val = C.getSVal(arg0Expr); + if (!Arg0Val.getAs<DefinedOrUnknownSVal>()) + return nullptr; + DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>(); + + SValBuilder &svalBuilder = C.getSValBuilder(); + + DefinedOrUnknownSVal PtrEQ = + svalBuilder.evalEQ(State, arg0Val, svalBuilder.makeNull()); + + // Get the size argument. + const Expr *Arg1 = CE->getArg(1); + + // Get the value of the size argument. + SVal TotalSize = C.getSVal(Arg1); + if (SuffixWithN) + TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2)); + if (!TotalSize.getAs<DefinedOrUnknownSVal>()) + return nullptr; + + // Compare the size argument to 0. + DefinedOrUnknownSVal SizeZero = + svalBuilder.evalEQ(State, TotalSize.castAs<DefinedOrUnknownSVal>(), + svalBuilder.makeIntValWithPtrWidth(0, false)); + + ProgramStateRef StatePtrIsNull, StatePtrNotNull; + std::tie(StatePtrIsNull, StatePtrNotNull) = State->assume(PtrEQ); + ProgramStateRef StateSizeIsZero, StateSizeNotZero; + std::tie(StateSizeIsZero, StateSizeNotZero) = State->assume(SizeZero); + // We only assume exceptional states if they are definitely true; if the + // state is under-constrained, assume regular realloc behavior. + bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull; + bool SizeIsZero = StateSizeIsZero && !StateSizeNotZero; + + // If the ptr is NULL and the size is not 0, the call is equivalent to + // malloc(size). + if (PrtIsNull && !SizeIsZero) { + ProgramStateRef stateMalloc = MallocMemAux(C, CE, TotalSize, + UndefinedVal(), StatePtrIsNull); + return stateMalloc; + } + + if (PrtIsNull && SizeIsZero) + return State; + + // Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size). + assert(!PrtIsNull); + SymbolRef FromPtr = arg0Val.getAsSymbol(); + SVal RetVal = C.getSVal(CE); + SymbolRef ToPtr = RetVal.getAsSymbol(); + if (!FromPtr || !ToPtr) + return nullptr; + + bool ReleasedAllocated = false; + + // If the size is 0, free the memory. + if (SizeIsZero) + if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero, 0, + false, ReleasedAllocated)){ + // The semantics of the return value are: + // If size was equal to 0, either NULL or a pointer suitable to be passed + // to free() is returned. We just free the input pointer and do not add + // any constrains on the output pointer. + return stateFree; + } + + // Default behavior. + if (ProgramStateRef stateFree = + FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) { + + ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize, + UnknownVal(), stateFree); + if (!stateRealloc) + return nullptr; + + ReallocPairKind Kind = RPToBeFreedAfterFailure; + if (FreesOnFail) + Kind = RPIsFreeOnFailure; + else if (!ReleasedAllocated) + Kind = RPDoNotTrackAfterFailure; + + // Record the info about the reallocated symbol so that we could properly + // process failed reallocation. + stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr, + ReallocPair(FromPtr, Kind)); + // The reallocated symbol should stay alive for as long as the new symbol. + C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr); + return stateRealloc; + } + return nullptr; +} + +ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE, + ProgramStateRef State) { + if (!State) + return nullptr; + + if (CE->getNumArgs() < 2) + return nullptr; + + SValBuilder &svalBuilder = C.getSValBuilder(); + SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); + SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1)); + + return MallocMemAux(C, CE, TotalSize, zeroVal, State); +} + +LeakInfo +MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym, + CheckerContext &C) const { + const LocationContext *LeakContext = N->getLocationContext(); + // Walk the ExplodedGraph backwards and find the first node that referred to + // the tracked symbol. + const ExplodedNode *AllocNode = N; + const MemRegion *ReferenceRegion = nullptr; + + while (N) { + ProgramStateRef State = N->getState(); + if (!State->get<RegionState>(Sym)) + break; + + // Find the most recent expression bound to the symbol in the current + // context. + if (!ReferenceRegion) { + if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) { + SVal Val = State->getSVal(MR); + if (Val.getAsLocSymbol() == Sym) { + const VarRegion* VR = MR->getBaseRegion()->getAs<VarRegion>(); + // Do not show local variables belonging to a function other than + // where the error is reported. + if (!VR || + (VR->getStackFrame() == LeakContext->getStackFrame())) + ReferenceRegion = MR; + } + } + } + + // Allocation node, is the last node in the current or parent context in + // which the symbol was tracked. + const LocationContext *NContext = N->getLocationContext(); + if (NContext == LeakContext || + NContext->isParentOf(LeakContext)) + AllocNode = N; + N = N->pred_empty() ? nullptr : *(N->pred_begin()); + } + + return LeakInfo(AllocNode, ReferenceRegion); +} + +void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N, + CheckerContext &C) const { + + if (!ChecksEnabled[CK_MallocChecker] && + !ChecksEnabled[CK_NewDeleteLeaksChecker]) + return; + + const RefState *RS = C.getState()->get<RegionState>(Sym); + assert(RS && "cannot leak an untracked symbol"); + AllocationFamily Family = RS->getAllocationFamily(); + + if (Family == AF_Alloca) + return; + + Optional<MallocChecker::CheckKind> + CheckKind = getCheckIfTracked(Family, true); + + if (!CheckKind.hasValue()) + return; + + assert(N); + if (!BT_Leak[*CheckKind]) { + BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak", + categories::MemoryError)); + // Leaks should not be reported if they are post-dominated by a sink: + // (1) Sinks are higher importance bugs. + // (2) NoReturnFunctionChecker uses sink nodes to represent paths ending + // with __noreturn functions such as assert() or exit(). We choose not + // to report leaks on such paths. + BT_Leak[*CheckKind]->setSuppressOnSink(true); + } + + // Most bug reports are cached at the location where they occurred. + // With leaks, we want to unique them by the location where they were + // allocated, and only report a single path. + PathDiagnosticLocation LocUsedForUniqueing; + const ExplodedNode *AllocNode = nullptr; + const MemRegion *Region = nullptr; + std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C); + + const Stmt *AllocationStmt = PathDiagnosticLocation::getStmt(AllocNode); + if (AllocationStmt) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt, + C.getSourceManager(), + AllocNode->getLocationContext()); + + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + if (Region && Region->canPrintPretty()) { + os << "Potential leak of memory pointed to by "; + Region->printPretty(os); + } else { + os << "Potential memory leak"; + } + + auto R = llvm::make_unique<BugReport>( + *BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing, + AllocNode->getLocationContext()->getDecl()); + R->markInteresting(Sym); + R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym, true)); + C.emitReport(std::move(R)); +} + +void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const +{ + ProgramStateRef state = C.getState(); + RegionStateTy OldRS = state->get<RegionState>(); + RegionStateTy::Factory &F = state->get_context<RegionState>(); + + RegionStateTy RS = OldRS; + SmallVector<SymbolRef, 2> Errors; + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + if (SymReaper.isDead(I->first)) { + if (I->second.isAllocated() || I->second.isAllocatedOfSizeZero()) + Errors.push_back(I->first); + // Remove the dead symbol from the map. + RS = F.remove(RS, I->first); + } + } + + if (RS == OldRS) { + // We shouldn't have touched other maps yet. + assert(state->get<ReallocPairs>() == + C.getState()->get<ReallocPairs>()); + assert(state->get<FreeReturnValue>() == + C.getState()->get<FreeReturnValue>()); + return; + } + + // Cleanup the Realloc Pairs Map. + ReallocPairsTy RP = state->get<ReallocPairs>(); + for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) { + if (SymReaper.isDead(I->first) || + SymReaper.isDead(I->second.ReallocatedSym)) { + state = state->remove<ReallocPairs>(I->first); + } + } + + // Cleanup the FreeReturnValue Map. + FreeReturnValueTy FR = state->get<FreeReturnValue>(); + for (FreeReturnValueTy::iterator I = FR.begin(), E = FR.end(); I != E; ++I) { + if (SymReaper.isDead(I->first) || + SymReaper.isDead(I->second)) { + state = state->remove<FreeReturnValue>(I->first); + } + } + + // Generate leak node. + ExplodedNode *N = C.getPredecessor(); + if (!Errors.empty()) { + static CheckerProgramPointTag Tag("MallocChecker", "DeadSymbolsLeak"); + N = C.generateNonFatalErrorNode(C.getState(), &Tag); + if (N) { + for (SmallVectorImpl<SymbolRef>::iterator + I = Errors.begin(), E = Errors.end(); I != E; ++I) { + reportLeak(*I, N, C); + } + } + } + + C.addTransition(state->set<RegionState>(RS), N); +} + +void MallocChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + + if (const CXXDestructorCall *DC = dyn_cast<CXXDestructorCall>(&Call)) { + SymbolRef Sym = DC->getCXXThisVal().getAsSymbol(); + if (!Sym || checkDoubleDelete(Sym, C)) + return; + } + + // We will check for double free in the post visit. + if (const AnyFunctionCall *FC = dyn_cast<AnyFunctionCall>(&Call)) { + const FunctionDecl *FD = FC->getDecl(); + if (!FD) + return; + + ASTContext &Ctx = C.getASTContext(); + if (ChecksEnabled[CK_MallocChecker] && + (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) || + isCMemFunction(FD, Ctx, AF_IfNameIndex, + MemoryOperationKind::MOK_Free))) + return; + } + + // Check if the callee of a method is deleted. + if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) { + SymbolRef Sym = CC->getCXXThisVal().getAsSymbol(); + if (!Sym || checkUseAfterFree(Sym, C, CC->getCXXThisExpr())) + return; + } + + // Check arguments for being used after free. + for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) { + SVal ArgSVal = Call.getArgSVal(I); + if (ArgSVal.getAs<Loc>()) { + SymbolRef Sym = ArgSVal.getAsSymbol(); + if (!Sym) + continue; + if (checkUseAfterFree(Sym, C, Call.getArgExpr(I))) + return; + } + } +} + +void MallocChecker::checkPreStmt(const ReturnStmt *S, + CheckerContext &C) const { + checkEscapeOnReturn(S, C); +} + +// In the CFG, automatic destructors come after the return statement. +// This callback checks for returning memory that is freed by automatic +// destructors, as those cannot be reached in checkPreStmt(). +void MallocChecker::checkEndFunction(const ReturnStmt *S, + CheckerContext &C) const { + checkEscapeOnReturn(S, C); +} + +void MallocChecker::checkEscapeOnReturn(const ReturnStmt *S, + CheckerContext &C) const { + if (!S) + return; + + const Expr *E = S->getRetValue(); + if (!E) + return; + + // Check if we are returning a symbol. + ProgramStateRef State = C.getState(); + SVal RetVal = C.getSVal(E); + SymbolRef Sym = RetVal.getAsSymbol(); + if (!Sym) + // If we are returning a field of the allocated struct or an array element, + // the callee could still free the memory. + // TODO: This logic should be a part of generic symbol escape callback. + if (const MemRegion *MR = RetVal.getAsRegion()) + if (isa<FieldRegion>(MR) || isa<ElementRegion>(MR)) + if (const SymbolicRegion *BMR = + dyn_cast<SymbolicRegion>(MR->getBaseRegion())) + Sym = BMR->getSymbol(); + + // Check if we are returning freed memory. + if (Sym) + checkUseAfterFree(Sym, C, E); +} + +// TODO: Blocks should be either inlined or should call invalidate regions +// upon invocation. After that's in place, special casing here will not be +// needed. +void MallocChecker::checkPostStmt(const BlockExpr *BE, + CheckerContext &C) const { + + // Scan the BlockDecRefExprs for any object the retain count checker + // may be tracking. + if (!BE->getBlockDecl()->hasCaptures()) + return; + + ProgramStateRef state = C.getState(); + const BlockDataRegion *R = + cast<BlockDataRegion>(C.getSVal(BE).getAsRegion()); + + BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), + E = R->referenced_vars_end(); + + if (I == E) + return; + + SmallVector<const MemRegion*, 10> Regions; + const LocationContext *LC = C.getLocationContext(); + MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); + + for ( ; I != E; ++I) { + const VarRegion *VR = I.getCapturedRegion(); + if (VR->getSuperRegion() == R) { + VR = MemMgr.getVarRegion(VR->getDecl(), LC); + } + Regions.push_back(VR); + } + + state = + state->scanReachableSymbols<StopTrackingCallback>(Regions).getState(); + C.addTransition(state); +} + +bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const { + assert(Sym); + const RefState *RS = C.getState()->get<RegionState>(Sym); + return (RS && RS->isReleased()); +} + +bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C, + const Stmt *S) const { + + if (isReleased(Sym, C)) { + ReportUseAfterFree(C, S->getSourceRange(), Sym); + return true; + } + + return false; +} + +void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C, + const Stmt *S) const { + assert(Sym); + + if (const RefState *RS = C.getState()->get<RegionState>(Sym)) { + if (RS->isAllocatedOfSizeZero()) + ReportUseZeroAllocated(C, RS->getStmt()->getSourceRange(), Sym); + } + else if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) { + ReportUseZeroAllocated(C, S->getSourceRange(), Sym); + } +} + +bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const { + + if (isReleased(Sym, C)) { + ReportDoubleDelete(C, Sym); + return true; + } + return false; +} + +// Check if the location is a freed symbolic region. +void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S, + CheckerContext &C) const { + SymbolRef Sym = l.getLocSymbolInBase(); + if (Sym) { + checkUseAfterFree(Sym, C, S); + checkUseZeroAllocated(Sym, C, S); + } +} + +// If a symbolic region is assumed to NULL (or another constant), stop tracking +// it - assuming that allocation failed on this path. +ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state, + SVal Cond, + bool Assumption) const { + RegionStateTy RS = state->get<RegionState>(); + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + // If the symbol is assumed to be NULL, remove it from consideration. + ConstraintManager &CMgr = state->getConstraintManager(); + ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey()); + if (AllocFailed.isConstrainedTrue()) + state = state->remove<RegionState>(I.getKey()); + } + + // Realloc returns 0 when reallocation fails, which means that we should + // restore the state of the pointer being reallocated. + ReallocPairsTy RP = state->get<ReallocPairs>(); + for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) { + // If the symbol is assumed to be NULL, remove it from consideration. + ConstraintManager &CMgr = state->getConstraintManager(); + ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey()); + if (!AllocFailed.isConstrainedTrue()) + continue; + + SymbolRef ReallocSym = I.getData().ReallocatedSym; + if (const RefState *RS = state->get<RegionState>(ReallocSym)) { + if (RS->isReleased()) { + if (I.getData().Kind == RPToBeFreedAfterFailure) + state = state->set<RegionState>(ReallocSym, + RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt())); + else if (I.getData().Kind == RPDoNotTrackAfterFailure) + state = state->remove<RegionState>(ReallocSym); + else + assert(I.getData().Kind == RPIsFreeOnFailure); + } + } + state = state->remove<ReallocPairs>(I.getKey()); + } + + return state; +} + +bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly( + const CallEvent *Call, + ProgramStateRef State, + SymbolRef &EscapingSymbol) const { + assert(Call); + EscapingSymbol = nullptr; + + // For now, assume that any C++ or block call can free memory. + // TODO: If we want to be more optimistic here, we'll need to make sure that + // regions escape to C++ containers. They seem to do that even now, but for + // mysterious reasons. + if (!(isa<SimpleFunctionCall>(Call) || isa<ObjCMethodCall>(Call))) + return true; + + // Check Objective-C messages by selector name. + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { + // If it's not a framework call, or if it takes a callback, assume it + // can free memory. + if (!Call->isInSystemHeader() || Call->argumentsMayEscape()) + return true; + + // If it's a method we know about, handle it explicitly post-call. + // This should happen before the "freeWhenDone" check below. + if (isKnownDeallocObjCMethodName(*Msg)) + return false; + + // If there's a "freeWhenDone" parameter, but the method isn't one we know + // about, we can't be sure that the object will use free() to deallocate the + // memory, so we can't model it explicitly. The best we can do is use it to + // decide whether the pointer escapes. + if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg)) + return *FreeWhenDone; + + // If the first selector piece ends with "NoCopy", and there is no + // "freeWhenDone" parameter set to zero, we know ownership is being + // transferred. Again, though, we can't be sure that the object will use + // free() to deallocate the memory, so we can't model it explicitly. + StringRef FirstSlot = Msg->getSelector().getNameForSlot(0); + if (FirstSlot.endswith("NoCopy")) + return true; + + // If the first selector starts with addPointer, insertPointer, + // or replacePointer, assume we are dealing with NSPointerArray or similar. + // This is similar to C++ containers (vector); we still might want to check + // that the pointers get freed by following the container itself. + if (FirstSlot.startswith("addPointer") || + FirstSlot.startswith("insertPointer") || + FirstSlot.startswith("replacePointer") || + FirstSlot.equals("valueWithPointer")) { + return true; + } + + // We should escape receiver on call to 'init'. This is especially relevant + // to the receiver, as the corresponding symbol is usually not referenced + // after the call. + if (Msg->getMethodFamily() == OMF_init) { + EscapingSymbol = Msg->getReceiverSVal().getAsSymbol(); + return true; + } + + // Otherwise, assume that the method does not free memory. + // Most framework methods do not free memory. + return false; + } + + // At this point the only thing left to handle is straight function calls. + const FunctionDecl *FD = cast<SimpleFunctionCall>(Call)->getDecl(); + if (!FD) + return true; + + ASTContext &ASTC = State->getStateManager().getContext(); + + // If it's one of the allocation functions we can reason about, we model + // its behavior explicitly. + if (isMemFunction(FD, ASTC)) + return false; + + // If it's not a system call, assume it frees memory. + if (!Call->isInSystemHeader()) + return true; + + // White list the system functions whose arguments escape. + const IdentifierInfo *II = FD->getIdentifier(); + if (!II) + return true; + StringRef FName = II->getName(); + + // White list the 'XXXNoCopy' CoreFoundation functions. + // We specifically check these before + if (FName.endswith("NoCopy")) { + // Look for the deallocator argument. We know that the memory ownership + // is not transferred only if the deallocator argument is + // 'kCFAllocatorNull'. + for (unsigned i = 1; i < Call->getNumArgs(); ++i) { + const Expr *ArgE = Call->getArgExpr(i)->IgnoreParenCasts(); + if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) { + StringRef DeallocatorName = DE->getFoundDecl()->getName(); + if (DeallocatorName == "kCFAllocatorNull") + return false; + } + } + return true; + } + + // Associating streams with malloced buffers. The pointer can escape if + // 'closefn' is specified (and if that function does free memory), + // but it will not if closefn is not specified. + // Currently, we do not inspect the 'closefn' function (PR12101). + if (FName == "funopen") + if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0)) + return false; + + // Do not warn on pointers passed to 'setbuf' when used with std streams, + // these leaks might be intentional when setting the buffer for stdio. + // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer + if (FName == "setbuf" || FName =="setbuffer" || + FName == "setlinebuf" || FName == "setvbuf") { + if (Call->getNumArgs() >= 1) { + const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts(); + if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE)) + if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl())) + if (D->getCanonicalDecl()->getName().find("std") != StringRef::npos) + return true; + } + } + + // A bunch of other functions which either take ownership of a pointer or + // wrap the result up in a struct or object, meaning it can be freed later. + // (See RetainCountChecker.) Not all the parameters here are invalidated, + // but the Malloc checker cannot differentiate between them. The right way + // of doing this would be to implement a pointer escapes callback. + if (FName == "CGBitmapContextCreate" || + FName == "CGBitmapContextCreateWithData" || + FName == "CVPixelBufferCreateWithBytes" || + FName == "CVPixelBufferCreateWithPlanarBytes" || + FName == "OSAtomicEnqueue") { + return true; + } + + if (FName == "postEvent" && + FD->getQualifiedNameAsString() == "QCoreApplication::postEvent") { + return true; + } + + if (FName == "postEvent" && + FD->getQualifiedNameAsString() == "QCoreApplication::postEvent") { + return true; + } + + if (FName == "connectImpl" && + FD->getQualifiedNameAsString() == "QObject::connectImpl") { + return true; + } + + // Handle cases where we know a buffer's /address/ can escape. + // Note that the above checks handle some special cases where we know that + // even though the address escapes, it's still our responsibility to free the + // buffer. + if (Call->argumentsMayEscape()) + return true; + + // Otherwise, assume that the function does not free memory. + // Most system calls do not free the memory. + return false; +} + +static bool retTrue(const RefState *RS) { + return true; +} + +static bool checkIfNewOrNewArrayFamily(const RefState *RS) { + return (RS->getAllocationFamily() == AF_CXXNewArray || + RS->getAllocationFamily() == AF_CXXNew); +} + +ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + return checkPointerEscapeAux(State, Escaped, Call, Kind, &retTrue); +} + +ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + return checkPointerEscapeAux(State, Escaped, Call, Kind, + &checkIfNewOrNewArrayFamily); +} + +ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + bool(*CheckRefState)(const RefState*)) const { + // If we know that the call does not free memory, or we want to process the + // call later, keep tracking the top level arguments. + SymbolRef EscapingSymbol = nullptr; + if (Kind == PSK_DirectEscapeOnCall && + !mayFreeAnyEscapedMemoryOrIsModeledExplicitly(Call, State, + EscapingSymbol) && + !EscapingSymbol) { + return State; + } + + for (InvalidatedSymbols::const_iterator I = Escaped.begin(), + E = Escaped.end(); + I != E; ++I) { + SymbolRef sym = *I; + + if (EscapingSymbol && EscapingSymbol != sym) + continue; + + if (const RefState *RS = State->get<RegionState>(sym)) { + if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) && + CheckRefState(RS)) { + State = State->remove<RegionState>(sym); + State = State->set<RegionState>(sym, RefState::getEscaped(RS)); + } + } + } + return State; +} + +static SymbolRef findFailedReallocSymbol(ProgramStateRef currState, + ProgramStateRef prevState) { + ReallocPairsTy currMap = currState->get<ReallocPairs>(); + ReallocPairsTy prevMap = prevState->get<ReallocPairs>(); + + for (ReallocPairsTy::iterator I = prevMap.begin(), E = prevMap.end(); + I != E; ++I) { + SymbolRef sym = I.getKey(); + if (!currMap.lookup(sym)) + return sym; + } + + return nullptr; +} + +static bool isReferenceCountingPointerDestructor(const CXXDestructorDecl *DD) { + if (const IdentifierInfo *II = DD->getParent()->getIdentifier()) { + StringRef N = II->getName(); + if (N.contains_lower("ptr") || N.contains_lower("pointer")) { + if (N.contains_lower("ref") || N.contains_lower("cnt") || + N.contains_lower("intrusive") || N.contains_lower("shared")) { + return true; + } + } + } + return false; +} + +std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { + + ProgramStateRef state = N->getState(); + ProgramStateRef statePrev = N->getFirstPred()->getState(); + + const RefState *RS = state->get<RegionState>(Sym); + const RefState *RSPrev = statePrev->get<RegionState>(Sym); + + const Stmt *S = PathDiagnosticLocation::getStmt(N); + // When dealing with containers, we sometimes want to give a note + // even if the statement is missing. + if (!S && (!RS || RS->getAllocationFamily() != AF_InnerBuffer)) + return nullptr; + + const LocationContext *CurrentLC = N->getLocationContext(); + + // If we find an atomic fetch_add or fetch_sub within the destructor in which + // the pointer was released (before the release), this is likely a destructor + // of a shared pointer. + // Because we don't model atomics, and also because we don't know that the + // original reference count is positive, we should not report use-after-frees + // on objects deleted in such destructors. This can probably be improved + // through better shared pointer modeling. + if (ReleaseDestructorLC) { + if (const auto *AE = dyn_cast<AtomicExpr>(S)) { + AtomicExpr::AtomicOp Op = AE->getOp(); + if (Op == AtomicExpr::AO__c11_atomic_fetch_add || + Op == AtomicExpr::AO__c11_atomic_fetch_sub) { + if (ReleaseDestructorLC == CurrentLC || + ReleaseDestructorLC->isParentOf(CurrentLC)) { + BR.markInvalid(getTag(), S); + } + } + } + } + + // FIXME: We will eventually need to handle non-statement-based events + // (__attribute__((cleanup))). + + // Find out if this is an interesting point and what is the kind. + StringRef Msg; + StackHintGeneratorForSymbol *StackHint = nullptr; + SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + + if (Mode == Normal) { + if (isAllocated(RS, RSPrev, S)) { + Msg = "Memory is allocated"; + StackHint = new StackHintGeneratorForSymbol(Sym, + "Returned allocated memory"); + } else if (isReleased(RS, RSPrev, S)) { + const auto Family = RS->getAllocationFamily(); + switch (Family) { + case AF_Alloca: + case AF_Malloc: + case AF_CXXNew: + case AF_CXXNewArray: + case AF_IfNameIndex: + Msg = "Memory is released"; + StackHint = new StackHintGeneratorForSymbol(Sym, + "Returning; memory was released"); + break; + case AF_InnerBuffer: { + const MemRegion *ObjRegion = + allocation_state::getContainerObjRegion(statePrev, Sym); + const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion); + QualType ObjTy = TypedRegion->getValueType(); + OS << "Inner buffer of '" << ObjTy.getAsString() << "' "; + + if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) { + OS << "deallocated by call to destructor"; + StackHint = new StackHintGeneratorForSymbol(Sym, + "Returning; inner buffer was deallocated"); + } else { + OS << "reallocated by call to '"; + const Stmt *S = RS->getStmt(); + if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) { + OS << MemCallE->getMethodDecl()->getNameAsString(); + } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) { + OS << OpCallE->getDirectCallee()->getNameAsString(); + } else if (const auto *CallE = dyn_cast<CallExpr>(S)) { + auto &CEMgr = BRC.getStateManager().getCallEventManager(); + CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC); + const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl()); + OS << (D ? D->getNameAsString() : "unknown"); + } + OS << "'"; + StackHint = new StackHintGeneratorForSymbol(Sym, + "Returning; inner buffer was reallocated"); + } + Msg = OS.str(); + break; + } + case AF_None: + llvm_unreachable("Unhandled allocation family!"); + } + + // See if we're releasing memory while inlining a destructor + // (or one of its callees). This turns on various common + // false positive suppressions. + bool FoundAnyDestructor = false; + for (const LocationContext *LC = CurrentLC; LC; LC = LC->getParent()) { + if (const auto *DD = dyn_cast<CXXDestructorDecl>(LC->getDecl())) { + if (isReferenceCountingPointerDestructor(DD)) { + // This immediately looks like a reference-counting destructor. + // We're bad at guessing the original reference count of the object, + // so suppress the report for now. + BR.markInvalid(getTag(), DD); + } else if (!FoundAnyDestructor) { + assert(!ReleaseDestructorLC && + "There can be only one release point!"); + // Suspect that it's a reference counting pointer destructor. + // On one of the next nodes might find out that it has atomic + // reference counting operations within it (see the code above), + // and if so, we'd conclude that it likely is a reference counting + // pointer destructor. + ReleaseDestructorLC = LC->getStackFrame(); + // It is unlikely that releasing memory is delegated to a destructor + // inside a destructor of a shared pointer, because it's fairly hard + // to pass the information that the pointer indeed needs to be + // released into it. So we're only interested in the innermost + // destructor. + FoundAnyDestructor = true; + } + } + } + } else if (isRelinquished(RS, RSPrev, S)) { + Msg = "Memory ownership is transferred"; + StackHint = new StackHintGeneratorForSymbol(Sym, ""); + } else if (isReallocFailedCheck(RS, RSPrev, S)) { + Mode = ReallocationFailed; + Msg = "Reallocation failed"; + StackHint = new StackHintGeneratorForReallocationFailed(Sym, + "Reallocation failed"); + + if (SymbolRef sym = findFailedReallocSymbol(state, statePrev)) { + // Is it possible to fail two reallocs WITHOUT testing in between? + assert((!FailedReallocSymbol || FailedReallocSymbol == sym) && + "We only support one failed realloc at a time."); + BR.markInteresting(sym); + FailedReallocSymbol = sym; + } + } + + // We are in a special mode if a reallocation failed later in the path. + } else if (Mode == ReallocationFailed) { + assert(FailedReallocSymbol && "No symbol to look for."); + + // Is this is the first appearance of the reallocated symbol? + if (!statePrev->get<RegionState>(FailedReallocSymbol)) { + // We're at the reallocation point. + Msg = "Attempt to reallocate memory"; + StackHint = new StackHintGeneratorForSymbol(Sym, + "Returned reallocated memory"); + FailedReallocSymbol = nullptr; + Mode = Normal; + } + } + + if (Msg.empty()) + return nullptr; + assert(StackHint); + + // Generate the extra diagnostic. + PathDiagnosticLocation Pos; + if (!S) { + assert(RS->getAllocationFamily() == AF_InnerBuffer); + auto PostImplCall = N->getLocation().getAs<PostImplicitCall>(); + if (!PostImplCall) + return nullptr; + Pos = PathDiagnosticLocation(PostImplCall->getLocation(), + BRC.getSourceManager()); + } else { + Pos = PathDiagnosticLocation(S, BRC.getSourceManager(), + N->getLocationContext()); + } + + return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true, StackHint); +} + +void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + + RegionStateTy RS = State->get<RegionState>(); + + if (!RS.isEmpty()) { + Out << Sep << "MallocChecker :" << NL; + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + const RefState *RefS = State->get<RegionState>(I.getKey()); + AllocationFamily Family = RefS->getAllocationFamily(); + Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family); + if (!CheckKind.hasValue()) + CheckKind = getCheckIfTracked(Family, true); + + I.getKey()->dumpToStream(Out); + Out << " : "; + I.getData().dump(Out); + if (CheckKind.hasValue()) + Out << " (" << CheckNames[*CheckKind].getName() << ")"; + Out << NL; + } + } +} + +namespace clang { +namespace ento { +namespace allocation_state { + +ProgramStateRef +markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) { + AllocationFamily Family = AF_InnerBuffer; + return State->set<RegionState>(Sym, RefState::getReleased(Family, Origin)); +} + +} // end namespace allocation_state +} // end namespace ento +} // end namespace clang + +void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) { + registerCStringCheckerBasic(mgr); + MallocChecker *checker = mgr.registerChecker<MallocChecker>(); + checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( + "Optimistic", false, checker); + checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true; + checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] = + mgr.getCurrentCheckName(); + // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete + // checker. + if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) { + checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true; + // FIXME: This does not set the correct name, but without this workaround + // no name will be set at all. + checker->CheckNames[MallocChecker::CK_NewDeleteChecker] = + mgr.getCurrentCheckName(); + } +} + +// Intended to be used in InnerPointerChecker to register the part of +// MallocChecker connected to it. +void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) { + registerCStringCheckerBasic(mgr); + MallocChecker *checker = mgr.registerChecker<MallocChecker>(); + checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( + "Optimistic", false, checker); + checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true; + checker->CheckNames[MallocChecker::CK_InnerPointerChecker] = + mgr.getCurrentCheckName(); +} + +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &mgr) { \ + registerCStringCheckerBasic(mgr); \ + MallocChecker *checker = mgr.registerChecker<MallocChecker>(); \ + checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( \ + "Optimistic", false, checker); \ + checker->ChecksEnabled[MallocChecker::CK_##name] = true; \ + checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \ + } + +REGISTER_CHECKER(MallocChecker) +REGISTER_CHECKER(NewDeleteChecker) +REGISTER_CHECKER(MismatchedDeallocatorChecker) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp new file mode 100644 index 000000000000..d02ed48bceaa --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp @@ -0,0 +1,340 @@ +// MallocOverflowSecurityChecker.cpp - Check for malloc overflows -*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker detects a common memory allocation security flaw. +// Suppose 'unsigned int n' comes from an untrusted source. If the +// code looks like 'malloc (n * 4)', and an attacker can make 'n' be +// say MAX_UINT/4+2, then instead of allocating the correct 'n' 4-byte +// elements, this will actually allocate only two because of overflow. +// Then when the rest of the program attempts to store values past the +// second element, these values will actually overwrite other items in +// the heap, probably allowing the attacker to execute arbitrary code. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/EvaluatedExprVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/SmallVector.h" +#include <utility> + +using namespace clang; +using namespace ento; +using llvm::APSInt; + +namespace { +struct MallocOverflowCheck { + const BinaryOperator *mulop; + const Expr *variable; + APSInt maxVal; + + MallocOverflowCheck(const BinaryOperator *m, const Expr *v, APSInt val) + : mulop(m), variable(v), maxVal(std::move(val)) {} +}; + +class MallocOverflowSecurityChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager &mgr, + BugReporter &BR) const; + + void CheckMallocArgument( + SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + const Expr *TheArgument, ASTContext &Context) const; + + void OutputPossibleOverflows( + SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + const Decl *D, BugReporter &BR, AnalysisManager &mgr) const; + +}; +} // end anonymous namespace + +// Return true for computations which evaluate to zero: e.g., mult by 0. +static inline bool EvaluatesToZero(APSInt &Val, BinaryOperatorKind op) { + return (op == BO_Mul) && (Val == 0); +} + +void MallocOverflowSecurityChecker::CheckMallocArgument( + SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + const Expr *TheArgument, + ASTContext &Context) const { + + /* Look for a linear combination with a single variable, and at least + one multiplication. + Reject anything that applies to the variable: an explicit cast, + conditional expression, an operation that could reduce the range + of the result, or anything too complicated :-). */ + const Expr *e = TheArgument; + const BinaryOperator * mulop = nullptr; + APSInt maxVal; + + for (;;) { + maxVal = 0; + e = e->IgnoreParenImpCasts(); + if (const BinaryOperator *binop = dyn_cast<BinaryOperator>(e)) { + BinaryOperatorKind opc = binop->getOpcode(); + // TODO: ignore multiplications by 1, reject if multiplied by 0. + if (mulop == nullptr && opc == BO_Mul) + mulop = binop; + if (opc != BO_Mul && opc != BO_Add && opc != BO_Sub && opc != BO_Shl) + return; + + const Expr *lhs = binop->getLHS(); + const Expr *rhs = binop->getRHS(); + if (rhs->isEvaluatable(Context)) { + e = lhs; + maxVal = rhs->EvaluateKnownConstInt(Context); + if (EvaluatesToZero(maxVal, opc)) + return; + } else if ((opc == BO_Add || opc == BO_Mul) && + lhs->isEvaluatable(Context)) { + maxVal = lhs->EvaluateKnownConstInt(Context); + if (EvaluatesToZero(maxVal, opc)) + return; + e = rhs; + } else + return; + } + else if (isa<DeclRefExpr>(e) || isa<MemberExpr>(e)) + break; + else + return; + } + + if (mulop == nullptr) + return; + + // We've found the right structure of malloc argument, now save + // the data so when the body of the function is completely available + // we can check for comparisons. + + // TODO: Could push this into the innermost scope where 'e' is + // defined, rather than the whole function. + PossibleMallocOverflows.push_back(MallocOverflowCheck(mulop, e, maxVal)); +} + +namespace { +// A worker class for OutputPossibleOverflows. +class CheckOverflowOps : + public EvaluatedExprVisitor<CheckOverflowOps> { +public: + typedef SmallVectorImpl<MallocOverflowCheck> theVecType; + +private: + theVecType &toScanFor; + ASTContext &Context; + + bool isIntZeroExpr(const Expr *E) const { + if (!E->getType()->isIntegralOrEnumerationType()) + return false; + Expr::EvalResult Result; + if (E->EvaluateAsInt(Result, Context)) + return Result.Val.getInt() == 0; + return false; + } + + static const Decl *getDecl(const DeclRefExpr *DR) { return DR->getDecl(); } + static const Decl *getDecl(const MemberExpr *ME) { + return ME->getMemberDecl(); + } + + template <typename T1> + void Erase(const T1 *DR, + llvm::function_ref<bool(const MallocOverflowCheck &)> Pred) { + auto P = [DR, Pred](const MallocOverflowCheck &Check) { + if (const auto *CheckDR = dyn_cast<T1>(Check.variable)) + return getDecl(CheckDR) == getDecl(DR) && Pred(Check); + return false; + }; + toScanFor.erase(std::remove_if(toScanFor.begin(), toScanFor.end(), P), + toScanFor.end()); + } + + void CheckExpr(const Expr *E_p) { + auto PredTrue = [](const MallocOverflowCheck &) { return true; }; + const Expr *E = E_p->IgnoreParenImpCasts(); + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) + Erase<DeclRefExpr>(DR, PredTrue); + else if (const auto *ME = dyn_cast<MemberExpr>(E)) { + Erase<MemberExpr>(ME, PredTrue); + } + } + + // Check if the argument to malloc is assigned a value + // which cannot cause an overflow. + // e.g., malloc (mul * x) and, + // case 1: mul = <constant value> + // case 2: mul = a/b, where b > x + void CheckAssignmentExpr(BinaryOperator *AssignEx) { + bool assignKnown = false; + bool numeratorKnown = false, denomKnown = false; + APSInt denomVal; + denomVal = 0; + + // Erase if the multiplicand was assigned a constant value. + const Expr *rhs = AssignEx->getRHS(); + if (rhs->isEvaluatable(Context)) + assignKnown = true; + + // Discard the report if the multiplicand was assigned a value, + // that can never overflow after multiplication. e.g., the assignment + // is a division operator and the denominator is > other multiplicand. + const Expr *rhse = rhs->IgnoreParenImpCasts(); + if (const BinaryOperator *BOp = dyn_cast<BinaryOperator>(rhse)) { + if (BOp->getOpcode() == BO_Div) { + const Expr *denom = BOp->getRHS()->IgnoreParenImpCasts(); + Expr::EvalResult Result; + if (denom->EvaluateAsInt(Result, Context)) { + denomVal = Result.Val.getInt(); + denomKnown = true; + } + const Expr *numerator = BOp->getLHS()->IgnoreParenImpCasts(); + if (numerator->isEvaluatable(Context)) + numeratorKnown = true; + } + } + if (!assignKnown && !denomKnown) + return; + auto denomExtVal = denomVal.getExtValue(); + + // Ignore negative denominator. + if (denomExtVal < 0) + return; + + const Expr *lhs = AssignEx->getLHS(); + const Expr *E = lhs->IgnoreParenImpCasts(); + + auto pred = [assignKnown, numeratorKnown, + denomExtVal](const MallocOverflowCheck &Check) { + return assignKnown || + (numeratorKnown && (denomExtVal >= Check.maxVal.getExtValue())); + }; + + if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E)) + Erase<DeclRefExpr>(DR, pred); + else if (const auto *ME = dyn_cast<MemberExpr>(E)) + Erase<MemberExpr>(ME, pred); + } + + public: + void VisitBinaryOperator(BinaryOperator *E) { + if (E->isComparisonOp()) { + const Expr * lhs = E->getLHS(); + const Expr * rhs = E->getRHS(); + // Ignore comparisons against zero, since they generally don't + // protect against an overflow. + if (!isIntZeroExpr(lhs) && !isIntZeroExpr(rhs)) { + CheckExpr(lhs); + CheckExpr(rhs); + } + } + if (E->isAssignmentOp()) + CheckAssignmentExpr(E); + EvaluatedExprVisitor<CheckOverflowOps>::VisitBinaryOperator(E); + } + + /* We specifically ignore loop conditions, because they're typically + not error checks. */ + void VisitWhileStmt(WhileStmt *S) { + return this->Visit(S->getBody()); + } + void VisitForStmt(ForStmt *S) { + return this->Visit(S->getBody()); + } + void VisitDoStmt(DoStmt *S) { + return this->Visit(S->getBody()); + } + + CheckOverflowOps(theVecType &v, ASTContext &ctx) + : EvaluatedExprVisitor<CheckOverflowOps>(ctx), + toScanFor(v), Context(ctx) + { } + }; +} + +// OutputPossibleOverflows - We've found a possible overflow earlier, +// now check whether Body might contain a comparison which might be +// preventing the overflow. +// This doesn't do flow analysis, range analysis, or points-to analysis; it's +// just a dumb "is there a comparison" scan. The aim here is to +// detect the most blatent cases of overflow and educate the +// programmer. +void MallocOverflowSecurityChecker::OutputPossibleOverflows( + SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows, + const Decl *D, BugReporter &BR, AnalysisManager &mgr) const { + // By far the most common case: nothing to check. + if (PossibleMallocOverflows.empty()) + return; + + // Delete any possible overflows which have a comparison. + CheckOverflowOps c(PossibleMallocOverflows, BR.getContext()); + c.Visit(mgr.getAnalysisDeclContext(D)->getBody()); + + // Output warnings for all overflows that are left. + for (CheckOverflowOps::theVecType::iterator + i = PossibleMallocOverflows.begin(), + e = PossibleMallocOverflows.end(); + i != e; + ++i) { + BR.EmitBasicReport( + D, this, "malloc() size overflow", categories::UnixAPI, + "the computation of the size of the memory allocation may overflow", + PathDiagnosticLocation::createOperatorLoc(i->mulop, + BR.getSourceManager()), + i->mulop->getSourceRange()); + } +} + +void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D, + AnalysisManager &mgr, + BugReporter &BR) const { + + CFG *cfg = mgr.getCFG(D); + if (!cfg) + return; + + // A list of variables referenced in possibly overflowing malloc operands. + SmallVector<MallocOverflowCheck, 2> PossibleMallocOverflows; + + for (CFG::iterator it = cfg->begin(), ei = cfg->end(); it != ei; ++it) { + CFGBlock *block = *it; + for (CFGBlock::iterator bi = block->begin(), be = block->end(); + bi != be; ++bi) { + if (Optional<CFGStmt> CS = bi->getAs<CFGStmt>()) { + if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) { + // Get the callee. + const FunctionDecl *FD = TheCall->getDirectCallee(); + + if (!FD) + continue; + + // Get the name of the callee. If it's a builtin, strip off the prefix. + IdentifierInfo *FnInfo = FD->getIdentifier(); + if (!FnInfo) + continue; + + if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) { + if (TheCall->getNumArgs() == 1) + CheckMallocArgument(PossibleMallocOverflows, TheCall->getArg(0), + mgr.getASTContext()); + } + } + } + } + } + + OutputPossibleOverflows(PossibleMallocOverflows, D, BR, mgr); +} + +void +ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) { + mgr.registerChecker<MallocOverflowSecurityChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp new file mode 100644 index 000000000000..bb245d82bc2b --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp @@ -0,0 +1,252 @@ +// MallocSizeofChecker.cpp - Check for dubious malloc arguments ---*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Reports inconsistencies between the casted type of the return value of a +// malloc/calloc/realloc call and the operand of any sizeof expressions +// contained within its argument(s). +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/AST/TypeLoc.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { + +typedef std::pair<const TypeSourceInfo *, const CallExpr *> TypeCallPair; +typedef llvm::PointerUnion<const Stmt *, const VarDecl *> ExprParent; + +class CastedAllocFinder + : public ConstStmtVisitor<CastedAllocFinder, TypeCallPair> { + IdentifierInfo *II_malloc, *II_calloc, *II_realloc; + +public: + struct CallRecord { + ExprParent CastedExprParent; + const Expr *CastedExpr; + const TypeSourceInfo *ExplicitCastType; + const CallExpr *AllocCall; + + CallRecord(ExprParent CastedExprParent, const Expr *CastedExpr, + const TypeSourceInfo *ExplicitCastType, + const CallExpr *AllocCall) + : CastedExprParent(CastedExprParent), CastedExpr(CastedExpr), + ExplicitCastType(ExplicitCastType), AllocCall(AllocCall) {} + }; + + typedef std::vector<CallRecord> CallVec; + CallVec Calls; + + CastedAllocFinder(ASTContext *Ctx) : + II_malloc(&Ctx->Idents.get("malloc")), + II_calloc(&Ctx->Idents.get("calloc")), + II_realloc(&Ctx->Idents.get("realloc")) {} + + void VisitChild(ExprParent Parent, const Stmt *S) { + TypeCallPair AllocCall = Visit(S); + if (AllocCall.second && AllocCall.second != S) + Calls.push_back(CallRecord(Parent, cast<Expr>(S), AllocCall.first, + AllocCall.second)); + } + + void VisitChildren(const Stmt *S) { + for (const Stmt *Child : S->children()) + if (Child) + VisitChild(S, Child); + } + + TypeCallPair VisitCastExpr(const CastExpr *E) { + return Visit(E->getSubExpr()); + } + + TypeCallPair VisitExplicitCastExpr(const ExplicitCastExpr *E) { + return TypeCallPair(E->getTypeInfoAsWritten(), + Visit(E->getSubExpr()).second); + } + + TypeCallPair VisitParenExpr(const ParenExpr *E) { + return Visit(E->getSubExpr()); + } + + TypeCallPair VisitStmt(const Stmt *S) { + VisitChildren(S); + return TypeCallPair(); + } + + TypeCallPair VisitCallExpr(const CallExpr *E) { + VisitChildren(E); + const FunctionDecl *FD = E->getDirectCallee(); + if (FD) { + IdentifierInfo *II = FD->getIdentifier(); + if (II == II_malloc || II == II_calloc || II == II_realloc) + return TypeCallPair((const TypeSourceInfo *)nullptr, E); + } + return TypeCallPair(); + } + + TypeCallPair VisitDeclStmt(const DeclStmt *S) { + for (const auto *I : S->decls()) + if (const VarDecl *VD = dyn_cast<VarDecl>(I)) + if (const Expr *Init = VD->getInit()) + VisitChild(VD, Init); + return TypeCallPair(); + } +}; + +class SizeofFinder : public ConstStmtVisitor<SizeofFinder> { +public: + std::vector<const UnaryExprOrTypeTraitExpr *> Sizeofs; + + void VisitBinMul(const BinaryOperator *E) { + Visit(E->getLHS()); + Visit(E->getRHS()); + } + + void VisitImplicitCastExpr(const ImplicitCastExpr *E) { + return Visit(E->getSubExpr()); + } + + void VisitParenExpr(const ParenExpr *E) { + return Visit(E->getSubExpr()); + } + + void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) { + if (E->getKind() != UETT_SizeOf) + return; + + Sizeofs.push_back(E); + } +}; + +// Determine if the pointee and sizeof types are compatible. Here +// we ignore constness of pointer types. +static bool typesCompatible(ASTContext &C, QualType A, QualType B) { + // sizeof(void*) is compatible with any other pointer. + if (B->isVoidPointerType() && A->getAs<PointerType>()) + return true; + + while (true) { + A = A.getCanonicalType(); + B = B.getCanonicalType(); + + if (A.getTypePtr() == B.getTypePtr()) + return true; + + if (const PointerType *ptrA = A->getAs<PointerType>()) + if (const PointerType *ptrB = B->getAs<PointerType>()) { + A = ptrA->getPointeeType(); + B = ptrB->getPointeeType(); + continue; + } + + break; + } + + return false; +} + +static bool compatibleWithArrayType(ASTContext &C, QualType PT, QualType T) { + // Ex: 'int a[10][2]' is compatible with 'int', 'int[2]', 'int[10][2]'. + while (const ArrayType *AT = T->getAsArrayTypeUnsafe()) { + QualType ElemType = AT->getElementType(); + if (typesCompatible(C, PT, AT->getElementType())) + return true; + T = ElemType; + } + + return false; +} + +class MallocSizeofChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) const { + AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(D); + CastedAllocFinder Finder(&BR.getContext()); + Finder.Visit(D->getBody()); + for (CastedAllocFinder::CallVec::iterator i = Finder.Calls.begin(), + e = Finder.Calls.end(); i != e; ++i) { + QualType CastedType = i->CastedExpr->getType(); + if (!CastedType->isPointerType()) + continue; + QualType PointeeType = CastedType->getAs<PointerType>()->getPointeeType(); + if (PointeeType->isVoidType()) + continue; + + for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(), + ae = i->AllocCall->arg_end(); ai != ae; ++ai) { + if (!(*ai)->getType()->isIntegralOrUnscopedEnumerationType()) + continue; + + SizeofFinder SFinder; + SFinder.Visit(*ai); + if (SFinder.Sizeofs.size() != 1) + continue; + + QualType SizeofType = SFinder.Sizeofs[0]->getTypeOfArgument(); + + if (typesCompatible(BR.getContext(), PointeeType, SizeofType)) + continue; + + // If the argument to sizeof is an array, the result could be a + // pointer to any array element. + if (compatibleWithArrayType(BR.getContext(), PointeeType, SizeofType)) + continue; + + const TypeSourceInfo *TSI = nullptr; + if (i->CastedExprParent.is<const VarDecl *>()) { + TSI = + i->CastedExprParent.get<const VarDecl *>()->getTypeSourceInfo(); + } else { + TSI = i->ExplicitCastType; + } + + SmallString<64> buf; + llvm::raw_svector_ostream OS(buf); + + OS << "Result of "; + const FunctionDecl *Callee = i->AllocCall->getDirectCallee(); + if (Callee && Callee->getIdentifier()) + OS << '\'' << Callee->getIdentifier()->getName() << '\''; + else + OS << "call"; + OS << " is converted to a pointer of type '" + << PointeeType.getAsString() << "', which is incompatible with " + << "sizeof operand type '" << SizeofType.getAsString() << "'"; + SmallVector<SourceRange, 4> Ranges; + Ranges.push_back(i->AllocCall->getCallee()->getSourceRange()); + Ranges.push_back(SFinder.Sizeofs[0]->getSourceRange()); + if (TSI) + Ranges.push_back(TSI->getTypeLoc().getSourceRange()); + + PathDiagnosticLocation L = + PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(), + BR.getSourceManager(), ADC); + + BR.EmitBasicReport(D, this, "Allocator sizeof operand mismatch", + categories::UnixAPI, OS.str(), L, Ranges); + } + } + } +}; + +} + +void ento::registerMallocSizeofChecker(CheckerManager &mgr) { + mgr.registerChecker<MallocSizeofChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp new file mode 100644 index 000000000000..e3b24f20b0f0 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp @@ -0,0 +1,90 @@ +// MmapWriteExecChecker.cpp - Check for the prot argument -----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker tests the 3rd argument of mmap's calls to check if +// it is writable and executable in the same time. It's somehow +// an optional checker since for example in JIT libraries it is pretty common. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" + +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; +using llvm::APSInt; + +namespace { +class MmapWriteExecChecker : public Checker<check::PreCall> { + CallDescription MmapFn; + CallDescription MprotectFn; + static int ProtWrite; + static int ProtExec; + static int ProtRead; + mutable std::unique_ptr<BugType> BT; +public: + MmapWriteExecChecker() : MmapFn("mmap", 6), MprotectFn("mprotect", 3) {} + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + int ProtExecOv; + int ProtReadOv; +}; +} + +int MmapWriteExecChecker::ProtWrite = 0x02; +int MmapWriteExecChecker::ProtExec = 0x04; +int MmapWriteExecChecker::ProtRead = 0x01; + +void MmapWriteExecChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (Call.isCalled(MmapFn) || Call.isCalled(MprotectFn)) { + SVal ProtVal = Call.getArgSVal(2); + Optional<nonloc::ConcreteInt> ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>(); + int64_t Prot = ProtLoc->getValue().getSExtValue(); + if (ProtExecOv != ProtExec) + ProtExec = ProtExecOv; + if (ProtReadOv != ProtRead) + ProtRead = ProtReadOv; + + // Wrong settings + if (ProtRead == ProtExec) + return; + + if ((Prot & (ProtWrite | ProtExec)) == (ProtWrite | ProtExec)) { + if (!BT) + BT.reset(new BugType(this, "W^X check fails, Write Exec prot flags set", "Security")); + + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + + auto Report = llvm::make_unique<BugReport>( + *BT, "Both PROT_WRITE and PROT_EXEC flags are set. This can " + "lead to exploitable memory regions, which could be overwritten " + "with malicious code", N); + Report->addRange(Call.getArgSourceRange(2)); + C.emitReport(std::move(Report)); + } + } +} + +void ento::registerMmapWriteExecChecker(CheckerManager &mgr) { + MmapWriteExecChecker *Mwec = + mgr.registerChecker<MmapWriteExecChecker>(); + Mwec->ProtExecOv = + mgr.getAnalyzerOptions() + .getCheckerIntegerOption("MmapProtExec", 0x04, Mwec); + Mwec->ProtReadOv = + mgr.getAnalyzerOptions() + .getCheckerIntegerOption("MmapProtRead", 0x01, Mwec); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp new file mode 100644 index 000000000000..6efa2dfbe5b4 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp @@ -0,0 +1,740 @@ +// MoveChecker.cpp - Check use of moved-from objects. - C++ ---------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines checker which checks for potential misuses of a moved-from +// object. That means method calls on the object or copying it in moved-from +// state. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ExprCXX.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/StringSet.h" + +using namespace clang; +using namespace ento; + +namespace { +struct RegionState { +private: + enum Kind { Moved, Reported } K; + RegionState(Kind InK) : K(InK) {} + +public: + bool isReported() const { return K == Reported; } + bool isMoved() const { return K == Moved; } + + static RegionState getReported() { return RegionState(Reported); } + static RegionState getMoved() { return RegionState(Moved); } + + bool operator==(const RegionState &X) const { return K == X.K; } + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); } +}; +} // end of anonymous namespace + +namespace { +class MoveChecker + : public Checker<check::PreCall, check::PostCall, + check::DeadSymbols, check::RegionChanges> { +public: + void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; + void checkPreCall(const CallEvent &MC, CheckerContext &C) const; + void checkPostCall(const CallEvent &MC, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + ProgramStateRef + checkRegionChanges(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> RequestedRegions, + ArrayRef<const MemRegion *> InvalidatedRegions, + const LocationContext *LCtx, const CallEvent *Call) const; + void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const override; + +private: + enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference }; + enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr }; + + enum AggressivenessKind { // In any case, don't warn after a reset. + AK_Invalid = -1, + AK_KnownsOnly = 0, // Warn only about known move-unsafe classes. + AK_KnownsAndLocals = 1, // Also warn about all local objects. + AK_All = 2, // Warn on any use-after-move. + AK_NumKinds = AK_All + }; + + static bool misuseCausesCrash(MisuseKind MK) { + return MK == MK_Dereference; + } + + struct ObjectKind { + // Is this a local variable or a local rvalue reference? + bool IsLocal; + // Is this an STL object? If so, of what kind? + StdObjectKind StdKind; + }; + + // STL smart pointers are automatically re-initialized to null when moved + // from. So we can't warn on many methods, but we can warn when it is + // dereferenced, which is UB even if the resulting lvalue never gets read. + const llvm::StringSet<> StdSmartPtrClasses = { + "shared_ptr", + "unique_ptr", + "weak_ptr", + }; + + // Not all of these are entirely move-safe, but they do provide *some* + // guarantees, and it means that somebody is using them after move + // in a valid manner. + // TODO: We can still try to identify *unsafe* use after move, + // like we did with smart pointers. + const llvm::StringSet<> StdSafeClasses = { + "basic_filebuf", + "basic_ios", + "future", + "optional", + "packaged_task" + "promise", + "shared_future", + "shared_lock", + "thread", + "unique_lock", + }; + + // Should we bother tracking the state of the object? + bool shouldBeTracked(ObjectKind OK) const { + // In non-aggressive mode, only warn on use-after-move of local variables + // (or local rvalue references) and of STL objects. The former is possible + // because local variables (or local rvalue references) are not tempting + // their user to re-use the storage. The latter is possible because STL + // objects are known to end up in a valid but unspecified state after the + // move and their state-reset methods are also known, which allows us to + // predict precisely when use-after-move is invalid. + // Some STL objects are known to conform to additional contracts after move, + // so they are not tracked. However, smart pointers specifically are tracked + // because we can perform extra checking over them. + // In aggressive mode, warn on any use-after-move because the user has + // intentionally asked us to completely eliminate use-after-move + // in his code. + return (Aggressiveness == AK_All) || + (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) || + OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr; + } + + // Some objects only suffer from some kinds of misuses, but we need to track + // them anyway because we cannot know in advance what misuse will we find. + bool shouldWarnAbout(ObjectKind OK, MisuseKind MK) const { + // Additionally, only warn on smart pointers when they are dereferenced (or + // local or we are aggressive). + return shouldBeTracked(OK) && + ((Aggressiveness == AK_All) || + (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) || + OK.StdKind != SK_SmartPtr || MK == MK_Dereference); + } + + // Obtains ObjectKind of an object. Because class declaration cannot always + // be easily obtained from the memory region, it is supplied separately. + ObjectKind classifyObject(const MemRegion *MR, const CXXRecordDecl *RD) const; + + // Classifies the object and dumps a user-friendly description string to + // the stream. + void explainObject(llvm::raw_ostream &OS, const MemRegion *MR, + const CXXRecordDecl *RD, MisuseKind MK) const; + + bool belongsTo(const CXXRecordDecl *RD, const llvm::StringSet<> &Set) const; + + class MovedBugVisitor : public BugReporterVisitor { + public: + MovedBugVisitor(const MoveChecker &Chk, const MemRegion *R, + const CXXRecordDecl *RD, MisuseKind MK) + : Chk(Chk), Region(R), RD(RD), MK(MK), Found(false) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Region); + // Don't add RD because it's, in theory, uniquely determined by + // the region. In practice though, it's not always possible to obtain + // the declaration directly from the region, that's why we store it + // in the first place. + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + const MoveChecker &Chk; + // The tracked region. + const MemRegion *Region; + // The class of the tracked object. + const CXXRecordDecl *RD; + // How exactly the object was misused. + const MisuseKind MK; + bool Found; + }; + + AggressivenessKind Aggressiveness; + +public: + void setAggressiveness(StringRef Str) { + Aggressiveness = + llvm::StringSwitch<AggressivenessKind>(Str) + .Case("KnownsOnly", AK_KnownsOnly) + .Case("KnownsAndLocals", AK_KnownsAndLocals) + .Case("All", AK_All) + .Default(AK_KnownsAndLocals); // A sane default. + }; + +private: + mutable std::unique_ptr<BugType> BT; + + // Check if the given form of potential misuse of a given object + // should be reported. If so, get it reported. The callback from which + // this function was called should immediately return after the call + // because this function adds one or two transitions. + void modelUse(ProgramStateRef State, const MemRegion *Region, + const CXXRecordDecl *RD, MisuseKind MK, + CheckerContext &C) const; + + // Returns the exploded node against which the report was emitted. + // The caller *must* add any further transitions against this node. + ExplodedNode *reportBug(const MemRegion *Region, const CXXRecordDecl *RD, + CheckerContext &C, MisuseKind MK) const; + + bool isInMoveSafeContext(const LocationContext *LC) const; + bool isStateResetMethod(const CXXMethodDecl *MethodDec) const; + bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const; + const ExplodedNode *getMoveLocation(const ExplodedNode *N, + const MemRegion *Region, + CheckerContext &C) const; +}; +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState) + +// If a region is removed all of the subregions needs to be removed too. +static ProgramStateRef removeFromState(ProgramStateRef State, + const MemRegion *Region) { + if (!Region) + return State; + for (auto &E : State->get<TrackedRegionMap>()) { + if (E.first->isSubRegionOf(Region)) + State = State->remove<TrackedRegionMap>(E.first); + } + return State; +} + +static bool isAnyBaseRegionReported(ProgramStateRef State, + const MemRegion *Region) { + for (auto &E : State->get<TrackedRegionMap>()) { + if (Region->isSubRegionOf(E.first) && E.second.isReported()) + return true; + } + return false; +} + +static const MemRegion *unwrapRValueReferenceIndirection(const MemRegion *MR) { + if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) { + SymbolRef Sym = SR->getSymbol(); + if (Sym->getType()->isRValueReferenceType()) + if (const MemRegion *OriginMR = Sym->getOriginRegion()) + return OriginMR; + } + return MR; +} + +std::shared_ptr<PathDiagnosticPiece> +MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, BugReport &BR) { + // We need only the last move of the reported object's region. + // The visitor walks the ExplodedGraph backwards. + if (Found) + return nullptr; + ProgramStateRef State = N->getState(); + ProgramStateRef StatePrev = N->getFirstPred()->getState(); + const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region); + const RegionState *TrackedObjectPrev = + StatePrev->get<TrackedRegionMap>(Region); + if (!TrackedObject) + return nullptr; + if (TrackedObjectPrev && TrackedObject) + return nullptr; + + // Retrieve the associated statement. + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + Found = true; + + SmallString<128> Str; + llvm::raw_svector_ostream OS(Str); + + ObjectKind OK = Chk.classifyObject(Region, RD); + switch (OK.StdKind) { + case SK_SmartPtr: + if (MK == MK_Dereference) { + OS << "Smart pointer"; + Chk.explainObject(OS, Region, RD, MK); + OS << " is reset to null when moved from"; + break; + } + + // If it's not a dereference, we don't care if it was reset to null + // or that it is even a smart pointer. + LLVM_FALLTHROUGH; + case SK_NonStd: + case SK_Safe: + OS << "Object"; + Chk.explainObject(OS, Region, RD, MK); + OS << " is moved"; + break; + case SK_Unsafe: + OS << "Object"; + Chk.explainObject(OS, Region, RD, MK); + OS << " is left in a valid but unspecified state after move"; + break; + } + + // Generate the extra diagnostic. + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true); +} + +const ExplodedNode *MoveChecker::getMoveLocation(const ExplodedNode *N, + const MemRegion *Region, + CheckerContext &C) const { + // Walk the ExplodedGraph backwards and find the first node that referred to + // the tracked region. + const ExplodedNode *MoveNode = N; + + while (N) { + ProgramStateRef State = N->getState(); + if (!State->get<TrackedRegionMap>(Region)) + break; + MoveNode = N; + N = N->pred_empty() ? nullptr : *(N->pred_begin()); + } + return MoveNode; +} + +void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region, + const CXXRecordDecl *RD, MisuseKind MK, + CheckerContext &C) const { + assert(!C.isDifferent() && "No transitions should have been made by now"); + const RegionState *RS = State->get<TrackedRegionMap>(Region); + ObjectKind OK = classifyObject(Region, RD); + + // Just in case: if it's not a smart pointer but it does have operator *, + // we shouldn't call the bug a dereference. + if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr) + MK = MK_FunCall; + + if (!RS || !shouldWarnAbout(OK, MK) + || isInMoveSafeContext(C.getLocationContext())) { + // Finalize changes made by the caller. + C.addTransition(State); + return; + } + + // Don't report it in case if any base region is already reported. + // But still generate a sink in case of UB. + // And still finalize changes made by the caller. + if (isAnyBaseRegionReported(State, Region)) { + if (misuseCausesCrash(MK)) { + C.generateSink(State, C.getPredecessor()); + } else { + C.addTransition(State); + } + return; + } + + ExplodedNode *N = reportBug(Region, RD, C, MK); + + // If the program has already crashed on this path, don't bother. + if (N->isSink()) + return; + + State = State->set<TrackedRegionMap>(Region, RegionState::getReported()); + C.addTransition(State, N); +} + +ExplodedNode *MoveChecker::reportBug(const MemRegion *Region, + const CXXRecordDecl *RD, CheckerContext &C, + MisuseKind MK) const { + if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode() + : C.generateNonFatalErrorNode()) { + + if (!BT) + BT.reset(new BugType(this, "Use-after-move", + "C++ move semantics")); + + // Uniqueing report to the same object. + PathDiagnosticLocation LocUsedForUniqueing; + const ExplodedNode *MoveNode = getMoveLocation(N, Region, C); + + if (const Stmt *MoveStmt = PathDiagnosticLocation::getStmt(MoveNode)) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin( + MoveStmt, C.getSourceManager(), MoveNode->getLocationContext()); + + // Creating the error message. + llvm::SmallString<128> Str; + llvm::raw_svector_ostream OS(Str); + switch(MK) { + case MK_FunCall: + OS << "Method called on moved-from object"; + explainObject(OS, Region, RD, MK); + break; + case MK_Copy: + OS << "Moved-from object"; + explainObject(OS, Region, RD, MK); + OS << " is copied"; + break; + case MK_Move: + OS << "Moved-from object"; + explainObject(OS, Region, RD, MK); + OS << " is moved"; + break; + case MK_Dereference: + OS << "Dereference of null smart pointer"; + explainObject(OS, Region, RD, MK); + break; + } + + auto R = + llvm::make_unique<BugReport>(*BT, OS.str(), N, LocUsedForUniqueing, + MoveNode->getLocationContext()->getDecl()); + R->addVisitor(llvm::make_unique<MovedBugVisitor>(*this, Region, RD, MK)); + C.emitReport(std::move(R)); + return N; + } + return nullptr; +} + +void MoveChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + const auto *AFC = dyn_cast<AnyFunctionCall>(&Call); + if (!AFC) + return; + + ProgramStateRef State = C.getState(); + const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl()); + if (!MethodDecl) + return; + + // Check if an object became moved-from. + // Object can become moved from after a call to move assignment operator or + // move constructor . + const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl); + if (ConstructorDecl && !ConstructorDecl->isMoveConstructor()) + return; + + if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator()) + return; + + const auto ArgRegion = AFC->getArgSVal(0).getAsRegion(); + if (!ArgRegion) + return; + + // Skip moving the object to itself. + const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call); + if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion) + return; + + if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC)) + if (IC->getCXXThisVal().getAsRegion() == ArgRegion) + return; + + const MemRegion *BaseRegion = ArgRegion->getBaseRegion(); + // Skip temp objects because of their short lifetime. + if (BaseRegion->getAs<CXXTempObjectRegion>() || + AFC->getArgExpr(0)->isRValue()) + return; + // If it has already been reported do not need to modify the state. + + if (State->get<TrackedRegionMap>(ArgRegion)) + return; + + const CXXRecordDecl *RD = MethodDecl->getParent(); + ObjectKind OK = classifyObject(ArgRegion, RD); + if (shouldBeTracked(OK)) { + // Mark object as moved-from. + State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved()); + C.addTransition(State); + return; + } + assert(!C.isDifferent() && "Should not have made transitions on this path!"); +} + +bool MoveChecker::isMoveSafeMethod(const CXXMethodDecl *MethodDec) const { + // We abandon the cases where bool/void/void* conversion happens. + if (const auto *ConversionDec = + dyn_cast_or_null<CXXConversionDecl>(MethodDec)) { + const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull(); + if (!Tp) + return false; + if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType()) + return true; + } + // Function call `empty` can be skipped. + return (MethodDec && MethodDec->getDeclName().isIdentifier() && + (MethodDec->getName().lower() == "empty" || + MethodDec->getName().lower() == "isempty")); +} + +bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const { + if (!MethodDec) + return false; + if (MethodDec->hasAttr<ReinitializesAttr>()) + return true; + if (MethodDec->getDeclName().isIdentifier()) { + std::string MethodName = MethodDec->getName().lower(); + // TODO: Some of these methods (eg., resize) are not always resetting + // the state, so we should consider looking at the arguments. + if (MethodName == "reset" || MethodName == "clear" || + MethodName == "destroy" || MethodName == "resize" || + MethodName == "shrink") + return true; + } + return false; +} + +// Don't report an error inside a move related operation. +// We assume that the programmer knows what she does. +bool MoveChecker::isInMoveSafeContext(const LocationContext *LC) const { + do { + const auto *CtxDec = LC->getDecl(); + auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec); + auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec); + auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec); + if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) || + (MethodDec && MethodDec->isOverloadedOperator() && + MethodDec->getOverloadedOperator() == OO_Equal) || + isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec)) + return true; + } while ((LC = LC->getParent())); + return false; +} + +bool MoveChecker::belongsTo(const CXXRecordDecl *RD, + const llvm::StringSet<> &Set) const { + const IdentifierInfo *II = RD->getIdentifier(); + return II && Set.count(II->getName()); +} + +MoveChecker::ObjectKind +MoveChecker::classifyObject(const MemRegion *MR, + const CXXRecordDecl *RD) const { + // Local variables and local rvalue references are classified as "Local". + // For the purposes of this checker, we classify move-safe STL types + // as not-"STL" types, because that's how the checker treats them. + MR = unwrapRValueReferenceIndirection(MR); + bool IsLocal = + MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace()); + + if (!RD || !RD->getDeclContext()->isStdNamespace()) + return { IsLocal, SK_NonStd }; + + if (belongsTo(RD, StdSmartPtrClasses)) + return { IsLocal, SK_SmartPtr }; + + if (belongsTo(RD, StdSafeClasses)) + return { IsLocal, SK_Safe }; + + return { IsLocal, SK_Unsafe }; +} + +void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR, + const CXXRecordDecl *RD, MisuseKind MK) const { + // We may need a leading space every time we actually explain anything, + // and we never know if we are to explain anything until we try. + if (const auto DR = + dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) { + const auto *RegionDecl = cast<NamedDecl>(DR->getDecl()); + OS << " '" << RegionDecl->getNameAsString() << "'"; + } + + ObjectKind OK = classifyObject(MR, RD); + switch (OK.StdKind) { + case SK_NonStd: + case SK_Safe: + break; + case SK_SmartPtr: + if (MK != MK_Dereference) + break; + + // We only care about the type if it's a dereference. + LLVM_FALLTHROUGH; + case SK_Unsafe: + OS << " of type '" << RD->getQualifiedNameAsString() << "'"; + break; + }; +} + +void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // Remove the MemRegions from the map on which a ctor/dtor call or assignment + // happened. + + // Checking constructor calls. + if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) { + State = removeFromState(State, CC->getCXXThisVal().getAsRegion()); + auto CtorDec = CC->getDecl(); + // Check for copying a moved-from object and report the bug. + if (CtorDec && CtorDec->isCopyOrMoveConstructor()) { + const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion(); + const CXXRecordDecl *RD = CtorDec->getParent(); + MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy; + modelUse(State, ArgRegion, RD, MK, C); + return; + } + } + + const auto IC = dyn_cast<CXXInstanceCall>(&Call); + if (!IC) + return; + + // Calling a destructor on a moved object is fine. + if (isa<CXXDestructorCall>(IC)) + return; + + const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion(); + if (!ThisRegion) + return; + + // The remaining part is check only for method call on a moved-from object. + const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl()); + if (!MethodDecl) + return; + + // We want to investigate the whole object, not only sub-object of a parent + // class in which the encountered method defined. + ThisRegion = ThisRegion->getMostDerivedObjectRegion(); + + if (isStateResetMethod(MethodDecl)) { + State = removeFromState(State, ThisRegion); + C.addTransition(State); + return; + } + + if (isMoveSafeMethod(MethodDecl)) + return; + + // Store class declaration as well, for bug reporting purposes. + const CXXRecordDecl *RD = MethodDecl->getParent(); + + if (MethodDecl->isOverloadedOperator()) { + OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator(); + + if (OOK == OO_Equal) { + // Remove the tracked object for every assignment operator, but report bug + // only for move or copy assignment's argument. + State = removeFromState(State, ThisRegion); + + if (MethodDecl->isCopyAssignmentOperator() || + MethodDecl->isMoveAssignmentOperator()) { + const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion(); + MisuseKind MK = + MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy; + modelUse(State, ArgRegion, RD, MK, C); + return; + } + C.addTransition(State); + return; + } + + if (OOK == OO_Star || OOK == OO_Arrow) { + modelUse(State, ThisRegion, RD, MK_Dereference, C); + return; + } + } + + modelUse(State, ThisRegion, RD, MK_FunCall, C); +} + +void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>(); + for (TrackedRegionMapTy::value_type E : TrackedRegions) { + const MemRegion *Region = E.first; + bool IsRegDead = !SymReaper.isLiveRegion(Region); + + // Remove the dead regions from the region map. + if (IsRegDead) { + State = State->remove<TrackedRegionMap>(Region); + } + } + C.addTransition(State); +} + +ProgramStateRef MoveChecker::checkRegionChanges( + ProgramStateRef State, const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> RequestedRegions, + ArrayRef<const MemRegion *> InvalidatedRegions, + const LocationContext *LCtx, const CallEvent *Call) const { + if (Call) { + // Relax invalidation upon function calls: only invalidate parameters + // that are passed directly via non-const pointers or non-const references + // or rvalue references. + // In case of an InstanceCall don't invalidate the this-region since + // it is fully handled in checkPreCall and checkPostCall. + const MemRegion *ThisRegion = nullptr; + if (const auto *IC = dyn_cast<CXXInstanceCall>(Call)) + ThisRegion = IC->getCXXThisVal().getAsRegion(); + + // Requested ("explicit") regions are the regions passed into the call + // directly, but not all of them end up being invalidated. + // But when they do, they appear in the InvalidatedRegions array as well. + for (const auto *Region : RequestedRegions) { + if (ThisRegion != Region) { + if (llvm::find(InvalidatedRegions, Region) != + std::end(InvalidatedRegions)) { + State = removeFromState(State, Region); + } + } + } + } else { + // For invalidations that aren't caused by calls, assume nothing. In + // particular, direct write into an object's field invalidates the status. + for (const auto *Region : InvalidatedRegions) + State = removeFromState(State, Region->getBaseRegion()); + } + + return State; +} + +void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + + TrackedRegionMapTy RS = State->get<TrackedRegionMap>(); + + if (!RS.isEmpty()) { + Out << Sep << "Moved-from objects :" << NL; + for (auto I: RS) { + I.first->dumpToStream(Out); + if (I.second.isMoved()) + Out << ": moved"; + else + Out << ": moved and reported"; + Out << NL; + } + } +} +void ento::registerMoveChecker(CheckerManager &mgr) { + MoveChecker *chk = mgr.registerChecker<MoveChecker>(); + chk->setAggressiveness( + mgr.getAnalyzerOptions().getCheckerStringOption("WarnOn", "", chk)); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp new file mode 100644 index 000000000000..4ed1b25cb09e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -0,0 +1,81 @@ +//=- NSAutoreleasePoolChecker.cpp --------------------------------*- 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 a NSAutoreleasePoolChecker, a small checker that warns +// about subpar uses of NSAutoreleasePool. Note that while the check itself +// (in its current form) could be written as a flow-insensitive check, in +// can be potentially enhanced in the future with flow-sensitive information. +// It is also a good example of the CheckerVisitor interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class NSAutoreleasePoolChecker + : public Checker<check::PreObjCMessage> { + mutable std::unique_ptr<BugType> BT; + mutable Selector releaseS; + +public: + void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; +}; + +} // end anonymous namespace + +void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg, + CheckerContext &C) const { + if (!msg.isInstanceMessage()) + return; + + const ObjCInterfaceDecl *OD = msg.getReceiverInterface(); + if (!OD) + return; + if (!OD->getIdentifier()->isStr("NSAutoreleasePool")) + return; + + if (releaseS.isNull()) + releaseS = GetNullarySelector("release", C.getASTContext()); + // Sending 'release' message? + if (msg.getSelector() != releaseS) + return; + + if (!BT) + BT.reset(new BugType(this, "Use -drain instead of -release", + "API Upgrade (Apple)")); + + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) { + assert(0); + return; + } + + auto Report = llvm::make_unique<BugReport>( + *BT, "Use -drain instead of -release when using NSAutoreleasePool and " + "garbage collection", N); + Report->addRange(msg.getSourceRange()); + C.emitReport(std::move(Report)); +} + +void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) { + if (mgr.getLangOpts().getGC() != LangOptions::NonGC) + mgr.registerChecker<NSAutoreleasePoolChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp new file mode 100644 index 000000000000..06c43c6b9470 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -0,0 +1,323 @@ +//=- NSErrorChecker.cpp - Coding conventions for uses of NSError -*- 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 a CheckNSError, a flow-insenstive check +// that determines if an Objective-C class interface correctly returns +// a non-void return type. +// +// File under feature request PR 2600. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +static bool IsNSError(QualType T, IdentifierInfo *II); +static bool IsCFError(QualType T, IdentifierInfo *II); + +//===----------------------------------------------------------------------===// +// NSErrorMethodChecker +//===----------------------------------------------------------------------===// + +namespace { +class NSErrorMethodChecker + : public Checker< check::ASTDecl<ObjCMethodDecl> > { + mutable IdentifierInfo *II; + +public: + NSErrorMethodChecker() : II(nullptr) {} + + void checkASTDecl(const ObjCMethodDecl *D, + AnalysisManager &mgr, BugReporter &BR) const; +}; +} + +void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D, + AnalysisManager &mgr, + BugReporter &BR) const { + if (!D->isThisDeclarationADefinition()) + return; + if (!D->getReturnType()->isVoidType()) + return; + + if (!II) + II = &D->getASTContext().Idents.get("NSError"); + + bool hasNSError = false; + for (const auto *I : D->parameters()) { + if (IsNSError(I->getType(), II)) { + hasNSError = true; + break; + } + } + + if (hasNSError) { + const char *err = "Method accepting NSError** " + "should have a non-void return value to indicate whether or not an " + "error occurred"; + PathDiagnosticLocation L = + PathDiagnosticLocation::create(D, BR.getSourceManager()); + BR.EmitBasicReport(D, this, "Bad return type when passing NSError**", + "Coding conventions (Apple)", err, L); + } +} + +//===----------------------------------------------------------------------===// +// CFErrorFunctionChecker +//===----------------------------------------------------------------------===// + +namespace { +class CFErrorFunctionChecker + : public Checker< check::ASTDecl<FunctionDecl> > { + mutable IdentifierInfo *II; + +public: + CFErrorFunctionChecker() : II(nullptr) {} + + void checkASTDecl(const FunctionDecl *D, + AnalysisManager &mgr, BugReporter &BR) const; +}; +} + +void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D, + AnalysisManager &mgr, + BugReporter &BR) const { + if (!D->doesThisDeclarationHaveABody()) + return; + if (!D->getReturnType()->isVoidType()) + return; + + if (!II) + II = &D->getASTContext().Idents.get("CFErrorRef"); + + bool hasCFError = false; + for (auto I : D->parameters()) { + if (IsCFError(I->getType(), II)) { + hasCFError = true; + break; + } + } + + if (hasCFError) { + const char *err = "Function accepting CFErrorRef* " + "should have a non-void return value to indicate whether or not an " + "error occurred"; + PathDiagnosticLocation L = + PathDiagnosticLocation::create(D, BR.getSourceManager()); + BR.EmitBasicReport(D, this, "Bad return type when passing CFErrorRef*", + "Coding conventions (Apple)", err, L); + } +} + +//===----------------------------------------------------------------------===// +// NSOrCFErrorDerefChecker +//===----------------------------------------------------------------------===// + +namespace { + +class NSErrorDerefBug : public BugType { +public: + NSErrorDerefBug(const CheckerBase *Checker) + : BugType(Checker, "NSError** null dereference", + "Coding conventions (Apple)") {} +}; + +class CFErrorDerefBug : public BugType { +public: + CFErrorDerefBug(const CheckerBase *Checker) + : BugType(Checker, "CFErrorRef* null dereference", + "Coding conventions (Apple)") {} +}; + +} + +namespace { +class NSOrCFErrorDerefChecker + : public Checker< check::Location, + check::Event<ImplicitNullDerefEvent> > { + mutable IdentifierInfo *NSErrorII, *CFErrorII; + mutable std::unique_ptr<NSErrorDerefBug> NSBT; + mutable std::unique_ptr<CFErrorDerefBug> CFBT; +public: + bool ShouldCheckNSError, ShouldCheckCFError; + NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr), + ShouldCheckNSError(0), ShouldCheckCFError(0) { } + + void checkLocation(SVal loc, bool isLoad, const Stmt *S, + CheckerContext &C) const; + void checkEvent(ImplicitNullDerefEvent event) const; +}; +} + +typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag; +REGISTER_TRAIT_WITH_PROGRAMSTATE(NSErrorOut, ErrorOutFlag) +REGISTER_TRAIT_WITH_PROGRAMSTATE(CFErrorOut, ErrorOutFlag) + +template <typename T> +static bool hasFlag(SVal val, ProgramStateRef state) { + if (SymbolRef sym = val.getAsSymbol()) + if (const unsigned *attachedFlags = state->get<T>(sym)) + return *attachedFlags; + return false; +} + +template <typename T> +static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) { + // We tag the symbol that the SVal wraps. + if (SymbolRef sym = val.getAsSymbol()) + C.addTransition(state->set<T>(sym, true)); +} + +static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) { + const StackFrameContext * SFC = C.getStackFrame(); + if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) { + const MemRegion* R = X->getRegion(); + if (const VarRegion *VR = R->getAs<VarRegion>()) + if (const StackArgumentsSpaceRegion * + stackReg = dyn_cast<StackArgumentsSpaceRegion>(VR->getMemorySpace())) + if (stackReg->getStackFrame() == SFC) + return VR->getValueType(); + } + + return QualType(); +} + +void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad, + const Stmt *S, + CheckerContext &C) const { + if (!isLoad) + return; + if (loc.isUndef() || !loc.getAs<Loc>()) + return; + + ASTContext &Ctx = C.getASTContext(); + ProgramStateRef state = C.getState(); + + // If we are loading from NSError**/CFErrorRef* parameter, mark the resulting + // SVal so that we can later check it when handling the + // ImplicitNullDerefEvent event. + // FIXME: Cumbersome! Maybe add hook at construction of SVals at start of + // function ? + + QualType parmT = parameterTypeFromSVal(loc, C); + if (parmT.isNull()) + return; + + if (!NSErrorII) + NSErrorII = &Ctx.Idents.get("NSError"); + if (!CFErrorII) + CFErrorII = &Ctx.Idents.get("CFErrorRef"); + + if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) { + setFlag<NSErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C); + return; + } + + if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) { + setFlag<CFErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C); + return; + } +} + +void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const { + if (event.IsLoad) + return; + + SVal loc = event.Location; + ProgramStateRef state = event.SinkNode->getState(); + BugReporter &BR = *event.BR; + + bool isNSError = hasFlag<NSErrorOut>(loc, state); + bool isCFError = false; + if (!isNSError) + isCFError = hasFlag<CFErrorOut>(loc, state); + + if (!(isNSError || isCFError)) + return; + + // Storing to possible null NSError/CFErrorRef out parameter. + SmallString<128> Buf; + llvm::raw_svector_ostream os(Buf); + + os << "Potential null dereference. According to coding standards "; + os << (isNSError + ? "in 'Creating and Returning NSError Objects' the parameter" + : "documented in CoreFoundation/CFError.h the parameter"); + + os << " may be null"; + + BugType *bug = nullptr; + if (isNSError) { + if (!NSBT) + NSBT.reset(new NSErrorDerefBug(this)); + bug = NSBT.get(); + } + else { + if (!CFBT) + CFBT.reset(new CFErrorDerefBug(this)); + bug = CFBT.get(); + } + BR.emitReport(llvm::make_unique<BugReport>(*bug, os.str(), event.SinkNode)); +} + +static bool IsNSError(QualType T, IdentifierInfo *II) { + + const PointerType* PPT = T->getAs<PointerType>(); + if (!PPT) + return false; + + const ObjCObjectPointerType* PT = + PPT->getPointeeType()->getAs<ObjCObjectPointerType>(); + + if (!PT) + return false; + + const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); + + // FIXME: Can ID ever be NULL? + if (ID) + return II == ID->getIdentifier(); + + return false; +} + +static bool IsCFError(QualType T, IdentifierInfo *II) { + const PointerType* PPT = T->getAs<PointerType>(); + if (!PPT) return false; + + const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>(); + if (!TT) return false; + + return TT->getDecl()->getIdentifier() == II; +} + +void ento::registerNSErrorChecker(CheckerManager &mgr) { + mgr.registerChecker<NSErrorMethodChecker>(); + NSOrCFErrorDerefChecker *checker = + mgr.registerChecker<NSOrCFErrorDerefChecker>(); + checker->ShouldCheckNSError = true; +} + +void ento::registerCFErrorChecker(CheckerManager &mgr) { + mgr.registerChecker<CFErrorFunctionChecker>(); + NSOrCFErrorDerefChecker *checker = + mgr.registerChecker<NSOrCFErrorDerefChecker>(); + checker->ShouldCheckCFError = true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp new file mode 100644 index 000000000000..83d4b5b0758b --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -0,0 +1,145 @@ +//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines NoReturnFunctionChecker, which evaluates functions that do not +// return to the caller. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Attr.h" +#include "clang/Analysis/SelectorExtras.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/StringSwitch.h" +#include <cstdarg> + +using namespace clang; +using namespace ento; + +namespace { + +class NoReturnFunctionChecker : public Checker< check::PostCall, + check::PostObjCMessage > { + mutable Selector HandleFailureInFunctionSel; + mutable Selector HandleFailureInMethodSel; +public: + void checkPostCall(const CallEvent &CE, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const; +}; + +} + +void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE, + CheckerContext &C) const { + bool BuildSinks = false; + + if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl())) + BuildSinks = FD->hasAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn(); + + const Expr *Callee = CE.getOriginExpr(); + if (!BuildSinks && Callee) + BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); + + if (!BuildSinks && CE.isGlobalCFunction()) { + if (const IdentifierInfo *II = CE.getCalleeIdentifier()) { + // HACK: Some functions are not marked noreturn, and don't return. + // Here are a few hardwired ones. If this takes too long, we can + // potentially cache these results. + BuildSinks + = llvm::StringSwitch<bool>(StringRef(II->getName())) + .Case("exit", true) + .Case("panic", true) + .Case("error", true) + .Case("Assert", true) + // FIXME: This is just a wrapper around throwing an exception. + // Eventually inter-procedural analysis should handle this easily. + .Case("ziperr", true) + .Case("assfail", true) + .Case("db_error", true) + .Case("__assert", true) + .Case("__assert2", true) + // For the purpose of static analysis, we do not care that + // this MSVC function will return if the user decides to continue. + .Case("_wassert", true) + .Case("__assert_rtn", true) + .Case("__assert_fail", true) + .Case("dtrace_assfail", true) + .Case("yy_fatal_error", true) + .Case("_XCAssertionFailureHandler", true) + .Case("_DTAssertionFailureHandler", true) + .Case("_TSAssertionFailureHandler", true) + .Default(false); + } + } + + if (BuildSinks) + C.generateSink(C.getState(), C.getPredecessor()); +} + +void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, + CheckerContext &C) const { + // Check if the method is annotated with analyzer_noreturn. + if (const ObjCMethodDecl *MD = Msg.getDecl()) { + MD = MD->getCanonicalDecl(); + if (MD->hasAttr<AnalyzerNoReturnAttr>()) { + C.generateSink(C.getState(), C.getPredecessor()); + return; + } + } + + // HACK: This entire check is to handle two messages in the Cocoa frameworks: + // -[NSAssertionHandler + // handleFailureInMethod:object:file:lineNumber:description:] + // -[NSAssertionHandler + // handleFailureInFunction:file:lineNumber:description:] + // Eventually these should be annotated with __attribute__((noreturn)). + // Because ObjC messages use dynamic dispatch, it is not generally safe to + // assume certain methods can't return. In cases where it is definitely valid, + // see if you can mark the methods noreturn or analyzer_noreturn instead of + // adding more explicit checks to this method. + + if (!Msg.isInstanceMessage()) + return; + + const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface(); + if (!Receiver) + return; + if (!Receiver->getIdentifier()->isStr("NSAssertionHandler")) + return; + + Selector Sel = Msg.getSelector(); + switch (Sel.getNumArgs()) { + default: + return; + case 4: + lazyInitKeywordSelector(HandleFailureInFunctionSel, C.getASTContext(), + "handleFailureInFunction", "file", "lineNumber", + "description"); + if (Sel != HandleFailureInFunctionSel) + return; + break; + case 5: + lazyInitKeywordSelector(HandleFailureInMethodSel, C.getASTContext(), + "handleFailureInMethod", "object", "file", + "lineNumber", "description"); + if (Sel != HandleFailureInMethodSel) + return; + break; + } + + // If we got here, it's one of the messages we care about. + C.generateSink(C.getState(), C.getPredecessor()); +} + +void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) { + mgr.registerChecker<NoReturnFunctionChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp new file mode 100644 index 000000000000..3c4363b6850e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp @@ -0,0 +1,219 @@ +//===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines NonNullParamChecker, which checks for arguments expected not to +// be null due to: +// - the corresponding parameters being declared to have nonnull attribute +// - the corresponding parameters being references; since the call would form +// a reference to a null pointer +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Attr.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class NonNullParamChecker + : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > { + mutable std::unique_ptr<BugType> BTAttrNonNull; + mutable std::unique_ptr<BugType> BTNullRefArg; + +public: + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + + std::unique_ptr<BugReport> + genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const; + std::unique_ptr<BugReport> + genReportReferenceToNullPointer(const ExplodedNode *ErrorN, + const Expr *ArgE) const; +}; +} // end anonymous namespace + +/// \return Bitvector marking non-null attributes. +static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) { + const Decl *FD = Call.getDecl(); + unsigned NumArgs = Call.getNumArgs(); + llvm::SmallBitVector AttrNonNull(NumArgs); + for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) { + if (!NonNull->args_size()) { + AttrNonNull.set(0, NumArgs); + break; + } + for (const ParamIdx &Idx : NonNull->args()) { + unsigned IdxAST = Idx.getASTIndex(); + if (IdxAST >= NumArgs) + continue; + AttrNonNull.set(IdxAST); + } + } + return AttrNonNull; +} + +void NonNullParamChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!Call.getDecl()) + return; + + llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call); + unsigned NumArgs = Call.getNumArgs(); + + ProgramStateRef state = C.getState(); + ArrayRef<ParmVarDecl*> parms = Call.parameters(); + + for (unsigned idx = 0; idx < NumArgs; ++idx) { + // For vararg functions, a corresponding parameter decl may not exist. + bool HasParam = idx < parms.size(); + + // Check if the parameter is a reference. We want to report when reference + // to a null pointer is passed as a parameter. + bool haveRefTypeParam = + HasParam ? parms[idx]->getType()->isReferenceType() : false; + bool haveAttrNonNull = AttrNonNull[idx]; + + // Check if the parameter is also marked 'nonnull'. + if (!haveAttrNonNull && HasParam) + haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>(); + + if (!haveAttrNonNull && !haveRefTypeParam) + continue; + + // If the value is unknown or undefined, we can't perform this check. + const Expr *ArgE = Call.getArgExpr(idx); + SVal V = Call.getArgSVal(idx); + auto DV = V.getAs<DefinedSVal>(); + if (!DV) + continue; + + assert(!haveRefTypeParam || DV->getAs<Loc>()); + + // Process the case when the argument is not a location. + if (haveAttrNonNull && !DV->getAs<Loc>()) { + // If the argument is a union type, we want to handle a potential + // transparent_union GCC extension. + if (!ArgE) + continue; + + QualType T = ArgE->getType(); + const RecordType *UT = T->getAsUnionType(); + if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>()) + continue; + + auto CSV = DV->getAs<nonloc::CompoundVal>(); + + // FIXME: Handle LazyCompoundVals? + if (!CSV) + continue; + + V = *(CSV->begin()); + DV = V.getAs<DefinedSVal>(); + assert(++CSV->begin() == CSV->end()); + // FIXME: Handle (some_union){ some_other_union_val }, which turns into + // a LazyCompoundVal inside a CompoundVal. + if (!V.getAs<Loc>()) + continue; + + // Retrieve the corresponding expression. + if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE)) + if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer())) + ArgE = dyn_cast<Expr>(*(IE->begin())); + } + + ConstraintManager &CM = C.getConstraintManager(); + ProgramStateRef stateNotNull, stateNull; + std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); + + // Generate an error node. Check for a null node in case + // we cache out. + if (stateNull && !stateNotNull) { + if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) { + + std::unique_ptr<BugReport> R; + if (haveAttrNonNull) + R = genReportNullAttrNonNull(errorNode, ArgE); + else if (haveRefTypeParam) + R = genReportReferenceToNullPointer(errorNode, ArgE); + + // Highlight the range of the argument that was null. + R->addRange(Call.getArgSourceRange(idx)); + + // Emit the bug report. + C.emitReport(std::move(R)); + } + + // Always return. Either we cached out or we just emitted an error. + return; + } + + if (stateNull) { + if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) { + ImplicitNullDerefEvent event = { + V, false, N, &C.getBugReporter(), + /*IsDirectDereference=*/haveRefTypeParam}; + dispatchEvent(event); + } + } + + // If a pointer value passed the check we should assume that it is + // indeed not null from this point forward. + state = stateNotNull; + } + + // If we reach here all of the arguments passed the nonnull check. + // If 'state' has been updated generated a new node. + C.addTransition(state); +} + +std::unique_ptr<BugReport> +NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode, + const Expr *ArgE) const { + // Lazily allocate the BugType object if it hasn't already been + // created. Ownership is transferred to the BugReporter object once + // the BugReport is passed to 'EmitWarning'. + if (!BTAttrNonNull) + BTAttrNonNull.reset(new BugType( + this, "Argument with 'nonnull' attribute passed null", "API")); + + auto R = llvm::make_unique<BugReport>( + *BTAttrNonNull, + "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode); + if (ArgE) + bugreporter::trackExpressionValue(ErrorNode, ArgE, *R); + + return R; +} + +std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer( + const ExplodedNode *ErrorNode, const Expr *ArgE) const { + if (!BTNullRefArg) + BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer")); + + auto R = llvm::make_unique<BugReport>( + *BTNullRefArg, "Forming reference to null pointer", ErrorNode); + if (ArgE) { + const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE); + if (!ArgEDeref) + ArgEDeref = ArgE; + bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R); + } + return R; + +} + +void ento::registerNonNullParamChecker(CheckerManager &mgr) { + mgr.registerChecker<NonNullParamChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp new file mode 100644 index 000000000000..ce9e950aa9ba --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp @@ -0,0 +1,140 @@ +//==- NonnullGlobalConstantsChecker.cpp ---------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker adds an assumption that constant globals of certain types* are +// non-null, as otherwise they generally do not convey any useful information. +// The assumption is useful, as many framework use e. g. global const strings, +// and the analyzer might not be able to infer the global value if the +// definition is in a separate translation unit. +// The following types (and their typedef aliases) are considered to be +// non-null: +// - `char* const` +// - `const CFStringRef` from CoreFoundation +// - `NSString* const` from Foundation +// - `CFBooleanRef` from Foundation +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { + +class NonnullGlobalConstantsChecker : public Checker<check::Location> { + mutable IdentifierInfo *NSStringII = nullptr; + mutable IdentifierInfo *CFStringRefII = nullptr; + mutable IdentifierInfo *CFBooleanRefII = nullptr; + +public: + NonnullGlobalConstantsChecker() {} + + void checkLocation(SVal l, bool isLoad, const Stmt *S, + CheckerContext &C) const; + +private: + void initIdentifierInfo(ASTContext &Ctx) const; + + bool isGlobalConstString(SVal V) const; + + bool isNonnullType(QualType Ty) const; +}; + +} // namespace + +/// Lazily initialize cache for required identifier information. +void NonnullGlobalConstantsChecker::initIdentifierInfo(ASTContext &Ctx) const { + if (NSStringII) + return; + + NSStringII = &Ctx.Idents.get("NSString"); + CFStringRefII = &Ctx.Idents.get("CFStringRef"); + CFBooleanRefII = &Ctx.Idents.get("CFBooleanRef"); +} + +/// Add an assumption that const string-like globals are non-null. +void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad, + const Stmt *S, + CheckerContext &C) const { + initIdentifierInfo(C.getASTContext()); + if (!isLoad || !location.isValid()) + return; + + ProgramStateRef State = C.getState(); + + if (isGlobalConstString(location)) { + SVal V = State->getSVal(location.castAs<Loc>()); + Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>(); + + if (Constr) { + + // Assume that the variable is non-null. + ProgramStateRef OutputState = State->assume(*Constr, true); + C.addTransition(OutputState); + } + } +} + +/// \param V loaded lvalue. +/// \return whether {@code val} is a string-like const global. +bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const { + Optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>(); + if (!RegionVal) + return false; + auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion()); + if (!Region) + return false; + const VarDecl *Decl = Region->getDecl(); + + if (!Decl->hasGlobalStorage()) + return false; + + QualType Ty = Decl->getType(); + bool HasConst = Ty.isConstQualified(); + if (isNonnullType(Ty) && HasConst) + return true; + + // Look through the typedefs. + while (auto *T = dyn_cast<TypedefType>(Ty)) { + Ty = T->getDecl()->getUnderlyingType(); + + // It is sufficient for any intermediate typedef + // to be classified const. + HasConst = HasConst || Ty.isConstQualified(); + if (isNonnullType(Ty) && HasConst) + return true; + } + return false; +} + +/// \return whether {@code type} is extremely unlikely to be null +bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const { + + if (Ty->isPointerType() && Ty->getPointeeType()->isCharType()) + return true; + + if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) { + return T->getInterfaceDecl() && + T->getInterfaceDecl()->getIdentifier() == NSStringII; + } else if (auto *T = dyn_cast<TypedefType>(Ty)) { + IdentifierInfo* II = T->getDecl()->getIdentifier(); + return II == CFStringRefII || II == CFBooleanRefII; + } + return false; +} + +void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) { + Mgr.registerChecker<NonnullGlobalConstantsChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp new file mode 100644 index 000000000000..e535d1ae27ac --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp @@ -0,0 +1,1216 @@ +//== Nullabilityhecker.cpp - Nullability checker ----------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker tries to find nullability violations. There are several kinds of +// possible violations: +// * Null pointer is passed to a pointer which has a _Nonnull type. +// * Null pointer is returned from a function which has a _Nonnull return type. +// * Nullable pointer is passed to a pointer which has a _Nonnull type. +// * Nullable pointer is returned from a function which has a _Nonnull return +// type. +// * Nullable pointer is dereferenced. +// +// This checker propagates the nullability information of the pointers and looks +// for the patterns that are described above. Explicit casts are trusted and are +// considered a way to suppress false positives for this checker. The other way +// to suppress warnings would be to add asserts or guarding if statements to the +// code. In addition to the nullability propagation this checker also uses some +// heuristics to suppress potential false positives. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" + +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" + +#include "llvm/ADT/StringExtras.h" +#include "llvm/Support/Path.h" + +using namespace clang; +using namespace ento; + +namespace { + +/// Returns the most nullable nullability. This is used for message expressions +/// like [receiver method], where the nullability of this expression is either +/// the nullability of the receiver or the nullability of the return type of the +/// method, depending on which is more nullable. Contradicted is considered to +/// be the most nullable, to avoid false positive results. +Nullability getMostNullable(Nullability Lhs, Nullability Rhs) { + return static_cast<Nullability>( + std::min(static_cast<char>(Lhs), static_cast<char>(Rhs))); +} + +const char *getNullabilityString(Nullability Nullab) { + switch (Nullab) { + case Nullability::Contradicted: + return "contradicted"; + case Nullability::Nullable: + return "nullable"; + case Nullability::Unspecified: + return "unspecified"; + case Nullability::Nonnull: + return "nonnull"; + } + llvm_unreachable("Unexpected enumeration."); + return ""; +} + +// These enums are used as an index to ErrorMessages array. +enum class ErrorKind : int { + NilAssignedToNonnull, + NilPassedToNonnull, + NilReturnedToNonnull, + NullableAssignedToNonnull, + NullableReturnedToNonnull, + NullableDereferenced, + NullablePassedToNonnull +}; + +class NullabilityChecker + : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>, + check::PostCall, check::PostStmt<ExplicitCastExpr>, + check::PostObjCMessage, check::DeadSymbols, + check::Event<ImplicitNullDerefEvent>> { + mutable std::unique_ptr<BugType> BT; + +public: + // If true, the checker will not diagnose nullabilility issues for calls + // to system headers. This option is motivated by the observation that large + // projects may have many nullability warnings. These projects may + // find warnings about nullability annotations that they have explicitly + // added themselves higher priority to fix than warnings on calls to system + // libraries. + DefaultBool NoDiagnoseCallsToSystemHeaders; + + void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; + void checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const; + void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + void checkEvent(ImplicitNullDerefEvent Event) const; + + void printState(raw_ostream &Out, ProgramStateRef State, const char *NL, + const char *Sep) const override; + + struct NullabilityChecksFilter { + DefaultBool CheckNullPassedToNonnull; + DefaultBool CheckNullReturnedFromNonnull; + DefaultBool CheckNullableDereferenced; + DefaultBool CheckNullablePassedToNonnull; + DefaultBool CheckNullableReturnedFromNonnull; + + CheckName CheckNameNullPassedToNonnull; + CheckName CheckNameNullReturnedFromNonnull; + CheckName CheckNameNullableDereferenced; + CheckName CheckNameNullablePassedToNonnull; + CheckName CheckNameNullableReturnedFromNonnull; + }; + + NullabilityChecksFilter Filter; + // When set to false no nullability information will be tracked in + // NullabilityMap. It is possible to catch errors like passing a null pointer + // to a callee that expects nonnull argument without the information that is + // stroed in the NullabilityMap. This is an optimization. + DefaultBool NeedTracking; + +private: + class NullabilityBugVisitor : public BugReporterVisitor { + public: + NullabilityBugVisitor(const MemRegion *M) : Region(M) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Region); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + // The tracked region. + const MemRegion *Region; + }; + + /// When any of the nonnull arguments of the analyzed function is null, do not + /// report anything and turn off the check. + /// + /// When \p SuppressPath is set to true, no more bugs will be reported on this + /// path by this checker. + void reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error, + ExplodedNode *N, const MemRegion *Region, + CheckerContext &C, + const Stmt *ValueExpr = nullptr, + bool SuppressPath = false) const; + + void reportBug(StringRef Msg, ErrorKind Error, ExplodedNode *N, + const MemRegion *Region, BugReporter &BR, + const Stmt *ValueExpr = nullptr) const { + if (!BT) + BT.reset(new BugType(this, "Nullability", categories::MemoryError)); + + auto R = llvm::make_unique<BugReport>(*BT, Msg, N); + if (Region) { + R->markInteresting(Region); + R->addVisitor(llvm::make_unique<NullabilityBugVisitor>(Region)); + } + if (ValueExpr) { + R->addRange(ValueExpr->getSourceRange()); + if (Error == ErrorKind::NilAssignedToNonnull || + Error == ErrorKind::NilPassedToNonnull || + Error == ErrorKind::NilReturnedToNonnull) + if (const auto *Ex = dyn_cast<Expr>(ValueExpr)) + bugreporter::trackExpressionValue(N, Ex, *R); + } + BR.emitReport(std::move(R)); + } + + /// If an SVal wraps a region that should be tracked, it will return a pointer + /// to the wrapped region. Otherwise it will return a nullptr. + const SymbolicRegion *getTrackRegion(SVal Val, + bool CheckSuperRegion = false) const; + + /// Returns true if the call is diagnosable in the current analyzer + /// configuration. + bool isDiagnosableCall(const CallEvent &Call) const { + if (NoDiagnoseCallsToSystemHeaders && Call.isInSystemHeader()) + return false; + + return true; + } +}; + +class NullabilityState { +public: + NullabilityState(Nullability Nullab, const Stmt *Source = nullptr) + : Nullab(Nullab), Source(Source) {} + + const Stmt *getNullabilitySource() const { return Source; } + + Nullability getValue() const { return Nullab; } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(static_cast<char>(Nullab)); + ID.AddPointer(Source); + } + + void print(raw_ostream &Out) const { + Out << getNullabilityString(Nullab) << "\n"; + } + +private: + Nullability Nullab; + // Source is the expression which determined the nullability. For example in a + // message like [nullable nonnull_returning] has nullable nullability, because + // the receiver is nullable. Here the receiver will be the source of the + // nullability. This is useful information when the diagnostics are generated. + const Stmt *Source; +}; + +bool operator==(NullabilityState Lhs, NullabilityState Rhs) { + return Lhs.getValue() == Rhs.getValue() && + Lhs.getNullabilitySource() == Rhs.getNullabilitySource(); +} + +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *, + NullabilityState) + +// We say "the nullability type invariant is violated" when a location with a +// non-null type contains NULL or a function with a non-null return type returns +// NULL. Violations of the nullability type invariant can be detected either +// directly (for example, when NULL is passed as an argument to a nonnull +// parameter) or indirectly (for example, when, inside a function, the +// programmer defensively checks whether a nonnull parameter contains NULL and +// finds that it does). +// +// As a matter of policy, the nullability checker typically warns on direct +// violations of the nullability invariant (although it uses various +// heuristics to suppress warnings in some cases) but will not warn if the +// invariant has already been violated along the path (either directly or +// indirectly). As a practical matter, this prevents the analyzer from +// (1) warning on defensive code paths where a nullability precondition is +// determined to have been violated, (2) warning additional times after an +// initial direct violation has been discovered, and (3) warning after a direct +// violation that has been implicitly or explicitly suppressed (for +// example, with a cast of NULL to _Nonnull). In essence, once an invariant +// violation is detected on a path, this checker will be essentially turned off +// for the rest of the analysis +// +// The analyzer takes this approach (rather than generating a sink node) to +// ensure coverage of defensive paths, which may be important for backwards +// compatibility in codebases that were developed without nullability in mind. +REGISTER_TRAIT_WITH_PROGRAMSTATE(InvariantViolated, bool) + +enum class NullConstraint { IsNull, IsNotNull, Unknown }; + +static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val, + ProgramStateRef State) { + ConditionTruthVal Nullness = State->isNull(Val); + if (Nullness.isConstrainedFalse()) + return NullConstraint::IsNotNull; + if (Nullness.isConstrainedTrue()) + return NullConstraint::IsNull; + return NullConstraint::Unknown; +} + +const SymbolicRegion * +NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const { + if (!NeedTracking) + return nullptr; + + auto RegionSVal = Val.getAs<loc::MemRegionVal>(); + if (!RegionSVal) + return nullptr; + + const MemRegion *Region = RegionSVal->getRegion(); + + if (CheckSuperRegion) { + if (auto FieldReg = Region->getAs<FieldRegion>()) + return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion()); + if (auto ElementReg = Region->getAs<ElementRegion>()) + return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion()); + } + + return dyn_cast<SymbolicRegion>(Region); +} + +std::shared_ptr<PathDiagnosticPiece> +NullabilityChecker::NullabilityBugVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) { + ProgramStateRef State = N->getState(); + ProgramStateRef StatePrev = N->getFirstPred()->getState(); + + const NullabilityState *TrackedNullab = State->get<NullabilityMap>(Region); + const NullabilityState *TrackedNullabPrev = + StatePrev->get<NullabilityMap>(Region); + if (!TrackedNullab) + return nullptr; + + if (TrackedNullabPrev && + TrackedNullabPrev->getValue() == TrackedNullab->getValue()) + return nullptr; + + // Retrieve the associated statement. + const Stmt *S = TrackedNullab->getNullabilitySource(); + if (!S || S->getBeginLoc().isInvalid()) { + S = PathDiagnosticLocation::getStmt(N); + } + + if (!S) + return nullptr; + + std::string InfoText = + (llvm::Twine("Nullability '") + + getNullabilityString(TrackedNullab->getValue()) + "' is inferred") + .str(); + + // Generate the extra diagnostic. + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true, + nullptr); +} + +/// Returns true when the value stored at the given location has been +/// constrained to null after being passed through an object of nonnnull type. +static bool checkValueAtLValForInvariantViolation(ProgramStateRef State, + SVal LV, QualType T) { + if (getNullabilityAnnotation(T) != Nullability::Nonnull) + return false; + + auto RegionVal = LV.getAs<loc::MemRegionVal>(); + if (!RegionVal) + return false; + + // If the value was constrained to null *after* it was passed through that + // location, it could not have been a concrete pointer *when* it was passed. + // In that case we would have handled the situation when the value was + // bound to that location, by emitting (or not emitting) a report. + // Therefore we are only interested in symbolic regions that can be either + // null or non-null depending on the value of their respective symbol. + auto StoredVal = State->getSVal(*RegionVal).getAs<loc::MemRegionVal>(); + if (!StoredVal || !isa<SymbolicRegion>(StoredVal->getRegion())) + return false; + + if (getNullConstraint(*StoredVal, State) == NullConstraint::IsNull) + return true; + + return false; +} + +static bool +checkParamsForPreconditionViolation(ArrayRef<ParmVarDecl *> Params, + ProgramStateRef State, + const LocationContext *LocCtxt) { + for (const auto *ParamDecl : Params) { + if (ParamDecl->isParameterPack()) + break; + + SVal LV = State->getLValue(ParamDecl, LocCtxt); + if (checkValueAtLValForInvariantViolation(State, LV, + ParamDecl->getType())) { + return true; + } + } + return false; +} + +static bool +checkSelfIvarsForInvariantViolation(ProgramStateRef State, + const LocationContext *LocCtxt) { + auto *MD = dyn_cast<ObjCMethodDecl>(LocCtxt->getDecl()); + if (!MD || !MD->isInstanceMethod()) + return false; + + const ImplicitParamDecl *SelfDecl = LocCtxt->getSelfDecl(); + if (!SelfDecl) + return false; + + SVal SelfVal = State->getSVal(State->getRegion(SelfDecl, LocCtxt)); + + const ObjCObjectPointerType *SelfType = + dyn_cast<ObjCObjectPointerType>(SelfDecl->getType()); + if (!SelfType) + return false; + + const ObjCInterfaceDecl *ID = SelfType->getInterfaceDecl(); + if (!ID) + return false; + + for (const auto *IvarDecl : ID->ivars()) { + SVal LV = State->getLValue(IvarDecl, SelfVal); + if (checkValueAtLValForInvariantViolation(State, LV, IvarDecl->getType())) { + return true; + } + } + return false; +} + +static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N, + CheckerContext &C) { + if (State->get<InvariantViolated>()) + return true; + + const LocationContext *LocCtxt = C.getLocationContext(); + const Decl *D = LocCtxt->getDecl(); + if (!D) + return false; + + ArrayRef<ParmVarDecl*> Params; + if (const auto *BD = dyn_cast<BlockDecl>(D)) + Params = BD->parameters(); + else if (const auto *FD = dyn_cast<FunctionDecl>(D)) + Params = FD->parameters(); + else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) + Params = MD->parameters(); + else + return false; + + if (checkParamsForPreconditionViolation(Params, State, LocCtxt) || + checkSelfIvarsForInvariantViolation(State, LocCtxt)) { + if (!N->isSink()) + C.addTransition(State->set<InvariantViolated>(true), N); + return true; + } + return false; +} + +void NullabilityChecker::reportBugIfInvariantHolds(StringRef Msg, + ErrorKind Error, ExplodedNode *N, const MemRegion *Region, + CheckerContext &C, const Stmt *ValueExpr, bool SuppressPath) const { + ProgramStateRef OriginalState = N->getState(); + + if (checkInvariantViolation(OriginalState, N, C)) + return; + if (SuppressPath) { + OriginalState = OriginalState->set<InvariantViolated>(true); + N = C.addTransition(OriginalState, N); + } + + reportBug(Msg, Error, N, Region, C.getBugReporter(), ValueExpr); +} + +/// Cleaning up the program state. +void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + NullabilityMapTy Nullabilities = State->get<NullabilityMap>(); + for (NullabilityMapTy::iterator I = Nullabilities.begin(), + E = Nullabilities.end(); + I != E; ++I) { + const auto *Region = I->first->getAs<SymbolicRegion>(); + assert(Region && "Non-symbolic region is tracked."); + if (SR.isDead(Region->getSymbol())) { + State = State->remove<NullabilityMap>(I->first); + } + } + // When one of the nonnull arguments are constrained to be null, nullability + // preconditions are violated. It is not enough to check this only when we + // actually report an error, because at that time interesting symbols might be + // reaped. + if (checkInvariantViolation(State, C.getPredecessor(), C)) + return; + C.addTransition(State); +} + +/// This callback triggers when a pointer is dereferenced and the analyzer does +/// not know anything about the value of that pointer. When that pointer is +/// nullable, this code emits a warning. +void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const { + if (Event.SinkNode->getState()->get<InvariantViolated>()) + return; + + const MemRegion *Region = + getTrackRegion(Event.Location, /*CheckSuperregion=*/true); + if (!Region) + return; + + ProgramStateRef State = Event.SinkNode->getState(); + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(Region); + + if (!TrackedNullability) + return; + + if (Filter.CheckNullableDereferenced && + TrackedNullability->getValue() == Nullability::Nullable) { + BugReporter &BR = *Event.BR; + // Do not suppress errors on defensive code paths, because dereferencing + // a nullable pointer is always an error. + if (Event.IsDirectDereference) + reportBug("Nullable pointer is dereferenced", + ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR); + else { + reportBug("Nullable pointer is passed to a callee that requires a " + "non-null", ErrorKind::NullablePassedToNonnull, + Event.SinkNode, Region, BR); + } + } +} + +/// Find the outermost subexpression of E that is not an implicit cast. +/// This looks through the implicit casts to _Nonnull that ARC adds to +/// return expressions of ObjC types when the return type of the function or +/// method is non-null but the express is not. +static const Expr *lookThroughImplicitCasts(const Expr *E) { + assert(E); + + while (auto *ICE = dyn_cast<ImplicitCastExpr>(E)) { + E = ICE->getSubExpr(); + } + + return E; +} + +/// This method check when nullable pointer or null value is returned from a +/// function that has nonnull return type. +void NullabilityChecker::checkPreStmt(const ReturnStmt *S, + CheckerContext &C) const { + auto RetExpr = S->getRetValue(); + if (!RetExpr) + return; + + if (!RetExpr->getType()->isAnyPointerType()) + return; + + ProgramStateRef State = C.getState(); + if (State->get<InvariantViolated>()) + return; + + auto RetSVal = C.getSVal(S).getAs<DefinedOrUnknownSVal>(); + if (!RetSVal) + return; + + bool InSuppressedMethodFamily = false; + + QualType RequiredRetType; + AnalysisDeclContext *DeclCtxt = + C.getLocationContext()->getAnalysisDeclContext(); + const Decl *D = DeclCtxt->getDecl(); + if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) { + // HACK: This is a big hammer to avoid warning when there are defensive + // nil checks in -init and -copy methods. We should add more sophisticated + // logic here to suppress on common defensive idioms but still + // warn when there is a likely problem. + ObjCMethodFamily Family = MD->getMethodFamily(); + if (OMF_init == Family || OMF_copy == Family || OMF_mutableCopy == Family) + InSuppressedMethodFamily = true; + + RequiredRetType = MD->getReturnType(); + } else if (auto *FD = dyn_cast<FunctionDecl>(D)) { + RequiredRetType = FD->getReturnType(); + } else { + return; + } + + NullConstraint Nullness = getNullConstraint(*RetSVal, State); + + Nullability RequiredNullability = getNullabilityAnnotation(RequiredRetType); + + // If the returned value is null but the type of the expression + // generating it is nonnull then we will suppress the diagnostic. + // This enables explicit suppression when returning a nil literal in a + // function with a _Nonnull return type: + // return (NSString * _Nonnull)0; + Nullability RetExprTypeLevelNullability = + getNullabilityAnnotation(lookThroughImplicitCasts(RetExpr)->getType()); + + bool NullReturnedFromNonNull = (RequiredNullability == Nullability::Nonnull && + Nullness == NullConstraint::IsNull); + if (Filter.CheckNullReturnedFromNonnull && + NullReturnedFromNonNull && + RetExprTypeLevelNullability != Nullability::Nonnull && + !InSuppressedMethodFamily && + C.getLocationContext()->inTopFrame()) { + static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull"); + ExplodedNode *N = C.generateErrorNode(State, &Tag); + if (!N) + return; + + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << (RetExpr->getType()->isObjCObjectPointerType() ? "nil" : "Null"); + OS << " returned from a " << C.getDeclDescription(D) << + " that is expected to return a non-null value"; + reportBugIfInvariantHolds(OS.str(), + ErrorKind::NilReturnedToNonnull, N, nullptr, C, + RetExpr); + return; + } + + // If null was returned from a non-null function, mark the nullability + // invariant as violated even if the diagnostic was suppressed. + if (NullReturnedFromNonNull) { + State = State->set<InvariantViolated>(true); + C.addTransition(State); + return; + } + + const MemRegion *Region = getTrackRegion(*RetSVal); + if (!Region) + return; + + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(Region); + if (TrackedNullability) { + Nullability TrackedNullabValue = TrackedNullability->getValue(); + if (Filter.CheckNullableReturnedFromNonnull && + Nullness != NullConstraint::IsNotNull && + TrackedNullabValue == Nullability::Nullable && + RequiredNullability == Nullability::Nonnull) { + static CheckerProgramPointTag Tag(this, "NullableReturnedFromNonnull"); + ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); + + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) << + " that is expected to return a non-null value"; + + reportBugIfInvariantHolds(OS.str(), + ErrorKind::NullableReturnedToNonnull, N, + Region, C); + } + return; + } + if (RequiredNullability == Nullability::Nullable) { + State = State->set<NullabilityMap>(Region, + NullabilityState(RequiredNullability, + S)); + C.addTransition(State); + } +} + +/// This callback warns when a nullable pointer or a null value is passed to a +/// function that expects its argument to be nonnull. +void NullabilityChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!Call.getDecl()) + return; + + ProgramStateRef State = C.getState(); + if (State->get<InvariantViolated>()) + return; + + ProgramStateRef OrigState = State; + + unsigned Idx = 0; + for (const ParmVarDecl *Param : Call.parameters()) { + if (Param->isParameterPack()) + break; + + if (Idx >= Call.getNumArgs()) + break; + + const Expr *ArgExpr = Call.getArgExpr(Idx); + auto ArgSVal = Call.getArgSVal(Idx++).getAs<DefinedOrUnknownSVal>(); + if (!ArgSVal) + continue; + + if (!Param->getType()->isAnyPointerType() && + !Param->getType()->isReferenceType()) + continue; + + NullConstraint Nullness = getNullConstraint(*ArgSVal, State); + + Nullability RequiredNullability = + getNullabilityAnnotation(Param->getType()); + Nullability ArgExprTypeLevelNullability = + getNullabilityAnnotation(ArgExpr->getType()); + + unsigned ParamIdx = Param->getFunctionScopeIndex() + 1; + + if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull && + ArgExprTypeLevelNullability != Nullability::Nonnull && + RequiredNullability == Nullability::Nonnull && + isDiagnosableCall(Call)) { + ExplodedNode *N = C.generateErrorNode(State); + if (!N) + return; + + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << (Param->getType()->isObjCObjectPointerType() ? "nil" : "Null"); + OS << " passed to a callee that requires a non-null " << ParamIdx + << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; + reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull, N, + nullptr, C, + ArgExpr, /*SuppressPath=*/false); + return; + } + + const MemRegion *Region = getTrackRegion(*ArgSVal); + if (!Region) + continue; + + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(Region); + + if (TrackedNullability) { + if (Nullness == NullConstraint::IsNotNull || + TrackedNullability->getValue() != Nullability::Nullable) + continue; + + if (Filter.CheckNullablePassedToNonnull && + RequiredNullability == Nullability::Nonnull && + isDiagnosableCall(Call)) { + ExplodedNode *N = C.addTransition(State); + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Nullable pointer is passed to a callee that requires a non-null " + << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter"; + reportBugIfInvariantHolds(OS.str(), + ErrorKind::NullablePassedToNonnull, N, + Region, C, ArgExpr, /*SuppressPath=*/true); + return; + } + if (Filter.CheckNullableDereferenced && + Param->getType()->isReferenceType()) { + ExplodedNode *N = C.addTransition(State); + reportBugIfInvariantHolds("Nullable pointer is dereferenced", + ErrorKind::NullableDereferenced, N, Region, + C, ArgExpr, /*SuppressPath=*/true); + return; + } + continue; + } + // No tracked nullability yet. + if (ArgExprTypeLevelNullability != Nullability::Nullable) + continue; + State = State->set<NullabilityMap>( + Region, NullabilityState(ArgExprTypeLevelNullability, ArgExpr)); + } + if (State != OrigState) + C.addTransition(State); +} + +/// Suppress the nullability warnings for some functions. +void NullabilityChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + auto Decl = Call.getDecl(); + if (!Decl) + return; + // ObjC Messages handles in a different callback. + if (Call.getKind() == CE_ObjCMessage) + return; + const FunctionType *FuncType = Decl->getFunctionType(); + if (!FuncType) + return; + QualType ReturnType = FuncType->getReturnType(); + if (!ReturnType->isAnyPointerType()) + return; + ProgramStateRef State = C.getState(); + if (State->get<InvariantViolated>()) + return; + + const MemRegion *Region = getTrackRegion(Call.getReturnValue()); + if (!Region) + return; + + // CG headers are misannotated. Do not warn for symbols that are the results + // of CG calls. + const SourceManager &SM = C.getSourceManager(); + StringRef FilePath = SM.getFilename(SM.getSpellingLoc(Decl->getBeginLoc())); + if (llvm::sys::path::filename(FilePath).startswith("CG")) { + State = State->set<NullabilityMap>(Region, Nullability::Contradicted); + C.addTransition(State); + return; + } + + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(Region); + + if (!TrackedNullability && + getNullabilityAnnotation(ReturnType) == Nullability::Nullable) { + State = State->set<NullabilityMap>(Region, Nullability::Nullable); + C.addTransition(State); + } +} + +static Nullability getReceiverNullability(const ObjCMethodCall &M, + ProgramStateRef State) { + if (M.isReceiverSelfOrSuper()) { + // For super and super class receivers we assume that the receiver is + // nonnull. + return Nullability::Nonnull; + } + // Otherwise look up nullability in the state. + SVal Receiver = M.getReceiverSVal(); + if (auto DefOrUnknown = Receiver.getAs<DefinedOrUnknownSVal>()) { + // If the receiver is constrained to be nonnull, assume that it is nonnull + // regardless of its type. + NullConstraint Nullness = getNullConstraint(*DefOrUnknown, State); + if (Nullness == NullConstraint::IsNotNull) + return Nullability::Nonnull; + } + auto ValueRegionSVal = Receiver.getAs<loc::MemRegionVal>(); + if (ValueRegionSVal) { + const MemRegion *SelfRegion = ValueRegionSVal->getRegion(); + assert(SelfRegion); + + const NullabilityState *TrackedSelfNullability = + State->get<NullabilityMap>(SelfRegion); + if (TrackedSelfNullability) + return TrackedSelfNullability->getValue(); + } + return Nullability::Unspecified; +} + +/// Calculate the nullability of the result of a message expr based on the +/// nullability of the receiver, the nullability of the return value, and the +/// constraints. +void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) const { + auto Decl = M.getDecl(); + if (!Decl) + return; + QualType RetType = Decl->getReturnType(); + if (!RetType->isAnyPointerType()) + return; + + ProgramStateRef State = C.getState(); + if (State->get<InvariantViolated>()) + return; + + const MemRegion *ReturnRegion = getTrackRegion(M.getReturnValue()); + if (!ReturnRegion) + return; + + auto Interface = Decl->getClassInterface(); + auto Name = Interface ? Interface->getName() : ""; + // In order to reduce the noise in the diagnostics generated by this checker, + // some framework and programming style based heuristics are used. These + // heuristics are for Cocoa APIs which have NS prefix. + if (Name.startswith("NS")) { + // Developers rely on dynamic invariants such as an item should be available + // in a collection, or a collection is not empty often. Those invariants can + // not be inferred by any static analysis tool. To not to bother the users + // with too many false positives, every item retrieval function should be + // ignored for collections. The instance methods of dictionaries in Cocoa + // are either item retrieval related or not interesting nullability wise. + // Using this fact, to keep the code easier to read just ignore the return + // value of every instance method of dictionaries. + if (M.isInstanceMessage() && Name.contains("Dictionary")) { + State = + State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); + C.addTransition(State); + return; + } + // For similar reasons ignore some methods of Cocoa arrays. + StringRef FirstSelectorSlot = M.getSelector().getNameForSlot(0); + if (Name.contains("Array") && + (FirstSelectorSlot == "firstObject" || + FirstSelectorSlot == "lastObject")) { + State = + State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted); + C.addTransition(State); + return; + } + + // Encoding related methods of string should not fail when lossless + // encodings are used. Using lossless encodings is so frequent that ignoring + // this class of methods reduced the emitted diagnostics by about 30% on + // some projects (and all of that was false positives). + if (Name.contains("String")) { + for (auto Param : M.parameters()) { + if (Param->getName() == "encoding") { + State = State->set<NullabilityMap>(ReturnRegion, + Nullability::Contradicted); + C.addTransition(State); + return; + } + } + } + } + + const ObjCMessageExpr *Message = M.getOriginExpr(); + Nullability SelfNullability = getReceiverNullability(M, State); + + const NullabilityState *NullabilityOfReturn = + State->get<NullabilityMap>(ReturnRegion); + + if (NullabilityOfReturn) { + // When we have a nullability tracked for the return value, the nullability + // of the expression will be the most nullable of the receiver and the + // return value. + Nullability RetValTracked = NullabilityOfReturn->getValue(); + Nullability ComputedNullab = + getMostNullable(RetValTracked, SelfNullability); + if (ComputedNullab != RetValTracked && + ComputedNullab != Nullability::Unspecified) { + const Stmt *NullabilitySource = + ComputedNullab == RetValTracked + ? NullabilityOfReturn->getNullabilitySource() + : Message->getInstanceReceiver(); + State = State->set<NullabilityMap>( + ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource)); + C.addTransition(State); + } + return; + } + + // No tracked information. Use static type information for return value. + Nullability RetNullability = getNullabilityAnnotation(RetType); + + // Properties might be computed. For this reason the static analyzer creates a + // new symbol each time an unknown property is read. To avoid false pozitives + // do not treat unknown properties as nullable, even when they explicitly + // marked nullable. + if (M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined) + RetNullability = Nullability::Nonnull; + + Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability); + if (ComputedNullab == Nullability::Nullable) { + const Stmt *NullabilitySource = ComputedNullab == RetNullability + ? Message + : Message->getInstanceReceiver(); + State = State->set<NullabilityMap>( + ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource)); + C.addTransition(State); + } +} + +/// Explicit casts are trusted. If there is a disagreement in the nullability +/// annotations in the destination and the source or '0' is casted to nonnull +/// track the value as having contraditory nullability. This will allow users to +/// suppress warnings. +void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE, + CheckerContext &C) const { + QualType OriginType = CE->getSubExpr()->getType(); + QualType DestType = CE->getType(); + if (!OriginType->isAnyPointerType()) + return; + if (!DestType->isAnyPointerType()) + return; + + ProgramStateRef State = C.getState(); + if (State->get<InvariantViolated>()) + return; + + Nullability DestNullability = getNullabilityAnnotation(DestType); + + // No explicit nullability in the destination type, so this cast does not + // change the nullability. + if (DestNullability == Nullability::Unspecified) + return; + + auto RegionSVal = C.getSVal(CE).getAs<DefinedOrUnknownSVal>(); + const MemRegion *Region = getTrackRegion(*RegionSVal); + if (!Region) + return; + + // When 0 is converted to nonnull mark it as contradicted. + if (DestNullability == Nullability::Nonnull) { + NullConstraint Nullness = getNullConstraint(*RegionSVal, State); + if (Nullness == NullConstraint::IsNull) { + State = State->set<NullabilityMap>(Region, Nullability::Contradicted); + C.addTransition(State); + return; + } + } + + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(Region); + + if (!TrackedNullability) { + if (DestNullability != Nullability::Nullable) + return; + State = State->set<NullabilityMap>(Region, + NullabilityState(DestNullability, CE)); + C.addTransition(State); + return; + } + + if (TrackedNullability->getValue() != DestNullability && + TrackedNullability->getValue() != Nullability::Contradicted) { + State = State->set<NullabilityMap>(Region, Nullability::Contradicted); + C.addTransition(State); + } +} + +/// For a given statement performing a bind, attempt to syntactically +/// match the expression resulting in the bound value. +static const Expr * matchValueExprForBind(const Stmt *S) { + // For `x = e` the value expression is the right-hand side. + if (auto *BinOp = dyn_cast<BinaryOperator>(S)) { + if (BinOp->getOpcode() == BO_Assign) + return BinOp->getRHS(); + } + + // For `int x = e` the value expression is the initializer. + if (auto *DS = dyn_cast<DeclStmt>(S)) { + if (DS->isSingleDecl()) { + auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + if (!VD) + return nullptr; + + if (const Expr *Init = VD->getInit()) + return Init; + } + } + + return nullptr; +} + +/// Returns true if \param S is a DeclStmt for a local variable that +/// ObjC automated reference counting initialized with zero. +static bool isARCNilInitializedLocal(CheckerContext &C, const Stmt *S) { + // We suppress diagnostics for ARC zero-initialized _Nonnull locals. This + // prevents false positives when a _Nonnull local variable cannot be + // initialized with an initialization expression: + // NSString * _Nonnull s; // no-warning + // @autoreleasepool { + // s = ... + // } + // + // FIXME: We should treat implicitly zero-initialized _Nonnull locals as + // uninitialized in Sema's UninitializedValues analysis to warn when a use of + // the zero-initialized definition will unexpectedly yield nil. + + // Locals are only zero-initialized when automated reference counting + // is turned on. + if (!C.getASTContext().getLangOpts().ObjCAutoRefCount) + return false; + + auto *DS = dyn_cast<DeclStmt>(S); + if (!DS || !DS->isSingleDecl()) + return false; + + auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + if (!VD) + return false; + + // Sema only zero-initializes locals with ObjCLifetimes. + if(!VD->getType().getQualifiers().hasObjCLifetime()) + return false; + + const Expr *Init = VD->getInit(); + assert(Init && "ObjC local under ARC without initializer"); + + // Return false if the local is explicitly initialized (e.g., with '= nil'). + if (!isa<ImplicitValueInitExpr>(Init)) + return false; + + return true; +} + +/// Propagate the nullability information through binds and warn when nullable +/// pointer or null symbol is assigned to a pointer with a nonnull type. +void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S, + CheckerContext &C) const { + const TypedValueRegion *TVR = + dyn_cast_or_null<TypedValueRegion>(L.getAsRegion()); + if (!TVR) + return; + + QualType LocType = TVR->getValueType(); + if (!LocType->isAnyPointerType()) + return; + + ProgramStateRef State = C.getState(); + if (State->get<InvariantViolated>()) + return; + + auto ValDefOrUnknown = V.getAs<DefinedOrUnknownSVal>(); + if (!ValDefOrUnknown) + return; + + NullConstraint RhsNullness = getNullConstraint(*ValDefOrUnknown, State); + + Nullability ValNullability = Nullability::Unspecified; + if (SymbolRef Sym = ValDefOrUnknown->getAsSymbol()) + ValNullability = getNullabilityAnnotation(Sym->getType()); + + Nullability LocNullability = getNullabilityAnnotation(LocType); + + // If the type of the RHS expression is nonnull, don't warn. This + // enables explicit suppression with a cast to nonnull. + Nullability ValueExprTypeLevelNullability = Nullability::Unspecified; + const Expr *ValueExpr = matchValueExprForBind(S); + if (ValueExpr) { + ValueExprTypeLevelNullability = + getNullabilityAnnotation(lookThroughImplicitCasts(ValueExpr)->getType()); + } + + bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull && + RhsNullness == NullConstraint::IsNull); + if (Filter.CheckNullPassedToNonnull && + NullAssignedToNonNull && + ValNullability != Nullability::Nonnull && + ValueExprTypeLevelNullability != Nullability::Nonnull && + !isARCNilInitializedLocal(C, S)) { + static CheckerProgramPointTag Tag(this, "NullPassedToNonnull"); + ExplodedNode *N = C.generateErrorNode(State, &Tag); + if (!N) + return; + + + const Stmt *ValueStmt = S; + if (ValueExpr) + ValueStmt = ValueExpr; + + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << (LocType->isObjCObjectPointerType() ? "nil" : "Null"); + OS << " assigned to a pointer which is expected to have non-null value"; + reportBugIfInvariantHolds(OS.str(), + ErrorKind::NilAssignedToNonnull, N, nullptr, C, + ValueStmt); + return; + } + + // If null was returned from a non-null function, mark the nullability + // invariant as violated even if the diagnostic was suppressed. + if (NullAssignedToNonNull) { + State = State->set<InvariantViolated>(true); + C.addTransition(State); + return; + } + + // Intentionally missing case: '0' is bound to a reference. It is handled by + // the DereferenceChecker. + + const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown); + if (!ValueRegion) + return; + + const NullabilityState *TrackedNullability = + State->get<NullabilityMap>(ValueRegion); + + if (TrackedNullability) { + if (RhsNullness == NullConstraint::IsNotNull || + TrackedNullability->getValue() != Nullability::Nullable) + return; + if (Filter.CheckNullablePassedToNonnull && + LocNullability == Nullability::Nonnull) { + static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull"); + ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag); + reportBugIfInvariantHolds("Nullable pointer is assigned to a pointer " + "which is expected to have non-null value", + ErrorKind::NullableAssignedToNonnull, N, + ValueRegion, C); + } + return; + } + + const auto *BinOp = dyn_cast<BinaryOperator>(S); + + if (ValNullability == Nullability::Nullable) { + // Trust the static information of the value more than the static + // information on the location. + const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() : S; + State = State->set<NullabilityMap>( + ValueRegion, NullabilityState(ValNullability, NullabilitySource)); + C.addTransition(State); + return; + } + + if (LocNullability == Nullability::Nullable) { + const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() : S; + State = State->set<NullabilityMap>( + ValueRegion, NullabilityState(LocNullability, NullabilitySource)); + C.addTransition(State); + } +} + +void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + + NullabilityMapTy B = State->get<NullabilityMap>(); + + if (State->get<InvariantViolated>()) + Out << Sep << NL + << "Nullability invariant was violated, warnings suppressed." << NL; + + if (B.isEmpty()) + return; + + if (!State->get<InvariantViolated>()) + Out << Sep << NL; + + for (NullabilityMapTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { + Out << I->first << " : "; + I->second.print(Out); + Out << NL; + } +} + +#define REGISTER_CHECKER(name, trackingRequired) \ + void ento::register##name##Checker(CheckerManager &mgr) { \ + NullabilityChecker *checker = mgr.registerChecker<NullabilityChecker>(); \ + checker->Filter.Check##name = true; \ + checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \ + checker->NeedTracking = checker->NeedTracking || trackingRequired; \ + checker->NoDiagnoseCallsToSystemHeaders = \ + checker->NoDiagnoseCallsToSystemHeaders || \ + mgr.getAnalyzerOptions().getCheckerBooleanOption( \ + "NoDiagnoseCallsToSystemHeaders", false, checker, true); \ + } + +// The checks are likely to be turned on by default and it is possible to do +// them without tracking any nullability related information. As an optimization +// no nullability information will be tracked when only these two checks are +// enables. +REGISTER_CHECKER(NullPassedToNonnull, false) +REGISTER_CHECKER(NullReturnedFromNonnull, false) + +REGISTER_CHECKER(NullableDereferenced, true) +REGISTER_CHECKER(NullablePassedToNonnull, true) +REGISTER_CHECKER(NullableReturnedFromNonnull, true) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp new file mode 100644 index 000000000000..4e3a7205f1f4 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp @@ -0,0 +1,351 @@ +//===- NumberObjectConversionChecker.cpp -------------------------*- 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 NumberObjectConversionChecker, which checks for a +// particular common mistake when dealing with numbers represented as objects +// passed around by pointers. Namely, the language allows to reinterpret the +// pointer as a number directly, often without throwing any warnings, +// but in most cases the result of such conversion is clearly unexpected, +// as pointer value, rather than number value represented by the pointee object, +// becomes the result of such operation. +// +// Currently the checker supports the Objective-C NSNumber class, +// and the OSBoolean class found in macOS low-level code; the latter +// can only hold boolean values. +// +// This checker has an option "Pedantic" (boolean), which enables detection of +// more conversion patterns (which are most likely more harmless, and therefore +// are more likely to produce false positives) - disabled by default, +// enabled with `-analyzer-config osx.NumberObjectConversion:Pedantic=true'. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/APSInt.h" + +using namespace clang; +using namespace ento; +using namespace ast_matchers; + +namespace { + +class NumberObjectConversionChecker : public Checker<check::ASTCodeBody> { +public: + bool Pedantic; + + void checkASTCodeBody(const Decl *D, AnalysisManager &AM, + BugReporter &BR) const; +}; + +class Callback : public MatchFinder::MatchCallback { + const NumberObjectConversionChecker *C; + BugReporter &BR; + AnalysisDeclContext *ADC; + +public: + Callback(const NumberObjectConversionChecker *C, + BugReporter &BR, AnalysisDeclContext *ADC) + : C(C), BR(BR), ADC(ADC) {} + virtual void run(const MatchFinder::MatchResult &Result); +}; +} // end of anonymous namespace + +void Callback::run(const MatchFinder::MatchResult &Result) { + bool IsPedanticMatch = + (Result.Nodes.getNodeAs<Stmt>("pedantic") != nullptr); + if (IsPedanticMatch && !C->Pedantic) + return; + + ASTContext &ACtx = ADC->getASTContext(); + + if (const Expr *CheckIfNull = + Result.Nodes.getNodeAs<Expr>("check_if_null")) { + // Unless the macro indicates that the intended type is clearly not + // a pointer type, we should avoid warning on comparing pointers + // to zero literals in non-pedantic mode. + // FIXME: Introduce an AST matcher to implement the macro-related logic? + bool MacroIndicatesWeShouldSkipTheCheck = false; + SourceLocation Loc = CheckIfNull->getBeginLoc(); + if (Loc.isMacroID()) { + StringRef MacroName = Lexer::getImmediateMacroName( + Loc, ACtx.getSourceManager(), ACtx.getLangOpts()); + if (MacroName == "NULL" || MacroName == "nil") + return; + if (MacroName == "YES" || MacroName == "NO") + MacroIndicatesWeShouldSkipTheCheck = true; + } + if (!MacroIndicatesWeShouldSkipTheCheck) { + Expr::EvalResult EVResult; + if (CheckIfNull->IgnoreParenCasts()->EvaluateAsInt( + EVResult, ACtx, Expr::SE_AllowSideEffects)) { + llvm::APSInt Result = EVResult.Val.getInt(); + if (Result == 0) { + if (!C->Pedantic) + return; + IsPedanticMatch = true; + } + } + } + } + + const Stmt *Conv = Result.Nodes.getNodeAs<Stmt>("conv"); + assert(Conv); + + const Expr *ConvertedCObject = Result.Nodes.getNodeAs<Expr>("c_object"); + const Expr *ConvertedCppObject = Result.Nodes.getNodeAs<Expr>("cpp_object"); + const Expr *ConvertedObjCObject = Result.Nodes.getNodeAs<Expr>("objc_object"); + bool IsCpp = (ConvertedCppObject != nullptr); + bool IsObjC = (ConvertedObjCObject != nullptr); + const Expr *Obj = IsObjC ? ConvertedObjCObject + : IsCpp ? ConvertedCppObject + : ConvertedCObject; + assert(Obj); + + bool IsComparison = + (Result.Nodes.getNodeAs<Stmt>("comparison") != nullptr); + + bool IsOSNumber = + (Result.Nodes.getNodeAs<Decl>("osnumber") != nullptr); + + bool IsInteger = + (Result.Nodes.getNodeAs<QualType>("int_type") != nullptr); + bool IsObjCBool = + (Result.Nodes.getNodeAs<QualType>("objc_bool_type") != nullptr); + bool IsCppBool = + (Result.Nodes.getNodeAs<QualType>("cpp_bool_type") != nullptr); + + llvm::SmallString<64> Msg; + llvm::raw_svector_ostream OS(Msg); + + // Remove ObjC ARC qualifiers. + QualType ObjT = Obj->getType().getUnqualifiedType(); + + // Remove consts from pointers. + if (IsCpp) { + assert(ObjT.getCanonicalType()->isPointerType()); + ObjT = ACtx.getPointerType( + ObjT->getPointeeType().getCanonicalType().getUnqualifiedType()); + } + + if (IsComparison) + OS << "Comparing "; + else + OS << "Converting "; + + OS << "a pointer value of type '" << ObjT.getAsString() << "' to a "; + + std::string EuphemismForPlain = "primitive"; + std::string SuggestedApi = IsObjC ? (IsInteger ? "" : "-boolValue") + : IsCpp ? (IsOSNumber ? "" : "getValue()") + : "CFNumberGetValue()"; + if (SuggestedApi.empty()) { + // A generic message if we're not sure what API should be called. + // FIXME: Pattern-match the integer type to make a better guess? + SuggestedApi = + "a method on '" + ObjT.getAsString() + "' to get the scalar value"; + // "scalar" is not quite correct or common, but some documentation uses it + // when describing object methods we suggest. For consistency, we use + // "scalar" in the whole sentence when we need to use this word in at least + // one place, otherwise we use "primitive". + EuphemismForPlain = "scalar"; + } + + if (IsInteger) + OS << EuphemismForPlain << " integer value"; + else if (IsObjCBool) + OS << EuphemismForPlain << " BOOL value"; + else if (IsCppBool) + OS << EuphemismForPlain << " bool value"; + else // Branch condition? + OS << EuphemismForPlain << " boolean value"; + + + if (IsPedanticMatch) + OS << "; instead, either compare the pointer to " + << (IsObjC ? "nil" : IsCpp ? "nullptr" : "NULL") << " or "; + else + OS << "; did you mean to "; + + if (IsComparison) + OS << "compare the result of calling " << SuggestedApi; + else + OS << "call " << SuggestedApi; + + if (!IsPedanticMatch) + OS << "?"; + + BR.EmitBasicReport( + ADC->getDecl(), C, "Suspicious number object conversion", "Logic error", + OS.str(), + PathDiagnosticLocation::createBegin(Obj, BR.getSourceManager(), ADC), + Conv->getSourceRange()); +} + +void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const { + // Currently this matches CoreFoundation opaque pointer typedefs. + auto CSuspiciousNumberObjectExprM = + expr(ignoringParenImpCasts( + expr(hasType( + typedefType(hasDeclaration(anyOf( + typedefDecl(hasName("CFNumberRef")), + typedefDecl(hasName("CFBooleanRef"))))))) + .bind("c_object"))); + + // Currently this matches XNU kernel number-object pointers. + auto CppSuspiciousNumberObjectExprM = + expr(ignoringParenImpCasts( + expr(hasType(hasCanonicalType( + pointerType(pointee(hasCanonicalType( + recordType(hasDeclaration( + anyOf( + cxxRecordDecl(hasName("OSBoolean")), + cxxRecordDecl(hasName("OSNumber")) + .bind("osnumber")))))))))) + .bind("cpp_object"))); + + // Currently this matches NeXTSTEP number objects. + auto ObjCSuspiciousNumberObjectExprM = + expr(ignoringParenImpCasts( + expr(hasType(hasCanonicalType( + objcObjectPointerType(pointee( + qualType(hasCanonicalType( + qualType(hasDeclaration( + objcInterfaceDecl(hasName("NSNumber"))))))))))) + .bind("objc_object"))); + + auto SuspiciousNumberObjectExprM = anyOf( + CSuspiciousNumberObjectExprM, + CppSuspiciousNumberObjectExprM, + ObjCSuspiciousNumberObjectExprM); + + // Useful for predicates like "Unless we've seen the same object elsewhere". + auto AnotherSuspiciousNumberObjectExprM = + expr(anyOf( + equalsBoundNode("c_object"), + equalsBoundNode("objc_object"), + equalsBoundNode("cpp_object"))); + + // The .bind here is in order to compose the error message more accurately. + auto ObjCSuspiciousScalarBooleanTypeM = + qualType(typedefType(hasDeclaration( + typedefDecl(hasName("BOOL"))))).bind("objc_bool_type"); + + // The .bind here is in order to compose the error message more accurately. + auto SuspiciousScalarBooleanTypeM = + qualType(anyOf(qualType(booleanType()).bind("cpp_bool_type"), + ObjCSuspiciousScalarBooleanTypeM)); + + // The .bind here is in order to compose the error message more accurately. + // Also avoid intptr_t and uintptr_t because they were specifically created + // for storing pointers. + auto SuspiciousScalarNumberTypeM = + qualType(hasCanonicalType(isInteger()), + unless(typedefType(hasDeclaration( + typedefDecl(matchesName("^::u?intptr_t$")))))) + .bind("int_type"); + + auto SuspiciousScalarTypeM = + qualType(anyOf(SuspiciousScalarBooleanTypeM, + SuspiciousScalarNumberTypeM)); + + auto SuspiciousScalarExprM = + expr(ignoringParenImpCasts(expr(hasType(SuspiciousScalarTypeM)))); + + auto ConversionThroughAssignmentM = + binaryOperator(allOf(hasOperatorName("="), + hasLHS(SuspiciousScalarExprM), + hasRHS(SuspiciousNumberObjectExprM))); + + auto ConversionThroughBranchingM = + ifStmt(allOf( + hasCondition(SuspiciousNumberObjectExprM), + unless(hasConditionVariableStatement(declStmt()) + ))).bind("pedantic"); + + auto ConversionThroughCallM = + callExpr(hasAnyArgument(allOf(hasType(SuspiciousScalarTypeM), + ignoringParenImpCasts( + SuspiciousNumberObjectExprM)))); + + // We bind "check_if_null" to modify the warning message + // in case it was intended to compare a pointer to 0 with a relatively-ok + // construct "x == 0" or "x != 0". + auto ConversionThroughEquivalenceM = + binaryOperator(allOf(anyOf(hasOperatorName("=="), hasOperatorName("!=")), + hasEitherOperand(SuspiciousNumberObjectExprM), + hasEitherOperand(SuspiciousScalarExprM + .bind("check_if_null")))) + .bind("comparison"); + + auto ConversionThroughComparisonM = + binaryOperator(allOf(anyOf(hasOperatorName(">="), hasOperatorName(">"), + hasOperatorName("<="), hasOperatorName("<")), + hasEitherOperand(SuspiciousNumberObjectExprM), + hasEitherOperand(SuspiciousScalarExprM))) + .bind("comparison"); + + auto ConversionThroughConditionalOperatorM = + conditionalOperator(allOf( + hasCondition(SuspiciousNumberObjectExprM), + unless(hasTrueExpression( + hasDescendant(AnotherSuspiciousNumberObjectExprM))), + unless(hasFalseExpression( + hasDescendant(AnotherSuspiciousNumberObjectExprM))))) + .bind("pedantic"); + + auto ConversionThroughExclamationMarkM = + unaryOperator(allOf(hasOperatorName("!"), + has(expr(SuspiciousNumberObjectExprM)))) + .bind("pedantic"); + + auto ConversionThroughExplicitBooleanCastM = + explicitCastExpr(allOf(hasType(SuspiciousScalarBooleanTypeM), + has(expr(SuspiciousNumberObjectExprM)))); + + auto ConversionThroughExplicitNumberCastM = + explicitCastExpr(allOf(hasType(SuspiciousScalarNumberTypeM), + has(expr(SuspiciousNumberObjectExprM)))); + + auto ConversionThroughInitializerM = + declStmt(hasSingleDecl( + varDecl(hasType(SuspiciousScalarTypeM), + hasInitializer(SuspiciousNumberObjectExprM)))); + + auto FinalM = stmt(anyOf(ConversionThroughAssignmentM, + ConversionThroughBranchingM, + ConversionThroughCallM, + ConversionThroughComparisonM, + ConversionThroughConditionalOperatorM, + ConversionThroughEquivalenceM, + ConversionThroughExclamationMarkM, + ConversionThroughExplicitBooleanCastM, + ConversionThroughExplicitNumberCastM, + ConversionThroughInitializerM)).bind("conv"); + + MatchFinder F; + Callback CB(this, BR, AM.getAnalysisDeclContext(D)); + + F.addMatcher(stmt(forEachDescendant(FinalM)), &CB); + F.match(*D->getBody(), AM.getASTContext()); +} + +void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) { + NumberObjectConversionChecker *Chk = + Mgr.registerChecker<NumberObjectConversionChecker>(); + Chk->Pedantic = + Mgr.getAnalyzerOptions().getCheckerBooleanOption("Pedantic", false, Chk); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp new file mode 100644 index 000000000000..185b57575cb0 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -0,0 +1,94 @@ +//== ObjCAtSyncChecker.cpp - nil mutex checker for @synchronized -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines ObjCAtSyncChecker, a builtin check that checks for null pointers +// used as mutexes for @synchronized. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/StmtObjC.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class ObjCAtSyncChecker + : public Checker< check::PreStmt<ObjCAtSynchronizedStmt> > { + mutable std::unique_ptr<BuiltinBug> BT_null; + mutable std::unique_ptr<BuiltinBug> BT_undef; + +public: + void checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const; +}; +} // end anonymous namespace + +void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S, + CheckerContext &C) const { + + const Expr *Ex = S->getSynchExpr(); + ProgramStateRef state = C.getState(); + SVal V = C.getSVal(Ex); + + // Uninitialized value used for the mutex? + if (V.getAs<UndefinedVal>()) { + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_undef) + BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex " + "for @synchronized")); + auto report = + llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N); + bugreporter::trackExpressionValue(N, Ex, *report); + C.emitReport(std::move(report)); + } + return; + } + + if (V.isUnknown()) + return; + + // Check for null mutexes. + ProgramStateRef notNullState, nullState; + std::tie(notNullState, nullState) = state->assume(V.castAs<DefinedSVal>()); + + if (nullState) { + if (!notNullState) { + // Generate an error node. This isn't a sink since + // a null mutex just means no synchronization occurs. + if (ExplodedNode *N = C.generateNonFatalErrorNode(nullState)) { + if (!BT_null) + BT_null.reset(new BuiltinBug( + this, "Nil value used as mutex for @synchronized() " + "(no synchronization will occur)")); + auto report = + llvm::make_unique<BugReport>(*BT_null, BT_null->getDescription(), N); + bugreporter::trackExpressionValue(N, Ex, *report); + + C.emitReport(std::move(report)); + return; + } + } + // Don't add a transition for 'nullState'. If the value is + // under-constrained to be null or non-null, assume it is non-null + // afterwards. + } + + if (notNullState) + C.addTransition(notNullState); +} + +void ento::registerObjCAtSyncChecker(CheckerManager &mgr) { + if (mgr.getLangOpts().ObjC) + mgr.registerChecker<ObjCAtSyncChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp new file mode 100644 index 000000000000..0424958f8e65 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp @@ -0,0 +1,209 @@ +//===- ObjCAutoreleaseWriteChecker.cpp ----------------------------*- 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 ObjCAutoreleaseWriteChecker which warns against writes +// into autoreleased out parameters which cause crashes. +// An example of a problematic write is a write to {@code error} in the example +// below: +// +// - (BOOL) mymethod:(NSError *__autoreleasing *)error list:(NSArray*) list { +// [list enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { +// NSString *myString = obj; +// if ([myString isEqualToString:@"error"] && error) +// *error = [NSError errorWithDomain:@"MyDomain" code:-1]; +// }]; +// return false; +// } +// +// Such code will crash on read from `*error` due to the autorelease pool +// in `enumerateObjectsUsingBlock` implementation freeing the error object +// on exit from the function. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/Twine.h" + +using namespace clang; +using namespace ento; +using namespace ast_matchers; + +namespace { + +const char *ProblematicWriteBind = "problematicwrite"; +const char *CapturedBind = "capturedbind"; +const char *ParamBind = "parambind"; +const char *IsMethodBind = "ismethodbind"; + +class ObjCAutoreleaseWriteChecker : public Checker<check::ASTCodeBody> { +public: + void checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const; +private: + std::vector<std::string> SelectorsWithAutoreleasingPool = { + // Common to NSArray, NSSet, NSOrderedSet + "enumerateObjectsUsingBlock:", + "enumerateObjectsWithOptions:usingBlock:", + + // Common to NSArray and NSOrderedSet + "enumerateObjectsAtIndexes:options:usingBlock:", + "indexOfObjectAtIndexes:options:passingTest:", + "indexesOfObjectsAtIndexes:options:passingTest:", + "indexOfObjectPassingTest:", + "indexOfObjectWithOptions:passingTest:", + "indexesOfObjectsPassingTest:", + "indexesOfObjectsWithOptions:passingTest:", + + // NSDictionary + "enumerateKeysAndObjectsUsingBlock:", + "enumerateKeysAndObjectsWithOptions:usingBlock:", + "keysOfEntriesPassingTest:", + "keysOfEntriesWithOptions:passingTest:", + + // NSSet + "objectsPassingTest:", + "objectsWithOptions:passingTest:", + "enumerateIndexPathsWithOptions:usingBlock:", + + // NSIndexSet + "enumerateIndexesWithOptions:usingBlock:", + "enumerateIndexesUsingBlock:", + "enumerateIndexesInRange:options:usingBlock:", + "enumerateRangesUsingBlock:", + "enumerateRangesWithOptions:usingBlock:", + "enumerateRangesInRange:options:usingBlock:", + "indexPassingTest:", + "indexesPassingTest:", + "indexWithOptions:passingTest:", + "indexesWithOptions:passingTest:", + "indexInRange:options:passingTest:", + "indexesInRange:options:passingTest:" + }; + + std::vector<std::string> FunctionsWithAutoreleasingPool = { + "dispatch_async", "dispatch_group_async", "dispatch_barrier_async"}; +}; +} + +static inline std::vector<llvm::StringRef> toRefs(std::vector<std::string> V) { + return std::vector<llvm::StringRef>(V.begin(), V.end()); +} + +static auto callsNames(std::vector<std::string> FunctionNames) + -> decltype(callee(functionDecl())) { + return callee(functionDecl(hasAnyName(toRefs(FunctionNames)))); +} + +static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR, + AnalysisManager &AM, + const ObjCAutoreleaseWriteChecker *Checker) { + AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D); + + const auto *PVD = Match.getNodeAs<ParmVarDecl>(ParamBind); + QualType Ty = PVD->getType(); + if (Ty->getPointeeType().getObjCLifetime() != Qualifiers::OCL_Autoreleasing) + return; + const char *ActionMsg = "Write to"; + const auto *MarkedStmt = Match.getNodeAs<Expr>(ProblematicWriteBind); + bool IsCapture = false; + + // Prefer to warn on write, but if not available, warn on capture. + if (!MarkedStmt) { + MarkedStmt = Match.getNodeAs<Expr>(CapturedBind); + assert(MarkedStmt); + ActionMsg = "Capture of"; + IsCapture = true; + } + + SourceRange Range = MarkedStmt->getSourceRange(); + PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin( + MarkedStmt, BR.getSourceManager(), ADC); + bool IsMethod = Match.getNodeAs<ObjCMethodDecl>(IsMethodBind) != nullptr; + const char *Name = IsMethod ? "method" : "function"; + + BR.EmitBasicReport( + ADC->getDecl(), Checker, + /*Name=*/(llvm::Twine(ActionMsg) + + " autoreleasing out parameter inside autorelease pool").str(), + /*Category=*/"Memory", + (llvm::Twine(ActionMsg) + " autoreleasing out parameter " + + (IsCapture ? "'" + PVD->getName() + "'" + " " : "") + "inside " + + "autorelease pool that may exit before " + Name + " returns; consider " + "writing first to a strong local variable declared outside of the block") + .str(), + Location, + Range); +} + +void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const { + + auto DoublePointerParamM = + parmVarDecl(hasType(hasCanonicalType(pointerType( + pointee(hasCanonicalType(objcObjectPointerType())))))) + .bind(ParamBind); + + auto ReferencedParamM = + declRefExpr(to(parmVarDecl(DoublePointerParamM))).bind(CapturedBind); + + // Write into a binded object, e.g. *ParamBind = X. + auto WritesIntoM = binaryOperator( + hasLHS(unaryOperator( + hasOperatorName("*"), + hasUnaryOperand( + ignoringParenImpCasts(ReferencedParamM)) + )), + hasOperatorName("=") + ).bind(ProblematicWriteBind); + + auto ArgumentCaptureM = hasAnyArgument( + ignoringParenImpCasts(ReferencedParamM)); + auto CapturedInParamM = stmt(anyOf( + callExpr(ArgumentCaptureM), + objcMessageExpr(ArgumentCaptureM))); + + // WritesIntoM happens inside a block passed as an argument. + auto WritesOrCapturesInBlockM = hasAnyArgument(allOf( + hasType(hasCanonicalType(blockPointerType())), + forEachDescendant( + stmt(anyOf(WritesIntoM, CapturedInParamM)) + ))); + + auto BlockPassedToMarkedFuncM = stmt(anyOf( + callExpr(allOf( + callsNames(FunctionsWithAutoreleasingPool), WritesOrCapturesInBlockM)), + objcMessageExpr(allOf( + hasAnySelector(toRefs(SelectorsWithAutoreleasingPool)), + WritesOrCapturesInBlockM)) + )); + + auto HasParamAndWritesInMarkedFuncM = allOf( + hasAnyParameter(DoublePointerParamM), + forEachDescendant(BlockPassedToMarkedFuncM)); + + auto MatcherM = decl(anyOf( + objcMethodDecl(HasParamAndWritesInMarkedFuncM).bind(IsMethodBind), + functionDecl(HasParamAndWritesInMarkedFuncM), + blockDecl(HasParamAndWritesInMarkedFuncM))); + + auto Matches = match(MatcherM, *D, AM.getASTContext()); + for (BoundNodes Match : Matches) + emitDiagnostics(Match, D, BR, AM, this); +} + +void ento::registerAutoreleaseWriteChecker(CheckerManager &Mgr) { + Mgr.registerChecker<ObjCAutoreleaseWriteChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp new file mode 100644 index 000000000000..34ce47823d51 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp @@ -0,0 +1,174 @@ +//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// An AST checker that looks for common pitfalls when using 'CFArray', +// 'CFDictionary', 'CFSet' APIs. +// +//===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class WalkAST : public StmtVisitor<WalkAST> { + BugReporter &BR; + const CheckerBase *Checker; + AnalysisDeclContext* AC; + ASTContext &ASTC; + uint64_t PtrWidth; + + /// Check if the type has pointer size (very conservative). + inline bool isPointerSize(const Type *T) { + if (!T) + return true; + if (T->isIncompleteType()) + return true; + return (ASTC.getTypeSize(T) == PtrWidth); + } + + /// Check if the type is a pointer/array to pointer sized values. + inline bool hasPointerToPointerSizedType(const Expr *E) { + QualType T = E->getType(); + + // The type could be either a pointer or array. + const Type *TP = T.getTypePtr(); + QualType PointeeT = TP->getPointeeType(); + if (!PointeeT.isNull()) { + // If the type is a pointer to an array, check the size of the array + // elements. To avoid false positives coming from assumption that the + // values x and &x are equal when x is an array. + if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual()) + if (isPointerSize(TElem)) + return true; + + // Else, check the pointee size. + return isPointerSize(PointeeT.getTypePtr()); + } + + if (const Type *TElem = TP->getArrayElementTypeNoTypeQual()) + return isPointerSize(TElem); + + // The type must be an array/pointer type. + + // This could be a null constant, which is allowed. + return static_cast<bool>( + E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull)); + } + +public: + WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac) + : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()), + PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {} + + // Statement visitor methods. + void VisitChildren(Stmt *S); + void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitCallExpr(CallExpr *CE); +}; +} // end anonymous namespace + +static StringRef getCalleeName(CallExpr *CE) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return StringRef(); + + IdentifierInfo *II = FD->getIdentifier(); + if (!II) // if no identifier, not a simple C function + return StringRef(); + + return II->getName(); +} + +void WalkAST::VisitCallExpr(CallExpr *CE) { + StringRef Name = getCalleeName(CE); + if (Name.empty()) + return; + + const Expr *Arg = nullptr; + unsigned ArgNum; + + if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) { + if (CE->getNumArgs() != 4) + return; + ArgNum = 1; + Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); + if (hasPointerToPointerSizedType(Arg)) + return; + } else if (Name.equals("CFDictionaryCreate")) { + if (CE->getNumArgs() != 6) + return; + // Check first argument. + ArgNum = 1; + Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); + if (hasPointerToPointerSizedType(Arg)) { + // Check second argument. + ArgNum = 2; + Arg = CE->getArg(ArgNum)->IgnoreParenCasts(); + if (hasPointerToPointerSizedType(Arg)) + // Both are good, return. + return; + } + } + + if (Arg) { + assert(ArgNum == 1 || ArgNum == 2); + + SmallString<64> BufName; + llvm::raw_svector_ostream OsName(BufName); + OsName << " Invalid use of '" << Name << "'" ; + + SmallString<256> Buf; + llvm::raw_svector_ostream Os(Buf); + // Use "second" and "third" since users will expect 1-based indexing + // for parameter names when mentioned in prose. + Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '" + << Name << "' must be a C array of pointer-sized values, not '" + << Arg->getType().getAsString() << "'"; + + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC); + BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(), + categories::CoreFoundationObjectiveC, Os.str(), CELoc, + Arg->getSourceRange()); + } + + // Recurse and check children. + VisitChildren(CE); +} + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt *Child : S->children()) + if (Child) + Visit(Child); +} + +namespace { +class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> { +public: + + void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr, + BugReporter &BR) const { + WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D)); + walker.Visit(D->getBody()); + } +}; +} + +void ento::registerObjCContainersASTChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCContainersASTChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp new file mode 100644 index 000000000000..1c8c0d8dedda --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp @@ -0,0 +1,189 @@ +//== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- C++ -*=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Performs path sensitive checks of Core Foundation static containers like +// CFArray. +// 1) Check for buffer overflows: +// In CFArrayGetArrayAtIndex( myArray, index), if the index is outside the +// index space of theArray (0 to N-1 inclusive (where N is the count of +// theArray), the behavior is undefined. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ParentMap.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +namespace { +class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>, + check::PostStmt<CallExpr>, + check::PointerEscape> { + mutable std::unique_ptr<BugType> BT; + inline void initBugType() const { + if (!BT) + BT.reset(new BugType(this, "CFArray API", + categories::CoreFoundationObjectiveC)); + } + + inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const { + SVal ArrayRef = C.getSVal(E); + SymbolRef ArraySym = ArrayRef.getAsSymbol(); + return ArraySym; + } + + void addSizeInfo(const Expr *Array, const Expr *Size, + CheckerContext &C) const; + +public: + /// A tag to id this checker. + static void *getTag() { static int Tag; return &Tag; } + + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; + + void printState(raw_ostream &OS, ProgramStateRef State, + const char *NL, const char *Sep) const; +}; +} // end anonymous namespace + +// ProgramState trait - a map from array symbol to its state. +REGISTER_MAP_WITH_PROGRAMSTATE(ArraySizeMap, SymbolRef, DefinedSVal) + +void ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SVal SizeV = C.getSVal(Size); + // Undefined is reported by another checker. + if (SizeV.isUnknownOrUndef()) + return; + + // Get the ArrayRef symbol. + SVal ArrayRef = C.getSVal(Array); + SymbolRef ArraySym = ArrayRef.getAsSymbol(); + if (!ArraySym) + return; + + C.addTransition( + State->set<ArraySizeMap>(ArraySym, SizeV.castAs<DefinedSVal>())); +} + +void ObjCContainersChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + StringRef Name = C.getCalleeName(CE); + if (Name.empty() || CE->getNumArgs() < 1) + return; + + // Add array size information to the state. + if (Name.equals("CFArrayCreate")) { + if (CE->getNumArgs() < 3) + return; + // Note, we can visit the Create method in the post-visit because + // the CFIndex parameter is passed in by value and will not be invalidated + // by the call. + addSizeInfo(CE, CE->getArg(2), C); + return; + } + + if (Name.equals("CFArrayGetCount")) { + addSizeInfo(CE->getArg(0), CE, C); + return; + } +} + +void ObjCContainersChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + StringRef Name = C.getCalleeName(CE); + if (Name.empty() || CE->getNumArgs() < 2) + return; + + // Check the array access. + if (Name.equals("CFArrayGetValueAtIndex")) { + ProgramStateRef State = C.getState(); + // Retrieve the size. + // Find out if we saw this array symbol before and have information about + // it. + const Expr *ArrayExpr = CE->getArg(0); + SymbolRef ArraySym = getArraySym(ArrayExpr, C); + if (!ArraySym) + return; + + const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym); + + if (!Size) + return; + + // Get the index. + const Expr *IdxExpr = CE->getArg(1); + SVal IdxVal = C.getSVal(IdxExpr); + if (IdxVal.isUnknownOrUndef()) + return; + DefinedSVal Idx = IdxVal.castAs<DefinedSVal>(); + + // Now, check if 'Idx in [0, Size-1]'. + const QualType T = IdxExpr->getType(); + ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T); + ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.generateErrorNode(StOutBound); + if (!N) + return; + initBugType(); + auto R = llvm::make_unique<BugReport>(*BT, "Index is out of bounds", N); + R->addRange(IdxExpr->getSourceRange()); + bugreporter::trackExpressionValue(N, IdxExpr, *R, + /*EnableNullFPSuppression=*/false); + C.emitReport(std::move(R)); + return; + } + } +} + +ProgramStateRef +ObjCContainersChecker::checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + for (const auto &Sym : Escaped) { + // When a symbol for a mutable array escapes, we can't reason precisely + // about its size any more -- so remove it from the map. + // Note that we aren't notified here when a CFMutableArrayRef escapes as a + // CFArrayRef. This is because CFArrayRef is typedef'd as a pointer to a + // const-qualified type. + State = State->remove<ArraySizeMap>(Sym); + } + return State; +} + +void ObjCContainersChecker::printState(raw_ostream &OS, ProgramStateRef State, + const char *NL, const char *Sep) const { + ArraySizeMapTy Map = State->get<ArraySizeMap>(); + if (Map.isEmpty()) + return; + + OS << Sep << "ObjC container sizes :" << NL; + for (auto I : Map) { + OS << I.first << " : " << I.second << NL; + } +} + +/// Register checker. +void ento::registerObjCContainersChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCContainersChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp new file mode 100644 index 000000000000..d383302b2790 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp @@ -0,0 +1,263 @@ +//==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a ObjCMissingSuperCallChecker, a checker that +// analyzes a UIViewController implementation to determine if it +// correctly calls super in the methods where this is mandatory. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +struct SelectorDescriptor { + const char *SelectorName; + unsigned ArgumentCount; +}; + +//===----------------------------------------------------------------------===// +// FindSuperCallVisitor - Identify specific calls to the superclass. +//===----------------------------------------------------------------------===// + +class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> { +public: + explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {} + + bool VisitObjCMessageExpr(ObjCMessageExpr *E) { + if (E->getSelector() == Sel) + if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance) + DoesCallSuper = true; + + // Recurse if we didn't find the super call yet. + return !DoesCallSuper; + } + + bool DoesCallSuper; + +private: + Selector Sel; +}; + +//===----------------------------------------------------------------------===// +// ObjCSuperCallChecker +//===----------------------------------------------------------------------===// + +class ObjCSuperCallChecker : public Checker< + check::ASTDecl<ObjCImplementationDecl> > { +public: + ObjCSuperCallChecker() : IsInitialized(false) {} + + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr, + BugReporter &BR) const; +private: + bool isCheckableClass(const ObjCImplementationDecl *D, + StringRef &SuperclassName) const; + void initializeSelectors(ASTContext &Ctx) const; + void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel, + StringRef ClassName) const; + mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass; + mutable bool IsInitialized; +}; + +} + +/// Determine whether the given class has a superclass that we want +/// to check. The name of the found superclass is stored in SuperclassName. +/// +/// \param D The declaration to check for superclasses. +/// \param[out] SuperclassName On return, the found superclass name. +bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D, + StringRef &SuperclassName) const { + const ObjCInterfaceDecl *ID = D->getClassInterface()->getSuperClass(); + for ( ; ID ; ID = ID->getSuperClass()) + { + SuperclassName = ID->getIdentifier()->getName(); + if (SelectorsForClass.count(SuperclassName)) + return true; + } + return false; +} + +void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx, + ArrayRef<SelectorDescriptor> Sel, + StringRef ClassName) const { + llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName]; + // Fill the Selectors SmallSet with all selectors we want to check. + for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end(); + I != E; ++I) { + SelectorDescriptor Descriptor = *I; + assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet. + + // Get the selector. + IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName); + + Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II); + ClassSelectors.insert(Sel); + } +} + +void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const { + + { // Initialize selectors for: UIViewController + const SelectorDescriptor Selectors[] = { + { "addChildViewController", 1 }, + { "viewDidAppear", 1 }, + { "viewDidDisappear", 1 }, + { "viewWillAppear", 1 }, + { "viewWillDisappear", 1 }, + { "removeFromParentViewController", 0 }, + { "didReceiveMemoryWarning", 0 }, + { "viewDidUnload", 0 }, + { "viewDidLoad", 0 }, + { "viewWillUnload", 0 }, + { "updateViewConstraints", 0 }, + { "encodeRestorableStateWithCoder", 1 }, + { "restoreStateWithCoder", 1 }}; + + fillSelectors(Ctx, Selectors, "UIViewController"); + } + + { // Initialize selectors for: UIResponder + const SelectorDescriptor Selectors[] = { + { "resignFirstResponder", 0 }}; + + fillSelectors(Ctx, Selectors, "UIResponder"); + } + + { // Initialize selectors for: NSResponder + const SelectorDescriptor Selectors[] = { + { "encodeRestorableStateWithCoder", 1 }, + { "restoreStateWithCoder", 1 }}; + + fillSelectors(Ctx, Selectors, "NSResponder"); + } + + { // Initialize selectors for: NSDocument + const SelectorDescriptor Selectors[] = { + { "encodeRestorableStateWithCoder", 1 }, + { "restoreStateWithCoder", 1 }}; + + fillSelectors(Ctx, Selectors, "NSDocument"); + } + + IsInitialized = true; +} + +void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D, + AnalysisManager &Mgr, + BugReporter &BR) const { + ASTContext &Ctx = BR.getContext(); + + // We need to initialize the selector table once. + if (!IsInitialized) + initializeSelectors(Ctx); + + // Find out whether this class has a superclass that we are supposed to check. + StringRef SuperclassName; + if (!isCheckableClass(D, SuperclassName)) + return; + + + // Iterate over all instance methods. + for (auto *MD : D->instance_methods()) { + Selector S = MD->getSelector(); + // Find out whether this is a selector that we want to check. + if (!SelectorsForClass[SuperclassName].count(S)) + continue; + + // Check if the method calls its superclass implementation. + if (MD->getBody()) + { + FindSuperCallVisitor Visitor(S); + Visitor.TraverseDecl(MD); + + // It doesn't call super, emit a diagnostic. + if (!Visitor.DoesCallSuper) { + PathDiagnosticLocation DLoc = + PathDiagnosticLocation::createEnd(MD->getBody(), + BR.getSourceManager(), + Mgr.getAnalysisDeclContext(D)); + + const char *Name = "Missing call to superclass"; + SmallString<320> Buf; + llvm::raw_svector_ostream os(Buf); + + os << "The '" << S.getAsString() + << "' instance method in " << SuperclassName.str() << " subclass '" + << *D << "' is missing a [super " << S.getAsString() << "] call"; + + BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC, + os.str(), DLoc); + } + } + } +} + + +//===----------------------------------------------------------------------===// +// Check registration. +//===----------------------------------------------------------------------===// + +void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) { + Mgr.registerChecker<ObjCSuperCallChecker>(); +} + + +/* + ToDo list for expanding this check in the future, the list is not exhaustive. + There are also cases where calling super is suggested but not "mandatory". + In addition to be able to check the classes and methods below, architectural + improvements like being able to allow for the super-call to be done in a called + method would be good too. + +UIDocument subclasses +- finishedHandlingError:recovered: (is multi-arg) +- finishedHandlingError:recovered: (is multi-arg) + +UIViewController subclasses +- loadView (should *never* call super) +- transitionFromViewController:toViewController: + duration:options:animations:completion: (is multi-arg) + +UICollectionViewController subclasses +- loadView (take care because UIViewController subclasses should NOT call super + in loadView, but UICollectionViewController subclasses should) + +NSObject subclasses +- doesNotRecognizeSelector (it only has to call super if it doesn't throw) + +UIPopoverBackgroundView subclasses (some of those are class methods) +- arrowDirection (should *never* call super) +- arrowOffset (should *never* call super) +- arrowBase (should *never* call super) +- arrowHeight (should *never* call super) +- contentViewInsets (should *never* call super) + +UITextSelectionRect subclasses (some of those are properties) +- rect (should *never* call super) +- range (should *never* call super) +- writingDirection (should *never* call super) +- isVertical (should *never* call super) +- containsStart (should *never* call super) +- containsEnd (should *never* call super) +*/ diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp new file mode 100644 index 000000000000..018d3fcfceb9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp @@ -0,0 +1,81 @@ +//==- ObjCPropertyChecker.cpp - Check ObjC properties ------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker finds issues with Objective-C properties. +// Currently finds only one kind of issue: +// - Find synthesized properties with copy attribute of mutable NS collection +// types. Calling -copy on such collections produces an immutable copy, +// which contradicts the type of the property. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" + +using namespace clang; +using namespace ento; + +namespace { +class ObjCPropertyChecker + : public Checker<check::ASTDecl<ObjCPropertyDecl>> { + void checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const; + +public: + void checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr, + BugReporter &BR) const; +}; +} // end anonymous namespace. + +void ObjCPropertyChecker::checkASTDecl(const ObjCPropertyDecl *D, + AnalysisManager &Mgr, + BugReporter &BR) const { + checkCopyMutable(D, BR); +} + +void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D, + BugReporter &BR) const { + if (D->isReadOnly() || D->getSetterKind() != ObjCPropertyDecl::Copy) + return; + + QualType T = D->getType(); + if (!T->isObjCObjectPointerType()) + return; + + const std::string &PropTypeName(T->getPointeeType().getCanonicalType() + .getUnqualifiedType() + .getAsString()); + if (!StringRef(PropTypeName).startswith("NSMutable")) + return; + + const ObjCImplDecl *ImplD = nullptr; + if (const ObjCInterfaceDecl *IntD = + dyn_cast<ObjCInterfaceDecl>(D->getDeclContext())) { + ImplD = IntD->getImplementation(); + } else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D->getDeclContext())) { + ImplD = CatD->getClassInterface()->getImplementation(); + } + + if (!ImplD || ImplD->HasUserDeclaredSetterMethod(D)) + return; + + SmallString<128> Str; + llvm::raw_svector_ostream OS(Str); + OS << "Property of mutable type '" << PropTypeName + << "' has 'copy' attribute; an immutable object will be stored instead"; + + BR.EmitBasicReport( + D, this, "Objective-C property misuse", "Logic error", OS.str(), + PathDiagnosticLocation::createBegin(D, BR.getSourceManager()), + D->getSourceRange()); +} + +void ento::registerObjCPropertyChecker(CheckerManager &Mgr) { + Mgr.registerChecker<ObjCPropertyChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp new file mode 100644 index 000000000000..efa804220765 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp @@ -0,0 +1,439 @@ +//== ObjCSelfInitChecker.cpp - Checker for 'self' initialization -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines ObjCSelfInitChecker, a builtin check that checks for uses of +// 'self' before proper initialization. +// +//===----------------------------------------------------------------------===// + +// This checks initialization methods to verify that they assign 'self' to the +// result of an initialization call (e.g. [super init], or [self initWith..]) +// before using 'self' or any instance variable. +// +// To perform the required checking, values are tagged with flags that indicate +// 1) if the object is the one pointed to by 'self', and 2) if the object +// is the result of an initializer (e.g. [super init]). +// +// Uses of an object that is true for 1) but not 2) trigger a diagnostic. +// The uses that are currently checked are: +// - Using instance variables. +// - Returning the object. +// +// Note that we don't check for an invalid 'self' that is the receiver of an +// obj-c message expression to cut down false positives where logging functions +// get information from self (like its class) or doing "invalidation" on self +// when the initialization fails. +// +// Because the object that 'self' points to gets invalidated when a call +// receives a reference to 'self', the checker keeps track and passes the flags +// for 1) and 2) to the new object that 'self' points to after the call. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ParentMap.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND); +static bool isInitializationMethod(const ObjCMethodDecl *MD); +static bool isInitMessage(const ObjCMethodCall &Msg); +static bool isSelfVar(SVal location, CheckerContext &C); + +namespace { +class ObjCSelfInitChecker : public Checker< check::PostObjCMessage, + check::PostStmt<ObjCIvarRefExpr>, + check::PreStmt<ReturnStmt>, + check::PreCall, + check::PostCall, + check::Location, + check::Bind > { + mutable std::unique_ptr<BugType> BT; + + void checkForInvalidSelf(const Expr *E, CheckerContext &C, + const char *errorStr) const; + +public: + ObjCSelfInitChecker() {} + void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const; + void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const; + void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const; + void checkLocation(SVal location, bool isLoad, const Stmt *S, + CheckerContext &C) const; + void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; + + void checkPreCall(const CallEvent &CE, CheckerContext &C) const; + void checkPostCall(const CallEvent &CE, CheckerContext &C) const; + + void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const override; +}; +} // end anonymous namespace + +namespace { +enum SelfFlagEnum { + /// No flag set. + SelfFlag_None = 0x0, + /// Value came from 'self'. + SelfFlag_Self = 0x1, + /// Value came from the result of an initializer (e.g. [super init]). + SelfFlag_InitRes = 0x2 +}; +} + +REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag, SymbolRef, unsigned) +REGISTER_TRAIT_WITH_PROGRAMSTATE(CalledInit, bool) + +/// A call receiving a reference to 'self' invalidates the object that +/// 'self' contains. This keeps the "self flags" assigned to the 'self' +/// object before the call so we can assign them to the new object that 'self' +/// points to after the call. +REGISTER_TRAIT_WITH_PROGRAMSTATE(PreCallSelfFlags, unsigned) + +static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) { + if (SymbolRef sym = val.getAsSymbol()) + if (const unsigned *attachedFlags = state->get<SelfFlag>(sym)) + return (SelfFlagEnum)*attachedFlags; + return SelfFlag_None; +} + +static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) { + return getSelfFlags(val, C.getState()); +} + +static void addSelfFlag(ProgramStateRef state, SVal val, + SelfFlagEnum flag, CheckerContext &C) { + // We tag the symbol that the SVal wraps. + if (SymbolRef sym = val.getAsSymbol()) { + state = state->set<SelfFlag>(sym, getSelfFlags(val, state) | flag); + C.addTransition(state); + } +} + +static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) { + return getSelfFlags(val, C) & flag; +} + +/// Returns true of the value of the expression is the object that 'self' +/// points to and is an object that did not come from the result of calling +/// an initializer. +static bool isInvalidSelf(const Expr *E, CheckerContext &C) { + SVal exprVal = C.getSVal(E); + if (!hasSelfFlag(exprVal, SelfFlag_Self, C)) + return false; // value did not come from 'self'. + if (hasSelfFlag(exprVal, SelfFlag_InitRes, C)) + return false; // 'self' is properly initialized. + + return true; +} + +void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C, + const char *errorStr) const { + if (!E) + return; + + if (!C.getState()->get<CalledInit>()) + return; + + if (!isInvalidSelf(E, C)) + return; + + // Generate an error node. + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + if (!BT) + BT.reset(new BugType(this, "Missing \"self = [(super or self) init...]\"", + categories::CoreFoundationObjectiveC)); + C.emitReport(llvm::make_unique<BugReport>(*BT, errorStr, N)); +} + +void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg, + CheckerContext &C) const { + // When encountering a message that does initialization (init rule), + // tag the return value so that we know later on that if self has this value + // then it is properly initialized. + + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisDeclContext()->getDecl()))) + return; + + if (isInitMessage(Msg)) { + // Tag the return value as the result of an initializer. + ProgramStateRef state = C.getState(); + + // FIXME this really should be context sensitive, where we record + // the current stack frame (for IPA). Also, we need to clean this + // value out when we return from this method. + state = state->set<CalledInit>(true); + + SVal V = C.getSVal(Msg.getOriginExpr()); + addSelfFlag(state, V, SelfFlag_InitRes, C); + return; + } + + // We don't check for an invalid 'self' in an obj-c message expression to cut + // down false positives where logging functions get information from self + // (like its class) or doing "invalidation" on self when the initialization + // fails. +} + +void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E, + CheckerContext &C) const { + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisDeclContext()->getDecl()))) + return; + + checkForInvalidSelf( + E->getBase(), C, + "Instance variable used while 'self' is not set to the result of " + "'[(super or self) init...]'"); +} + +void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S, + CheckerContext &C) const { + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisDeclContext()->getDecl()))) + return; + + checkForInvalidSelf(S->getRetValue(), C, + "Returning 'self' while it is not set to the result of " + "'[(super or self) init...]'"); +} + +// When a call receives a reference to 'self', [Pre/Post]Call pass +// the SelfFlags from the object 'self' points to before the call to the new +// object after the call. This is to avoid invalidation of 'self' by logging +// functions. +// Another common pattern in classes with multiple initializers is to put the +// subclass's common initialization bits into a static function that receives +// the value of 'self', e.g: +// @code +// if (!(self = [super init])) +// return nil; +// if (!(self = _commonInit(self))) +// return nil; +// @endcode +// Until we can use inter-procedural analysis, in such a call, transfer the +// SelfFlags to the result of the call. + +void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE, + CheckerContext &C) const { + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisDeclContext()->getDecl()))) + return; + + ProgramStateRef state = C.getState(); + unsigned NumArgs = CE.getNumArgs(); + // If we passed 'self' as and argument to the call, record it in the state + // to be propagated after the call. + // Note, we could have just given up, but try to be more optimistic here and + // assume that the functions are going to continue initialization or will not + // modify self. + for (unsigned i = 0; i < NumArgs; ++i) { + SVal argV = CE.getArgSVal(i); + if (isSelfVar(argV, C)) { + unsigned selfFlags = getSelfFlags(state->getSVal(argV.castAs<Loc>()), C); + C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); + return; + } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { + unsigned selfFlags = getSelfFlags(argV, C); + C.addTransition(state->set<PreCallSelfFlags>(selfFlags)); + return; + } + } +} + +void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE, + CheckerContext &C) const { + // FIXME: A callback should disable checkers at the start of functions. + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisDeclContext()->getDecl()))) + return; + + ProgramStateRef state = C.getState(); + SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>(); + if (!prevFlags) + return; + state = state->remove<PreCallSelfFlags>(); + + unsigned NumArgs = CE.getNumArgs(); + for (unsigned i = 0; i < NumArgs; ++i) { + SVal argV = CE.getArgSVal(i); + if (isSelfVar(argV, C)) { + // If the address of 'self' is being passed to the call, assume that the + // 'self' after the call will have the same flags. + // EX: log(&self) + addSelfFlag(state, state->getSVal(argV.castAs<Loc>()), prevFlags, C); + return; + } else if (hasSelfFlag(argV, SelfFlag_Self, C)) { + // If 'self' is passed to the call by value, assume that the function + // returns 'self'. So assign the flags, which were set on 'self' to the + // return value. + // EX: self = performMoreInitialization(self) + addSelfFlag(state, CE.getReturnValue(), prevFlags, C); + return; + } + } + + C.addTransition(state); +} + +void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad, + const Stmt *S, + CheckerContext &C) const { + if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>( + C.getCurrentAnalysisDeclContext()->getDecl()))) + return; + + // Tag the result of a load from 'self' so that we can easily know that the + // value is the object that 'self' points to. + ProgramStateRef state = C.getState(); + if (isSelfVar(location, C)) + addSelfFlag(state, state->getSVal(location.castAs<Loc>()), SelfFlag_Self, + C); +} + + +void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S, + CheckerContext &C) const { + // Allow assignment of anything to self. Self is a local variable in the + // initializer, so it is legal to assign anything to it, like results of + // static functions/method calls. After self is assigned something we cannot + // reason about, stop enforcing the rules. + // (Only continue checking if the assigned value should be treated as self.) + if ((isSelfVar(loc, C)) && + !hasSelfFlag(val, SelfFlag_InitRes, C) && + !hasSelfFlag(val, SelfFlag_Self, C) && + !isSelfVar(val, C)) { + + // Stop tracking the checker-specific state in the state. + ProgramStateRef State = C.getState(); + State = State->remove<CalledInit>(); + if (SymbolRef sym = loc.getAsSymbol()) + State = State->remove<SelfFlag>(sym); + C.addTransition(State); + } +} + +void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + SelfFlagTy FlagMap = State->get<SelfFlag>(); + bool DidCallInit = State->get<CalledInit>(); + SelfFlagEnum PreCallFlags = (SelfFlagEnum)State->get<PreCallSelfFlags>(); + + if (FlagMap.isEmpty() && !DidCallInit && !PreCallFlags) + return; + + Out << Sep << NL << *this << " :" << NL; + + if (DidCallInit) + Out << " An init method has been called." << NL; + + if (PreCallFlags != SelfFlag_None) { + if (PreCallFlags & SelfFlag_Self) { + Out << " An argument of the current call came from the 'self' variable." + << NL; + } + if (PreCallFlags & SelfFlag_InitRes) { + Out << " An argument of the current call came from an init method." + << NL; + } + } + + Out << NL; + for (SelfFlagTy::iterator I = FlagMap.begin(), E = FlagMap.end(); + I != E; ++I) { + Out << I->first << " : "; + + if (I->second == SelfFlag_None) + Out << "none"; + + if (I->second & SelfFlag_Self) + Out << "self variable"; + + if (I->second & SelfFlag_InitRes) { + if (I->second != SelfFlag_InitRes) + Out << " | "; + Out << "result of init method"; + } + + Out << NL; + } +} + + +// FIXME: A callback should disable checkers at the start of functions. +static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) { + if (!ND) + return false; + + const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ND); + if (!MD) + return false; + if (!isInitializationMethod(MD)) + return false; + + // self = [super init] applies only to NSObject subclasses. + // For instance, NSProxy doesn't implement -init. + ASTContext &Ctx = MD->getASTContext(); + IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); + ObjCInterfaceDecl *ID = MD->getClassInterface()->getSuperClass(); + for ( ; ID ; ID = ID->getSuperClass()) { + IdentifierInfo *II = ID->getIdentifier(); + + if (II == NSObjectII) + break; + } + return ID != nullptr; +} + +/// Returns true if the location is 'self'. +static bool isSelfVar(SVal location, CheckerContext &C) { + AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext(); + if (!analCtx->getSelfDecl()) + return false; + if (!location.getAs<loc::MemRegionVal>()) + return false; + + loc::MemRegionVal MRV = location.castAs<loc::MemRegionVal>(); + if (const DeclRegion *DR = dyn_cast<DeclRegion>(MRV.stripCasts())) + return (DR->getDecl() == analCtx->getSelfDecl()); + + return false; +} + +static bool isInitializationMethod(const ObjCMethodDecl *MD) { + return MD->getMethodFamily() == OMF_init; +} + +static bool isInitMessage(const ObjCMethodCall &Call) { + return Call.getMethodFamily() == OMF_init; +} + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +void ento::registerObjCSelfInitChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCSelfInitChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp new file mode 100644 index 000000000000..9058784dd345 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp @@ -0,0 +1,289 @@ +//===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines ObjCSuperDeallocChecker, a builtin check that warns when +// self is used after a call to [super dealloc] in MRR mode. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" + +using namespace clang; +using namespace ento; + +namespace { +class ObjCSuperDeallocChecker + : public Checker<check::PostObjCMessage, check::PreObjCMessage, + check::PreCall, check::Location> { + + mutable IdentifierInfo *IIdealloc, *IINSObject; + mutable Selector SELdealloc; + + std::unique_ptr<BugType> DoubleSuperDeallocBugType; + + void initIdentifierInfoAndSelectors(ASTContext &Ctx) const; + + bool isSuperDeallocMessage(const ObjCMethodCall &M) const; + +public: + ObjCSuperDeallocChecker(); + void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const; + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + + void checkLocation(SVal l, bool isLoad, const Stmt *S, + CheckerContext &C) const; + +private: + + void diagnoseCallArguments(const CallEvent &CE, CheckerContext &C) const; + + void reportUseAfterDealloc(SymbolRef Sym, StringRef Desc, const Stmt *S, + CheckerContext &C) const; +}; + +} // End anonymous namespace. + +// Remember whether [super dealloc] has previously been called on the +// SymbolRef for the receiver. +REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef) + +namespace { +class SuperDeallocBRVisitor final : public BugReporterVisitor { + SymbolRef ReceiverSymbol; + bool Satisfied; + +public: + SuperDeallocBRVisitor(SymbolRef ReceiverSymbol) + : ReceiverSymbol(ReceiverSymbol), + Satisfied(false) {} + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + BugReport &BR) override; + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.Add(ReceiverSymbol); + } +}; +} // End anonymous namespace. + +void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) const { + + ProgramStateRef State = C.getState(); + SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol(); + if (!ReceiverSymbol) { + diagnoseCallArguments(M, C); + return; + } + + bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol); + if (!AlreadyCalled) + return; + + StringRef Desc; + + if (isSuperDeallocMessage(M)) { + Desc = "[super dealloc] should not be called multiple times"; + } else { + Desc = StringRef(); + } + + reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C); +} + +void ObjCSuperDeallocChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + diagnoseCallArguments(Call, C); +} + +void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M, + CheckerContext &C) const { + // Check for [super dealloc] method call. + if (!isSuperDeallocMessage(M)) + return; + + ProgramStateRef State = C.getState(); + SymbolRef ReceiverSymbol = M.getSelfSVal().getAsSymbol(); + assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?"); + + // We add this transition in checkPostObjCMessage to avoid warning when + // we inline a call to [super dealloc] where the inlined call itself + // calls [super dealloc]. + State = State->add<CalledSuperDealloc>(ReceiverSymbol); + C.addTransition(State); +} + +void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S, + CheckerContext &C) const { + SymbolRef BaseSym = L.getLocSymbolInBase(); + if (!BaseSym) + return; + + ProgramStateRef State = C.getState(); + + if (!State->contains<CalledSuperDealloc>(BaseSym)) + return; + + const MemRegion *R = L.getAsRegion(); + if (!R) + return; + + // Climb the super regions to find the base symbol while recording + // the second-to-last region for error reporting. + const MemRegion *PriorSubRegion = nullptr; + while (const SubRegion *SR = dyn_cast<SubRegion>(R)) { + if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) { + BaseSym = SymR->getSymbol(); + break; + } else { + R = SR->getSuperRegion(); + PriorSubRegion = SR; + } + } + + StringRef Desc = StringRef(); + auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion); + + std::string Buf; + llvm::raw_string_ostream OS(Buf); + if (IvarRegion) { + OS << "Use of instance variable '" << *IvarRegion->getDecl() << + "' after 'self' has been deallocated"; + Desc = OS.str(); + } + + reportUseAfterDealloc(BaseSym, Desc, S, C); +} + +/// Report a use-after-dealloc on Sym. If not empty, +/// Desc will be used to describe the error; otherwise, +/// a default warning will be used. +void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym, + StringRef Desc, + const Stmt *S, + CheckerContext &C) const { + // We have a use of self after free. + // This likely causes a crash, so stop exploring the + // path by generating a sink. + ExplodedNode *ErrNode = C.generateErrorNode(); + // If we've already reached this node on another path, return. + if (!ErrNode) + return; + + if (Desc.empty()) + Desc = "Use of 'self' after it has been deallocated"; + + // Generate the report. + std::unique_ptr<BugReport> BR( + new BugReport(*DoubleSuperDeallocBugType, Desc, ErrNode)); + BR->addRange(S->getSourceRange()); + BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(Sym)); + C.emitReport(std::move(BR)); +} + +/// Diagnose if any of the arguments to CE have already been +/// dealloc'd. +void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + unsigned ArgCount = CE.getNumArgs(); + for (unsigned I = 0; I < ArgCount; I++) { + SymbolRef Sym = CE.getArgSVal(I).getAsSymbol(); + if (!Sym) + continue; + + if (State->contains<CalledSuperDealloc>(Sym)) { + reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C); + return; + } + } +} + +ObjCSuperDeallocChecker::ObjCSuperDeallocChecker() + : IIdealloc(nullptr), IINSObject(nullptr) { + + DoubleSuperDeallocBugType.reset( + new BugType(this, "[super dealloc] should not be called more than once", + categories::CoreFoundationObjectiveC)); +} + +void +ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const { + if (IIdealloc) + return; + + IIdealloc = &Ctx.Idents.get("dealloc"); + IINSObject = &Ctx.Idents.get("NSObject"); + + SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc); +} + +bool +ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const { + if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance) + return false; + + ASTContext &Ctx = M.getState()->getStateManager().getContext(); + initIdentifierInfoAndSelectors(Ctx); + + return M.getSelector() == SELdealloc; +} + +std::shared_ptr<PathDiagnosticPiece> +SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, BugReport &) { + if (Satisfied) + return nullptr; + + ProgramStateRef State = Succ->getState(); + + bool CalledNow = + Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol); + bool CalledBefore = + Succ->getFirstPred()->getState()->contains<CalledSuperDealloc>( + ReceiverSymbol); + + // Is Succ the node on which the analyzer noted that [super dealloc] was + // called on ReceiverSymbol? + if (CalledNow && !CalledBefore) { + Satisfied = true; + + ProgramPoint P = Succ->getLocation(); + PathDiagnosticLocation L = + PathDiagnosticLocation::create(P, BRC.getSourceManager()); + + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + return std::make_shared<PathDiagnosticEventPiece>( + L, "[super dealloc] called here"); + } + + return nullptr; +} + +//===----------------------------------------------------------------------===// +// Checker Registration. +//===----------------------------------------------------------------------===// + +void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) { + const LangOptions &LangOpts = Mgr.getLangOpts(); + if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount) + return; + Mgr.registerChecker<ObjCSuperDeallocChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp new file mode 100644 index 000000000000..7f7b45316087 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -0,0 +1,188 @@ +//==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- 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 a CheckObjCUnusedIvars, a checker that +// analyzes an Objective-C class's interface/implementation to determine if it +// has any ivars that are never accessed. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/Checker.h" + +using namespace clang; +using namespace ento; + +enum IVarState { Unused, Used }; +typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap; + +static void Scan(IvarUsageMap& M, const Stmt *S) { + if (!S) + return; + + if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) { + const ObjCIvarDecl *D = Ex->getDecl(); + IvarUsageMap::iterator I = M.find(D); + if (I != M.end()) + I->second = Used; + return; + } + + // Blocks can reference an instance variable of a class. + if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) { + Scan(M, BE->getBody()); + return; + } + + if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S)) + for (PseudoObjectExpr::const_semantics_iterator + i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) { + const Expr *sub = *i; + if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub)) + sub = OVE->getSourceExpr(); + Scan(M, sub); + } + + for (const Stmt *SubStmt : S->children()) + Scan(M, SubStmt); +} + +static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) { + if (!D) + return; + + const ObjCIvarDecl *ID = D->getPropertyIvarDecl(); + + if (!ID) + return; + + IvarUsageMap::iterator I = M.find(ID); + if (I != M.end()) + I->second = Used; +} + +static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) { + // Scan the methods for accesses. + for (const auto *I : D->instance_methods()) + Scan(M, I->getBody()); + + if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) { + // Scan for @synthesized property methods that act as setters/getters + // to an ivar. + for (const auto *I : ID->property_impls()) + Scan(M, I); + + // Scan the associated categories as well. + for (const auto *Cat : ID->getClassInterface()->visible_categories()) { + if (const ObjCCategoryImplDecl *CID = Cat->getImplementation()) + Scan(M, CID); + } + } +} + +static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, + SourceManager &SM) { + for (const auto *I : C->decls()) + if (const auto *FD = dyn_cast<FunctionDecl>(I)) { + SourceLocation L = FD->getBeginLoc(); + if (SM.getFileID(L) == FID) + Scan(M, FD->getBody()); + } +} + +static void checkObjCUnusedIvar(const ObjCImplementationDecl *D, + BugReporter &BR, + const CheckerBase *Checker) { + + const ObjCInterfaceDecl *ID = D->getClassInterface(); + IvarUsageMap M; + + // Iterate over the ivars. + for (const auto *Ivar : ID->ivars()) { + // Ignore ivars that... + // (a) aren't private + // (b) explicitly marked unused + // (c) are iboutlets + // (d) are unnamed bitfields + if (Ivar->getAccessControl() != ObjCIvarDecl::Private || + Ivar->hasAttr<UnusedAttr>() || Ivar->hasAttr<IBOutletAttr>() || + Ivar->hasAttr<IBOutletCollectionAttr>() || + Ivar->isUnnamedBitfield()) + continue; + + M[Ivar] = Unused; + } + + if (M.empty()) + return; + + // Now scan the implementation declaration. + Scan(M, D); + + // Any potentially unused ivars? + bool hasUnused = false; + for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) + if (I->second == Unused) { + hasUnused = true; + break; + } + + if (!hasUnused) + return; + + // We found some potentially unused ivars. Scan the entire translation unit + // for functions inside the @implementation that reference these ivars. + // FIXME: In the future hopefully we can just use the lexical DeclContext + // to go from the ObjCImplementationDecl to the lexically "nested" + // C functions. + SourceManager &SM = BR.getSourceManager(); + Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM); + + // Find ivars that are unused. + for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I) + if (I->second == Unused) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Instance variable '" << *I->first << "' in class '" << *ID + << "' is never used by the methods in its @implementation " + "(although it may be used by category methods)."; + + PathDiagnosticLocation L = + PathDiagnosticLocation::create(I->first, BR.getSourceManager()); + BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization", + os.str(), L); + } +} + +//===----------------------------------------------------------------------===// +// ObjCUnusedIvarsChecker +//===----------------------------------------------------------------------===// + +namespace { +class ObjCUnusedIvarsChecker : public Checker< + check::ASTDecl<ObjCImplementationDecl> > { +public: + void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr, + BugReporter &BR) const { + checkObjCUnusedIvar(D, BR, this); + } +}; +} + +void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) { + mgr.registerChecker<ObjCUnusedIvarsChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp new file mode 100644 index 000000000000..211db392bf71 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp @@ -0,0 +1,353 @@ +//=======- PaddingChecker.cpp ------------------------------------*- 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 a checker that checks for padding that could be +// removed by re-ordering members. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/MathExtras.h" +#include "llvm/Support/raw_ostream.h" +#include <numeric> + +using namespace clang; +using namespace ento; + +namespace { +class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> { +private: + mutable std::unique_ptr<BugType> PaddingBug; + mutable int64_t AllowedPad; + mutable BugReporter *BR; + +public: + void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR, + BugReporter &BRArg) const { + BR = &BRArg; + AllowedPad = + MGR.getAnalyzerOptions() + .getCheckerIntegerOption("AllowedPad", 24, this); + assert(AllowedPad >= 0 && "AllowedPad option should be non-negative"); + + // The calls to checkAST* from AnalysisConsumer don't + // visit template instantiations or lambda classes. We + // want to visit those, so we make our own RecursiveASTVisitor. + struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> { + const PaddingChecker *Checker; + bool shouldVisitTemplateInstantiations() const { return true; } + bool shouldVisitImplicitCode() const { return true; } + explicit LocalVisitor(const PaddingChecker *Checker) : Checker(Checker) {} + bool VisitRecordDecl(const RecordDecl *RD) { + Checker->visitRecord(RD); + return true; + } + bool VisitVarDecl(const VarDecl *VD) { + Checker->visitVariable(VD); + return true; + } + // TODO: Visit array new and mallocs for arrays. + }; + + LocalVisitor visitor(this); + visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD)); + } + + /// Look for records of overly padded types. If padding * + /// PadMultiplier exceeds AllowedPad, then generate a report. + /// PadMultiplier is used to share code with the array padding + /// checker. + void visitRecord(const RecordDecl *RD, uint64_t PadMultiplier = 1) const { + if (shouldSkipDecl(RD)) + return; + + // TODO: Figure out why we are going through declarations and not only + // definitions. + if (!(RD = RD->getDefinition())) + return; + + // This is the simplest correct case: a class with no fields and one base + // class. Other cases are more complicated because of how the base classes + // & fields might interact, so we don't bother dealing with them. + // TODO: Support other combinations of base classes and fields. + if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) + if (CXXRD->field_empty() && CXXRD->getNumBases() == 1) + return visitRecord(CXXRD->bases().begin()->getType()->getAsRecordDecl(), + PadMultiplier); + + auto &ASTContext = RD->getASTContext(); + const ASTRecordLayout &RL = ASTContext.getASTRecordLayout(RD); + assert(llvm::isPowerOf2_64(RL.getAlignment().getQuantity())); + + CharUnits BaselinePad = calculateBaselinePad(RD, ASTContext, RL); + if (BaselinePad.isZero()) + return; + + CharUnits OptimalPad; + SmallVector<const FieldDecl *, 20> OptimalFieldsOrder; + std::tie(OptimalPad, OptimalFieldsOrder) = + calculateOptimalPad(RD, ASTContext, RL); + + CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad); + if (DiffPad.getQuantity() <= AllowedPad) { + assert(!DiffPad.isNegative() && "DiffPad should not be negative"); + // There is not enough excess padding to trigger a warning. + return; + } + reportRecord(RD, BaselinePad, OptimalPad, OptimalFieldsOrder); + } + + /// Look for arrays of overly padded types. If the padding of the + /// array type exceeds AllowedPad, then generate a report. + void visitVariable(const VarDecl *VD) const { + const ArrayType *ArrTy = VD->getType()->getAsArrayTypeUnsafe(); + if (ArrTy == nullptr) + return; + uint64_t Elts = 0; + if (const ConstantArrayType *CArrTy = dyn_cast<ConstantArrayType>(ArrTy)) + Elts = CArrTy->getSize().getZExtValue(); + if (Elts == 0) + return; + const RecordType *RT = ArrTy->getElementType()->getAs<RecordType>(); + if (RT == nullptr) + return; + + // TODO: Recurse into the fields to see if they have excess padding. + visitRecord(RT->getDecl(), Elts); + } + + bool shouldSkipDecl(const RecordDecl *RD) const { + // TODO: Figure out why we are going through declarations and not only + // definitions. + if (!(RD = RD->getDefinition())) + return true; + auto Location = RD->getLocation(); + // If the construct doesn't have a source file, then it's not something + // we want to diagnose. + if (!Location.isValid()) + return true; + SrcMgr::CharacteristicKind Kind = + BR->getSourceManager().getFileCharacteristic(Location); + // Throw out all records that come from system headers. + if (Kind != SrcMgr::C_User) + return true; + + // Not going to attempt to optimize unions. + if (RD->isUnion()) + return true; + if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) { + // Tail padding with base classes ends up being very complicated. + // We will skip objects with base classes for now, unless they do not + // have fields. + // TODO: Handle more base class scenarios. + if (!CXXRD->field_empty() && CXXRD->getNumBases() != 0) + return true; + if (CXXRD->field_empty() && CXXRD->getNumBases() != 1) + return true; + // Virtual bases are complicated, skipping those for now. + if (CXXRD->getNumVBases() != 0) + return true; + // Can't layout a template, so skip it. We do still layout the + // instantiations though. + if (CXXRD->getTypeForDecl()->isDependentType()) + return true; + if (CXXRD->getTypeForDecl()->isInstantiationDependentType()) + return true; + } + // How do you reorder fields if you haven't got any? + else if (RD->field_empty()) + return true; + + auto IsTrickyField = [](const FieldDecl *FD) -> bool { + // Bitfield layout is hard. + if (FD->isBitField()) + return true; + + // Variable length arrays are tricky too. + QualType Ty = FD->getType(); + if (Ty->isIncompleteArrayType()) + return true; + return false; + }; + + if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField)) + return true; + return false; + } + + static CharUnits calculateBaselinePad(const RecordDecl *RD, + const ASTContext &ASTContext, + const ASTRecordLayout &RL) { + CharUnits PaddingSum; + CharUnits Offset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0)); + for (const FieldDecl *FD : RD->fields()) { + // This checker only cares about the padded size of the + // field, and not the data size. If the field is a record + // with tail padding, then we won't put that number in our + // total because reordering fields won't fix that problem. + CharUnits FieldSize = ASTContext.getTypeSizeInChars(FD->getType()); + auto FieldOffsetBits = RL.getFieldOffset(FD->getFieldIndex()); + CharUnits FieldOffset = ASTContext.toCharUnitsFromBits(FieldOffsetBits); + PaddingSum += (FieldOffset - Offset); + Offset = FieldOffset + FieldSize; + } + PaddingSum += RL.getSize() - Offset; + return PaddingSum; + } + + /// Optimal padding overview: + /// 1. Find a close approximation to where we can place our first field. + /// This will usually be at offset 0. + /// 2. Try to find the best field that can legally be placed at the current + /// offset. + /// a. "Best" is the largest alignment that is legal, but smallest size. + /// This is to account for overly aligned types. + /// 3. If no fields can fit, pad by rounding the current offset up to the + /// smallest alignment requirement of our fields. Measure and track the + // amount of padding added. Go back to 2. + /// 4. Increment the current offset by the size of the chosen field. + /// 5. Remove the chosen field from the set of future possibilities. + /// 6. Go back to 2 if there are still unplaced fields. + /// 7. Add tail padding by rounding the current offset up to the structure + /// alignment. Track the amount of padding added. + + static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>> + calculateOptimalPad(const RecordDecl *RD, const ASTContext &ASTContext, + const ASTRecordLayout &RL) { + struct FieldInfo { + CharUnits Align; + CharUnits Size; + const FieldDecl *Field; + bool operator<(const FieldInfo &RHS) const { + // Order from small alignments to large alignments, + // then large sizes to small sizes. + // then large field indices to small field indices + return std::make_tuple(Align, -Size, + Field ? -static_cast<int>(Field->getFieldIndex()) + : 0) < + std::make_tuple( + RHS.Align, -RHS.Size, + RHS.Field ? -static_cast<int>(RHS.Field->getFieldIndex()) + : 0); + } + }; + SmallVector<FieldInfo, 20> Fields; + auto GatherSizesAndAlignments = [](const FieldDecl *FD) { + FieldInfo RetVal; + RetVal.Field = FD; + auto &Ctx = FD->getASTContext(); + std::tie(RetVal.Size, RetVal.Align) = + Ctx.getTypeInfoInChars(FD->getType()); + assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity())); + if (auto Max = FD->getMaxAlignment()) + RetVal.Align = std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align); + return RetVal; + }; + std::transform(RD->field_begin(), RD->field_end(), + std::back_inserter(Fields), GatherSizesAndAlignments); + llvm::sort(Fields); + // This lets us skip over vptrs and non-virtual bases, + // so that we can just worry about the fields in our object. + // Note that this does cause us to miss some cases where we + // could pack more bytes in to a base class's tail padding. + CharUnits NewOffset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0)); + CharUnits NewPad; + SmallVector<const FieldDecl *, 20> OptimalFieldsOrder; + while (!Fields.empty()) { + unsigned TrailingZeros = + llvm::countTrailingZeros((unsigned long long)NewOffset.getQuantity()); + // If NewOffset is zero, then countTrailingZeros will be 64. Shifting + // 64 will overflow our unsigned long long. Shifting 63 will turn + // our long long (and CharUnits internal type) negative. So shift 62. + long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u); + CharUnits CurAlignment = CharUnits::fromQuantity(CurAlignmentBits); + FieldInfo InsertPoint = {CurAlignment, CharUnits::Zero(), nullptr}; + auto CurBegin = Fields.begin(); + auto CurEnd = Fields.end(); + + // In the typical case, this will find the last element + // of the vector. We won't find a middle element unless + // we started on a poorly aligned address or have an overly + // aligned field. + auto Iter = std::upper_bound(CurBegin, CurEnd, InsertPoint); + if (Iter != CurBegin) { + // We found a field that we can layout with the current alignment. + --Iter; + NewOffset += Iter->Size; + OptimalFieldsOrder.push_back(Iter->Field); + Fields.erase(Iter); + } else { + // We are poorly aligned, and we need to pad in order to layout another + // field. Round up to at least the smallest field alignment that we + // currently have. + CharUnits NextOffset = NewOffset.alignTo(Fields[0].Align); + NewPad += NextOffset - NewOffset; + NewOffset = NextOffset; + } + } + // Calculate tail padding. + CharUnits NewSize = NewOffset.alignTo(RL.getAlignment()); + NewPad += NewSize - NewOffset; + return {NewPad, std::move(OptimalFieldsOrder)}; + } + + void reportRecord( + const RecordDecl *RD, CharUnits BaselinePad, CharUnits OptimalPad, + const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const { + if (!PaddingBug) + PaddingBug = + llvm::make_unique<BugType>(this, "Excessive Padding", "Performance"); + + SmallString<100> Buf; + llvm::raw_svector_ostream Os(Buf); + Os << "Excessive padding in '"; + Os << QualType::getAsString(RD->getTypeForDecl(), Qualifiers(), + LangOptions()) + << "'"; + + if (auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) { + // TODO: make this show up better in the console output and in + // the HTML. Maybe just make it show up in HTML like the path + // diagnostics show. + SourceLocation ILoc = TSD->getPointOfInstantiation(); + if (ILoc.isValid()) + Os << " instantiated here: " + << ILoc.printToString(BR->getSourceManager()); + } + + Os << " (" << BaselinePad.getQuantity() << " padding bytes, where " + << OptimalPad.getQuantity() << " is optimal). \n" + << "Optimal fields order: \n"; + for (const auto *FD : OptimalFieldsOrder) + Os << FD->getName() << ", \n"; + Os << "consider reordering the fields or adding explicit padding " + "members."; + + PathDiagnosticLocation CELoc = + PathDiagnosticLocation::create(RD, BR->getSourceManager()); + auto Report = llvm::make_unique<BugReport>(*PaddingBug, Os.str(), CELoc); + Report->setDeclWithIssue(RD); + Report->addRange(RD->getSourceRange()); + BR->emitReport(std::move(Report)); + } +}; +} // namespace + +void ento::registerPaddingChecker(CheckerManager &Mgr) { + Mgr.registerChecker<PaddingChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp new file mode 100644 index 000000000000..de3a16ebc729 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -0,0 +1,345 @@ +//=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines PointerArithChecker, a builtin checker that checks for +// pointer arithmetic on locations other than array elements. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +enum class AllocKind { + SingleObject, + Array, + Unknown, + Reinterpreted // Single object interpreted as an array. +}; +} // end namespace + +namespace llvm { +template <> struct FoldingSetTrait<AllocKind> { + static inline void Profile(AllocKind X, FoldingSetNodeID &ID) { + ID.AddInteger(static_cast<int>(X)); + } +}; +} // end namespace llvm + +namespace { +class PointerArithChecker + : public Checker< + check::PreStmt<BinaryOperator>, check::PreStmt<UnaryOperator>, + check::PreStmt<ArraySubscriptExpr>, check::PreStmt<CastExpr>, + check::PostStmt<CastExpr>, check::PostStmt<CXXNewExpr>, + check::PostStmt<CallExpr>, check::DeadSymbols> { + AllocKind getKindOfNewOp(const CXXNewExpr *NE, const FunctionDecl *FD) const; + const MemRegion *getArrayRegion(const MemRegion *Region, bool &Polymorphic, + AllocKind &AKind, CheckerContext &C) const; + const MemRegion *getPointedRegion(const MemRegion *Region, + CheckerContext &C) const; + void reportPointerArithMisuse(const Expr *E, CheckerContext &C, + bool PointedNeeded = false) const; + void initAllocIdentifiers(ASTContext &C) const; + + mutable std::unique_ptr<BuiltinBug> BT_pointerArith; + mutable std::unique_ptr<BuiltinBug> BT_polyArray; + mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions; + +public: + void checkPreStmt(const UnaryOperator *UOp, CheckerContext &C) const; + void checkPreStmt(const BinaryOperator *BOp, CheckerContext &C) const; + void checkPreStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const; + void checkPreStmt(const CastExpr *CE, CheckerContext &C) const; + void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; + void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const; + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; +}; +} // end namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind) + +void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + // TODO: intentional leak. Some information is garbage collected too early, + // see http://reviews.llvm.org/D14203 for further information. + /*ProgramStateRef State = C.getState(); + RegionStateTy RegionStates = State->get<RegionState>(); + for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end(); + I != E; ++I) { + if (!SR.isLiveRegion(I->first)) + State = State->remove<RegionState>(I->first); + } + C.addTransition(State);*/ +} + +AllocKind PointerArithChecker::getKindOfNewOp(const CXXNewExpr *NE, + const FunctionDecl *FD) const { + // This checker try not to assume anything about placement and overloaded + // new to avoid false positives. + if (isa<CXXMethodDecl>(FD)) + return AllocKind::Unknown; + if (FD->getNumParams() != 1 || FD->isVariadic()) + return AllocKind::Unknown; + if (NE->isArray()) + return AllocKind::Array; + + return AllocKind::SingleObject; +} + +const MemRegion * +PointerArithChecker::getPointedRegion(const MemRegion *Region, + CheckerContext &C) const { + assert(Region); + ProgramStateRef State = C.getState(); + SVal S = State->getSVal(Region); + return S.getAsRegion(); +} + +/// Checks whether a region is the part of an array. +/// In case there is a derived to base cast above the array element, the +/// Polymorphic output value is set to true. AKind output value is set to the +/// allocation kind of the inspected region. +const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region, + bool &Polymorphic, + AllocKind &AKind, + CheckerContext &C) const { + assert(Region); + while (Region->getKind() == MemRegion::Kind::CXXBaseObjectRegionKind) { + Region = Region->getAs<CXXBaseObjectRegion>()->getSuperRegion(); + Polymorphic = true; + } + if (Region->getKind() == MemRegion::Kind::ElementRegionKind) { + Region = Region->getAs<ElementRegion>()->getSuperRegion(); + } + + ProgramStateRef State = C.getState(); + if (const AllocKind *Kind = State->get<RegionState>(Region)) { + AKind = *Kind; + if (*Kind == AllocKind::Array) + return Region; + else + return nullptr; + } + // When the region is symbolic and we do not have any information about it, + // assume that this is an array to avoid false positives. + if (Region->getKind() == MemRegion::Kind::SymbolicRegionKind) + return Region; + + // No AllocKind stored and not symbolic, assume that it points to a single + // object. + return nullptr; +} + +void PointerArithChecker::reportPointerArithMisuse(const Expr *E, + CheckerContext &C, + bool PointedNeeded) const { + SourceRange SR = E->getSourceRange(); + if (SR.isInvalid()) + return; + + ProgramStateRef State = C.getState(); + const MemRegion *Region = C.getSVal(E).getAsRegion(); + if (!Region) + return; + if (PointedNeeded) + Region = getPointedRegion(Region, C); + if (!Region) + return; + + bool IsPolymorphic = false; + AllocKind Kind = AllocKind::Unknown; + if (const MemRegion *ArrayRegion = + getArrayRegion(Region, IsPolymorphic, Kind, C)) { + if (!IsPolymorphic) + return; + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + if (!BT_polyArray) + BT_polyArray.reset(new BuiltinBug( + this, "Dangerous pointer arithmetic", + "Pointer arithmetic on a pointer to base class is dangerous " + "because derived and base class may have different size.")); + auto R = llvm::make_unique<BugReport>(*BT_polyArray, + BT_polyArray->getDescription(), N); + R->addRange(E->getSourceRange()); + R->markInteresting(ArrayRegion); + C.emitReport(std::move(R)); + } + return; + } + + if (Kind == AllocKind::Reinterpreted) + return; + + // We might not have enough information about symbolic regions. + if (Kind != AllocKind::SingleObject && + Region->getKind() == MemRegion::Kind::SymbolicRegionKind) + return; + + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + if (!BT_pointerArith) + BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic", + "Pointer arithmetic on non-array " + "variables relies on memory layout, " + "which is dangerous.")); + auto R = llvm::make_unique<BugReport>(*BT_pointerArith, + BT_pointerArith->getDescription(), N); + R->addRange(SR); + R->markInteresting(Region); + C.emitReport(std::move(R)); + } +} + +void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const { + if (!AllocFunctions.empty()) + return; + AllocFunctions.insert(&C.Idents.get("alloca")); + AllocFunctions.insert(&C.Idents.get("malloc")); + AllocFunctions.insert(&C.Idents.get("realloc")); + AllocFunctions.insert(&C.Idents.get("calloc")); + AllocFunctions.insert(&C.Idents.get("valloc")); +} + +void PointerArithChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD) + return; + IdentifierInfo *FunI = FD->getIdentifier(); + initAllocIdentifiers(C.getASTContext()); + if (AllocFunctions.count(FunI) == 0) + return; + + SVal SV = C.getSVal(CE); + const MemRegion *Region = SV.getAsRegion(); + if (!Region) + return; + // Assume that C allocation functions allocate arrays to avoid false + // positives. + // TODO: Add heuristics to distinguish alloc calls that allocates single + // objecs. + State = State->set<RegionState>(Region, AllocKind::Array); + C.addTransition(State); +} + +void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE, + CheckerContext &C) const { + const FunctionDecl *FD = NE->getOperatorNew(); + if (!FD) + return; + + AllocKind Kind = getKindOfNewOp(NE, FD); + + ProgramStateRef State = C.getState(); + SVal AllocedVal = C.getSVal(NE); + const MemRegion *Region = AllocedVal.getAsRegion(); + if (!Region) + return; + State = State->set<RegionState>(Region, Kind); + C.addTransition(State); +} + +void PointerArithChecker::checkPostStmt(const CastExpr *CE, + CheckerContext &C) const { + if (CE->getCastKind() != CastKind::CK_BitCast) + return; + + const Expr *CastedExpr = CE->getSubExpr(); + ProgramStateRef State = C.getState(); + SVal CastedVal = C.getSVal(CastedExpr); + + const MemRegion *Region = CastedVal.getAsRegion(); + if (!Region) + return; + + // Suppress reinterpret casted hits. + State = State->set<RegionState>(Region, AllocKind::Reinterpreted); + C.addTransition(State); +} + +void PointerArithChecker::checkPreStmt(const CastExpr *CE, + CheckerContext &C) const { + if (CE->getCastKind() != CastKind::CK_ArrayToPointerDecay) + return; + + const Expr *CastedExpr = CE->getSubExpr(); + ProgramStateRef State = C.getState(); + SVal CastedVal = C.getSVal(CastedExpr); + + const MemRegion *Region = CastedVal.getAsRegion(); + if (!Region) + return; + + if (const AllocKind *Kind = State->get<RegionState>(Region)) { + if (*Kind == AllocKind::Array || *Kind == AllocKind::Reinterpreted) + return; + } + State = State->set<RegionState>(Region, AllocKind::Array); + C.addTransition(State); +} + +void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp, + CheckerContext &C) const { + if (!UOp->isIncrementDecrementOp() || !UOp->getType()->isPointerType()) + return; + reportPointerArithMisuse(UOp->getSubExpr(), C, true); +} + +void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr, + CheckerContext &C) const { + SVal Idx = C.getSVal(SubsExpr->getIdx()); + + // Indexing with 0 is OK. + if (Idx.isZeroConstant()) + return; + + // Indexing vector-type expressions is also OK. + if (SubsExpr->getBase()->getType()->isVectorType()) + return; + reportPointerArithMisuse(SubsExpr->getBase(), C); +} + +void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp, + CheckerContext &C) const { + BinaryOperatorKind OpKind = BOp->getOpcode(); + if (!BOp->isAdditiveOp() && OpKind != BO_AddAssign && OpKind != BO_SubAssign) + return; + + const Expr *Lhs = BOp->getLHS(); + const Expr *Rhs = BOp->getRHS(); + ProgramStateRef State = C.getState(); + + if (Rhs->getType()->isIntegerType() && Lhs->getType()->isPointerType()) { + SVal RHSVal = C.getSVal(Rhs); + if (State->isNull(RHSVal).isConstrainedTrue()) + return; + reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp()); + } + // The int += ptr; case is not valid C++. + if (Lhs->getType()->isIntegerType() && Rhs->getType()->isPointerType()) { + SVal LHSVal = C.getSVal(Lhs); + if (State->isNull(LHSVal).isConstrainedTrue()) + return; + reportPointerArithMisuse(Rhs, C); + } +} + +void ento::registerPointerArithChecker(CheckerManager &mgr) { + mgr.registerChecker<PointerArithChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp new file mode 100644 index 000000000000..41490e45f241 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -0,0 +1,75 @@ +//=== PointerSubChecker.cpp - Pointer subtraction checker ------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines PointerSubChecker, a builtin checker that checks for +// pointer subtractions on two pointers pointing to different memory chunks. +// This check corresponds to CWE-469. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class PointerSubChecker + : public Checker< check::PreStmt<BinaryOperator> > { + mutable std::unique_ptr<BuiltinBug> BT; + +public: + void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; +}; +} + +void PointerSubChecker::checkPreStmt(const BinaryOperator *B, + CheckerContext &C) const { + // When doing pointer subtraction, if the two pointers do not point to the + // same memory chunk, emit a warning. + if (B->getOpcode() != BO_Sub) + return; + + SVal LV = C.getSVal(B->getLHS()); + SVal RV = C.getSVal(B->getRHS()); + + const MemRegion *LR = LV.getAsRegion(); + const MemRegion *RR = RV.getAsRegion(); + + if (!(LR && RR)) + return; + + const MemRegion *BaseLR = LR->getBaseRegion(); + const MemRegion *BaseRR = RR->getBaseRegion(); + + if (BaseLR == BaseRR) + return; + + // Allow arithmetic on different symbolic regions. + if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR)) + return; + + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + if (!BT) + BT.reset( + new BuiltinBug(this, "Pointer subtraction", + "Subtraction of two pointers that do not point to " + "the same memory chunk may cause incorrect result.")); + auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); + R->addRange(B->getSourceRange()); + C.emitReport(std::move(R)); + } +} + +void ento::registerPointerSubChecker(CheckerManager &mgr) { + mgr.registerChecker<PointerSubChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp new file mode 100644 index 000000000000..66cc37278809 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -0,0 +1,483 @@ +//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines PthreadLockChecker, a simple lock -> unlock checker. +// Also handles XNU locks, which behave similarly enough to share code. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" + +using namespace clang; +using namespace ento; + +namespace { + +struct LockState { + enum Kind { + Destroyed, + Locked, + Unlocked, + UntouchedAndPossiblyDestroyed, + UnlockedAndPossiblyDestroyed + } K; + +private: + LockState(Kind K) : K(K) {} + +public: + static LockState getLocked() { return LockState(Locked); } + static LockState getUnlocked() { return LockState(Unlocked); } + static LockState getDestroyed() { return LockState(Destroyed); } + static LockState getUntouchedAndPossiblyDestroyed() { + return LockState(UntouchedAndPossiblyDestroyed); + } + static LockState getUnlockedAndPossiblyDestroyed() { + return LockState(UnlockedAndPossiblyDestroyed); + } + + bool operator==(const LockState &X) const { + return K == X.K; + } + + bool isLocked() const { return K == Locked; } + bool isUnlocked() const { return K == Unlocked; } + bool isDestroyed() const { return K == Destroyed; } + bool isUntouchedAndPossiblyDestroyed() const { + return K == UntouchedAndPossiblyDestroyed; + } + bool isUnlockedAndPossiblyDestroyed() const { + return K == UnlockedAndPossiblyDestroyed; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + } +}; + +class PthreadLockChecker + : public Checker<check::PostStmt<CallExpr>, check::DeadSymbols> { + mutable std::unique_ptr<BugType> BT_doublelock; + mutable std::unique_ptr<BugType> BT_doubleunlock; + mutable std::unique_ptr<BugType> BT_destroylock; + mutable std::unique_ptr<BugType> BT_initlock; + mutable std::unique_ptr<BugType> BT_lor; + enum LockingSemantics { + NotApplicable = 0, + PthreadSemantics, + XNUSemantics + }; +public: + void checkPostStmt(const CallExpr *CE, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const override; + + void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock, + bool isTryLock, enum LockingSemantics semantics) const; + + void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const; + void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock, + enum LockingSemantics semantics) const; + void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const; + void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const; + ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state, + const MemRegion *lockR, + const SymbolRef *sym) const; +}; +} // end anonymous namespace + +// A stack of locks for tracking lock-unlock order. +REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *) + +// An entry for tracking lock states. +REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState) + +// Return values for unresolved calls to pthread_mutex_destroy(). +REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef) + +void PthreadLockChecker::checkPostStmt(const CallExpr *CE, + CheckerContext &C) const { + StringRef FName = C.getCalleeName(CE); + if (FName.empty()) + return; + + if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2) + return; + + if (FName == "pthread_mutex_lock" || + FName == "pthread_rwlock_rdlock" || + FName == "pthread_rwlock_wrlock") + AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, PthreadSemantics); + else if (FName == "lck_mtx_lock" || + FName == "lck_rw_lock_exclusive" || + FName == "lck_rw_lock_shared") + AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, XNUSemantics); + else if (FName == "pthread_mutex_trylock" || + FName == "pthread_rwlock_tryrdlock" || + FName == "pthread_rwlock_trywrlock") + AcquireLock(C, CE, C.getSVal(CE->getArg(0)), + true, PthreadSemantics); + else if (FName == "lck_mtx_try_lock" || + FName == "lck_rw_try_lock_exclusive" || + FName == "lck_rw_try_lock_shared") + AcquireLock(C, CE, C.getSVal(CE->getArg(0)), true, XNUSemantics); + else if (FName == "pthread_mutex_unlock" || + FName == "pthread_rwlock_unlock" || + FName == "lck_mtx_unlock" || + FName == "lck_rw_done") + ReleaseLock(C, CE, C.getSVal(CE->getArg(0))); + else if (FName == "pthread_mutex_destroy") + DestroyLock(C, CE, C.getSVal(CE->getArg(0)), PthreadSemantics); + else if (FName == "lck_mtx_destroy") + DestroyLock(C, CE, C.getSVal(CE->getArg(0)), XNUSemantics); + else if (FName == "pthread_mutex_init") + InitLock(C, CE, C.getSVal(CE->getArg(0))); +} + +// When a lock is destroyed, in some semantics(like PthreadSemantics) we are not +// sure if the destroy call has succeeded or failed, and the lock enters one of +// the 'possibly destroyed' state. There is a short time frame for the +// programmer to check the return value to see if the lock was successfully +// destroyed. Before we model the next operation over that lock, we call this +// function to see if the return value was checked by now and set the lock state +// - either to destroyed state or back to its previous state. + +// In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is +// successfully destroyed and it returns a non-zero value otherwise. +ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex( + ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const { + const LockState *lstate = state->get<LockMap>(lockR); + // Existence in DestroyRetVal ensures existence in LockMap. + // Existence in Destroyed also ensures that the lock state for lockR is either + // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed. + assert(lstate->isUntouchedAndPossiblyDestroyed() || + lstate->isUnlockedAndPossiblyDestroyed()); + + ConstraintManager &CMgr = state->getConstraintManager(); + ConditionTruthVal retZero = CMgr.isNull(state, *sym); + if (retZero.isConstrainedFalse()) { + if (lstate->isUntouchedAndPossiblyDestroyed()) + state = state->remove<LockMap>(lockR); + else if (lstate->isUnlockedAndPossiblyDestroyed()) + state = state->set<LockMap>(lockR, LockState::getUnlocked()); + } else + state = state->set<LockMap>(lockR, LockState::getDestroyed()); + + // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is + // now resolved. + state = state->remove<DestroyRetVal>(lockR); + return state; +} + +void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + LockMapTy LM = State->get<LockMap>(); + if (!LM.isEmpty()) { + Out << Sep << "Mutex states:" << NL; + for (auto I : LM) { + I.first->dumpToStream(Out); + if (I.second.isLocked()) + Out << ": locked"; + else if (I.second.isUnlocked()) + Out << ": unlocked"; + else if (I.second.isDestroyed()) + Out << ": destroyed"; + else if (I.second.isUntouchedAndPossiblyDestroyed()) + Out << ": not tracked, possibly destroyed"; + else if (I.second.isUnlockedAndPossiblyDestroyed()) + Out << ": unlocked, possibly destroyed"; + Out << NL; + } + } + + LockSetTy LS = State->get<LockSet>(); + if (!LS.isEmpty()) { + Out << Sep << "Mutex lock order:" << NL; + for (auto I: LS) { + I->dumpToStream(Out); + Out << NL; + } + } + + // TODO: Dump destroyed mutex symbols? +} + +void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock, + enum LockingSemantics semantics) const { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + ProgramStateRef state = C.getState(); + const SymbolRef *sym = state->get<DestroyRetVal>(lockR); + if (sym) + state = resolvePossiblyDestroyedMutex(state, lockR, sym); + + SVal X = C.getSVal(CE); + if (X.isUnknownOrUndef()) + return; + + DefinedSVal retVal = X.castAs<DefinedSVal>(); + + if (const LockState *LState = state->get<LockMap>(lockR)) { + if (LState->isLocked()) { + if (!BT_doublelock) + BT_doublelock.reset(new BugType(this, "Double locking", + "Lock checker")); + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + auto report = llvm::make_unique<BugReport>( + *BT_doublelock, "This lock has already been acquired", N); + report->addRange(CE->getArg(0)->getSourceRange()); + C.emitReport(std::move(report)); + return; + } else if (LState->isDestroyed()) { + reportUseDestroyedBug(C, CE); + return; + } + } + + ProgramStateRef lockSucc = state; + if (isTryLock) { + // Bifurcate the state, and allow a mode where the lock acquisition fails. + ProgramStateRef lockFail; + switch (semantics) { + case PthreadSemantics: + std::tie(lockFail, lockSucc) = state->assume(retVal); + break; + case XNUSemantics: + std::tie(lockSucc, lockFail) = state->assume(retVal); + break; + default: + llvm_unreachable("Unknown tryLock locking semantics"); + } + assert(lockFail && lockSucc); + C.addTransition(lockFail); + + } else if (semantics == PthreadSemantics) { + // Assume that the return value was 0. + lockSucc = state->assume(retVal, false); + assert(lockSucc); + + } else { + // XNU locking semantics return void on non-try locks + assert((semantics == XNUSemantics) && "Unknown locking semantics"); + lockSucc = state; + } + + // Record that the lock was acquired. + lockSucc = lockSucc->add<LockSet>(lockR); + lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked()); + C.addTransition(lockSucc); +} + +void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock) const { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + ProgramStateRef state = C.getState(); + const SymbolRef *sym = state->get<DestroyRetVal>(lockR); + if (sym) + state = resolvePossiblyDestroyedMutex(state, lockR, sym); + + if (const LockState *LState = state->get<LockMap>(lockR)) { + if (LState->isUnlocked()) { + if (!BT_doubleunlock) + BT_doubleunlock.reset(new BugType(this, "Double unlocking", + "Lock checker")); + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + auto Report = llvm::make_unique<BugReport>( + *BT_doubleunlock, "This lock has already been unlocked", N); + Report->addRange(CE->getArg(0)->getSourceRange()); + C.emitReport(std::move(Report)); + return; + } else if (LState->isDestroyed()) { + reportUseDestroyedBug(C, CE); + return; + } + } + + LockSetTy LS = state->get<LockSet>(); + + // FIXME: Better analysis requires IPA for wrappers. + + if (!LS.isEmpty()) { + const MemRegion *firstLockR = LS.getHead(); + if (firstLockR != lockR) { + if (!BT_lor) + BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker")); + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + auto report = llvm::make_unique<BugReport>( + *BT_lor, "This was not the most recently acquired lock. Possible " + "lock order reversal", N); + report->addRange(CE->getArg(0)->getSourceRange()); + C.emitReport(std::move(report)); + return; + } + // Record that the lock was released. + state = state->set<LockSet>(LS.getTail()); + } + + state = state->set<LockMap>(lockR, LockState::getUnlocked()); + C.addTransition(state); +} + +void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE, + SVal Lock, + enum LockingSemantics semantics) const { + + const MemRegion *LockR = Lock.getAsRegion(); + if (!LockR) + return; + + ProgramStateRef State = C.getState(); + + const SymbolRef *sym = State->get<DestroyRetVal>(LockR); + if (sym) + State = resolvePossiblyDestroyedMutex(State, LockR, sym); + + const LockState *LState = State->get<LockMap>(LockR); + // Checking the return value of the destroy method only in the case of + // PthreadSemantics + if (semantics == PthreadSemantics) { + if (!LState || LState->isUnlocked()) { + SymbolRef sym = C.getSVal(CE).getAsSymbol(); + if (!sym) { + State = State->remove<LockMap>(LockR); + C.addTransition(State); + return; + } + State = State->set<DestroyRetVal>(LockR, sym); + if (LState && LState->isUnlocked()) + State = State->set<LockMap>( + LockR, LockState::getUnlockedAndPossiblyDestroyed()); + else + State = State->set<LockMap>( + LockR, LockState::getUntouchedAndPossiblyDestroyed()); + C.addTransition(State); + return; + } + } else { + if (!LState || LState->isUnlocked()) { + State = State->set<LockMap>(LockR, LockState::getDestroyed()); + C.addTransition(State); + return; + } + } + StringRef Message; + + if (LState->isLocked()) { + Message = "This lock is still locked"; + } else { + Message = "This lock has already been destroyed"; + } + + if (!BT_destroylock) + BT_destroylock.reset(new BugType(this, "Destroy invalid lock", + "Lock checker")); + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N); + Report->addRange(CE->getArg(0)->getSourceRange()); + C.emitReport(std::move(Report)); +} + +void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE, + SVal Lock) const { + + const MemRegion *LockR = Lock.getAsRegion(); + if (!LockR) + return; + + ProgramStateRef State = C.getState(); + + const SymbolRef *sym = State->get<DestroyRetVal>(LockR); + if (sym) + State = resolvePossiblyDestroyedMutex(State, LockR, sym); + + const struct LockState *LState = State->get<LockMap>(LockR); + if (!LState || LState->isDestroyed()) { + State = State->set<LockMap>(LockR, LockState::getUnlocked()); + C.addTransition(State); + return; + } + + StringRef Message; + + if (LState->isLocked()) { + Message = "This lock is still being held"; + } else { + Message = "This lock has already been initialized"; + } + + if (!BT_initlock) + BT_initlock.reset(new BugType(this, "Init invalid lock", + "Lock checker")); + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N); + Report->addRange(CE->getArg(0)->getSourceRange()); + C.emitReport(std::move(Report)); +} + +void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C, + const CallExpr *CE) const { + if (!BT_destroylock) + BT_destroylock.reset(new BugType(this, "Use destroyed lock", + "Lock checker")); + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + auto Report = llvm::make_unique<BugReport>( + *BT_destroylock, "This lock has already been destroyed", N); + Report->addRange(CE->getArg(0)->getSourceRange()); + C.emitReport(std::move(Report)); +} + +void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + // TODO: Clean LockMap when a mutex region dies. + + DestroyRetValTy TrackedSymbols = State->get<DestroyRetVal>(); + for (DestroyRetValTy::iterator I = TrackedSymbols.begin(), + E = TrackedSymbols.end(); + I != E; ++I) { + const SymbolRef Sym = I->second; + const MemRegion *lockR = I->first; + bool IsSymDead = SymReaper.isDead(Sym); + // Remove the dead symbol from the return value symbols map. + if (IsSymDead) + State = resolvePossiblyDestroyedMutex(State, lockR, &Sym); + } + C.addTransition(State); +} + +void ento::registerPthreadLockChecker(CheckerManager &mgr) { + mgr.registerChecker<PthreadLockChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp new file mode 100644 index 000000000000..0652af856643 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp @@ -0,0 +1,1547 @@ +//==-- RetainCountChecker.cpp - Checks for leaks and other issues -*- 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 the methods for RetainCountChecker, which implements +// a reference count checker for Core Foundation and Cocoa on (Mac OS X). +// +//===----------------------------------------------------------------------===// + +#include "RetainCountChecker.h" + +using namespace clang; +using namespace ento; +using namespace retaincountchecker; +using llvm::StrInStrNoCase; + +REGISTER_MAP_WITH_PROGRAMSTATE(RefBindings, SymbolRef, RefVal) + +namespace clang { +namespace ento { +namespace retaincountchecker { + +const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym) { + return State->get<RefBindings>(Sym); +} + +ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym, + RefVal Val) { + assert(Sym != nullptr); + return State->set<RefBindings>(Sym, Val); +} + +ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) { + return State->remove<RefBindings>(Sym); +} + +class UseAfterRelease : public RefCountBug { +public: + UseAfterRelease(const CheckerBase *checker) + : RefCountBug(checker, "Use-after-release") {} + + const char *getDescription() const override { + return "Reference-counted object is used after it is released"; + } +}; + +class BadRelease : public RefCountBug { +public: + BadRelease(const CheckerBase *checker) : RefCountBug(checker, "Bad release") {} + + const char *getDescription() const override { + return "Incorrect decrement of the reference count of an object that is " + "not owned at this point by the caller"; + } +}; + +class DeallocNotOwned : public RefCountBug { +public: + DeallocNotOwned(const CheckerBase *checker) + : RefCountBug(checker, "-dealloc sent to non-exclusively owned object") {} + + const char *getDescription() const override { + return "-dealloc sent to object that may be referenced elsewhere"; + } +}; + +class OverAutorelease : public RefCountBug { +public: + OverAutorelease(const CheckerBase *checker) + : RefCountBug(checker, "Object autoreleased too many times") {} + + const char *getDescription() const override { + return "Object autoreleased too many times"; + } +}; + +class ReturnedNotOwnedForOwned : public RefCountBug { +public: + ReturnedNotOwnedForOwned(const CheckerBase *checker) + : RefCountBug(checker, "Method should return an owned object") {} + + const char *getDescription() const override { + return "Object with a +0 retain count returned to caller where a +1 " + "(owning) retain count is expected"; + } +}; + +class Leak : public RefCountBug { +public: + Leak(const CheckerBase *checker, StringRef name) : RefCountBug(checker, name) { + // Leaks should not be reported if they are post-dominated by a sink. + setSuppressOnSink(true); + } + + const char *getDescription() const override { return ""; } + + bool isLeak() const override { return true; } +}; + +} // end namespace retaincountchecker +} // end namespace ento +} // end namespace clang + +void RefVal::print(raw_ostream &Out) const { + if (!T.isNull()) + Out << "Tracked " << T.getAsString() << " | "; + + switch (getKind()) { + default: llvm_unreachable("Invalid RefVal kind"); + case Owned: { + Out << "Owned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case NotOwned: { + Out << "NotOwned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case ReturnedOwned: { + Out << "ReturnedOwned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case ReturnedNotOwned: { + Out << "ReturnedNotOwned"; + unsigned cnt = getCount(); + if (cnt) Out << " (+ " << cnt << ")"; + break; + } + + case Released: + Out << "Released"; + break; + + case ErrorDeallocNotOwned: + Out << "-dealloc (not-owned)"; + break; + + case ErrorLeak: + Out << "Leaked"; + break; + + case ErrorLeakReturned: + Out << "Leaked (Bad naming)"; + break; + + case ErrorUseAfterRelease: + Out << "Use-After-Release [ERROR]"; + break; + + case ErrorReleaseNotOwned: + Out << "Release of Not-Owned [ERROR]"; + break; + + case RefVal::ErrorOverAutorelease: + Out << "Over-autoreleased"; + break; + + case RefVal::ErrorReturnedNotOwned: + Out << "Non-owned object returned instead of owned"; + break; + } + + switch (getIvarAccessHistory()) { + case IvarAccessHistory::None: + break; + case IvarAccessHistory::AccessedDirectly: + Out << " [direct ivar access]"; + break; + case IvarAccessHistory::ReleasedAfterDirectAccess: + Out << " [released after direct ivar access]"; + } + + if (ACnt) { + Out << " [autorelease -" << ACnt << ']'; + } +} + +namespace { +class StopTrackingCallback final : public SymbolVisitor { + ProgramStateRef state; +public: + StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {} + ProgramStateRef getState() const { return state; } + + bool VisitSymbol(SymbolRef sym) override { + state = state->remove<RefBindings>(sym); + return true; + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Handle statements that may have an effect on refcounts. +//===----------------------------------------------------------------------===// + +void RetainCountChecker::checkPostStmt(const BlockExpr *BE, + CheckerContext &C) const { + + // Scan the BlockDecRefExprs for any object the retain count checker + // may be tracking. + if (!BE->getBlockDecl()->hasCaptures()) + return; + + ProgramStateRef state = C.getState(); + auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion()); + + BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), + E = R->referenced_vars_end(); + + if (I == E) + return; + + // FIXME: For now we invalidate the tracking of all symbols passed to blocks + // via captured variables, even though captured variables result in a copy + // and in implicit increment/decrement of a retain count. + SmallVector<const MemRegion*, 10> Regions; + const LocationContext *LC = C.getLocationContext(); + MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); + + for ( ; I != E; ++I) { + const VarRegion *VR = I.getCapturedRegion(); + if (VR->getSuperRegion() == R) { + VR = MemMgr.getVarRegion(VR->getDecl(), LC); + } + Regions.push_back(VR); + } + + state = state->scanReachableSymbols<StopTrackingCallback>(Regions).getState(); + C.addTransition(state); +} + +void RetainCountChecker::checkPostStmt(const CastExpr *CE, + CheckerContext &C) const { + const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE); + if (!BE) + return; + + ArgEffect AE = ArgEffect(IncRef, ObjKind::ObjC); + + switch (BE->getBridgeKind()) { + case OBC_Bridge: + // Do nothing. + return; + case OBC_BridgeRetained: + AE = AE.withKind(IncRef); + break; + case OBC_BridgeTransfer: + AE = AE.withKind(DecRefBridgedTransferred); + break; + } + + ProgramStateRef state = C.getState(); + SymbolRef Sym = C.getSVal(CE).getAsLocSymbol(); + if (!Sym) + return; + const RefVal* T = getRefBinding(state, Sym); + if (!T) + return; + + RefVal::Kind hasErr = (RefVal::Kind) 0; + state = updateSymbol(state, Sym, *T, AE, hasErr, C); + + if (hasErr) { + // FIXME: If we get an error during a bridge cast, should we report it? + return; + } + + C.addTransition(state); +} + +void RetainCountChecker::processObjCLiterals(CheckerContext &C, + const Expr *Ex) const { + ProgramStateRef state = C.getState(); + const ExplodedNode *pred = C.getPredecessor(); + for (const Stmt *Child : Ex->children()) { + SVal V = pred->getSVal(Child); + if (SymbolRef sym = V.getAsSymbol()) + if (const RefVal* T = getRefBinding(state, sym)) { + RefVal::Kind hasErr = (RefVal::Kind) 0; + state = updateSymbol(state, sym, *T, + ArgEffect(MayEscape, ObjKind::ObjC), hasErr, C); + if (hasErr) { + processNonLeakError(state, Child->getSourceRange(), hasErr, sym, C); + return; + } + } + } + + // Return the object as autoreleased. + // RetEffect RE = RetEffect::MakeNotOwned(ObjKind::ObjC); + if (SymbolRef sym = + state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) { + QualType ResultTy = Ex->getType(); + state = setRefBinding(state, sym, + RefVal::makeNotOwned(ObjKind::ObjC, ResultTy)); + } + + C.addTransition(state); +} + +void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL, + CheckerContext &C) const { + // Apply the 'MayEscape' to all values. + processObjCLiterals(C, AL); +} + +void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL, + CheckerContext &C) const { + // Apply the 'MayEscape' to all keys and values. + processObjCLiterals(C, DL); +} + +void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex, + CheckerContext &C) const { + const ExplodedNode *Pred = C.getPredecessor(); + ProgramStateRef State = Pred->getState(); + + if (SymbolRef Sym = Pred->getSVal(Ex).getAsSymbol()) { + QualType ResultTy = Ex->getType(); + State = setRefBinding(State, Sym, + RefVal::makeNotOwned(ObjKind::ObjC, ResultTy)); + } + + C.addTransition(State); +} + +void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE, + CheckerContext &C) const { + Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>(); + if (!IVarLoc) + return; + + ProgramStateRef State = C.getState(); + SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol(); + if (!Sym || !dyn_cast_or_null<ObjCIvarRegion>(Sym->getOriginRegion())) + return; + + // Accessing an ivar directly is unusual. If we've done that, be more + // forgiving about what the surrounding code is allowed to do. + + QualType Ty = Sym->getType(); + ObjKind Kind; + if (Ty->isObjCRetainableType()) + Kind = ObjKind::ObjC; + else if (coreFoundation::isCFObjectRef(Ty)) + Kind = ObjKind::CF; + else + return; + + // If the value is already known to be nil, don't bother tracking it. + ConstraintManager &CMgr = State->getConstraintManager(); + if (CMgr.isNull(State, Sym).isConstrainedTrue()) + return; + + if (const RefVal *RV = getRefBinding(State, Sym)) { + // If we've seen this symbol before, or we're only seeing it now because + // of something the analyzer has synthesized, don't do anything. + if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None || + isSynthesizedAccessor(C.getStackFrame())) { + return; + } + + // Note that this value has been loaded from an ivar. + C.addTransition(setRefBinding(State, Sym, RV->withIvarAccess())); + return; + } + + RefVal PlusZero = RefVal::makeNotOwned(Kind, Ty); + + // In a synthesized accessor, the effective retain count is +0. + if (isSynthesizedAccessor(C.getStackFrame())) { + C.addTransition(setRefBinding(State, Sym, PlusZero)); + return; + } + + State = setRefBinding(State, Sym, PlusZero.withIvarAccess()); + C.addTransition(State); +} + +void RetainCountChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + RetainSummaryManager &Summaries = getSummaryManager(C); + + // Leave null if no receiver. + QualType ReceiverType; + if (const auto *MC = dyn_cast<ObjCMethodCall>(&Call)) { + if (MC->isInstanceMessage()) { + SVal ReceiverV = MC->getReceiverSVal(); + if (SymbolRef Sym = ReceiverV.getAsLocSymbol()) + if (const RefVal *T = getRefBinding(C.getState(), Sym)) + ReceiverType = T->getType(); + } + } + + const RetainSummary *Summ = Summaries.getSummary(Call, ReceiverType); + + if (C.wasInlined) { + processSummaryOfInlined(*Summ, Call, C); + return; + } + checkSummary(*Summ, Call, C); +} + +RefCountBug * +RetainCountChecker::getLeakWithinFunctionBug(const LangOptions &LOpts) const { + if (!leakWithinFunction) + leakWithinFunction.reset(new Leak(this, "Leak")); + return leakWithinFunction.get(); +} + +RefCountBug * +RetainCountChecker::getLeakAtReturnBug(const LangOptions &LOpts) const { + if (!leakAtReturn) + leakAtReturn.reset(new Leak(this, "Leak of returned object")); + return leakAtReturn.get(); +} + +/// GetReturnType - Used to get the return type of a message expression or +/// function call with the intention of affixing that type to a tracked symbol. +/// While the return type can be queried directly from RetEx, when +/// invoking class methods we augment to the return type to be that of +/// a pointer to the class (as opposed it just being id). +// FIXME: We may be able to do this with related result types instead. +// This function is probably overestimating. +static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) { + QualType RetTy = RetE->getType(); + // If RetE is not a message expression just return its type. + // If RetE is a message expression, return its types if it is something + /// more specific than id. + if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(RetE)) + if (const ObjCObjectPointerType *PT = RetTy->getAs<ObjCObjectPointerType>()) + if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() || + PT->isObjCClassType()) { + // At this point we know the return type of the message expression is + // id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this + // is a call to a class method whose type we can resolve. In such + // cases, promote the return type to XXX* (where XXX is the class). + const ObjCInterfaceDecl *D = ME->getReceiverInterface(); + return !D ? RetTy : + Ctx.getObjCObjectPointerType(Ctx.getObjCInterfaceType(D)); + } + + return RetTy; +} + +static Optional<RefVal> refValFromRetEffect(RetEffect RE, + QualType ResultTy) { + if (RE.isOwned()) { + return RefVal::makeOwned(RE.getObjKind(), ResultTy); + } else if (RE.notOwned()) { + return RefVal::makeNotOwned(RE.getObjKind(), ResultTy); + } + + return None; +} + +static bool isPointerToObject(QualType QT) { + QualType PT = QT->getPointeeType(); + if (!PT.isNull()) + if (PT->getAsCXXRecordDecl()) + return true; + return false; +} + +/// Whether the tracked value should be escaped on a given call. +/// OSObjects are escaped when passed to void * / etc. +static bool shouldEscapeOSArgumentOnCall(const CallEvent &CE, unsigned ArgIdx, + const RefVal *TrackedValue) { + if (TrackedValue->getObjKind() != ObjKind::OS) + return false; + if (ArgIdx >= CE.parameters().size()) + return false; + return !isPointerToObject(CE.parameters()[ArgIdx]->getType()); +} + +// We don't always get the exact modeling of the function with regards to the +// retain count checker even when the function is inlined. For example, we need +// to stop tracking the symbols which were marked with StopTrackingHard. +void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ, + const CallEvent &CallOrMsg, + CheckerContext &C) const { + ProgramStateRef state = C.getState(); + + // Evaluate the effect of the arguments. + for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) { + SVal V = CallOrMsg.getArgSVal(idx); + + if (SymbolRef Sym = V.getAsLocSymbol()) { + bool ShouldRemoveBinding = Summ.getArg(idx).getKind() == StopTrackingHard; + if (const RefVal *T = getRefBinding(state, Sym)) + if (shouldEscapeOSArgumentOnCall(CallOrMsg, idx, T)) + ShouldRemoveBinding = true; + + if (ShouldRemoveBinding) + state = removeRefBinding(state, Sym); + } + } + + // Evaluate the effect on the message receiver. + if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg)) { + if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) { + if (Summ.getReceiverEffect().getKind() == StopTrackingHard) { + state = removeRefBinding(state, Sym); + } + } + } + + // Consult the summary for the return value. + RetEffect RE = Summ.getRetEffect(); + + if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) { + if (RE.getKind() == RetEffect::NoRetHard) + state = removeRefBinding(state, Sym); + } + + C.addTransition(state); +} + +static bool shouldEscapeRegion(const MemRegion *R) { + + // We do not currently model what happens when a symbol is + // assigned to a struct field, so be conservative here and let the symbol + // go. TODO: This could definitely be improved upon. + return !R->hasStackStorage() || !isa<VarRegion>(R); +} + +static SmallVector<ProgramStateRef, 2> +updateOutParameters(ProgramStateRef State, const RetainSummary &Summ, + const CallEvent &CE) { + + SVal L = CE.getReturnValue(); + + // Splitting is required to support out parameters, + // as out parameters might be created only on the "success" branch. + // We want to avoid eagerly splitting unless out parameters are actually + // needed. + bool SplitNecessary = false; + for (auto &P : Summ.getArgEffects()) + if (P.second.getKind() == RetainedOutParameterOnNonZero || + P.second.getKind() == RetainedOutParameterOnZero) + SplitNecessary = true; + + ProgramStateRef AssumeNonZeroReturn = State; + ProgramStateRef AssumeZeroReturn = State; + + if (SplitNecessary) { + if (auto DL = L.getAs<DefinedOrUnknownSVal>()) { + AssumeNonZeroReturn = AssumeNonZeroReturn->assume(*DL, true); + AssumeZeroReturn = AssumeZeroReturn->assume(*DL, false); + } + } + + for (unsigned idx = 0, e = CE.getNumArgs(); idx != e; ++idx) { + SVal ArgVal = CE.getArgSVal(idx); + ArgEffect AE = Summ.getArg(idx); + + auto *ArgRegion = dyn_cast_or_null<TypedValueRegion>(ArgVal.getAsRegion()); + if (!ArgRegion) + continue; + + QualType PointeeTy = ArgRegion->getValueType(); + SVal PointeeVal = State->getSVal(ArgRegion); + SymbolRef Pointee = PointeeVal.getAsLocSymbol(); + if (!Pointee) + continue; + + if (shouldEscapeRegion(ArgRegion)) + continue; + + auto makeNotOwnedParameter = [&](ProgramStateRef St) { + return setRefBinding(St, Pointee, + RefVal::makeNotOwned(AE.getObjKind(), PointeeTy)); + }; + auto makeOwnedParameter = [&](ProgramStateRef St) { + return setRefBinding(St, Pointee, + RefVal::makeOwned(ObjKind::OS, PointeeTy)); + }; + + switch (AE.getKind()) { + case UnretainedOutParameter: + AssumeNonZeroReturn = makeNotOwnedParameter(AssumeNonZeroReturn); + AssumeZeroReturn = makeNotOwnedParameter(AssumeZeroReturn); + break; + case RetainedOutParameter: + AssumeNonZeroReturn = makeOwnedParameter(AssumeNonZeroReturn); + AssumeZeroReturn = makeOwnedParameter(AssumeZeroReturn); + break; + case RetainedOutParameterOnNonZero: + AssumeNonZeroReturn = makeOwnedParameter(AssumeNonZeroReturn); + break; + case RetainedOutParameterOnZero: + AssumeZeroReturn = makeOwnedParameter(AssumeZeroReturn); + break; + default: + break; + } + } + + if (SplitNecessary) { + return {AssumeNonZeroReturn, AssumeZeroReturn}; + } else { + assert(AssumeZeroReturn == AssumeNonZeroReturn); + return {AssumeZeroReturn}; + } +} + +void RetainCountChecker::checkSummary(const RetainSummary &Summ, + const CallEvent &CallOrMsg, + CheckerContext &C) const { + ProgramStateRef state = C.getState(); + + // Evaluate the effect of the arguments. + RefVal::Kind hasErr = (RefVal::Kind) 0; + SourceRange ErrorRange; + SymbolRef ErrorSym = nullptr; + + // Helper tag for providing diagnostics: indicate whether dealloc was sent + // at this location. + static CheckerProgramPointTag DeallocSentTag(this, DeallocTagDescription); + bool DeallocSent = false; + + for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) { + SVal V = CallOrMsg.getArgSVal(idx); + + ArgEffect Effect = Summ.getArg(idx); + if (SymbolRef Sym = V.getAsLocSymbol()) { + if (const RefVal *T = getRefBinding(state, Sym)) { + + if (shouldEscapeOSArgumentOnCall(CallOrMsg, idx, T)) + Effect = ArgEffect(StopTrackingHard, ObjKind::OS); + + state = updateSymbol(state, Sym, *T, Effect, hasErr, C); + if (hasErr) { + ErrorRange = CallOrMsg.getArgSourceRange(idx); + ErrorSym = Sym; + break; + } else if (Effect.getKind() == Dealloc) { + DeallocSent = true; + } + } + } + } + + // Evaluate the effect on the message receiver / `this` argument. + bool ReceiverIsTracked = false; + if (!hasErr) { + if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg)) { + if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) { + if (const RefVal *T = getRefBinding(state, Sym)) { + ReceiverIsTracked = true; + state = updateSymbol(state, Sym, *T, + Summ.getReceiverEffect(), hasErr, C); + if (hasErr) { + ErrorRange = MsgInvocation->getOriginExpr()->getReceiverRange(); + ErrorSym = Sym; + } else if (Summ.getReceiverEffect().getKind() == Dealloc) { + DeallocSent = true; + } + } + } + } else if (const auto *MCall = dyn_cast<CXXMemberCall>(&CallOrMsg)) { + if (SymbolRef Sym = MCall->getCXXThisVal().getAsLocSymbol()) { + if (const RefVal *T = getRefBinding(state, Sym)) { + state = updateSymbol(state, Sym, *T, Summ.getThisEffect(), + hasErr, C); + if (hasErr) { + ErrorRange = MCall->getOriginExpr()->getSourceRange(); + ErrorSym = Sym; + } + } + } + } + } + + // Process any errors. + if (hasErr) { + processNonLeakError(state, ErrorRange, hasErr, ErrorSym, C); + return; + } + + // Consult the summary for the return value. + RetEffect RE = Summ.getRetEffect(); + + if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) { + if (ReceiverIsTracked) + RE = getSummaryManager(C).getObjAllocRetEffect(); + else + RE = RetEffect::MakeNoRet(); + } + + if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) { + QualType ResultTy = CallOrMsg.getResultType(); + if (RE.notOwned()) { + const Expr *Ex = CallOrMsg.getOriginExpr(); + assert(Ex); + ResultTy = GetReturnType(Ex, C.getASTContext()); + } + if (Optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy)) + state = setRefBinding(state, Sym, *updatedRefVal); + } + + SmallVector<ProgramStateRef, 2> Out = + updateOutParameters(state, Summ, CallOrMsg); + + for (ProgramStateRef St : Out) { + if (DeallocSent) { + C.addTransition(St, C.getPredecessor(), &DeallocSentTag); + } else { + C.addTransition(St); + } + } +} + +ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state, + SymbolRef sym, RefVal V, + ArgEffect AE, + RefVal::Kind &hasErr, + CheckerContext &C) const { + bool IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount; + if (AE.getObjKind() == ObjKind::ObjC && IgnoreRetainMsg) { + switch (AE.getKind()) { + default: + break; + case IncRef: + AE = AE.withKind(DoNothing); + break; + case DecRef: + AE = AE.withKind(DoNothing); + break; + case DecRefAndStopTrackingHard: + AE = AE.withKind(StopTracking); + break; + } + } + + // Handle all use-after-releases. + if (V.getKind() == RefVal::Released) { + V = V ^ RefVal::ErrorUseAfterRelease; + hasErr = V.getKind(); + return setRefBinding(state, sym, V); + } + + switch (AE.getKind()) { + case UnretainedOutParameter: + case RetainedOutParameter: + case RetainedOutParameterOnZero: + case RetainedOutParameterOnNonZero: + llvm_unreachable("Applies to pointer-to-pointer parameters, which should " + "not have ref state."); + + case Dealloc: // NB. we only need to add a note in a non-error case. + switch (V.getKind()) { + default: + llvm_unreachable("Invalid RefVal state for an explicit dealloc."); + case RefVal::Owned: + // The object immediately transitions to the released state. + V = V ^ RefVal::Released; + V.clearCounts(); + return setRefBinding(state, sym, V); + case RefVal::NotOwned: + V = V ^ RefVal::ErrorDeallocNotOwned; + hasErr = V.getKind(); + break; + } + break; + + case MayEscape: + if (V.getKind() == RefVal::Owned) { + V = V ^ RefVal::NotOwned; + break; + } + + LLVM_FALLTHROUGH; + + case DoNothing: + return state; + + case Autorelease: + // Update the autorelease counts. + V = V.autorelease(); + break; + + case StopTracking: + case StopTrackingHard: + return removeRefBinding(state, sym); + + case IncRef: + switch (V.getKind()) { + default: + llvm_unreachable("Invalid RefVal state for a retain."); + case RefVal::Owned: + case RefVal::NotOwned: + V = V + 1; + break; + } + break; + + case DecRef: + case DecRefBridgedTransferred: + case DecRefAndStopTrackingHard: + switch (V.getKind()) { + default: + // case 'RefVal::Released' handled above. + llvm_unreachable("Invalid RefVal state for a release."); + + case RefVal::Owned: + assert(V.getCount() > 0); + if (V.getCount() == 1) { + if (AE.getKind() == DecRefBridgedTransferred || + V.getIvarAccessHistory() == + RefVal::IvarAccessHistory::AccessedDirectly) + V = V ^ RefVal::NotOwned; + else + V = V ^ RefVal::Released; + } else if (AE.getKind() == DecRefAndStopTrackingHard) { + return removeRefBinding(state, sym); + } + + V = V - 1; + break; + + case RefVal::NotOwned: + if (V.getCount() > 0) { + if (AE.getKind() == DecRefAndStopTrackingHard) + return removeRefBinding(state, sym); + V = V - 1; + } else if (V.getIvarAccessHistory() == + RefVal::IvarAccessHistory::AccessedDirectly) { + // Assume that the instance variable was holding on the object at + // +1, and we just didn't know. + if (AE.getKind() == DecRefAndStopTrackingHard) + return removeRefBinding(state, sym); + V = V.releaseViaIvar() ^ RefVal::Released; + } else { + V = V ^ RefVal::ErrorReleaseNotOwned; + hasErr = V.getKind(); + } + break; + } + break; + } + return setRefBinding(state, sym, V); +} + +void RetainCountChecker::processNonLeakError(ProgramStateRef St, + SourceRange ErrorRange, + RefVal::Kind ErrorKind, + SymbolRef Sym, + CheckerContext &C) const { + // HACK: Ignore retain-count issues on values accessed through ivars, + // because of cases like this: + // [_contentView retain]; + // [_contentView removeFromSuperview]; + // [self addSubview:_contentView]; // invalidates 'self' + // [_contentView release]; + if (const RefVal *RV = getRefBinding(St, Sym)) + if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None) + return; + + ExplodedNode *N = C.generateErrorNode(St); + if (!N) + return; + + RefCountBug *BT; + switch (ErrorKind) { + default: + llvm_unreachable("Unhandled error."); + case RefVal::ErrorUseAfterRelease: + if (!useAfterRelease) + useAfterRelease.reset(new UseAfterRelease(this)); + BT = useAfterRelease.get(); + break; + case RefVal::ErrorReleaseNotOwned: + if (!releaseNotOwned) + releaseNotOwned.reset(new BadRelease(this)); + BT = releaseNotOwned.get(); + break; + case RefVal::ErrorDeallocNotOwned: + if (!deallocNotOwned) + deallocNotOwned.reset(new DeallocNotOwned(this)); + BT = deallocNotOwned.get(); + break; + } + + assert(BT); + auto report = llvm::make_unique<RefCountReport>( + *BT, C.getASTContext().getLangOpts(), N, Sym); + report->addRange(ErrorRange); + C.emitReport(std::move(report)); +} + +//===----------------------------------------------------------------------===// +// Handle the return values of retain-count-related functions. +//===----------------------------------------------------------------------===// + +bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { + // Get the callee. We're only interested in simple C functions. + ProgramStateRef state = C.getState(); + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD) + return false; + + RetainSummaryManager &SmrMgr = getSummaryManager(C); + QualType ResultTy = CE->getCallReturnType(C.getASTContext()); + + // See if the function has 'rc_ownership_trusted_implementation' + // annotate attribute. If it does, we will not inline it. + bool hasTrustedImplementationAnnotation = false; + + const LocationContext *LCtx = C.getLocationContext(); + + using BehaviorSummary = RetainSummaryManager::BehaviorSummary; + Optional<BehaviorSummary> BSmr = + SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation); + + // See if it's one of the specific functions we know how to eval. + if (!BSmr) + return false; + + // Bind the return value. + if (BSmr == BehaviorSummary::Identity || + BSmr == BehaviorSummary::IdentityOrZero) { + SVal RetVal = state->getSVal(CE->getArg(0), LCtx); + + // If the receiver is unknown or the function has + // 'rc_ownership_trusted_implementation' annotate attribute, conjure a + // return value. + if (RetVal.isUnknown() || + (hasTrustedImplementationAnnotation && !ResultTy.isNull())) { + SValBuilder &SVB = C.getSValBuilder(); + RetVal = + SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount()); + } + state = state->BindExpr(CE, LCtx, RetVal, /*Invalidate=*/false); + + if (BSmr == BehaviorSummary::IdentityOrZero) { + // Add a branch where the output is zero. + ProgramStateRef NullOutputState = C.getState(); + + // Assume that output is zero on the other branch. + NullOutputState = NullOutputState->BindExpr( + CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false); + + C.addTransition(NullOutputState); + + // And on the original branch assume that both input and + // output are non-zero. + if (auto L = RetVal.getAs<DefinedOrUnknownSVal>()) + state = state->assume(*L, /*Assumption=*/true); + + } + } + + C.addTransition(state); + return true; +} + +ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S, + CheckerContext &C) const { + ExplodedNode *Pred = C.getPredecessor(); + + // Only adjust the reference count if this is the top-level call frame, + // and not the result of inlining. In the future, we should do + // better checking even for inlined calls, and see if they match + // with their expected semantics (e.g., the method should return a retained + // object, etc.). + if (!C.inTopFrame()) + return Pred; + + if (!S) + return Pred; + + const Expr *RetE = S->getRetValue(); + if (!RetE) + return Pred; + + ProgramStateRef state = C.getState(); + SymbolRef Sym = + state->getSValAsScalarOrLoc(RetE, C.getLocationContext()).getAsLocSymbol(); + if (!Sym) + return Pred; + + // Get the reference count binding (if any). + const RefVal *T = getRefBinding(state, Sym); + if (!T) + return Pred; + + // Change the reference count. + RefVal X = *T; + + switch (X.getKind()) { + case RefVal::Owned: { + unsigned cnt = X.getCount(); + assert(cnt > 0); + X.setCount(cnt - 1); + X = X ^ RefVal::ReturnedOwned; + break; + } + + case RefVal::NotOwned: { + unsigned cnt = X.getCount(); + if (cnt) { + X.setCount(cnt - 1); + X = X ^ RefVal::ReturnedOwned; + } else { + X = X ^ RefVal::ReturnedNotOwned; + } + break; + } + + default: + return Pred; + } + + // Update the binding. + state = setRefBinding(state, Sym, X); + Pred = C.addTransition(state); + + // At this point we have updated the state properly. + // Everything after this is merely checking to see if the return value has + // been over- or under-retained. + + // Did we cache out? + if (!Pred) + return nullptr; + + // Update the autorelease counts. + static CheckerProgramPointTag AutoreleaseTag(this, "Autorelease"); + state = handleAutoreleaseCounts(state, Pred, &AutoreleaseTag, C, Sym, X, S); + + // Have we generated a sink node? + if (!state) + return nullptr; + + // Get the updated binding. + T = getRefBinding(state, Sym); + assert(T); + X = *T; + + // Consult the summary of the enclosing method. + RetainSummaryManager &Summaries = getSummaryManager(C); + const Decl *CD = &Pred->getCodeDecl(); + RetEffect RE = RetEffect::MakeNoRet(); + + // FIXME: What is the convention for blocks? Is there one? + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) { + const RetainSummary *Summ = Summaries.getMethodSummary(MD); + RE = Summ->getRetEffect(); + } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) { + if (!isa<CXXMethodDecl>(FD)) { + const RetainSummary *Summ = Summaries.getFunctionSummary(FD); + RE = Summ->getRetEffect(); + } + } + + return checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state); +} + +ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S, + CheckerContext &C, + ExplodedNode *Pred, + RetEffect RE, RefVal X, + SymbolRef Sym, + ProgramStateRef state) const { + // HACK: Ignore retain-count issues on values accessed through ivars, + // because of cases like this: + // [_contentView retain]; + // [_contentView removeFromSuperview]; + // [self addSubview:_contentView]; // invalidates 'self' + // [_contentView release]; + if (X.getIvarAccessHistory() != RefVal::IvarAccessHistory::None) + return Pred; + + // Any leaks or other errors? + if (X.isReturnedOwned() && X.getCount() == 0) { + if (RE.getKind() != RetEffect::NoRet) { + if (!RE.isOwned()) { + + // The returning type is a CF, we expect the enclosing method should + // return ownership. + X = X ^ RefVal::ErrorLeakReturned; + + // Generate an error node. + state = setRefBinding(state, Sym, X); + + static CheckerProgramPointTag ReturnOwnLeakTag(this, "ReturnsOwnLeak"); + ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag); + if (N) { + const LangOptions &LOpts = C.getASTContext().getLangOpts(); + auto R = llvm::make_unique<RefLeakReport>( + *getLeakAtReturnBug(LOpts), LOpts, N, Sym, C); + C.emitReport(std::move(R)); + } + return N; + } + } + } else if (X.isReturnedNotOwned()) { + if (RE.isOwned()) { + if (X.getIvarAccessHistory() == + RefVal::IvarAccessHistory::AccessedDirectly) { + // Assume the method was trying to transfer a +1 reference from a + // strong ivar to the caller. + state = setRefBinding(state, Sym, + X.releaseViaIvar() ^ RefVal::ReturnedOwned); + } else { + // Trying to return a not owned object to a caller expecting an + // owned object. + state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned); + + static CheckerProgramPointTag + ReturnNotOwnedTag(this, "ReturnNotOwnedForOwned"); + + ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag); + if (N) { + if (!returnNotOwnedForOwned) + returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this)); + + auto R = llvm::make_unique<RefCountReport>( + *returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym); + C.emitReport(std::move(R)); + } + return N; + } + } + } + return Pred; +} + +//===----------------------------------------------------------------------===// +// Check various ways a symbol can be invalidated. +//===----------------------------------------------------------------------===// + +void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S, + CheckerContext &C) const { + // Are we storing to something that causes the value to "escape"? + bool escapes = true; + + // A value escapes in three possible cases (this may change): + // + // (1) we are binding to something that is not a memory region. + // (2) we are binding to a memregion that does not have stack storage + ProgramStateRef state = C.getState(); + + if (auto regionLoc = loc.getAs<loc::MemRegionVal>()) { + escapes = shouldEscapeRegion(regionLoc->getRegion()); + } + + // If we are storing the value into an auto function scope variable annotated + // with (__attribute__((cleanup))), stop tracking the value to avoid leak + // false positives. + if (const auto *LVR = dyn_cast_or_null<VarRegion>(loc.getAsRegion())) { + const VarDecl *VD = LVR->getDecl(); + if (VD->hasAttr<CleanupAttr>()) { + escapes = true; + } + } + + // If our store can represent the binding and we aren't storing to something + // that doesn't have local storage then just return and have the simulation + // state continue as is. + if (!escapes) + return; + + // Otherwise, find all symbols referenced by 'val' that we are tracking + // and stop tracking them. + state = state->scanReachableSymbols<StopTrackingCallback>(val).getState(); + C.addTransition(state); +} + +ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state, + SVal Cond, + bool Assumption) const { + // FIXME: We may add to the interface of evalAssume the list of symbols + // whose assumptions have changed. For now we just iterate through the + // bindings and check if any of the tracked symbols are NULL. This isn't + // too bad since the number of symbols we will track in practice are + // probably small and evalAssume is only called at branches and a few + // other places. + RefBindingsTy B = state->get<RefBindings>(); + + if (B.isEmpty()) + return state; + + bool changed = false; + RefBindingsTy::Factory &RefBFactory = state->get_context<RefBindings>(); + + for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { + // Check if the symbol is null stop tracking the symbol. + ConstraintManager &CMgr = state->getConstraintManager(); + ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey()); + if (AllocFailed.isConstrainedTrue()) { + changed = true; + B = RefBFactory.remove(B, I.getKey()); + } + } + + if (changed) + state = state->set<RefBindings>(B); + + return state; +} + +ProgramStateRef +RetainCountChecker::checkRegionChanges(ProgramStateRef state, + const InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call) const { + if (!invalidated) + return state; + + llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols; + for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(), + E = ExplicitRegions.end(); I != E; ++I) { + if (const SymbolicRegion *SR = (*I)->StripCasts()->getAs<SymbolicRegion>()) + WhitelistedSymbols.insert(SR->getSymbol()); + } + + for (SymbolRef sym : + llvm::make_range(invalidated->begin(), invalidated->end())) { + if (WhitelistedSymbols.count(sym)) + continue; + // Remove any existing reference-count binding. + state = removeRefBinding(state, sym); + } + return state; +} + +ProgramStateRef +RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state, + ExplodedNode *Pred, + const ProgramPointTag *Tag, + CheckerContext &Ctx, + SymbolRef Sym, + RefVal V, + const ReturnStmt *S) const { + unsigned ACnt = V.getAutoreleaseCount(); + + // No autorelease counts? Nothing to be done. + if (!ACnt) + return state; + + unsigned Cnt = V.getCount(); + + // FIXME: Handle sending 'autorelease' to already released object. + + if (V.getKind() == RefVal::ReturnedOwned) + ++Cnt; + + // If we would over-release here, but we know the value came from an ivar, + // assume it was a strong ivar that's just been relinquished. + if (ACnt > Cnt && + V.getIvarAccessHistory() == RefVal::IvarAccessHistory::AccessedDirectly) { + V = V.releaseViaIvar(); + --ACnt; + } + + if (ACnt <= Cnt) { + if (ACnt == Cnt) { + V.clearCounts(); + if (V.getKind() == RefVal::ReturnedOwned) { + V = V ^ RefVal::ReturnedNotOwned; + } else { + V = V ^ RefVal::NotOwned; + } + } else { + V.setCount(V.getCount() - ACnt); + V.setAutoreleaseCount(0); + } + return setRefBinding(state, Sym, V); + } + + // HACK: Ignore retain-count issues on values accessed through ivars, + // because of cases like this: + // [_contentView retain]; + // [_contentView removeFromSuperview]; + // [self addSubview:_contentView]; // invalidates 'self' + // [_contentView release]; + if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None) + return state; + + // Woah! More autorelease counts then retain counts left. + // Emit hard error. + V = V ^ RefVal::ErrorOverAutorelease; + state = setRefBinding(state, Sym, V); + + ExplodedNode *N = Ctx.generateSink(state, Pred, Tag); + if (N) { + SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << "Object was autoreleased "; + if (V.getAutoreleaseCount() > 1) + os << V.getAutoreleaseCount() << " times but the object "; + else + os << "but "; + os << "has a +" << V.getCount() << " retain count"; + + if (!overAutorelease) + overAutorelease.reset(new OverAutorelease(this)); + + const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); + auto R = llvm::make_unique<RefCountReport>(*overAutorelease, LOpts, N, Sym, + os.str()); + Ctx.emitReport(std::move(R)); + } + + return nullptr; +} + +ProgramStateRef +RetainCountChecker::handleSymbolDeath(ProgramStateRef state, + SymbolRef sid, RefVal V, + SmallVectorImpl<SymbolRef> &Leaked) const { + bool hasLeak; + + // HACK: Ignore retain-count issues on values accessed through ivars, + // because of cases like this: + // [_contentView retain]; + // [_contentView removeFromSuperview]; + // [self addSubview:_contentView]; // invalidates 'self' + // [_contentView release]; + if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None) + hasLeak = false; + else if (V.isOwned()) + hasLeak = true; + else if (V.isNotOwned() || V.isReturnedOwned()) + hasLeak = (V.getCount() > 0); + else + hasLeak = false; + + if (!hasLeak) + return removeRefBinding(state, sid); + + Leaked.push_back(sid); + return setRefBinding(state, sid, V ^ RefVal::ErrorLeak); +} + +ExplodedNode * +RetainCountChecker::processLeaks(ProgramStateRef state, + SmallVectorImpl<SymbolRef> &Leaked, + CheckerContext &Ctx, + ExplodedNode *Pred) const { + // Generate an intermediate node representing the leak point. + ExplodedNode *N = Ctx.addTransition(state, Pred); + + if (N) { + for (SmallVectorImpl<SymbolRef>::iterator + I = Leaked.begin(), E = Leaked.end(); I != E; ++I) { + + const LangOptions &LOpts = Ctx.getASTContext().getLangOpts(); + RefCountBug *BT = Pred ? getLeakWithinFunctionBug(LOpts) + : getLeakAtReturnBug(LOpts); + assert(BT && "BugType not initialized."); + + Ctx.emitReport( + llvm::make_unique<RefLeakReport>(*BT, LOpts, N, *I, Ctx)); + } + } + + return N; +} + +static bool isISLObjectRef(QualType Ty) { + return StringRef(Ty.getAsString()).startswith("isl_"); +} + +void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const { + if (!Ctx.inTopFrame()) + return; + + RetainSummaryManager &SmrMgr = getSummaryManager(Ctx); + const LocationContext *LCtx = Ctx.getLocationContext(); + const FunctionDecl *FD = dyn_cast<FunctionDecl>(LCtx->getDecl()); + + if (!FD || SmrMgr.isTrustedReferenceCountImplementation(FD)) + return; + + ProgramStateRef state = Ctx.getState(); + const RetainSummary *FunctionSummary = SmrMgr.getFunctionSummary(FD); + ArgEffects CalleeSideArgEffects = FunctionSummary->getArgEffects(); + + for (unsigned idx = 0, e = FD->getNumParams(); idx != e; ++idx) { + const ParmVarDecl *Param = FD->getParamDecl(idx); + SymbolRef Sym = state->getSVal(state->getRegion(Param, LCtx)).getAsSymbol(); + + QualType Ty = Param->getType(); + const ArgEffect *AE = CalleeSideArgEffects.lookup(idx); + if (AE && AE->getKind() == DecRef && isISLObjectRef(Ty)) { + state = setRefBinding( + state, Sym, RefVal::makeOwned(ObjKind::Generalized, Ty)); + } else if (isISLObjectRef(Ty)) { + state = setRefBinding( + state, Sym, + RefVal::makeNotOwned(ObjKind::Generalized, Ty)); + } + } + + Ctx.addTransition(state); +} + +void RetainCountChecker::checkEndFunction(const ReturnStmt *RS, + CheckerContext &Ctx) const { + ExplodedNode *Pred = processReturn(RS, Ctx); + + // Created state cached out. + if (!Pred) { + return; + } + + ProgramStateRef state = Pred->getState(); + RefBindingsTy B = state->get<RefBindings>(); + + // Don't process anything within synthesized bodies. + const LocationContext *LCtx = Pred->getLocationContext(); + if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized()) { + assert(!LCtx->inTopFrame()); + return; + } + + for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { + state = handleAutoreleaseCounts(state, Pred, /*Tag=*/nullptr, Ctx, + I->first, I->second); + if (!state) + return; + } + + // If the current LocationContext has a parent, don't check for leaks. + // We will do that later. + // FIXME: we should instead check for imbalances of the retain/releases, + // and suggest annotations. + if (LCtx->getParent()) + return; + + B = state->get<RefBindings>(); + SmallVector<SymbolRef, 10> Leaked; + + for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) + state = handleSymbolDeath(state, I->first, I->second, Leaked); + + processLeaks(state, Leaked, Ctx, Pred); +} + +void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ExplodedNode *Pred = C.getPredecessor(); + + ProgramStateRef state = C.getState(); + RefBindingsTy B = state->get<RefBindings>(); + SmallVector<SymbolRef, 10> Leaked; + + // Update counts from autorelease pools + for (const auto &I: state->get<RefBindings>()) { + SymbolRef Sym = I.first; + if (SymReaper.isDead(Sym)) { + static CheckerProgramPointTag Tag(this, "DeadSymbolAutorelease"); + const RefVal &V = I.second; + state = handleAutoreleaseCounts(state, Pred, &Tag, C, Sym, V); + if (!state) + return; + + // Fetch the new reference count from the state, and use it to handle + // this symbol. + state = handleSymbolDeath(state, Sym, *getRefBinding(state, Sym), Leaked); + } + } + + if (Leaked.empty()) { + C.addTransition(state); + return; + } + + Pred = processLeaks(state, Leaked, C, Pred); + + // Did we cache out? + if (!Pred) + return; + + // Now generate a new node that nukes the old bindings. + // The only bindings left at this point are the leaked symbols. + RefBindingsTy::Factory &F = state->get_context<RefBindings>(); + B = state->get<RefBindings>(); + + for (SmallVectorImpl<SymbolRef>::iterator I = Leaked.begin(), + E = Leaked.end(); + I != E; ++I) + B = F.remove(B, *I); + + state = state->set<RefBindings>(B); + C.addTransition(state, Pred); +} + +void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const { + + RefBindingsTy B = State->get<RefBindings>(); + + if (B.isEmpty()) + return; + + Out << Sep << NL; + + for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) { + Out << I->first << " : "; + I->second.print(Out); + Out << NL; + } +} + +//===----------------------------------------------------------------------===// +// Checker registration. +//===----------------------------------------------------------------------===// + +void ento::registerRetainCountChecker(CheckerManager &Mgr) { + auto *Chk = Mgr.registerChecker<RetainCountChecker>(); + Chk->TrackObjCAndCFObjects = true; +} + +// FIXME: remove this, hack for backwards compatibility: +// it should be possible to enable the NS/CF retain count checker as +// osx.cocoa.RetainCount, and it should be possible to disable +// osx.OSObjectRetainCount using osx.cocoa.RetainCount:CheckOSObject=false. +static bool hasPrevCheckOSObjectOptionDisabled(AnalyzerOptions &Options) { + auto I = Options.Config.find("osx.cocoa.RetainCount:CheckOSObject"); + if (I != Options.Config.end()) + return I->getValue() == "false"; + return false; +} + +void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) { + auto *Chk = Mgr.registerChecker<RetainCountChecker>(); + if (!hasPrevCheckOSObjectOptionDisabled(Mgr.getAnalyzerOptions())) + Chk->TrackOSObjects = true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h new file mode 100644 index 000000000000..31e2d9ae4932 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h @@ -0,0 +1,393 @@ +//==--- RetainCountChecker.h - Checks for leaks and other issues -*- 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 the methods for RetainCountChecker, which implements +// a reference count checker for Core Foundation and Cocoa on (Mac OS X). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_H + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "RetainCountDiagnostics.h" +#include "clang/AST/Attr.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/ParentMap.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Analysis/SelectorExtras.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/Core/RetainSummaryManager.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include <cstdarg> +#include <utility> + +namespace clang { +namespace ento { +namespace retaincountchecker { + +/// Metadata on reference. +class RefVal { +public: + enum Kind { + Owned = 0, // Owning reference. + NotOwned, // Reference is not owned by still valid (not freed). + Released, // Object has been released. + ReturnedOwned, // Returned object passes ownership to caller. + ReturnedNotOwned, // Return object does not pass ownership to caller. + ERROR_START, + ErrorDeallocNotOwned, // -dealloc called on non-owned object. + ErrorUseAfterRelease, // Object used after released. + ErrorReleaseNotOwned, // Release of an object that was not owned. + ERROR_LEAK_START, + ErrorLeak, // A memory leak due to excessive reference counts. + ErrorLeakReturned, // A memory leak due to the returning method not having + // the correct naming conventions. + ErrorOverAutorelease, + ErrorReturnedNotOwned + }; + + /// Tracks how an object referenced by an ivar has been used. + /// + /// This accounts for us not knowing if an arbitrary ivar is supposed to be + /// stored at +0 or +1. + enum class IvarAccessHistory { + None, + AccessedDirectly, + ReleasedAfterDirectAccess + }; + +private: + /// The number of outstanding retains. + unsigned Cnt; + /// The number of outstanding autoreleases. + unsigned ACnt; + /// The (static) type of the object at the time we started tracking it. + QualType T; + + /// The current state of the object. + /// + /// See the RefVal::Kind enum for possible values. + unsigned RawKind : 5; + + /// The kind of object being tracked (CF or ObjC or OSObject), if known. + /// + /// See the ObjKind enum for possible values. + unsigned RawObjectKind : 3; + + /// True if the current state and/or retain count may turn out to not be the + /// best possible approximation of the reference counting state. + /// + /// If true, the checker may decide to throw away ("override") this state + /// in favor of something else when it sees the object being used in new ways. + /// + /// This setting should not be propagated to state derived from this state. + /// Once we start deriving new states, it would be inconsistent to override + /// them. + unsigned RawIvarAccessHistory : 2; + + RefVal(Kind k, ObjKind o, unsigned cnt, unsigned acnt, QualType t, + IvarAccessHistory IvarAccess) + : Cnt(cnt), ACnt(acnt), T(t), RawKind(static_cast<unsigned>(k)), + RawObjectKind(static_cast<unsigned>(o)), + RawIvarAccessHistory(static_cast<unsigned>(IvarAccess)) { + assert(getKind() == k && "not enough bits for the kind"); + assert(getObjKind() == o && "not enough bits for the object kind"); + assert(getIvarAccessHistory() == IvarAccess && "not enough bits"); + } + +public: + Kind getKind() const { return static_cast<Kind>(RawKind); } + + ObjKind getObjKind() const { + return static_cast<ObjKind>(RawObjectKind); + } + + unsigned getCount() const { return Cnt; } + unsigned getAutoreleaseCount() const { return ACnt; } + unsigned getCombinedCounts() const { return Cnt + ACnt; } + void clearCounts() { + Cnt = 0; + ACnt = 0; + } + void setCount(unsigned i) { + Cnt = i; + } + void setAutoreleaseCount(unsigned i) { + ACnt = i; + } + + QualType getType() const { return T; } + + /// Returns what the analyzer knows about direct accesses to a particular + /// instance variable. + /// + /// If the object with this refcount wasn't originally from an Objective-C + /// ivar region, this should always return IvarAccessHistory::None. + IvarAccessHistory getIvarAccessHistory() const { + return static_cast<IvarAccessHistory>(RawIvarAccessHistory); + } + + bool isOwned() const { + return getKind() == Owned; + } + + bool isNotOwned() const { + return getKind() == NotOwned; + } + + bool isReturnedOwned() const { + return getKind() == ReturnedOwned; + } + + bool isReturnedNotOwned() const { + return getKind() == ReturnedNotOwned; + } + + /// Create a state for an object whose lifetime is the responsibility of the + /// current function, at least partially. + /// + /// Most commonly, this is an owned object with a retain count of +1. + static RefVal makeOwned(ObjKind o, QualType t) { + return RefVal(Owned, o, /*Count=*/1, 0, t, IvarAccessHistory::None); + } + + /// Create a state for an object whose lifetime is not the responsibility of + /// the current function. + /// + /// Most commonly, this is an unowned object with a retain count of +0. + static RefVal makeNotOwned(ObjKind o, QualType t) { + return RefVal(NotOwned, o, /*Count=*/0, 0, t, IvarAccessHistory::None); + } + + RefVal operator-(size_t i) const { + return RefVal(getKind(), getObjKind(), getCount() - i, + getAutoreleaseCount(), getType(), getIvarAccessHistory()); + } + + RefVal operator+(size_t i) const { + return RefVal(getKind(), getObjKind(), getCount() + i, + getAutoreleaseCount(), getType(), getIvarAccessHistory()); + } + + RefVal operator^(Kind k) const { + return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(), + getType(), getIvarAccessHistory()); + } + + RefVal autorelease() const { + return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1, + getType(), getIvarAccessHistory()); + } + + RefVal withIvarAccess() const { + assert(getIvarAccessHistory() == IvarAccessHistory::None); + return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(), + getType(), IvarAccessHistory::AccessedDirectly); + } + + RefVal releaseViaIvar() const { + assert(getIvarAccessHistory() == IvarAccessHistory::AccessedDirectly); + return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(), + getType(), IvarAccessHistory::ReleasedAfterDirectAccess); + } + + // Comparison, profiling, and pretty-printing. + bool hasSameState(const RefVal &X) const { + return getKind() == X.getKind() && Cnt == X.Cnt && ACnt == X.ACnt && + getIvarAccessHistory() == X.getIvarAccessHistory(); + } + + bool operator==(const RefVal& X) const { + return T == X.T && hasSameState(X) && getObjKind() == X.getObjKind(); + } + + void Profile(llvm::FoldingSetNodeID& ID) const { + ID.Add(T); + ID.AddInteger(RawKind); + ID.AddInteger(Cnt); + ID.AddInteger(ACnt); + ID.AddInteger(RawObjectKind); + ID.AddInteger(RawIvarAccessHistory); + } + + void print(raw_ostream &Out) const; +}; + +class RetainCountChecker + : public Checker< check::Bind, + check::DeadSymbols, + check::BeginFunction, + check::EndFunction, + check::PostStmt<BlockExpr>, + check::PostStmt<CastExpr>, + check::PostStmt<ObjCArrayLiteral>, + check::PostStmt<ObjCDictionaryLiteral>, + check::PostStmt<ObjCBoxedExpr>, + check::PostStmt<ObjCIvarRefExpr>, + check::PostCall, + check::RegionChanges, + eval::Assume, + eval::Call > { + mutable std::unique_ptr<RefCountBug> useAfterRelease, releaseNotOwned; + mutable std::unique_ptr<RefCountBug> deallocNotOwned; + mutable std::unique_ptr<RefCountBug> overAutorelease, returnNotOwnedForOwned; + mutable std::unique_ptr<RefCountBug> leakWithinFunction, leakAtReturn; + + mutable std::unique_ptr<RetainSummaryManager> Summaries; +public: + static constexpr const char *DeallocTagDescription = "DeallocSent"; + + /// Track Objective-C and CoreFoundation objects. + bool TrackObjCAndCFObjects = false; + + /// Track sublcasses of OSObject. + bool TrackOSObjects = false; + + RetainCountChecker() {} + + RefCountBug *getLeakWithinFunctionBug(const LangOptions &LOpts) const; + + RefCountBug *getLeakAtReturnBug(const LangOptions &LOpts) const; + + RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const { + // FIXME: We don't support ARC being turned on and off during one analysis. + // (nor, for that matter, do we support changing ASTContexts) + bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount; + if (!Summaries) { + Summaries.reset(new RetainSummaryManager( + Ctx, ARCEnabled, TrackObjCAndCFObjects, TrackOSObjects)); + } else { + assert(Summaries->isARCEnabled() == ARCEnabled); + } + return *Summaries; + } + + RetainSummaryManager &getSummaryManager(CheckerContext &C) const { + return getSummaryManager(C.getASTContext()); + } + + void printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep) const override; + + void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const; + void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; + void checkPostStmt(const CastExpr *CE, CheckerContext &C) const; + + void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const; + void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const; + void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const; + + void checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const; + + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + + void checkSummary(const RetainSummary &Summ, const CallEvent &Call, + CheckerContext &C) const; + + void processSummaryOfInlined(const RetainSummary &Summ, + const CallEvent &Call, + CheckerContext &C) const; + + bool evalCall(const CallExpr *CE, CheckerContext &C) const; + + ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond, + bool Assumption) const; + + ProgramStateRef + checkRegionChanges(ProgramStateRef state, + const InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext* LCtx, + const CallEvent *Call) const; + + ExplodedNode* checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C, + ExplodedNode *Pred, RetEffect RE, RefVal X, + SymbolRef Sym, ProgramStateRef state) const; + + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + void checkBeginFunction(CheckerContext &C) const; + void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; + + ProgramStateRef updateSymbol(ProgramStateRef state, SymbolRef sym, + RefVal V, ArgEffect E, RefVal::Kind &hasErr, + CheckerContext &C) const; + + void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange, + RefVal::Kind ErrorKind, SymbolRef Sym, + CheckerContext &C) const; + + void processObjCLiterals(CheckerContext &C, const Expr *Ex) const; + + ProgramStateRef handleSymbolDeath(ProgramStateRef state, + SymbolRef sid, RefVal V, + SmallVectorImpl<SymbolRef> &Leaked) const; + + ProgramStateRef + handleAutoreleaseCounts(ProgramStateRef state, ExplodedNode *Pred, + const ProgramPointTag *Tag, CheckerContext &Ctx, + SymbolRef Sym, + RefVal V, + const ReturnStmt *S=nullptr) const; + + ExplodedNode *processLeaks(ProgramStateRef state, + SmallVectorImpl<SymbolRef> &Leaked, + CheckerContext &Ctx, + ExplodedNode *Pred = nullptr) const; + +private: + /// Perform the necessary checks and state adjustments at the end of the + /// function. + /// \p S Return statement, may be null. + ExplodedNode * processReturn(const ReturnStmt *S, CheckerContext &C) const; +}; + +//===----------------------------------------------------------------------===// +// RefBindings - State used to track object reference counts. +//===----------------------------------------------------------------------===// + +const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym); + +ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym, + RefVal Val); + +ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym); + +/// Returns true if this stack frame is for an Objective-C method that is a +/// property getter or setter whose body has been synthesized by the analyzer. +inline bool isSynthesizedAccessor(const StackFrameContext *SFC) { + auto Method = dyn_cast_or_null<ObjCMethodDecl>(SFC->getDecl()); + if (!Method || !Method->isPropertyAccessor()) + return false; + + return SFC->getAnalysisDeclContext()->isBodyAutosynthesized(); +} + +} // end namespace retaincountchecker +} // end namespace ento +} // end namespace clang + +#endif diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp new file mode 100644 index 000000000000..cda1a928de13 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp @@ -0,0 +1,794 @@ +// RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- 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 diagnostics for RetainCountChecker, which implements +// a reference count checker for Core Foundation and Cocoa on (Mac OS X). +// +//===----------------------------------------------------------------------===// + +#include "RetainCountDiagnostics.h" +#include "RetainCountChecker.h" + +using namespace clang; +using namespace ento; +using namespace retaincountchecker; + +static bool isNumericLiteralExpression(const Expr *E) { + // FIXME: This set of cases was copied from SemaExprObjC. + return isa<IntegerLiteral>(E) || + isa<CharacterLiteral>(E) || + isa<FloatingLiteral>(E) || + isa<ObjCBoolLiteralExpr>(E) || + isa<CXXBoolLiteralExpr>(E); +} + +/// If type represents a pointer to CXXRecordDecl, +/// and is not a typedef, return the decl name. +/// Otherwise, return the serialization of type. +static std::string getPrettyTypeName(QualType QT) { + QualType PT = QT->getPointeeType(); + if (!PT.isNull() && !QT->getAs<TypedefType>()) + if (const auto *RD = PT->getAsCXXRecordDecl()) + return RD->getName(); + return QT.getAsString(); +} + +/// Write information about the type state change to {@code os}, +/// return whether the note should be generated. +static bool shouldGenerateNote(llvm::raw_string_ostream &os, + const RefVal *PrevT, const RefVal &CurrV, + bool DeallocSent) { + // Get the previous type state. + RefVal PrevV = *PrevT; + + // Specially handle -dealloc. + if (DeallocSent) { + // Determine if the object's reference count was pushed to zero. + assert(!PrevV.hasSameState(CurrV) && "The state should have changed."); + // We may not have transitioned to 'release' if we hit an error. + // This case is handled elsewhere. + if (CurrV.getKind() == RefVal::Released) { + assert(CurrV.getCombinedCounts() == 0); + os << "Object released by directly sending the '-dealloc' message"; + return true; + } + } + + // Determine if the typestate has changed. + if (!PrevV.hasSameState(CurrV)) + switch (CurrV.getKind()) { + case RefVal::Owned: + case RefVal::NotOwned: + if (PrevV.getCount() == CurrV.getCount()) { + // Did an autorelease message get sent? + if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount()) + return false; + + assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); + os << "Object autoreleased"; + return true; + } + + if (PrevV.getCount() > CurrV.getCount()) + os << "Reference count decremented."; + else + os << "Reference count incremented."; + + if (unsigned Count = CurrV.getCount()) + os << " The object now has a +" << Count << " retain count."; + + return true; + + case RefVal::Released: + if (CurrV.getIvarAccessHistory() == + RefVal::IvarAccessHistory::ReleasedAfterDirectAccess && + CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) { + os << "Strong instance variable relinquished. "; + } + os << "Object released."; + return true; + + case RefVal::ReturnedOwned: + // Autoreleases can be applied after marking a node ReturnedOwned. + if (CurrV.getAutoreleaseCount()) + return false; + + os << "Object returned to caller as an owning reference (single " + "retain count transferred to caller)"; + return true; + + case RefVal::ReturnedNotOwned: + os << "Object returned to caller with a +0 retain count"; + return true; + + default: + return false; + } + return true; +} + +/// Finds argument index of the out paramter in the call {@code S} +/// corresponding to the symbol {@code Sym}. +/// If none found, returns None. +static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt, + const LocationContext *LCtx, + SymbolRef &Sym, + Optional<CallEventRef<>> CE) { + if (!CE) + return None; + + for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++) + if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion()) + if (const auto *TR = dyn_cast<TypedValueRegion>(MR)) + if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym) + return Idx; + + return None; +} + +static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt, + const LocationContext *LCtx, + const RefVal &CurrV, SymbolRef &Sym, + const Stmt *S, + llvm::raw_string_ostream &os) { + CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager(); + if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { + // Get the name of the callee (if it is available) + // from the tracked SVal. + SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx); + const FunctionDecl *FD = X.getAsFunctionDecl(); + + // If failed, try to get it from AST. + if (!FD) + FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl()); + + if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) { + os << "Call to method '" << MD->getQualifiedNameAsString() << '\''; + } else if (FD) { + os << "Call to function '" << FD->getQualifiedNameAsString() << '\''; + } else { + os << "function call"; + } + } else if (isa<CXXNewExpr>(S)) { + os << "Operator 'new'"; + } else { + assert(isa<ObjCMessageExpr>(S)); + CallEventRef<ObjCMethodCall> Call = + Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx); + + switch (Call->getMessageKind()) { + case OCM_Message: + os << "Method"; + break; + case OCM_PropertyAccess: + os << "Property"; + break; + case OCM_Subscript: + os << "Subscript"; + break; + } + } + + Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx); + auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE); + + // If index is not found, we assume that the symbol was returned. + if (!Idx) { + os << " returns "; + } else { + os << " writes "; + } + + if (CurrV.getObjKind() == ObjKind::CF) { + os << "a Core Foundation object of type '" + << Sym->getType().getAsString() << "' with a "; + } else if (CurrV.getObjKind() == ObjKind::OS) { + os << "an OSObject of type '" << getPrettyTypeName(Sym->getType()) + << "' with a "; + } else if (CurrV.getObjKind() == ObjKind::Generalized) { + os << "an object of type '" << Sym->getType().getAsString() + << "' with a "; + } else { + assert(CurrV.getObjKind() == ObjKind::ObjC); + QualType T = Sym->getType(); + if (!isa<ObjCObjectPointerType>(T)) { + os << "an Objective-C object with a "; + } else { + const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T); + os << "an instance of " << PT->getPointeeType().getAsString() + << " with a "; + } + } + + if (CurrV.isOwned()) { + os << "+1 retain count"; + } else { + assert(CurrV.isNotOwned()); + os << "+0 retain count"; + } + + if (Idx) { + os << " into an out parameter '"; + const ParmVarDecl *PVD = (*CE)->parameters()[*Idx]; + PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), + /*Qualified=*/false); + os << "'"; + + QualType RT = (*CE)->getResultType(); + if (!RT.isNull() && !RT->isVoidType()) { + SVal RV = (*CE)->getReturnValue(); + if (CurrSt->isNull(RV).isConstrainedTrue()) { + os << " (assuming the call returns zero)"; + } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) { + os << " (assuming the call returns non-zero)"; + } + + } + } +} + +namespace clang { +namespace ento { +namespace retaincountchecker { + +class RefCountReportVisitor : public BugReporterVisitor { +protected: + SymbolRef Sym; + +public: + RefCountReportVisitor(SymbolRef sym) : Sym(sym) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int x = 0; + ID.AddPointer(&x); + ID.AddPointer(Sym); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR) override; +}; + +class RefLeakReportVisitor : public RefCountReportVisitor { +public: + RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {} + + std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC, + const ExplodedNode *N, + BugReport &BR) override; +}; + +} // end namespace retaincountchecker +} // end namespace ento +} // end namespace clang + + +/// Find the first node with the parent stack frame. +static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) { + const StackFrameContext *SC = Pred->getStackFrame(); + if (SC->inTopFrame()) + return nullptr; + const StackFrameContext *PC = SC->getParent()->getStackFrame(); + if (!PC) + return nullptr; + + const ExplodedNode *N = Pred; + while (N && N->getStackFrame() != PC) { + N = N->getFirstPred(); + } + return N; +} + + +/// Insert a diagnostic piece at function exit +/// if a function parameter is annotated as "os_consumed", +/// but it does not actually consume the reference. +static std::shared_ptr<PathDiagnosticEventPiece> +annotateConsumedSummaryMismatch(const ExplodedNode *N, + CallExitBegin &CallExitLoc, + const SourceManager &SM, + CallEventManager &CEMgr) { + + const ExplodedNode *CN = getCalleeNode(N); + if (!CN) + return nullptr; + + CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState()); + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + ArrayRef<const ParmVarDecl *> Parameters = Call->parameters(); + for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) { + const ParmVarDecl *PVD = Parameters[I]; + + if (!PVD->hasAttr<OSConsumedAttr>()) + continue; + + if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) { + const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR); + const RefVal *CountAtExit = getRefBinding(N->getState(), SR); + + if (!CountBeforeCall || !CountAtExit) + continue; + + unsigned CountBefore = CountBeforeCall->getCount(); + unsigned CountAfter = CountAtExit->getCount(); + + bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1; + if (!AsExpected) { + os << "Parameter '"; + PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(), + /*Qualified=*/false); + os << "' is marked as consuming, but the function did not consume " + << "the reference\n"; + } + } + } + + if (os.str().empty()) + return nullptr; + + // FIXME: remove the code duplication with NoStoreFuncVisitor. + PathDiagnosticLocation L; + if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) { + L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext()); + } else { + L = PathDiagnosticLocation( + Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM); + } + + return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); +} + +std::shared_ptr<PathDiagnosticPiece> +RefCountReportVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, BugReport &BR) { + + const SourceManager &SM = BRC.getSourceManager(); + CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); + if (auto CE = N->getLocationAs<CallExitBegin>()) + if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr)) + return PD; + + // FIXME: We will eventually need to handle non-statement-based events + // (__attribute__((cleanup))). + if (!N->getLocation().getAs<StmtPoint>()) + return nullptr; + + // Check if the type state has changed. + const ExplodedNode *PrevNode = N->getFirstPred(); + ProgramStateRef PrevSt = PrevNode->getState(); + ProgramStateRef CurrSt = N->getState(); + const LocationContext *LCtx = N->getLocationContext(); + + const RefVal* CurrT = getRefBinding(CurrSt, Sym); + if (!CurrT) return nullptr; + + const RefVal &CurrV = *CurrT; + const RefVal *PrevT = getRefBinding(PrevSt, Sym); + + // Create a string buffer to constain all the useful things we want + // to tell the user. + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + // This is the allocation site since the previous node had no bindings + // for this symbol. + if (!PrevT) { + const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); + + if (isa<ObjCIvarRefExpr>(S) && + isSynthesizedAccessor(LCtx->getStackFrame())) { + S = LCtx->getStackFrame()->getCallSite(); + } + + if (isa<ObjCArrayLiteral>(S)) { + os << "NSArray literal is an object with a +0 retain count"; + } else if (isa<ObjCDictionaryLiteral>(S)) { + os << "NSDictionary literal is an object with a +0 retain count"; + } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) { + if (isNumericLiteralExpression(BL->getSubExpr())) + os << "NSNumber literal is an object with a +0 retain count"; + else { + const ObjCInterfaceDecl *BoxClass = nullptr; + if (const ObjCMethodDecl *Method = BL->getBoxingMethod()) + BoxClass = Method->getClassInterface(); + + // We should always be able to find the boxing class interface, + // but consider this future-proofing. + if (BoxClass) { + os << *BoxClass << " b"; + } else { + os << "B"; + } + + os << "oxed expression produces an object with a +0 retain count"; + } + } else if (isa<ObjCIvarRefExpr>(S)) { + os << "Object loaded from instance variable"; + } else { + generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os); + } + + PathDiagnosticLocation Pos(S, SM, N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); + } + + // Gather up the effects that were performed on the object at this + // program point + bool DeallocSent = false; + + if (N->getLocation().getTag() && + N->getLocation().getTag()->getTagDescription().contains( + RetainCountChecker::DeallocTagDescription)) { + // We only have summaries attached to nodes after evaluating CallExpr and + // ObjCMessageExprs. + const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); + + if (const CallExpr *CE = dyn_cast<CallExpr>(S)) { + // Iterate through the parameter expressions and see if the symbol + // was ever passed as an argument. + unsigned i = 0; + + for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) { + + // Retrieve the value of the argument. Is it the symbol + // we are interested in? + if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym) + continue; + + // We have an argument. Get the effect! + DeallocSent = true; + } + } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) { + if (const Expr *receiver = ME->getInstanceReceiver()) { + if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx) + .getAsLocSymbol() == Sym) { + // The symbol we are tracking is the receiver. + DeallocSent = true; + } + } + } + } + + if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent)) + return nullptr; + + if (os.str().empty()) + return nullptr; // We have nothing to say! + + const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt(); + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str()); + + // Add the range by scanning the children of the statement for any bindings + // to Sym. + for (const Stmt *Child : S->children()) + if (const Expr *Exp = dyn_cast_or_null<Expr>(Child)) + if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) { + P->addRange(Exp->getSourceRange()); + break; + } + + return std::move(P); +} + +static Optional<std::string> describeRegion(const MemRegion *MR) { + if (const auto *VR = dyn_cast_or_null<VarRegion>(MR)) + return std::string(VR->getDecl()->getName()); + // Once we support more storage locations for bindings, + // this would need to be improved. + return None; +} + +namespace { +// Find the first node in the current function context that referred to the +// tracked symbol and the memory location that value was stored to. Note, the +// value is only reported if the allocation occurred in the same function as +// the leak. The function can also return a location context, which should be +// treated as interesting. +struct AllocationInfo { + const ExplodedNode* N; + const MemRegion *R; + const LocationContext *InterestingMethodContext; + AllocationInfo(const ExplodedNode *InN, + const MemRegion *InR, + const LocationContext *InInterestingMethodContext) : + N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {} +}; +} // end anonymous namespace + +static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr, + const ExplodedNode *N, SymbolRef Sym) { + const ExplodedNode *AllocationNode = N; + const ExplodedNode *AllocationNodeInCurrentOrParentContext = N; + const MemRegion *FirstBinding = nullptr; + const LocationContext *LeakContext = N->getLocationContext(); + + // The location context of the init method called on the leaked object, if + // available. + const LocationContext *InitMethodContext = nullptr; + + while (N) { + ProgramStateRef St = N->getState(); + const LocationContext *NContext = N->getLocationContext(); + + if (!getRefBinding(St, Sym)) + break; + + StoreManager::FindUniqueBinding FB(Sym); + StateMgr.iterBindings(St, FB); + + if (FB) { + const MemRegion *R = FB.getRegion(); + // Do not show local variables belonging to a function other than + // where the error is reported. + if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace())) + if (MR->getStackFrame() == LeakContext->getStackFrame()) + FirstBinding = R; + } + + // AllocationNode is the last node in which the symbol was tracked. + AllocationNode = N; + + // AllocationNodeInCurrentContext, is the last node in the current or + // parent context in which the symbol was tracked. + // + // Note that the allocation site might be in the parent context. For example, + // the case where an allocation happens in a block that captures a reference + // to it and that reference is overwritten/dropped by another call to + // the block. + if (NContext == LeakContext || NContext->isParentOf(LeakContext)) + AllocationNodeInCurrentOrParentContext = N; + + // Find the last init that was called on the given symbol and store the + // init method's location context. + if (!InitMethodContext) + if (auto CEP = N->getLocation().getAs<CallEnter>()) { + const Stmt *CE = CEP->getCallExpr(); + if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) { + const Stmt *RecExpr = ME->getInstanceReceiver(); + if (RecExpr) { + SVal RecV = St->getSVal(RecExpr, NContext); + if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym) + InitMethodContext = CEP->getCalleeContext(); + } + } + } + + N = N->getFirstPred(); + } + + // If we are reporting a leak of the object that was allocated with alloc, + // mark its init method as interesting. + const LocationContext *InterestingMethodContext = nullptr; + if (InitMethodContext) { + const ProgramPoint AllocPP = AllocationNode->getLocation(); + if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>()) + if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>()) + if (ME->getMethodFamily() == OMF_alloc) + InterestingMethodContext = InitMethodContext; + } + + // If allocation happened in a function different from the leak node context, + // do not report the binding. + assert(N && "Could not find allocation node"); + + if (AllocationNodeInCurrentOrParentContext && + AllocationNodeInCurrentOrParentContext->getLocationContext() != + LeakContext) + FirstBinding = nullptr; + + return AllocationInfo(AllocationNodeInCurrentOrParentContext, + FirstBinding, + InterestingMethodContext); +} + +std::shared_ptr<PathDiagnosticPiece> +RefCountReportVisitor::getEndPath(BugReporterContext &BRC, + const ExplodedNode *EndN, BugReport &BR) { + BR.markInteresting(Sym); + return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR); +} + +std::shared_ptr<PathDiagnosticPiece> +RefLeakReportVisitor::getEndPath(BugReporterContext &BRC, + const ExplodedNode *EndN, BugReport &BR) { + + // Tell the BugReporterContext to report cases when the tracked symbol is + // assigned to different variables, etc. + BR.markInteresting(Sym); + + // We are reporting a leak. Walk up the graph to get to the first node where + // the symbol appeared, and also get the first VarDecl that tracked object + // is stored to. + AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym); + + const MemRegion* FirstBinding = AllocI.R; + BR.markInteresting(AllocI.InterestingMethodContext); + + SourceManager& SM = BRC.getSourceManager(); + + // Compute an actual location for the leak. Sometimes a leak doesn't + // occur at an actual statement (e.g., transition between blocks; end + // of function) so we need to walk the graph and compute a real location. + const ExplodedNode *LeakN = EndN; + PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM); + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "Object leaked: "; + + Optional<std::string> RegionDescription = describeRegion(FirstBinding); + if (RegionDescription) { + os << "object allocated and stored into '" << *RegionDescription << '\''; + } else { + os << "allocated object of type '" << getPrettyTypeName(Sym->getType()) + << "'"; + } + + // Get the retain count. + const RefVal* RV = getRefBinding(EndN->getState(), Sym); + assert(RV); + + if (RV->getKind() == RefVal::ErrorLeakReturned) { + // FIXME: Per comments in rdar://6320065, "create" only applies to CF + // objects. Only "copy", "alloc", "retain" and "new" transfer ownership + // to the caller for NS objects. + const Decl *D = &EndN->getCodeDecl(); + + os << (isa<ObjCMethodDecl>(D) ? " is returned from a method " + : " is returned from a function "); + + if (D->hasAttr<CFReturnsNotRetainedAttr>()) { + os << "that is annotated as CF_RETURNS_NOT_RETAINED"; + } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) { + os << "that is annotated as NS_RETURNS_NOT_RETAINED"; + } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) { + os << "that is annotated as OS_RETURNS_NOT_RETAINED"; + } else { + if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) { + if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) { + os << "managed by Automatic Reference Counting"; + } else { + os << "whose name ('" << MD->getSelector().getAsString() + << "') does not start with " + "'copy', 'mutableCopy', 'alloc' or 'new'." + " This violates the naming convention rules" + " given in the Memory Management Guide for Cocoa"; + } + } else { + const FunctionDecl *FD = cast<FunctionDecl>(D); + os << "whose name ('" << *FD + << "') does not contain 'Copy' or 'Create'. This violates the naming" + " convention rules given in the Memory Management Guide for Core" + " Foundation"; + } + } + } else { + os << " is not referenced later in this execution path and has a retain " + "count of +" << RV->getCount(); + } + + return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); +} + +RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts, + ExplodedNode *n, SymbolRef sym, + bool registerVisitor) + : BugReport(D, D.getDescription(), n), Sym(sym) { + if (registerVisitor) + addVisitor(llvm::make_unique<RefCountReportVisitor>(sym)); +} + +RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts, + ExplodedNode *n, SymbolRef sym, + StringRef endText) + : BugReport(D, D.getDescription(), endText, n) { + + addVisitor(llvm::make_unique<RefCountReportVisitor>(sym)); +} + +void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) { + const SourceManager& SMgr = Ctx.getSourceManager(); + + if (!sym->getOriginRegion()) + return; + + auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion()); + if (Region) { + const Decl *PDecl = Region->getDecl(); + if (PDecl && isa<ParmVarDecl>(PDecl)) { + PathDiagnosticLocation ParamLocation = + PathDiagnosticLocation::create(PDecl, SMgr); + Location = ParamLocation; + UniqueingLocation = ParamLocation; + UniqueingDecl = Ctx.getLocationContext()->getDecl(); + } + } +} + +void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx, + SymbolRef sym) { + // Most bug reports are cached at the location where they occurred. + // With leaks, we want to unique them by the location where they were + // allocated, and only report a single path. To do this, we need to find + // the allocation site of a piece of tracked memory, which we do via a + // call to GetAllocationSite. This will walk the ExplodedGraph backwards. + // Note that this is *not* the trimmed graph; we are guaranteed, however, + // that all ancestor nodes that represent the allocation site have the + // same SourceLocation. + const ExplodedNode *AllocNode = nullptr; + + const SourceManager& SMgr = Ctx.getSourceManager(); + + AllocationInfo AllocI = + GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym); + + AllocNode = AllocI.N; + AllocBinding = AllocI.R; + markInteresting(AllocI.InterestingMethodContext); + + // Get the SourceLocation for the allocation site. + // FIXME: This will crash the analyzer if an allocation comes from an + // implicit call (ex: a destructor call). + // (Currently there are no such allocations in Cocoa, though.) + AllocStmt = PathDiagnosticLocation::getStmt(AllocNode); + + if (!AllocStmt) { + AllocBinding = nullptr; + return; + } + + PathDiagnosticLocation AllocLocation = + PathDiagnosticLocation::createBegin(AllocStmt, SMgr, + AllocNode->getLocationContext()); + Location = AllocLocation; + + // Set uniqieing info, which will be used for unique the bug reports. The + // leaks should be uniqued on the allocation site. + UniqueingLocation = AllocLocation; + UniqueingDecl = AllocNode->getLocationContext()->getDecl(); +} + +void RefLeakReport::createDescription(CheckerContext &Ctx) { + assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid()); + Description.clear(); + llvm::raw_string_ostream os(Description); + os << "Potential leak of an object"; + + Optional<std::string> RegionDescription = describeRegion(AllocBinding); + if (RegionDescription) { + os << " stored into '" << *RegionDescription << '\''; + } else { + + // If we can't figure out the name, just supply the type information. + os << " of type '" << getPrettyTypeName(Sym->getType()) << "'"; + } +} + +RefLeakReport::RefLeakReport(RefCountBug &D, const LangOptions &LOpts, + ExplodedNode *n, SymbolRef sym, + CheckerContext &Ctx) + : RefCountReport(D, LOpts, n, sym, false) { + + deriveAllocLocation(Ctx, sym); + if (!AllocBinding) + deriveParamLocation(Ctx, sym); + + createDescription(Ctx); + + addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym)); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h new file mode 100644 index 000000000000..9f796abe8eae --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h @@ -0,0 +1,85 @@ +//== RetainCountDiagnostics.h - Checks for leaks and other issues -*- 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 diagnostics for RetainCountChecker, which implements +// a reference count checker for Core Foundation and Cocoa on (Mac OS X). +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H +#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H + +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/RetainSummaryManager.h" + +namespace clang { +namespace ento { +namespace retaincountchecker { + +class RefCountBug : public BugType { +protected: + RefCountBug(const CheckerBase *checker, StringRef name) + : BugType(checker, name, categories::MemoryRefCount) {} + +public: + virtual const char *getDescription() const = 0; + + virtual bool isLeak() const { return false; } +}; + +class RefCountReport : public BugReport { +protected: + SymbolRef Sym; + +public: + RefCountReport(RefCountBug &D, const LangOptions &LOpts, + ExplodedNode *n, SymbolRef sym, + bool registerVisitor = true); + + RefCountReport(RefCountBug &D, const LangOptions &LOpts, + ExplodedNode *n, SymbolRef sym, + StringRef endText); + + llvm::iterator_range<ranges_iterator> getRanges() override { + const RefCountBug& BugTy = static_cast<RefCountBug&>(getBugType()); + if (!BugTy.isLeak()) + return BugReport::getRanges(); + return llvm::make_range(ranges_iterator(), ranges_iterator()); + } +}; + +class RefLeakReport : public RefCountReport { + const MemRegion* AllocBinding; + const Stmt *AllocStmt; + + // Finds the function declaration where a leak warning for the parameter + // 'sym' should be raised. + void deriveParamLocation(CheckerContext &Ctx, SymbolRef sym); + // Finds the location where a leak warning for 'sym' should be raised. + void deriveAllocLocation(CheckerContext &Ctx, SymbolRef sym); + // Produces description of a leak warning which is printed on the console. + void createDescription(CheckerContext &Ctx); + +public: + RefLeakReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n, + SymbolRef sym, CheckerContext &Ctx); + + PathDiagnosticLocation getLocation(const SourceManager &SM) const override { + assert(Location.isValid()); + return Location; + } +}; + +} // end namespace retaincountchecker +} // end namespace ento +} // end namespace clang + +#endif diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp new file mode 100644 index 000000000000..17ef39531628 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -0,0 +1,92 @@ +//== ReturnPointerRangeChecker.cpp ------------------------------*- 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 ReturnPointerRangeChecker, which is a path-sensitive check +// which looks for an out-of-bound pointer being returned to callers. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class ReturnPointerRangeChecker : + public Checker< check::PreStmt<ReturnStmt> > { + mutable std::unique_ptr<BuiltinBug> BT; + +public: + void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; +}; +} + +void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS, + CheckerContext &C) const { + ProgramStateRef state = C.getState(); + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + SVal V = C.getSVal(RetE); + const MemRegion *R = V.getAsRegion(); + + const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R); + if (!ER) + return; + + DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); + // Zero index is always in bound, this also passes ElementRegions created for + // pointer casts. + if (Idx.isZeroConstant()) + return; + // FIXME: All of this out-of-bounds checking should eventually be refactored + // into a common place. + + DefinedOrUnknownSVal NumElements + = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), + ER->getValueType()); + + ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); + ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.generateErrorNode(StOutBound); + + if (!N) + return; + + // FIXME: This bug correspond to CWE-466. Eventually we should have bug + // types explicitly reference such exploit categories (when applicable). + if (!BT) + BT.reset(new BuiltinBug( + this, "Return of pointer value outside of expected range", + "Returned pointer value points outside the original object " + "(potential buffer overflow)")); + + // FIXME: It would be nice to eventually make this diagnostic more clear, + // e.g., by referencing the original declaration or by saying *why* this + // reference is outside the range. + + // Generate a report for this bug. + auto report = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); + + report->addRange(RetE->getSourceRange()); + C.emitReport(std::move(report)); + } +} + +void ento::registerReturnPointerRangeChecker(CheckerManager &mgr) { + mgr.registerChecker<ReturnPointerRangeChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp new file mode 100644 index 000000000000..3e0613e8ba68 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -0,0 +1,123 @@ +//== ReturnUndefChecker.cpp -------------------------------------*- 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 ReturnUndefChecker, which is a path-sensitive +// check which looks for undefined or garbage values being returned to the +// caller. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class ReturnUndefChecker : public Checker< check::PreStmt<ReturnStmt> > { + mutable std::unique_ptr<BuiltinBug> BT_Undef; + mutable std::unique_ptr<BuiltinBug> BT_NullReference; + + void emitUndef(CheckerContext &C, const Expr *RetE) const; + void checkReference(CheckerContext &C, const Expr *RetE, + DefinedOrUnknownSVal RetVal) const; +public: + void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; +}; +} + +void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS, + CheckerContext &C) const { + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + SVal RetVal = C.getSVal(RetE); + + const StackFrameContext *SFC = C.getStackFrame(); + QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl()); + + if (RetVal.isUndef()) { + // "return;" is modeled to evaluate to an UndefinedVal. Allow UndefinedVal + // to be returned in functions returning void to support this pattern: + // void foo() { + // return; + // } + // void test() { + // return foo(); + // } + if (!RT.isNull() && RT->isVoidType()) + return; + + // Not all blocks have explicitly-specified return types; if the return type + // is not available, but the return value expression has 'void' type, assume + // Sema already checked it. + if (RT.isNull() && isa<BlockDecl>(SFC->getDecl()) && + RetE->getType()->isVoidType()) + return; + + emitUndef(C, RetE); + return; + } + + if (RT.isNull()) + return; + + if (RT->isReferenceType()) { + checkReference(C, RetE, RetVal.castAs<DefinedOrUnknownSVal>()); + return; + } +} + +static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE, + const Expr *TrackingE = nullptr) { + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + auto Report = llvm::make_unique<BugReport>(BT, BT.getDescription(), N); + + Report->addRange(RetE->getSourceRange()); + bugreporter::trackExpressionValue(N, TrackingE ? TrackingE : RetE, *Report); + + C.emitReport(std::move(Report)); +} + +void ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const { + if (!BT_Undef) + BT_Undef.reset( + new BuiltinBug(this, "Garbage return value", + "Undefined or garbage value returned to caller")); + emitBug(C, *BT_Undef, RetE); +} + +void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE, + DefinedOrUnknownSVal RetVal) const { + ProgramStateRef StNonNull, StNull; + std::tie(StNonNull, StNull) = C.getState()->assume(RetVal); + + if (StNonNull) { + // Going forward, assume the location is non-null. + C.addTransition(StNonNull); + return; + } + + // The return value is known to be null. Emit a bug report. + if (!BT_NullReference) + BT_NullReference.reset(new BuiltinBug(this, "Returning null reference")); + + emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE)); +} + +void ento::registerReturnUndefChecker(CheckerManager &mgr) { + mgr.registerChecker<ReturnUndefChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp new file mode 100644 index 000000000000..cf03b3c21132 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp @@ -0,0 +1,205 @@ +//=- RunLoopAutoreleaseLeakChecker.cpp --------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +// +//===----------------------------------------------------------------------===// +// +// A checker for detecting leaks resulting from allocating temporary +// autoreleased objects before starting the main run loop. +// +// Checks for two antipatterns: +// 1. ObjCMessageExpr followed by [[NSRunLoop mainRunLoop] run] in the same +// autorelease pool. +// 2. ObjCMessageExpr followed by [[NSRunLoop mainRunLoop] run] in no +// autorelease pool. +// +// Any temporary objects autoreleased in code called in those expressions +// will not be deallocated until the program exits, and are effectively leaks. +// +//===----------------------------------------------------------------------===// +// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; +using namespace ast_matchers; + +namespace { + +const char * RunLoopBind = "NSRunLoopM"; +const char * RunLoopRunBind = "RunLoopRunM"; +const char * OtherMsgBind = "OtherMessageSentM"; +const char * AutoreleasePoolBind = "AutoreleasePoolM"; +const char * OtherStmtAutoreleasePoolBind = "OtherAutoreleasePoolM"; + +class RunLoopAutoreleaseLeakChecker : public Checker<check::ASTCodeBody> { + +public: + void checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const; + +}; + +} // end anonymous namespace + +/// \return Whether {@code A} occurs before {@code B} in traversal of +/// {@code Parent}. +/// Conceptually a very incomplete/unsound approximation of happens-before +/// relationship (A is likely to be evaluated before B), +/// but useful enough in this case. +static bool seenBefore(const Stmt *Parent, const Stmt *A, const Stmt *B) { + for (const Stmt *C : Parent->children()) { + if (!C) continue; + + if (C == A) + return true; + + if (C == B) + return false; + + return seenBefore(C, A, B); + } + return false; +} + +static void emitDiagnostics(BoundNodes &Match, + const Decl *D, + BugReporter &BR, + AnalysisManager &AM, + const RunLoopAutoreleaseLeakChecker *Checker) { + + assert(D->hasBody()); + const Stmt *DeclBody = D->getBody(); + + AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D); + + const auto *ME = Match.getNodeAs<ObjCMessageExpr>(OtherMsgBind); + assert(ME); + + const auto *AP = + Match.getNodeAs<ObjCAutoreleasePoolStmt>(AutoreleasePoolBind); + const auto *OAP = + Match.getNodeAs<ObjCAutoreleasePoolStmt>(OtherStmtAutoreleasePoolBind); + bool HasAutoreleasePool = (AP != nullptr); + + const auto *RL = Match.getNodeAs<ObjCMessageExpr>(RunLoopBind); + const auto *RLR = Match.getNodeAs<Stmt>(RunLoopRunBind); + assert(RLR && "Run loop launch not found"); + assert(ME != RLR); + + // Launch of run loop occurs before the message-sent expression is seen. + if (seenBefore(DeclBody, RLR, ME)) + return; + + if (HasAutoreleasePool && (OAP != AP)) + return; + + PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin( + ME, BR.getSourceManager(), ADC); + SourceRange Range = ME->getSourceRange(); + + BR.EmitBasicReport(ADC->getDecl(), Checker, + /*Name=*/"Memory leak inside autorelease pool", + /*Category=*/"Memory", + /*Name=*/ + (Twine("Temporary objects allocated in the") + + " autorelease pool " + + (HasAutoreleasePool ? "" : "of last resort ") + + "followed by the launch of " + + (RL ? "main run loop " : "xpc_main ") + + "may never get released; consider moving them to a " + "separate autorelease pool") + .str(), + Location, Range); +} + +static StatementMatcher getRunLoopRunM(StatementMatcher Extra = anything()) { + StatementMatcher MainRunLoopM = + objcMessageExpr(hasSelector("mainRunLoop"), + hasReceiverType(asString("NSRunLoop")), + Extra) + .bind(RunLoopBind); + + StatementMatcher MainRunLoopRunM = objcMessageExpr(hasSelector("run"), + hasReceiver(MainRunLoopM), + Extra).bind(RunLoopRunBind); + + StatementMatcher XPCRunM = + callExpr(callee(functionDecl(hasName("xpc_main")))).bind(RunLoopRunBind); + return anyOf(MainRunLoopRunM, XPCRunM); +} + +static StatementMatcher getOtherMessageSentM(StatementMatcher Extra = anything()) { + return objcMessageExpr(unless(anyOf(equalsBoundNode(RunLoopBind), + equalsBoundNode(RunLoopRunBind))), + Extra) + .bind(OtherMsgBind); +} + +static void +checkTempObjectsInSamePool(const Decl *D, AnalysisManager &AM, BugReporter &BR, + const RunLoopAutoreleaseLeakChecker *Chkr) { + StatementMatcher RunLoopRunM = getRunLoopRunM(); + StatementMatcher OtherMessageSentM = getOtherMessageSentM( + hasAncestor(autoreleasePoolStmt().bind(OtherStmtAutoreleasePoolBind))); + + StatementMatcher RunLoopInAutorelease = + autoreleasePoolStmt( + hasDescendant(RunLoopRunM), + hasDescendant(OtherMessageSentM)).bind(AutoreleasePoolBind); + + DeclarationMatcher GroupM = decl(hasDescendant(RunLoopInAutorelease)); + + auto Matches = match(GroupM, *D, AM.getASTContext()); + for (BoundNodes Match : Matches) + emitDiagnostics(Match, D, BR, AM, Chkr); +} + +static void +checkTempObjectsInNoPool(const Decl *D, AnalysisManager &AM, BugReporter &BR, + const RunLoopAutoreleaseLeakChecker *Chkr) { + + auto NoPoolM = unless(hasAncestor(autoreleasePoolStmt())); + + StatementMatcher RunLoopRunM = getRunLoopRunM(NoPoolM); + StatementMatcher OtherMessageSentM = getOtherMessageSentM(NoPoolM); + + DeclarationMatcher GroupM = functionDecl( + isMain(), + hasDescendant(RunLoopRunM), + hasDescendant(OtherMessageSentM) + ); + + auto Matches = match(GroupM, *D, AM.getASTContext()); + + for (BoundNodes Match : Matches) + emitDiagnostics(Match, D, BR, AM, Chkr); + +} + +void RunLoopAutoreleaseLeakChecker::checkASTCodeBody(const Decl *D, + AnalysisManager &AM, + BugReporter &BR) const { + checkTempObjectsInSamePool(D, AM, BR, this); + checkTempObjectsInNoPool(D, AM, BR, this); +} + +void ento::registerRunLoopAutoreleaseLeakChecker(CheckerManager &mgr) { + mgr.registerChecker<RunLoopAutoreleaseLeakChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp new file mode 100644 index 000000000000..819d437e6883 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp @@ -0,0 +1,271 @@ +//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines a checker for proper use of fopen/fclose APIs. +// - If a file has been closed with fclose, it should not be accessed again. +// Accessing a closed file results in undefined behavior. +// - If a file was opened with fopen, it must be closed with fclose before +// the execution ends. Failing to do so results in a resource leak. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <utility> + +using namespace clang; +using namespace ento; + +namespace { +typedef SmallVector<SymbolRef, 2> SymbolVector; + +struct StreamState { +private: + enum Kind { Opened, Closed } K; + StreamState(Kind InK) : K(InK) { } + +public: + bool isOpened() const { return K == Opened; } + bool isClosed() const { return K == Closed; } + + static StreamState getOpened() { return StreamState(Opened); } + static StreamState getClosed() { return StreamState(Closed); } + + bool operator==(const StreamState &X) const { + return K == X.K; + } + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + } +}; + +class SimpleStreamChecker : public Checker<check::PostCall, + check::PreCall, + check::DeadSymbols, + check::PointerEscape> { + CallDescription OpenFn, CloseFn; + + std::unique_ptr<BugType> DoubleCloseBugType; + std::unique_ptr<BugType> LeakBugType; + + void reportDoubleClose(SymbolRef FileDescSym, + const CallEvent &Call, + CheckerContext &C) const; + + void reportLeaks(ArrayRef<SymbolRef> LeakedStreams, CheckerContext &C, + ExplodedNode *ErrNode) const; + + bool guaranteedNotToCloseFile(const CallEvent &Call) const; + +public: + SimpleStreamChecker(); + + /// Process fopen. + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + /// Process fclose. + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + + /// Stop tracking addresses which escape. + ProgramStateRef checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const; +}; + +} // end anonymous namespace + +/// The state of the checker is a map from tracked stream symbols to their +/// state. Let's store it in the ProgramState. +REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) + +namespace { +class StopTrackingCallback final : public SymbolVisitor { + ProgramStateRef state; +public: + StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {} + ProgramStateRef getState() const { return state; } + + bool VisitSymbol(SymbolRef sym) override { + state = state->remove<StreamMap>(sym); + return true; + } +}; +} // end anonymous namespace + +SimpleStreamChecker::SimpleStreamChecker() + : OpenFn("fopen"), CloseFn("fclose", 1) { + // Initialize the bug types. + DoubleCloseBugType.reset( + new BugType(this, "Double fclose", "Unix Stream API Error")); + + LeakBugType.reset( + new BugType(this, "Resource Leak", "Unix Stream API Error")); + // Sinks are higher importance bugs as well as calls to assert() or exit(0). + LeakBugType->setSuppressOnSink(true); +} + +void SimpleStreamChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + if (!Call.isGlobalCFunction()) + return; + + if (!Call.isCalled(OpenFn)) + return; + + // Get the symbolic value corresponding to the file handle. + SymbolRef FileDesc = Call.getReturnValue().getAsSymbol(); + if (!FileDesc) + return; + + // Generate the next transition (an edge in the exploded graph). + ProgramStateRef State = C.getState(); + State = State->set<StreamMap>(FileDesc, StreamState::getOpened()); + C.addTransition(State); +} + +void SimpleStreamChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!Call.isGlobalCFunction()) + return; + + if (!Call.isCalled(CloseFn)) + return; + + // Get the symbolic value corresponding to the file handle. + SymbolRef FileDesc = Call.getArgSVal(0).getAsSymbol(); + if (!FileDesc) + return; + + // Check if the stream has already been closed. + ProgramStateRef State = C.getState(); + const StreamState *SS = State->get<StreamMap>(FileDesc); + if (SS && SS->isClosed()) { + reportDoubleClose(FileDesc, Call, C); + return; + } + + // Generate the next transition, in which the stream is closed. + State = State->set<StreamMap>(FileDesc, StreamState::getClosed()); + C.addTransition(State); +} + +static bool isLeaked(SymbolRef Sym, const StreamState &SS, + bool IsSymDead, ProgramStateRef State) { + if (IsSymDead && SS.isOpened()) { + // If a symbol is NULL, assume that fopen failed on this path. + // A symbol should only be considered leaked if it is non-null. + ConstraintManager &CMgr = State->getConstraintManager(); + ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym); + return !OpenFailed.isConstrainedTrue(); + } + return false; +} + +void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + SymbolVector LeakedStreams; + StreamMapTy TrackedStreams = State->get<StreamMap>(); + for (StreamMapTy::iterator I = TrackedStreams.begin(), + E = TrackedStreams.end(); I != E; ++I) { + SymbolRef Sym = I->first; + bool IsSymDead = SymReaper.isDead(Sym); + + // Collect leaked symbols. + if (isLeaked(Sym, I->second, IsSymDead, State)) + LeakedStreams.push_back(Sym); + + // Remove the dead symbol from the streams map. + if (IsSymDead) + State = State->remove<StreamMap>(Sym); + } + + ExplodedNode *N = C.generateNonFatalErrorNode(State); + if (!N) + return; + reportLeaks(LeakedStreams, C, N); +} + +void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym, + const CallEvent &Call, + CheckerContext &C) const { + // We reached a bug, stop exploring the path here by generating a sink. + ExplodedNode *ErrNode = C.generateErrorNode(); + // If we've already reached this node on another path, return. + if (!ErrNode) + return; + + // Generate the report. + auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType, + "Closing a previously closed file stream", ErrNode); + R->addRange(Call.getSourceRange()); + R->markInteresting(FileDescSym); + C.emitReport(std::move(R)); +} + +void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams, + CheckerContext &C, + ExplodedNode *ErrNode) const { + // Attach bug reports to the leak node. + // TODO: Identify the leaked file descriptor. + for (SymbolRef LeakedStream : LeakedStreams) { + auto R = llvm::make_unique<BugReport>(*LeakBugType, + "Opened file is never closed; potential resource leak", ErrNode); + R->markInteresting(LeakedStream); + C.emitReport(std::move(R)); + } +} + +bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{ + // If it's not in a system header, assume it might close a file. + if (!Call.isInSystemHeader()) + return false; + + // Handle cases where we know a buffer's /address/ can escape. + if (Call.argumentsMayEscape()) + return false; + + // Note, even though fclose closes the file, we do not list it here + // since the checker is modeling the call. + + return true; +} + +// If the pointer we are tracking escaped, do not track the symbol as +// we cannot reason about it anymore. +ProgramStateRef +SimpleStreamChecker::checkPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind) const { + // If we know that the call cannot close a file, there is nothing to do. + if (Kind == PSK_DirectEscapeOnCall && guaranteedNotToCloseFile(*Call)) { + return State; + } + + for (InvalidatedSymbols::const_iterator I = Escaped.begin(), + E = Escaped.end(); + I != E; ++I) { + SymbolRef Sym = *I; + + // The symbol escaped. Optimistically, assume that the corresponding file + // handle will be closed somewhere else. + State = State->remove<StreamMap>(Sym); + } + return State; +} + +void ento::registerSimpleStreamChecker(CheckerManager &mgr) { + mgr.registerChecker<SimpleStreamChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp new file mode 100644 index 000000000000..0f53d826a5f6 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp @@ -0,0 +1,371 @@ +//=== StackAddrEscapeChecker.cpp ----------------------------------*- 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 stack address leak checker, which checks if an invalid +// stack address is stored into a global or heap location. See CERT DCL30-C. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ExprCXX.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; +using namespace ento; + +namespace { +class StackAddrEscapeChecker + : public Checker<check::PreCall, check::PreStmt<ReturnStmt>, + check::EndFunction> { + mutable IdentifierInfo *dispatch_semaphore_tII; + mutable std::unique_ptr<BuiltinBug> BT_stackleak; + mutable std::unique_ptr<BuiltinBug> BT_returnstack; + mutable std::unique_ptr<BuiltinBug> BT_capturedstackasync; + mutable std::unique_ptr<BuiltinBug> BT_capturedstackret; + +public: + enum CheckKind { + CK_StackAddrEscapeChecker, + CK_StackAddrAsyncEscapeChecker, + CK_NumCheckKinds + }; + + DefaultBool ChecksEnabled[CK_NumCheckKinds]; + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; + void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const; + +private: + void checkReturnedBlockCaptures(const BlockDataRegion &B, + CheckerContext &C) const; + void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B, + CheckerContext &C) const; + void EmitStackError(CheckerContext &C, const MemRegion *R, + const Expr *RetE) const; + bool isSemaphoreCaptured(const BlockDecl &B) const; + static SourceRange genName(raw_ostream &os, const MemRegion *R, + ASTContext &Ctx); + static SmallVector<const MemRegion *, 4> + getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C); + static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C); + static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C); +}; +} // namespace + +SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R, + ASTContext &Ctx) { + // Get the base region, stripping away fields and elements. + R = R->getBaseRegion(); + SourceManager &SM = Ctx.getSourceManager(); + SourceRange range; + os << "Address of "; + + // Check if the region is a compound literal. + if (const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) { + const CompoundLiteralExpr *CL = CR->getLiteralExpr(); + os << "stack memory associated with a compound literal " + "declared on line " + << SM.getExpansionLineNumber(CL->getBeginLoc()) << " returned to caller"; + range = CL->getSourceRange(); + } else if (const auto *AR = dyn_cast<AllocaRegion>(R)) { + const Expr *ARE = AR->getExpr(); + SourceLocation L = ARE->getBeginLoc(); + range = ARE->getSourceRange(); + os << "stack memory allocated by call to alloca() on line " + << SM.getExpansionLineNumber(L); + } else if (const auto *BR = dyn_cast<BlockDataRegion>(R)) { + const BlockDecl *BD = BR->getCodeRegion()->getDecl(); + SourceLocation L = BD->getBeginLoc(); + range = BD->getSourceRange(); + os << "stack-allocated block declared on line " + << SM.getExpansionLineNumber(L); + } else if (const auto *VR = dyn_cast<VarRegion>(R)) { + os << "stack memory associated with local variable '" << VR->getString() + << '\''; + range = VR->getDecl()->getSourceRange(); + } else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) { + QualType Ty = TOR->getValueType().getLocalUnqualifiedType(); + os << "stack memory associated with temporary object of type '"; + Ty.print(os, Ctx.getPrintingPolicy()); + os << "'"; + range = TOR->getExpr()->getSourceRange(); + } else { + llvm_unreachable("Invalid region in ReturnStackAddressChecker."); + } + + return range; +} + +bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R, + CheckerContext &C) { + assert(R && "MemRegion should not be null"); + return C.getASTContext().getLangOpts().ObjCAutoRefCount && + isa<BlockDataRegion>(R); +} + +bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R, + CheckerContext &C) { + const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace()); + return S->getStackFrame() != C.getStackFrame(); +} + +bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const { + if (!dispatch_semaphore_tII) + dispatch_semaphore_tII = &B.getASTContext().Idents.get("dispatch_semaphore_t"); + for (const auto &C : B.captures()) { + const auto *T = C.getVariable()->getType()->getAs<TypedefType>(); + if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII) + return true; + } + return false; +} + +SmallVector<const MemRegion *, 4> +StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B, + CheckerContext &C) { + SmallVector<const MemRegion *, 4> Regions; + BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin(); + BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end(); + for (; I != E; ++I) { + SVal Val = C.getState()->getSVal(I.getCapturedRegion()); + const MemRegion *Region = Val.getAsRegion(); + if (Region && isa<StackSpaceRegion>(Region->getMemorySpace())) + Regions.push_back(Region); + } + return Regions; +} + +void StackAddrEscapeChecker::EmitStackError(CheckerContext &C, + const MemRegion *R, + const Expr *RetE) const { + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + return; + if (!BT_returnstack) + BT_returnstack = llvm::make_unique<BuiltinBug>( + this, "Return of address to stack-allocated memory"); + // Generate a report for this bug. + SmallString<128> buf; + llvm::raw_svector_ostream os(buf); + SourceRange range = genName(os, R, C.getASTContext()); + os << " returned to caller"; + auto report = llvm::make_unique<BugReport>(*BT_returnstack, os.str(), N); + report->addRange(RetE->getSourceRange()); + if (range.isValid()) + report->addRange(range); + C.emitReport(std::move(report)); +} + +void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures( + const BlockDataRegion &B, CheckerContext &C) const { + // There is a not-too-uncommon idiom + // where a block passed to dispatch_async captures a semaphore + // and then the thread (which called dispatch_async) is blocked on waiting + // for the completion of the execution of the block + // via dispatch_semaphore_wait. To avoid false-positives (for now) + // we ignore all the blocks which have captured + // a variable of the type "dispatch_semaphore_t". + if (isSemaphoreCaptured(*B.getDecl())) + return; + for (const MemRegion *Region : getCapturedStackRegions(B, C)) { + // The block passed to dispatch_async may capture another block + // created on the stack. However, there is no leak in this situaton, + // no matter if ARC or no ARC is enabled: + // dispatch_async copies the passed "outer" block (via Block_copy) + // and if the block has captured another "inner" block, + // the "inner" block will be copied as well. + if (isa<BlockDataRegion>(Region)) + continue; + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + continue; + if (!BT_capturedstackasync) + BT_capturedstackasync = llvm::make_unique<BuiltinBug>( + this, "Address of stack-allocated memory is captured"); + SmallString<128> Buf; + llvm::raw_svector_ostream Out(Buf); + SourceRange Range = genName(Out, Region, C.getASTContext()); + Out << " is captured by an asynchronously-executed block"; + auto Report = + llvm::make_unique<BugReport>(*BT_capturedstackasync, Out.str(), N); + if (Range.isValid()) + Report->addRange(Range); + C.emitReport(std::move(Report)); + } +} + +void StackAddrEscapeChecker::checkReturnedBlockCaptures( + const BlockDataRegion &B, CheckerContext &C) const { + for (const MemRegion *Region : getCapturedStackRegions(B, C)) { + if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C)) + continue; + ExplodedNode *N = C.generateNonFatalErrorNode(); + if (!N) + continue; + if (!BT_capturedstackret) + BT_capturedstackret = llvm::make_unique<BuiltinBug>( + this, "Address of stack-allocated memory is captured"); + SmallString<128> Buf; + llvm::raw_svector_ostream Out(Buf); + SourceRange Range = genName(Out, Region, C.getASTContext()); + Out << " is captured by a returned block"; + auto Report = + llvm::make_unique<BugReport>(*BT_capturedstackret, Out.str(), N); + if (Range.isValid()) + Report->addRange(Range); + C.emitReport(std::move(Report)); + } +} + +void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker]) + return; + if (!Call.isGlobalCFunction("dispatch_after") && + !Call.isGlobalCFunction("dispatch_async")) + return; + for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) { + if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>( + Call.getArgSVal(Idx).getAsRegion())) + checkAsyncExecutedBlockCaptures(*B, C); + } +} + +void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS, + CheckerContext &C) const { + if (!ChecksEnabled[CK_StackAddrEscapeChecker]) + return; + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + RetE = RetE->IgnoreParens(); + + SVal V = C.getSVal(RetE); + const MemRegion *R = V.getAsRegion(); + if (!R) + return; + + if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R)) + checkReturnedBlockCaptures(*B, C); + + if (!isa<StackSpaceRegion>(R->getMemorySpace()) || + isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C)) + return; + + // Returning a record by value is fine. (In this case, the returned + // expression will be a copy-constructor, possibly wrapped in an + // ExprWithCleanups node.) + if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE)) + RetE = Cleanup->getSubExpr(); + if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType()) + return; + + // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied + // so the stack address is not escaping here. + if (auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) { + if (isa<BlockDataRegion>(R) && + ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) { + return; + } + } + + EmitStackError(C, R, RetE); +} + +void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS, + CheckerContext &Ctx) const { + if (!ChecksEnabled[CK_StackAddrEscapeChecker]) + return; + + ProgramStateRef State = Ctx.getState(); + + // Iterate over all bindings to global variables and see if it contains + // a memory region in the stack space. + class CallBack : public StoreManager::BindingsHandler { + private: + CheckerContext &Ctx; + const StackFrameContext *CurSFC; + + public: + SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V; + + CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {} + + bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region, + SVal Val) override { + + if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace())) + return true; + const MemRegion *VR = Val.getAsRegion(); + if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) && + !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx)) + V.emplace_back(Region, VR); + return true; + } + }; + + CallBack Cb(Ctx); + State->getStateManager().getStoreManager().iterBindings(State->getStore(), + Cb); + + if (Cb.V.empty()) + return; + + // Generate an error node. + ExplodedNode *N = Ctx.generateNonFatalErrorNode(State); + if (!N) + return; + + if (!BT_stackleak) + BT_stackleak = llvm::make_unique<BuiltinBug>( + this, "Stack address stored into global variable", + "Stack address was saved into a global variable. " + "This is dangerous because the address will become " + "invalid after returning from the function"); + + for (const auto &P : Cb.V) { + // Generate a report for this bug. + SmallString<128> Buf; + llvm::raw_svector_ostream Out(Buf); + SourceRange Range = genName(Out, P.second, Ctx.getASTContext()); + Out << " is still referred to by the "; + if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace())) + Out << "static"; + else + Out << "global"; + Out << " variable '"; + const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion()); + Out << *VR->getDecl() + << "' upon returning to the caller. This will be a dangling reference"; + auto Report = llvm::make_unique<BugReport>(*BT_stackleak, Out.str(), N); + if (Range.isValid()) + Report->addRange(Range); + + Ctx.emitReport(std::move(Report)); + } +} + +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &Mgr) { \ + StackAddrEscapeChecker *Chk = \ + Mgr.registerChecker<StackAddrEscapeChecker>(); \ + Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \ + } + +REGISTER_CHECKER(StackAddrEscapeChecker) +REGISTER_CHECKER(StackAddrAsyncEscapeChecker) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp new file mode 100644 index 000000000000..6478128ce954 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -0,0 +1,1058 @@ +//=== StdLibraryFunctionsChecker.cpp - Model standard functions -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker improves modeling of a few simple library functions. +// It does not generate warnings. +// +// This checker provides a specification format - `FunctionSummaryTy' - and +// contains descriptions of some library functions in this format. Each +// specification contains a list of branches for splitting the program state +// upon call, and range constraints on argument and return-value symbols that +// are satisfied on each branch. This spec can be expanded to include more +// items, like external effects of the function. +// +// The main difference between this approach and the body farms technique is +// in more explicit control over how many branches are produced. For example, +// consider standard C function `ispunct(int x)', which returns a non-zero value +// iff `x' is a punctuation character, that is, when `x' is in range +// ['!', '/'] [':', '@'] U ['[', '\`'] U ['{', '~']. +// `FunctionSummaryTy' provides only two branches for this function. However, +// any attempt to describe this range with if-statements in the body farm +// would result in many more branches. Because each branch needs to be analyzed +// independently, this significantly reduces performance. Additionally, +// once we consider a branch on which `x' is in range, say, ['!', '/'], +// we assume that such branch is an important separate path through the program, +// which may lead to false positives because considering this particular path +// was not consciously intended, and therefore it might have been unreachable. +// +// This checker uses eval::Call for modeling "pure" functions, for which +// their `FunctionSummaryTy' is a precise model. This avoids unnecessary +// invalidation passes. Conflicts with other checkers are unlikely because +// if the function has no other effects, other checkers would probably never +// want to improve upon the modeling done by this checker. +// +// Non-"pure" functions, for which only partial improvement over the default +// behavior is expected, are modeled via check::PostCall, non-intrusively. +// +// The following standard C functions are currently supported: +// +// fgetc getline isdigit isupper +// fread isalnum isgraph isxdigit +// fwrite isalpha islower read +// getc isascii isprint write +// getchar isblank ispunct +// getdelim iscntrl isspace +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace clang::ento; + +namespace { +class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> { + /// Below is a series of typedefs necessary to define function specs. + /// We avoid nesting types here because each additional qualifier + /// would need to be repeated in every function spec. + struct FunctionSummaryTy; + + /// Specify how much the analyzer engine should entrust modeling this function + /// to us. If he doesn't, he performs additional invalidations. + enum InvalidationKindTy { NoEvalCall, EvalCallAsPure }; + + /// A pair of ValueRangeKindTy and IntRangeVectorTy would describe a range + /// imposed on a particular argument or return value symbol. + /// + /// Given a range, should the argument stay inside or outside this range? + /// The special `ComparesToArgument' value indicates that we should + /// impose a constraint that involves other argument or return value symbols. + enum ValueRangeKindTy { OutOfRange, WithinRange, ComparesToArgument }; + + // The universal integral type to use in value range descriptions. + // Unsigned to make sure overflows are well-defined. + typedef uint64_t RangeIntTy; + + /// Normally, describes a single range constraint, eg. {{0, 1}, {3, 4}} is + /// a non-negative integer, which less than 5 and not equal to 2. For + /// `ComparesToArgument', holds information about how exactly to compare to + /// the argument. + typedef std::vector<std::pair<RangeIntTy, RangeIntTy>> IntRangeVectorTy; + + /// A reference to an argument or return value by its number. + /// ArgNo in CallExpr and CallEvent is defined as Unsigned, but + /// obviously uint32_t should be enough for all practical purposes. + typedef uint32_t ArgNoTy; + static const ArgNoTy Ret = std::numeric_limits<ArgNoTy>::max(); + + /// Incapsulates a single range on a single symbol within a branch. + class ValueRange { + ArgNoTy ArgNo; // Argument to which we apply the range. + ValueRangeKindTy Kind; // Kind of range definition. + IntRangeVectorTy Args; // Polymorphic arguments. + + public: + ValueRange(ArgNoTy ArgNo, ValueRangeKindTy Kind, + const IntRangeVectorTy &Args) + : ArgNo(ArgNo), Kind(Kind), Args(Args) {} + + ArgNoTy getArgNo() const { return ArgNo; } + ValueRangeKindTy getKind() const { return Kind; } + + BinaryOperator::Opcode getOpcode() const { + assert(Kind == ComparesToArgument); + assert(Args.size() == 1); + BinaryOperator::Opcode Op = + static_cast<BinaryOperator::Opcode>(Args[0].first); + assert(BinaryOperator::isComparisonOp(Op) && + "Only comparison ops are supported for ComparesToArgument"); + return Op; + } + + ArgNoTy getOtherArgNo() const { + assert(Kind == ComparesToArgument); + assert(Args.size() == 1); + return static_cast<ArgNoTy>(Args[0].second); + } + + const IntRangeVectorTy &getRanges() const { + assert(Kind != ComparesToArgument); + return Args; + } + + // We avoid creating a virtual apply() method because + // it makes initializer lists harder to write. + private: + ProgramStateRef + applyAsOutOfRange(ProgramStateRef State, const CallEvent &Call, + const FunctionSummaryTy &Summary) const; + ProgramStateRef + applyAsWithinRange(ProgramStateRef State, const CallEvent &Call, + const FunctionSummaryTy &Summary) const; + ProgramStateRef + applyAsComparesToArgument(ProgramStateRef State, const CallEvent &Call, + const FunctionSummaryTy &Summary) const; + + public: + ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, + const FunctionSummaryTy &Summary) const { + switch (Kind) { + case OutOfRange: + return applyAsOutOfRange(State, Call, Summary); + case WithinRange: + return applyAsWithinRange(State, Call, Summary); + case ComparesToArgument: + return applyAsComparesToArgument(State, Call, Summary); + } + llvm_unreachable("Unknown ValueRange kind!"); + } + }; + + /// The complete list of ranges that defines a single branch. + typedef std::vector<ValueRange> ValueRangeSet; + + /// Includes information about function prototype (which is necessary to + /// ensure we're modeling the right function and casting values properly), + /// approach to invalidation, and a list of branches - essentially, a list + /// of list of ranges - essentially, a list of lists of lists of segments. + struct FunctionSummaryTy { + const std::vector<QualType> ArgTypes; + const QualType RetType; + const InvalidationKindTy InvalidationKind; + const std::vector<ValueRangeSet> Ranges; + + private: + static void assertTypeSuitableForSummary(QualType T) { + assert(!T->isVoidType() && + "We should have had no significant void types in the spec"); + assert(T.isCanonical() && + "We should only have canonical types in the spec"); + // FIXME: lift this assert (but not the ones above!) + assert(T->isIntegralOrEnumerationType() && + "We only support integral ranges in the spec"); + } + + public: + QualType getArgType(ArgNoTy ArgNo) const { + QualType T = (ArgNo == Ret) ? RetType : ArgTypes[ArgNo]; + assertTypeSuitableForSummary(T); + return T; + } + + /// Try our best to figure out if the call expression is the call of + /// *the* library function to which this specification applies. + bool matchesCall(const CallExpr *CE) const; + }; + + // The same function (as in, function identifier) may have different + // summaries assigned to it, with different argument and return value types. + // We call these "variants" of the function. This can be useful for handling + // C++ function overloads, and also it can be used when the same function + // may have different definitions on different platforms. + typedef std::vector<FunctionSummaryTy> FunctionVariantsTy; + + // The map of all functions supported by the checker. It is initialized + // lazily, and it doesn't change after initialization. + typedef llvm::StringMap<FunctionVariantsTy> FunctionSummaryMapTy; + mutable FunctionSummaryMapTy FunctionSummaryMap; + + // Auxiliary functions to support ArgNoTy within all structures + // in a unified manner. + static QualType getArgType(const FunctionSummaryTy &Summary, ArgNoTy ArgNo) { + return Summary.getArgType(ArgNo); + } + static QualType getArgType(const CallEvent &Call, ArgNoTy ArgNo) { + return ArgNo == Ret ? Call.getResultType().getCanonicalType() + : Call.getArgExpr(ArgNo)->getType().getCanonicalType(); + } + static QualType getArgType(const CallExpr *CE, ArgNoTy ArgNo) { + return ArgNo == Ret ? CE->getType().getCanonicalType() + : CE->getArg(ArgNo)->getType().getCanonicalType(); + } + static SVal getArgSVal(const CallEvent &Call, ArgNoTy ArgNo) { + return ArgNo == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgNo); + } + +public: + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + bool evalCall(const CallExpr *CE, CheckerContext &C) const; + +private: + Optional<FunctionSummaryTy> findFunctionSummary(const FunctionDecl *FD, + const CallExpr *CE, + CheckerContext &C) const; + + void initFunctionSummaries(BasicValueFactory &BVF) const; +}; +} // end of anonymous namespace + +ProgramStateRef StdLibraryFunctionsChecker::ValueRange::applyAsOutOfRange( + ProgramStateRef State, const CallEvent &Call, + const FunctionSummaryTy &Summary) const { + + ProgramStateManager &Mgr = State->getStateManager(); + SValBuilder &SVB = Mgr.getSValBuilder(); + BasicValueFactory &BVF = SVB.getBasicValueFactory(); + ConstraintManager &CM = Mgr.getConstraintManager(); + QualType T = getArgType(Summary, getArgNo()); + SVal V = getArgSVal(Call, getArgNo()); + + if (auto N = V.getAs<NonLoc>()) { + const IntRangeVectorTy &R = getRanges(); + size_t E = R.size(); + for (size_t I = 0; I != E; ++I) { + const llvm::APSInt &Min = BVF.getValue(R[I].first, T); + const llvm::APSInt &Max = BVF.getValue(R[I].second, T); + assert(Min <= Max); + State = CM.assumeInclusiveRange(State, *N, Min, Max, false); + if (!State) + break; + } + } + + return State; +} + +ProgramStateRef +StdLibraryFunctionsChecker::ValueRange::applyAsWithinRange( + ProgramStateRef State, const CallEvent &Call, + const FunctionSummaryTy &Summary) const { + + ProgramStateManager &Mgr = State->getStateManager(); + SValBuilder &SVB = Mgr.getSValBuilder(); + BasicValueFactory &BVF = SVB.getBasicValueFactory(); + ConstraintManager &CM = Mgr.getConstraintManager(); + QualType T = getArgType(Summary, getArgNo()); + SVal V = getArgSVal(Call, getArgNo()); + + // "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R". + // We cut off [T_MIN, min(R) - 1] and [max(R) + 1, T_MAX] if necessary, + // and then cut away all holes in R one by one. + if (auto N = V.getAs<NonLoc>()) { + const IntRangeVectorTy &R = getRanges(); + size_t E = R.size(); + + const llvm::APSInt &MinusInf = BVF.getMinValue(T); + const llvm::APSInt &PlusInf = BVF.getMaxValue(T); + + const llvm::APSInt &Left = BVF.getValue(R[0].first - 1ULL, T); + if (Left != PlusInf) { + assert(MinusInf <= Left); + State = CM.assumeInclusiveRange(State, *N, MinusInf, Left, false); + if (!State) + return nullptr; + } + + const llvm::APSInt &Right = BVF.getValue(R[E - 1].second + 1ULL, T); + if (Right != MinusInf) { + assert(Right <= PlusInf); + State = CM.assumeInclusiveRange(State, *N, Right, PlusInf, false); + if (!State) + return nullptr; + } + + for (size_t I = 1; I != E; ++I) { + const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, T); + const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, T); + assert(Min <= Max); + State = CM.assumeInclusiveRange(State, *N, Min, Max, false); + if (!State) + return nullptr; + } + } + + return State; +} + +ProgramStateRef +StdLibraryFunctionsChecker::ValueRange::applyAsComparesToArgument( + ProgramStateRef State, const CallEvent &Call, + const FunctionSummaryTy &Summary) const { + + ProgramStateManager &Mgr = State->getStateManager(); + SValBuilder &SVB = Mgr.getSValBuilder(); + QualType CondT = SVB.getConditionType(); + QualType T = getArgType(Summary, getArgNo()); + SVal V = getArgSVal(Call, getArgNo()); + + BinaryOperator::Opcode Op = getOpcode(); + ArgNoTy OtherArg = getOtherArgNo(); + SVal OtherV = getArgSVal(Call, OtherArg); + QualType OtherT = getArgType(Call, OtherArg); + // Note: we avoid integral promotion for comparison. + OtherV = SVB.evalCast(OtherV, T, OtherT); + if (auto CompV = SVB.evalBinOp(State, Op, V, OtherV, CondT) + .getAs<DefinedOrUnknownSVal>()) + State = State->assume(*CompV, true); + return State; +} + +void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl()); + if (!FD) + return; + + const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + if (!CE) + return; + + Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C); + if (!FoundSummary) + return; + + // Now apply ranges. + const FunctionSummaryTy &Summary = *FoundSummary; + ProgramStateRef State = C.getState(); + + for (const auto &VRS: Summary.Ranges) { + ProgramStateRef NewState = State; + for (const auto &VR: VRS) { + NewState = VR.apply(NewState, Call, Summary); + if (!NewState) + break; + } + + if (NewState && NewState != State) + C.addTransition(NewState); + } +} + +bool StdLibraryFunctionsChecker::evalCall(const CallExpr *CE, + CheckerContext &C) const { + const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl()); + if (!FD) + return false; + + Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C); + if (!FoundSummary) + return false; + + const FunctionSummaryTy &Summary = *FoundSummary; + switch (Summary.InvalidationKind) { + case EvalCallAsPure: { + ProgramStateRef State = C.getState(); + const LocationContext *LC = C.getLocationContext(); + SVal V = C.getSValBuilder().conjureSymbolVal( + CE, LC, CE->getType().getCanonicalType(), C.blockCount()); + State = State->BindExpr(CE, LC, V); + C.addTransition(State); + return true; + } + case NoEvalCall: + // Summary tells us to avoid performing eval::Call. The function is possibly + // evaluated by another checker, or evaluated conservatively. + return false; + } + llvm_unreachable("Unknown invalidation kind!"); +} + +bool StdLibraryFunctionsChecker::FunctionSummaryTy::matchesCall( + const CallExpr *CE) const { + // Check number of arguments: + if (CE->getNumArgs() != ArgTypes.size()) + return false; + + // Check return type if relevant: + if (!RetType.isNull() && RetType != CE->getType().getCanonicalType()) + return false; + + // Check argument types when relevant: + for (size_t I = 0, E = ArgTypes.size(); I != E; ++I) { + QualType FormalT = ArgTypes[I]; + // Null type marks irrelevant arguments. + if (FormalT.isNull()) + continue; + + assertTypeSuitableForSummary(FormalT); + + QualType ActualT = StdLibraryFunctionsChecker::getArgType(CE, I); + assert(ActualT.isCanonical()); + if (ActualT != FormalT) + return false; + } + + return true; +} + +Optional<StdLibraryFunctionsChecker::FunctionSummaryTy> +StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD, + const CallExpr *CE, + CheckerContext &C) const { + // Note: we cannot always obtain FD from CE + // (eg. virtual call, or call by pointer). + assert(CE); + + if (!FD) + return None; + + SValBuilder &SVB = C.getSValBuilder(); + BasicValueFactory &BVF = SVB.getBasicValueFactory(); + initFunctionSummaries(BVF); + + IdentifierInfo *II = FD->getIdentifier(); + if (!II) + return None; + StringRef Name = II->getName(); + if (Name.empty() || !C.isCLibraryFunction(FD, Name)) + return None; + + auto FSMI = FunctionSummaryMap.find(Name); + if (FSMI == FunctionSummaryMap.end()) + return None; + + // Verify that function signature matches the spec in advance. + // Otherwise we might be modeling the wrong function. + // Strict checking is important because we will be conducting + // very integral-type-sensitive operations on arguments and + // return values. + const FunctionVariantsTy &SpecVariants = FSMI->second; + for (const FunctionSummaryTy &Spec : SpecVariants) + if (Spec.matchesCall(CE)) + return Spec; + + return None; +} + +void StdLibraryFunctionsChecker::initFunctionSummaries( + BasicValueFactory &BVF) const { + if (!FunctionSummaryMap.empty()) + return; + + ASTContext &ACtx = BVF.getContext(); + + // These types are useful for writing specifications quickly, + // New specifications should probably introduce more types. + // Some types are hard to obtain from the AST, eg. "ssize_t". + // In such cases it should be possible to provide multiple variants + // of function summary for common cases (eg. ssize_t could be int or long + // or long long, so three summary variants would be enough). + // Of course, function variants are also useful for C++ overloads. + QualType Irrelevant; // A placeholder, whenever we do not care about the type. + QualType IntTy = ACtx.IntTy; + QualType LongTy = ACtx.LongTy; + QualType LongLongTy = ACtx.LongLongTy; + QualType SizeTy = ACtx.getSizeType(); + + RangeIntTy IntMax = BVF.getMaxValue(IntTy).getLimitedValue(); + RangeIntTy LongMax = BVF.getMaxValue(LongTy).getLimitedValue(); + RangeIntTy LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue(); + + // We are finally ready to define specifications for all supported functions. + // + // The signature needs to have the correct number of arguments. + // However, we insert `Irrelevant' when the type is insignificant. + // + // Argument ranges should always cover all variants. If return value + // is completely unknown, omit it from the respective range set. + // + // All types in the spec need to be canonical. + // + // Every item in the list of range sets represents a particular + // execution path the analyzer would need to explore once + // the call is modeled - a new program state is constructed + // for every range set, and each range line in the range set + // corresponds to a specific constraint within this state. + // + // Upon comparing to another argument, the other argument is casted + // to the current argument's type. This avoids proper promotion but + // seems useful. For example, read() receives size_t argument, + // and its return value, which is of type ssize_t, cannot be greater + // than this argument. If we made a promotion, and the size argument + // is equal to, say, 10, then we'd impose a range of [0, 10] on the + // return value, however the correct range is [-1, 10]. + // + // Please update the list of functions in the header after editing! + // + // The format is as follows: + // + //{ "function name", + // { spec: + // { argument types list, ... }, + // return type, purity, { range set list: + // { range list: + // { argument index, within or out of, {{from, to}, ...} }, + // { argument index, compares to argument, {{how, which}} }, + // ... + // } + // } + // } + //} + +#define SUMMARY_WITH_VARIANTS(identifier) {#identifier, { +#define END_SUMMARY_WITH_VARIANTS }}, +#define VARIANT(argument_types, return_type, invalidation_approach) \ + { argument_types, return_type, invalidation_approach, { +#define END_VARIANT } }, +#define SUMMARY(identifier, argument_types, return_type, \ + invalidation_approach) \ + { #identifier, { { argument_types, return_type, invalidation_approach, { +#define END_SUMMARY } } } }, +#define ARGUMENT_TYPES(...) { __VA_ARGS__ } +#define RETURN_TYPE(x) x +#define INVALIDATION_APPROACH(x) x +#define CASE { +#define END_CASE }, +#define ARGUMENT_CONDITION(argument_number, condition_kind) \ + { argument_number, condition_kind, { +#define END_ARGUMENT_CONDITION }}, +#define RETURN_VALUE_CONDITION(condition_kind) \ + { Ret, condition_kind, { +#define END_RETURN_VALUE_CONDITION }}, +#define ARG_NO(x) x##U +#define RANGE(x, y) { x, y }, +#define SINGLE_VALUE(x) RANGE(x, x) +#define IS_LESS_THAN(arg) { BO_LE, arg } + + FunctionSummaryMap = { + // The isascii() family of functions. + SUMMARY(isalnum, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE // Boils down to isupper() or islower() or isdigit() + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE('0', '9') + RANGE('A', 'Z') + RANGE('a', 'z') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE // The locale-specific range. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE(128, 255) + END_ARGUMENT_CONDITION + // No post-condition. We are completely unaware of + // locale-specific return values. + END_CASE + CASE + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE('0', '9') + RANGE('A', 'Z') + RANGE('a', 'z') + RANGE(128, 255) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(isalpha, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE // isupper() or islower(). Note that 'Z' is less than 'a'. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE('A', 'Z') + RANGE('a', 'z') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE // The locale-specific range. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE(128, 255) + END_ARGUMENT_CONDITION + END_CASE + CASE // Other. + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE('A', 'Z') + RANGE('a', 'z') + RANGE(128, 255) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(isascii, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE // Is ASCII. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE(0, 127) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE(0, 127) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(isblank, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + SINGLE_VALUE('\t') + SINGLE_VALUE(' ') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + SINGLE_VALUE('\t') + SINGLE_VALUE(' ') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(iscntrl, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE // 0..31 or 127 + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE(0, 32) + SINGLE_VALUE(127) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE(0, 32) + SINGLE_VALUE(127) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(isdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE // Is a digit. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE('0', '9') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE('0', '9') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(isgraph, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE(33, 126) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE(33, 126) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(islower, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE // Is certainly lowercase. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE('a', 'z') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE // Is ascii but not lowercase. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE(0, 127) + END_ARGUMENT_CONDITION + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE('a', 'z') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE // The locale-specific range. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE(128, 255) + END_ARGUMENT_CONDITION + END_CASE + CASE // Is not an unsigned char. + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE(0, 255) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(isprint, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE(32, 126) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE(32, 126) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(ispunct, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE('!', '/') + RANGE(':', '@') + RANGE('[', '`') + RANGE('{', '~') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE('!', '/') + RANGE(':', '@') + RANGE('[', '`') + RANGE('{', '~') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(isspace, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE // Space, '\f', '\n', '\r', '\t', '\v'. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE(9, 13) + SINGLE_VALUE(' ') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE // The locale-specific range. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE(128, 255) + END_ARGUMENT_CONDITION + END_CASE + CASE + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE(9, 13) + SINGLE_VALUE(' ') + RANGE(128, 255) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(isupper, ARGUMENT_TYPES(IntTy), RETURN_TYPE (IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE // Is certainly uppercase. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE('A', 'Z') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE // The locale-specific range. + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE(128, 255) + END_ARGUMENT_CONDITION + END_CASE + CASE // Other. + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE('A', 'Z') RANGE(128, 255) + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(isxdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + ARGUMENT_CONDITION(ARG_NO(0), WithinRange) + RANGE('0', '9') + RANGE('A', 'F') + RANGE('a', 'f') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(OutOfRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + ARGUMENT_CONDITION(ARG_NO(0), OutOfRange) + RANGE('0', '9') + RANGE('A', 'F') + RANGE('a', 'f') + END_ARGUMENT_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(0) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + + // The getc() family of functions that returns either a char or an EOF. + SUMMARY(getc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(NoEvalCall)) + CASE // FIXME: EOF is assumed to be defined as -1. + RETURN_VALUE_CONDITION(WithinRange) + RANGE(-1, 255) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(fgetc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(NoEvalCall)) + CASE // FIXME: EOF is assumed to be defined as -1. + RETURN_VALUE_CONDITION(WithinRange) + RANGE(-1, 255) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(getchar, ARGUMENT_TYPES(), RETURN_TYPE(IntTy), + INVALIDATION_APPROACH(NoEvalCall)) + CASE // FIXME: EOF is assumed to be defined as -1. + RETURN_VALUE_CONDITION(WithinRange) + RANGE(-1, 255) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + + // read()-like functions that never return more than buffer size. + // We are not sure how ssize_t is defined on every platform, so we provide + // three variants that should cover common cases. + SUMMARY_WITH_VARIANTS(read) + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(ComparesToArgument) + IS_LESS_THAN(ARG_NO(2)) + END_RETURN_VALUE_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + RANGE(-1, IntMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(ComparesToArgument) + IS_LESS_THAN(ARG_NO(2)) + END_RETURN_VALUE_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + RANGE(-1, LongMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(ComparesToArgument) + IS_LESS_THAN(ARG_NO(2)) + END_RETURN_VALUE_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + RANGE(-1, LongLongMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + END_SUMMARY_WITH_VARIANTS + SUMMARY_WITH_VARIANTS(write) + // Again, due to elusive nature of ssize_t, we have duplicate + // our summaries to cover different variants. + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(ComparesToArgument) + IS_LESS_THAN(ARG_NO(2)) + END_RETURN_VALUE_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + RANGE(-1, IntMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(ComparesToArgument) + IS_LESS_THAN(ARG_NO(2)) + END_RETURN_VALUE_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + RANGE(-1, LongMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(ComparesToArgument) + IS_LESS_THAN(ARG_NO(2)) + END_RETURN_VALUE_CONDITION + RETURN_VALUE_CONDITION(WithinRange) + RANGE(-1, LongLongMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + END_SUMMARY_WITH_VARIANTS + SUMMARY(fread, + ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant), + RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(ComparesToArgument) + IS_LESS_THAN(ARG_NO(2)) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(fwrite, + ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant), + RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(ComparesToArgument) + IS_LESS_THAN(ARG_NO(2)) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + + // getline()-like functions either fail or read at least the delimiter. + SUMMARY_WITH_VARIANTS(getline) + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(-1) + RANGE(1, IntMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(-1) + RANGE(1, LongMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(-1) + RANGE(1, LongLongMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + END_SUMMARY_WITH_VARIANTS + SUMMARY_WITH_VARIANTS(getdelim) + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(-1) + RANGE(1, IntMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(-1) + RANGE(1, LongMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) + CASE + RETURN_VALUE_CONDITION(WithinRange) + SINGLE_VALUE(-1) + RANGE(1, LongLongMax) + END_RETURN_VALUE_CONDITION + END_CASE + END_VARIANT + END_SUMMARY_WITH_VARIANTS + }; +} + +void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) { + // If this checker grows large enough to support C++, Objective-C, or other + // standard libraries, we could use multiple register...Checker() functions, + // which would register various checkers with the help of the same Checker + // class, turning on different function summaries. + mgr.registerChecker<StdLibraryFunctionsChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp new file mode 100644 index 000000000000..92647f032730 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -0,0 +1,411 @@ +//===-- StreamChecker.cpp -----------------------------------------*- 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 checkers that model and check stream handling functions. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" + +using namespace clang; +using namespace ento; + +namespace { + +struct StreamState { + enum Kind { Opened, Closed, OpenFailed, Escaped } K; + const Stmt *S; + + StreamState(Kind k, const Stmt *s) : K(k), S(s) {} + + bool isOpened() const { return K == Opened; } + bool isClosed() const { return K == Closed; } + //bool isOpenFailed() const { return K == OpenFailed; } + //bool isEscaped() const { return K == Escaped; } + + bool operator==(const StreamState &X) const { + return K == X.K && S == X.S; + } + + static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); } + static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); } + static StreamState getOpenFailed(const Stmt *s) { + return StreamState(OpenFailed, s); + } + static StreamState getEscaped(const Stmt *s) { + return StreamState(Escaped, s); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + ID.AddPointer(S); + } +}; + +class StreamChecker : public Checker<eval::Call, + check::DeadSymbols > { + mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread, + *II_fwrite, + *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos, + *II_clearerr, *II_feof, *II_ferror, *II_fileno; + mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence, + BT_doubleclose, BT_ResourceLeak; + +public: + StreamChecker() + : II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr), + II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr), + II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr), + II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr), + II_ferror(nullptr), II_fileno(nullptr) {} + + bool evalCall(const CallExpr *CE, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const; + +private: + void Fopen(CheckerContext &C, const CallExpr *CE) const; + void Tmpfile(CheckerContext &C, const CallExpr *CE) const; + void Fclose(CheckerContext &C, const CallExpr *CE) const; + void Fread(CheckerContext &C, const CallExpr *CE) const; + void Fwrite(CheckerContext &C, const CallExpr *CE) const; + void Fseek(CheckerContext &C, const CallExpr *CE) const; + void Ftell(CheckerContext &C, const CallExpr *CE) const; + void Rewind(CheckerContext &C, const CallExpr *CE) const; + void Fgetpos(CheckerContext &C, const CallExpr *CE) const; + void Fsetpos(CheckerContext &C, const CallExpr *CE) const; + void Clearerr(CheckerContext &C, const CallExpr *CE) const; + void Feof(CheckerContext &C, const CallExpr *CE) const; + void Ferror(CheckerContext &C, const CallExpr *CE) const; + void Fileno(CheckerContext &C, const CallExpr *CE) const; + + void OpenFileAux(CheckerContext &C, const CallExpr *CE) const; + + ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state, + CheckerContext &C) const; + ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state, + CheckerContext &C) const; +}; + +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState) + + +bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const { + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return false; + + ASTContext &Ctx = C.getASTContext(); + if (!II_fopen) + II_fopen = &Ctx.Idents.get("fopen"); + if (!II_tmpfile) + II_tmpfile = &Ctx.Idents.get("tmpfile"); + if (!II_fclose) + II_fclose = &Ctx.Idents.get("fclose"); + if (!II_fread) + II_fread = &Ctx.Idents.get("fread"); + if (!II_fwrite) + II_fwrite = &Ctx.Idents.get("fwrite"); + if (!II_fseek) + II_fseek = &Ctx.Idents.get("fseek"); + if (!II_ftell) + II_ftell = &Ctx.Idents.get("ftell"); + if (!II_rewind) + II_rewind = &Ctx.Idents.get("rewind"); + if (!II_fgetpos) + II_fgetpos = &Ctx.Idents.get("fgetpos"); + if (!II_fsetpos) + II_fsetpos = &Ctx.Idents.get("fsetpos"); + if (!II_clearerr) + II_clearerr = &Ctx.Idents.get("clearerr"); + if (!II_feof) + II_feof = &Ctx.Idents.get("feof"); + if (!II_ferror) + II_ferror = &Ctx.Idents.get("ferror"); + if (!II_fileno) + II_fileno = &Ctx.Idents.get("fileno"); + + if (FD->getIdentifier() == II_fopen) { + Fopen(C, CE); + return true; + } + if (FD->getIdentifier() == II_tmpfile) { + Tmpfile(C, CE); + return true; + } + if (FD->getIdentifier() == II_fclose) { + Fclose(C, CE); + return true; + } + if (FD->getIdentifier() == II_fread) { + Fread(C, CE); + return true; + } + if (FD->getIdentifier() == II_fwrite) { + Fwrite(C, CE); + return true; + } + if (FD->getIdentifier() == II_fseek) { + Fseek(C, CE); + return true; + } + if (FD->getIdentifier() == II_ftell) { + Ftell(C, CE); + return true; + } + if (FD->getIdentifier() == II_rewind) { + Rewind(C, CE); + return true; + } + if (FD->getIdentifier() == II_fgetpos) { + Fgetpos(C, CE); + return true; + } + if (FD->getIdentifier() == II_fsetpos) { + Fsetpos(C, CE); + return true; + } + if (FD->getIdentifier() == II_clearerr) { + Clearerr(C, CE); + return true; + } + if (FD->getIdentifier() == II_feof) { + Feof(C, CE); + return true; + } + if (FD->getIdentifier() == II_ferror) { + Ferror(C, CE); + return true; + } + if (FD->getIdentifier() == II_fileno) { + Fileno(C, CE); + return true; + } + + return false; +} + +void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const { + OpenFileAux(C, CE); +} + +void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const { + OpenFileAux(C, CE); +} + +void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + SValBuilder &svalBuilder = C.getSValBuilder(); + const LocationContext *LCtx = C.getPredecessor()->getLocationContext(); + DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, + C.blockCount()) + .castAs<DefinedSVal>(); + state = state->BindExpr(CE, C.getLocationContext(), RetVal); + + ConstraintManager &CM = C.getConstraintManager(); + // Bifurcate the state into two: one with a valid FILE* pointer, the other + // with a NULL. + ProgramStateRef stateNotNull, stateNull; + std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal); + + if (SymbolRef Sym = RetVal.getAsSymbol()) { + // if RetVal is not NULL, set the symbol's state to Opened. + stateNotNull = + stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE)); + stateNull = + stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE)); + + C.addTransition(stateNotNull); + C.addTransition(stateNull); + } +} + +void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C); + if (state) + C.addTransition(state); +} + +void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C)) + return; +} + +void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C)) + return; +} + +void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + if (!(state = CheckNullStream(C.getSVal(CE->getArg(0)), state, C))) + return; + // Check the legality of the 'whence' argument of 'fseek'. + SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext()); + Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>(); + + if (!CI) + return; + + int64_t x = CI->getValue().getSExtValue(); + if (x >= 0 && x <= 2) + return; + + if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) { + if (!BT_illegalwhence) + BT_illegalwhence.reset( + new BuiltinBug(this, "Illegal whence argument", + "The whence argument to fseek() should be " + "SEEK_SET, SEEK_END, or SEEK_CUR.")); + C.emitReport(llvm::make_unique<BugReport>( + *BT_illegalwhence, BT_illegalwhence->getDescription(), N)); + } +} + +void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const { + ProgramStateRef state = C.getState(); + if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C)) + return; +} + +ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state, + CheckerContext &C) const { + Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>(); + if (!DV) + return nullptr; + + ConstraintManager &CM = C.getConstraintManager(); + ProgramStateRef stateNotNull, stateNull; + std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); + + if (!stateNotNull && stateNull) { + if (ExplodedNode *N = C.generateErrorNode(stateNull)) { + if (!BT_nullfp) + BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer", + "Stream pointer might be NULL.")); + C.emitReport(llvm::make_unique<BugReport>( + *BT_nullfp, BT_nullfp->getDescription(), N)); + } + return nullptr; + } + return stateNotNull; +} + +ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE, + ProgramStateRef state, + CheckerContext &C) const { + SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol(); + if (!Sym) + return state; + + const StreamState *SS = state->get<StreamMap>(Sym); + + // If the file stream is not tracked, return. + if (!SS) + return state; + + // Check: Double close a File Descriptor could cause undefined behaviour. + // Conforming to man-pages + if (SS->isClosed()) { + ExplodedNode *N = C.generateErrorNode(); + if (N) { + if (!BT_doubleclose) + BT_doubleclose.reset(new BuiltinBug( + this, "Double fclose", "Try to close a file Descriptor already" + " closed. Cause undefined behaviour.")); + C.emitReport(llvm::make_unique<BugReport>( + *BT_doubleclose, BT_doubleclose->getDescription(), N)); + } + return nullptr; + } + + // Close the File Descriptor. + return state->set<StreamMap>(Sym, StreamState::getClosed(CE)); +} + +void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper, + CheckerContext &C) const { + ProgramStateRef state = C.getState(); + + // TODO: Clean up the state. + const StreamMapTy &Map = state->get<StreamMap>(); + for (const auto &I: Map) { + SymbolRef Sym = I.first; + const StreamState &SS = I.second; + if (!SymReaper.isDead(Sym) || !SS.isOpened()) + continue; + + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + if (!BT_ResourceLeak) + BT_ResourceLeak.reset( + new BuiltinBug(this, "Resource Leak", + "Opened File never closed. Potential Resource leak.")); + C.emitReport(llvm::make_unique<BugReport>( + *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N)); + } +} + +void ento::registerStreamChecker(CheckerManager &mgr) { + mgr.registerChecker<StreamChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp new file mode 100644 index 000000000000..3aa8e95d0ad0 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp @@ -0,0 +1,62 @@ +//== TaintTesterChecker.cpp ----------------------------------- -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker can be used for testing how taint data is propagated. +// +//===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class TaintTesterChecker : public Checker< check::PostStmt<Expr> > { + + mutable std::unique_ptr<BugType> BT; + void initBugType() const; + + /// Given a pointer argument, get the symbol of the value it contains + /// (points to). + SymbolRef getPointedToSymbol(CheckerContext &C, + const Expr* Arg, + bool IssueWarning = true) const; + +public: + void checkPostStmt(const Expr *E, CheckerContext &C) const; +}; +} + +inline void TaintTesterChecker::initBugType() const { + if (!BT) + BT.reset(new BugType(this, "Tainted data", "General")); +} + +void TaintTesterChecker::checkPostStmt(const Expr *E, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (!State) + return; + + if (State->isTainted(E, C.getLocationContext())) { + if (ExplodedNode *N = C.generateNonFatalErrorNode()) { + initBugType(); + auto report = llvm::make_unique<BugReport>(*BT, "tainted",N); + report->addRange(E->getSourceRange()); + C.emitReport(std::move(report)); + } + } +} + +void ento::registerTaintTesterChecker(CheckerManager &mgr) { + mgr.registerChecker<TaintTesterChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp new file mode 100644 index 000000000000..527e371571f1 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp @@ -0,0 +1,263 @@ +//== TestAfterDivZeroChecker.cpp - Test after division by zero checker --*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines TestAfterDivZeroChecker, a builtin check that performs checks +// for division by zero where the division occurs before comparison with zero. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/FoldingSet.h" + +using namespace clang; +using namespace ento; + +namespace { + +class ZeroState { +private: + SymbolRef ZeroSymbol; + unsigned BlockID; + const StackFrameContext *SFC; + +public: + ZeroState(SymbolRef S, unsigned B, const StackFrameContext *SFC) + : ZeroSymbol(S), BlockID(B), SFC(SFC) {} + + const StackFrameContext *getStackFrameContext() const { return SFC; } + + bool operator==(const ZeroState &X) const { + return BlockID == X.BlockID && SFC == X.SFC && ZeroSymbol == X.ZeroSymbol; + } + + bool operator<(const ZeroState &X) const { + if (BlockID != X.BlockID) + return BlockID < X.BlockID; + if (SFC != X.SFC) + return SFC < X.SFC; + return ZeroSymbol < X.ZeroSymbol; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(BlockID); + ID.AddPointer(SFC); + ID.AddPointer(ZeroSymbol); + } +}; + +class DivisionBRVisitor : public BugReporterVisitor { +private: + SymbolRef ZeroSymbol; + const StackFrameContext *SFC; + bool Satisfied; + +public: + DivisionBRVisitor(SymbolRef ZeroSymbol, const StackFrameContext *SFC) + : ZeroSymbol(ZeroSymbol), SFC(SFC), Satisfied(false) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.Add(ZeroSymbol); + ID.Add(SFC); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + BugReport &BR) override; +}; + +class TestAfterDivZeroChecker + : public Checker<check::PreStmt<BinaryOperator>, check::BranchCondition, + check::EndFunction> { + mutable std::unique_ptr<BuiltinBug> DivZeroBug; + void reportBug(SVal Val, CheckerContext &C) const; + +public: + void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const; + void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const; + void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; + void setDivZeroMap(SVal Var, CheckerContext &C) const; + bool hasDivZeroMap(SVal Var, const CheckerContext &C) const; + bool isZero(SVal S, CheckerContext &C) const; +}; +} // end anonymous namespace + +REGISTER_SET_WITH_PROGRAMSTATE(DivZeroMap, ZeroState) + +std::shared_ptr<PathDiagnosticPiece> +DivisionBRVisitor::VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, BugReport &BR) { + if (Satisfied) + return nullptr; + + const Expr *E = nullptr; + + if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) + if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) { + BinaryOperator::Opcode Op = BO->getOpcode(); + if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign || + Op == BO_RemAssign) { + E = BO->getRHS(); + } + } + + if (!E) + return nullptr; + + SVal S = Succ->getSVal(E); + if (ZeroSymbol == S.getAsSymbol() && SFC == Succ->getStackFrame()) { + Satisfied = true; + + // Construct a new PathDiagnosticPiece. + ProgramPoint P = Succ->getLocation(); + PathDiagnosticLocation L = + PathDiagnosticLocation::create(P, BRC.getSourceManager()); + + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + return std::make_shared<PathDiagnosticEventPiece>( + L, "Division with compared value made here"); + } + + return nullptr; +} + +bool TestAfterDivZeroChecker::isZero(SVal S, CheckerContext &C) const { + Optional<DefinedSVal> DSV = S.getAs<DefinedSVal>(); + + if (!DSV) + return false; + + ConstraintManager &CM = C.getConstraintManager(); + return !CM.assume(C.getState(), *DSV, true); +} + +void TestAfterDivZeroChecker::setDivZeroMap(SVal Var, CheckerContext &C) const { + SymbolRef SR = Var.getAsSymbol(); + if (!SR) + return; + + ProgramStateRef State = C.getState(); + State = + State->add<DivZeroMap>(ZeroState(SR, C.getBlockID(), C.getStackFrame())); + C.addTransition(State); +} + +bool TestAfterDivZeroChecker::hasDivZeroMap(SVal Var, + const CheckerContext &C) const { + SymbolRef SR = Var.getAsSymbol(); + if (!SR) + return false; + + ZeroState ZS(SR, C.getBlockID(), C.getStackFrame()); + return C.getState()->contains<DivZeroMap>(ZS); +} + +void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const { + if (ExplodedNode *N = C.generateErrorNode(C.getState())) { + if (!DivZeroBug) + DivZeroBug.reset(new BuiltinBug(this, "Division by zero")); + + auto R = llvm::make_unique<BugReport>( + *DivZeroBug, "Value being compared against zero has already been used " + "for division", + N); + + R->addVisitor(llvm::make_unique<DivisionBRVisitor>(Val.getAsSymbol(), + C.getStackFrame())); + C.emitReport(std::move(R)); + } +} + +void TestAfterDivZeroChecker::checkEndFunction(const ReturnStmt *, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + DivZeroMapTy DivZeroes = State->get<DivZeroMap>(); + if (DivZeroes.isEmpty()) + return; + + DivZeroMapTy::Factory &F = State->get_context<DivZeroMap>(); + for (llvm::ImmutableSet<ZeroState>::iterator I = DivZeroes.begin(), + E = DivZeroes.end(); + I != E; ++I) { + ZeroState ZS = *I; + if (ZS.getStackFrameContext() == C.getStackFrame()) + DivZeroes = F.remove(DivZeroes, ZS); + } + C.addTransition(State->set<DivZeroMap>(DivZeroes)); +} + +void TestAfterDivZeroChecker::checkPreStmt(const BinaryOperator *B, + CheckerContext &C) const { + BinaryOperator::Opcode Op = B->getOpcode(); + if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign || + Op == BO_RemAssign) { + SVal S = C.getSVal(B->getRHS()); + + if (!isZero(S, C)) + setDivZeroMap(S, C); + } +} + +void TestAfterDivZeroChecker::checkBranchCondition(const Stmt *Condition, + CheckerContext &C) const { + if (const BinaryOperator *B = dyn_cast<BinaryOperator>(Condition)) { + if (B->isComparisonOp()) { + const IntegerLiteral *IntLiteral = dyn_cast<IntegerLiteral>(B->getRHS()); + bool LRHS = true; + if (!IntLiteral) { + IntLiteral = dyn_cast<IntegerLiteral>(B->getLHS()); + LRHS = false; + } + + if (!IntLiteral || IntLiteral->getValue() != 0) + return; + + SVal Val = C.getSVal(LRHS ? B->getLHS() : B->getRHS()); + if (hasDivZeroMap(Val, C)) + reportBug(Val, C); + } + } else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(Condition)) { + if (U->getOpcode() == UO_LNot) { + SVal Val; + if (const ImplicitCastExpr *I = + dyn_cast<ImplicitCastExpr>(U->getSubExpr())) + Val = C.getSVal(I->getSubExpr()); + + if (hasDivZeroMap(Val, C)) + reportBug(Val, C); + else { + Val = C.getSVal(U->getSubExpr()); + if (hasDivZeroMap(Val, C)) + reportBug(Val, C); + } + } + } else if (const ImplicitCastExpr *IE = + dyn_cast<ImplicitCastExpr>(Condition)) { + SVal Val = C.getSVal(IE->getSubExpr()); + + if (hasDivZeroMap(Val, C)) + reportBug(Val, C); + else { + SVal Val = C.getSVal(Condition); + + if (hasDivZeroMap(Val, C)) + reportBug(Val, C); + } + } +} + +void ento::registerTestAfterDivZeroChecker(CheckerManager &mgr) { + mgr.registerChecker<TestAfterDivZeroChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp new file mode 100644 index 000000000000..2f06469bb209 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp @@ -0,0 +1,114 @@ +//== TraversalChecker.cpp -------------------------------------- -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// These checkers print various aspects of the ExprEngine's traversal of the CFG +// as it builds the ExplodedGraph. +// +//===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/StmtObjC.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class TraversalDumper : public Checker< check::BranchCondition, + check::BeginFunction, + check::EndFunction > { +public: + void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const; + void checkBeginFunction(CheckerContext &C) const; + void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; +}; +} + +void TraversalDumper::checkBranchCondition(const Stmt *Condition, + CheckerContext &C) const { + // Special-case Objective-C's for-in loop, which uses the entire loop as its + // condition. We just print the collection expression. + const Stmt *Parent = dyn_cast<ObjCForCollectionStmt>(Condition); + if (!Parent) { + const ParentMap &Parents = C.getLocationContext()->getParentMap(); + Parent = Parents.getParent(Condition); + } + + // It is mildly evil to print directly to llvm::outs() rather than emitting + // warnings, but this ensures things do not get filtered out by the rest of + // the static analyzer machinery. + SourceLocation Loc = Parent->getBeginLoc(); + llvm::outs() << C.getSourceManager().getSpellingLineNumber(Loc) << " " + << Parent->getStmtClassName() << "\n"; +} + +void TraversalDumper::checkBeginFunction(CheckerContext &C) const { + llvm::outs() << "--BEGIN FUNCTION--\n"; +} + +void TraversalDumper::checkEndFunction(const ReturnStmt *RS, + CheckerContext &C) const { + llvm::outs() << "--END FUNCTION--\n"; +} + +void ento::registerTraversalDumper(CheckerManager &mgr) { + mgr.registerChecker<TraversalDumper>(); +} + +//------------------------------------------------------------------------------ + +namespace { +class CallDumper : public Checker< check::PreCall, + check::PostCall > { +public: + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; +}; +} + +void CallDumper::checkPreCall(const CallEvent &Call, CheckerContext &C) const { + unsigned Indentation = 0; + for (const LocationContext *LC = C.getLocationContext()->getParent(); + LC != nullptr; LC = LC->getParent()) + ++Indentation; + + // It is mildly evil to print directly to llvm::outs() rather than emitting + // warnings, but this ensures things do not get filtered out by the rest of + // the static analyzer machinery. + llvm::outs().indent(Indentation); + Call.dump(llvm::outs()); +} + +void CallDumper::checkPostCall(const CallEvent &Call, CheckerContext &C) const { + const Expr *CallE = Call.getOriginExpr(); + if (!CallE) + return; + + unsigned Indentation = 0; + for (const LocationContext *LC = C.getLocationContext()->getParent(); + LC != nullptr; LC = LC->getParent()) + ++Indentation; + + // It is mildly evil to print directly to llvm::outs() rather than emitting + // warnings, but this ensures things do not get filtered out by the rest of + // the static analyzer machinery. + llvm::outs().indent(Indentation); + if (Call.getResultType()->isVoidType()) + llvm::outs() << "Returning void\n"; + else + llvm::outs() << "Returning " << C.getSVal(CallE) << "\n"; +} + +void ento::registerCallDumper(CheckerManager &mgr) { + mgr.registerChecker<CallDumper>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp new file mode 100644 index 000000000000..5e777803af00 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp @@ -0,0 +1,255 @@ +//== TrustNonnullChecker.cpp --------- API nullability modeling -*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker adds nullability-related assumptions: +// +// 1. Methods annotated with _Nonnull +// which come from system headers actually return a non-null pointer. +// +// 2. NSDictionary key is non-null after the keyword subscript operation +// on read if and only if the resulting expression is non-null. +// +// 3. NSMutableDictionary index is non-null after a write operation. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Analysis/SelectorExtras.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" + +using namespace clang; +using namespace ento; + +/// Records implications between symbols. +/// The semantics is: +/// (antecedent != 0) => (consequent != 0) +/// These implications are then read during the evaluation of the assumption, +/// and the appropriate antecedents are applied. +REGISTER_MAP_WITH_PROGRAMSTATE(NonNullImplicationMap, SymbolRef, SymbolRef) + +/// The semantics is: +/// (antecedent == 0) => (consequent == 0) +REGISTER_MAP_WITH_PROGRAMSTATE(NullImplicationMap, SymbolRef, SymbolRef) + +namespace { + +class TrustNonnullChecker : public Checker<check::PostCall, + check::PostObjCMessage, + check::DeadSymbols, + eval::Assume> { + // Do not try to iterate over symbols with higher complexity. + static unsigned constexpr ComplexityThreshold = 10; + Selector ObjectForKeyedSubscriptSel; + Selector ObjectForKeySel; + Selector SetObjectForKeyedSubscriptSel; + Selector SetObjectForKeySel; + +public: + TrustNonnullChecker(ASTContext &Ctx) + : ObjectForKeyedSubscriptSel( + getKeywordSelector(Ctx, "objectForKeyedSubscript")), + ObjectForKeySel(getKeywordSelector(Ctx, "objectForKey")), + SetObjectForKeyedSubscriptSel( + getKeywordSelector(Ctx, "setObject", "forKeyedSubscript")), + SetObjectForKeySel(getKeywordSelector(Ctx, "setObject", "forKey")) {} + + ProgramStateRef evalAssume(ProgramStateRef State, + SVal Cond, + bool Assumption) const { + const SymbolRef CondS = Cond.getAsSymbol(); + if (!CondS || CondS->computeComplexity() > ComplexityThreshold) + return State; + + for (auto B=CondS->symbol_begin(), E=CondS->symbol_end(); B != E; ++B) { + const SymbolRef Antecedent = *B; + State = addImplication(Antecedent, State, true); + State = addImplication(Antecedent, State, false); + } + + return State; + } + + void checkPostCall(const CallEvent &Call, CheckerContext &C) const { + // Only trust annotations for system headers for non-protocols. + if (!Call.isInSystemHeader()) + return; + + ProgramStateRef State = C.getState(); + + if (isNonNullPtr(Call, C)) + if (auto L = Call.getReturnValue().getAs<Loc>()) + State = State->assume(*L, /*Assumption=*/true); + + C.addTransition(State); + } + + void checkPostObjCMessage(const ObjCMethodCall &Msg, + CheckerContext &C) const { + const ObjCInterfaceDecl *ID = Msg.getReceiverInterface(); + if (!ID) + return; + + ProgramStateRef State = C.getState(); + + // Index to setter for NSMutableDictionary is assumed to be non-null, + // as an exception is thrown otherwise. + if (interfaceHasSuperclass(ID, "NSMutableDictionary") && + (Msg.getSelector() == SetObjectForKeyedSubscriptSel || + Msg.getSelector() == SetObjectForKeySel)) { + if (auto L = Msg.getArgSVal(1).getAs<Loc>()) + State = State->assume(*L, /*Assumption=*/true); + } + + // Record an implication: index is non-null if the output is non-null. + if (interfaceHasSuperclass(ID, "NSDictionary") && + (Msg.getSelector() == ObjectForKeyedSubscriptSel || + Msg.getSelector() == ObjectForKeySel)) { + SymbolRef ArgS = Msg.getArgSVal(0).getAsSymbol(); + SymbolRef RetS = Msg.getReturnValue().getAsSymbol(); + + if (ArgS && RetS) { + // Emulate an implication: the argument is non-null if + // the return value is non-null. + State = State->set<NonNullImplicationMap>(RetS, ArgS); + + // Conversely, when the argument is null, the return value + // is definitely null. + State = State->set<NullImplicationMap>(ArgS, RetS); + } + } + + C.addTransition(State); + } + + void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + + State = dropDeadFromGDM<NullImplicationMap>(SymReaper, State); + State = dropDeadFromGDM<NonNullImplicationMap>(SymReaper, State); + + C.addTransition(State); + } + +private: + + /// \returns State with GDM \p MapName where all dead symbols were + // removed. + template <typename MapName> + ProgramStateRef dropDeadFromGDM(SymbolReaper &SymReaper, + ProgramStateRef State) const { + for (const std::pair<SymbolRef, SymbolRef> &P : State->get<MapName>()) + if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second)) + State = State->remove<MapName>(P.first); + return State; + } + + /// \returns Whether we trust the result of the method call to be + /// a non-null pointer. + bool isNonNullPtr(const CallEvent &Call, CheckerContext &C) const { + QualType ExprRetType = Call.getResultType(); + if (!ExprRetType->isAnyPointerType()) + return false; + + if (getNullabilityAnnotation(ExprRetType) == Nullability::Nonnull) + return true; + + // The logic for ObjC instance method calls is more complicated, + // as the return value is nil when the receiver is nil. + if (!isa<ObjCMethodCall>(&Call)) + return false; + + const auto *MCall = cast<ObjCMethodCall>(&Call); + const ObjCMethodDecl *MD = MCall->getDecl(); + + // Distrust protocols. + if (isa<ObjCProtocolDecl>(MD->getDeclContext())) + return false; + + QualType DeclRetType = MD->getReturnType(); + if (getNullabilityAnnotation(DeclRetType) != Nullability::Nonnull) + return false; + + // For class messages it is sufficient for the declaration to be + // annotated _Nonnull. + if (!MCall->isInstanceMessage()) + return true; + + // Alternatively, the analyzer could know that the receiver is not null. + SVal Receiver = MCall->getReceiverSVal(); + ConditionTruthVal TV = C.getState()->isNonNull(Receiver); + if (TV.isConstrainedTrue()) + return true; + + return false; + } + + /// \return Whether \p ID has a superclass by the name \p ClassName. + bool interfaceHasSuperclass(const ObjCInterfaceDecl *ID, + StringRef ClassName) const { + if (ID->getIdentifier()->getName() == ClassName) + return true; + + if (const ObjCInterfaceDecl *Super = ID->getSuperClass()) + return interfaceHasSuperclass(Super, ClassName); + + return false; + } + + + /// \return a state with an optional implication added (if exists) + /// from a map of recorded implications. + /// If \p Negated is true, checks NullImplicationMap, and assumes + /// the negation of \p Antecedent. + /// Checks NonNullImplicationMap and assumes \p Antecedent otherwise. + ProgramStateRef addImplication(SymbolRef Antecedent, + ProgramStateRef InputState, + bool Negated) const { + if (!InputState) + return nullptr; + SValBuilder &SVB = InputState->getStateManager().getSValBuilder(); + const SymbolRef *Consequent = + Negated ? InputState->get<NonNullImplicationMap>(Antecedent) + : InputState->get<NullImplicationMap>(Antecedent); + if (!Consequent) + return InputState; + + SVal AntecedentV = SVB.makeSymbolVal(Antecedent); + ProgramStateRef State = InputState; + + if ((Negated && InputState->isNonNull(AntecedentV).isConstrainedTrue()) + || (!Negated && InputState->isNull(AntecedentV).isConstrainedTrue())) { + SVal ConsequentS = SVB.makeSymbolVal(*Consequent); + State = InputState->assume(ConsequentS.castAs<DefinedSVal>(), Negated); + if (!State) + return nullptr; + + // Drop implications from the map. + if (Negated) { + State = State->remove<NonNullImplicationMap>(Antecedent); + State = State->remove<NullImplicationMap>(*Consequent); + } else { + State = State->remove<NullImplicationMap>(Antecedent); + State = State->remove<NonNullImplicationMap>(*Consequent); + } + } + + return State; + } +}; + +} // end empty namespace + + +void ento::registerTrustNonnullChecker(CheckerManager &Mgr) { + Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext()); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp new file mode 100644 index 000000000000..d7fad4e475ab --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -0,0 +1,111 @@ +//=== UndefBranchChecker.cpp -----------------------------------*- 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 UndefBranchChecker, which checks for undefined branch +// condition. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include <utility> + +using namespace clang; +using namespace ento; + +namespace { + +class UndefBranchChecker : public Checker<check::BranchCondition> { + mutable std::unique_ptr<BuiltinBug> BT; + + struct FindUndefExpr { + ProgramStateRef St; + const LocationContext *LCtx; + + FindUndefExpr(ProgramStateRef S, const LocationContext *L) + : St(std::move(S)), LCtx(L) {} + + const Expr *FindExpr(const Expr *Ex) { + if (!MatchesCriteria(Ex)) + return nullptr; + + for (const Stmt *SubStmt : Ex->children()) + if (const Expr *ExI = dyn_cast_or_null<Expr>(SubStmt)) + if (const Expr *E2 = FindExpr(ExI)) + return E2; + + return Ex; + } + + bool MatchesCriteria(const Expr *Ex) { + return St->getSVal(Ex, LCtx).isUndef(); + } + }; + +public: + void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const; +}; + +} + +void UndefBranchChecker::checkBranchCondition(const Stmt *Condition, + CheckerContext &Ctx) const { + SVal X = Ctx.getSVal(Condition); + if (X.isUndef()) { + // Generate a sink node, which implicitly marks both outgoing branches as + // infeasible. + ExplodedNode *N = Ctx.generateErrorNode(); + if (N) { + if (!BT) + BT.reset(new BuiltinBug( + this, "Branch condition evaluates to a garbage value")); + + // What's going on here: we want to highlight the subexpression of the + // condition that is the most likely source of the "uninitialized + // branch condition." We do a recursive walk of the condition's + // subexpressions and roughly look for the most nested subexpression + // that binds to Undefined. We then highlight that expression's range. + + // Get the predecessor node and check if is a PostStmt with the Stmt + // being the terminator condition. We want to inspect the state + // of that node instead because it will contain main information about + // the subexpressions. + + // Note: any predecessor will do. They should have identical state, + // since all the BlockEdge did was act as an error sink since the value + // had to already be undefined. + assert (!N->pred_empty()); + const Expr *Ex = cast<Expr>(Condition); + ExplodedNode *PrevN = *N->pred_begin(); + ProgramPoint P = PrevN->getLocation(); + ProgramStateRef St = N->getState(); + + if (Optional<PostStmt> PS = P.getAs<PostStmt>()) + if (PS->getStmt() == Ex) + St = PrevN->getState(); + + FindUndefExpr FindIt(St, Ctx.getLocationContext()); + Ex = FindIt.FindExpr(Ex); + + // Emit the bug report. + auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N); + bugreporter::trackExpressionValue(N, Ex, *R); + R->addRange(Ex->getSourceRange()); + + Ctx.emitReport(std::move(R)); + } + } +} + +void ento::registerUndefBranchChecker(CheckerManager &mgr) { + mgr.registerChecker<UndefBranchChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp new file mode 100644 index 000000000000..8a625227b81e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -0,0 +1,102 @@ +// UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker detects blocks that capture uninitialized values. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/Attr.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefCapturedBlockVarChecker + : public Checker< check::PostStmt<BlockExpr> > { + mutable std::unique_ptr<BugType> BT; + +public: + void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const; +}; +} // end anonymous namespace + +static const DeclRefExpr *FindBlockDeclRefExpr(const Stmt *S, + const VarDecl *VD) { + if (const DeclRefExpr *BR = dyn_cast<DeclRefExpr>(S)) + if (BR->getDecl() == VD) + return BR; + + for (const Stmt *Child : S->children()) + if (Child) + if (const DeclRefExpr *BR = FindBlockDeclRefExpr(Child, VD)) + return BR; + + return nullptr; +} + +void +UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE, + CheckerContext &C) const { + if (!BE->getBlockDecl()->hasCaptures()) + return; + + ProgramStateRef state = C.getState(); + auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion()); + + BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(), + E = R->referenced_vars_end(); + + for (; I != E; ++I) { + // This VarRegion is the region associated with the block; we need + // the one associated with the encompassing context. + const VarRegion *VR = I.getCapturedRegion(); + const VarDecl *VD = VR->getDecl(); + + if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage()) + continue; + + // Get the VarRegion associated with VD in the local stack frame. + if (Optional<UndefinedVal> V = + state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) { + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT) + BT.reset( + new BuiltinBug(this, "uninitialized variable captured by block")); + + // Generate a bug report. + SmallString<128> buf; + llvm::raw_svector_ostream os(buf); + + os << "Variable '" << VD->getName() + << "' is uninitialized when captured by block"; + + auto R = llvm::make_unique<BugReport>(*BT, os.str(), N); + if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) + R->addRange(Ex->getSourceRange()); + R->addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + *V, VR, /*EnableNullFPSuppression*/ false)); + R->disablePathPruning(); + // need location of block + C.emitReport(std::move(R)); + } + } + } +} + +void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) { + mgr.registerChecker<UndefCapturedBlockVarChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp new file mode 100644 index 000000000000..624cff6048fd --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -0,0 +1,188 @@ +//=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefResultChecker, a builtin check in ExprEngine that +// performs checks for undefined results of non-assignment binary operators. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefResultChecker + : public Checker< check::PostStmt<BinaryOperator> > { + + mutable std::unique_ptr<BugType> BT; + +public: + void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const; +}; +} // end anonymous namespace + +static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) { + ProgramStateRef state = C.getState(); + + if (!isa<ArraySubscriptExpr>(Ex)) + return false; + + SVal Loc = C.getSVal(Ex); + if (!Loc.isValid()) + return false; + + const MemRegion *MR = Loc.castAs<loc::MemRegionVal>().getRegion(); + const ElementRegion *ER = dyn_cast<ElementRegion>(MR); + if (!ER) + return false; + + DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>(); + DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements( + state, ER->getSuperRegion(), ER->getValueType()); + ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true); + ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false); + return StOutBound && !StInBound; +} + +static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C) { + return C.isGreaterOrEqual( + B->getRHS(), C.getASTContext().getIntWidth(B->getLHS()->getType())); +} + +static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B, + CheckerContext &C) { + SValBuilder &SB = C.getSValBuilder(); + ProgramStateRef State = C.getState(); + const llvm::APSInt *LHS = SB.getKnownValue(State, C.getSVal(B->getLHS())); + const llvm::APSInt *RHS = SB.getKnownValue(State, C.getSVal(B->getRHS())); + assert(LHS && RHS && "Values unknown, inconsistent state"); + return (unsigned)RHS->getZExtValue() > LHS->countLeadingZeros(); +} + +void UndefResultChecker::checkPostStmt(const BinaryOperator *B, + CheckerContext &C) const { + if (C.getSVal(B).isUndef()) { + + // Do not report assignments of uninitialized values inside swap functions. + // This should allow to swap partially uninitialized structs + // (radar://14129997) + if (const FunctionDecl *EnclosingFunctionDecl = + dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl())) + if (C.getCalleeName(EnclosingFunctionDecl) == "swap") + return; + + // Generate an error node. + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + + if (!BT) + BT.reset( + new BuiltinBug(this, "Result of operation is garbage or undefined")); + + SmallString<256> sbuf; + llvm::raw_svector_ostream OS(sbuf); + const Expr *Ex = nullptr; + bool isLeft = true; + + if (C.getSVal(B->getLHS()).isUndef()) { + Ex = B->getLHS()->IgnoreParenCasts(); + isLeft = true; + } + else if (C.getSVal(B->getRHS()).isUndef()) { + Ex = B->getRHS()->IgnoreParenCasts(); + isLeft = false; + } + + if (Ex) { + OS << "The " << (isLeft ? "left" : "right") << " operand of '" + << BinaryOperator::getOpcodeStr(B->getOpcode()) + << "' is a garbage value"; + if (isArrayIndexOutOfBounds(C, Ex)) + OS << " due to array index out of bounds"; + } else { + // Neither operand was undefined, but the result is undefined. + if ((B->getOpcode() == BinaryOperatorKind::BO_Shl || + B->getOpcode() == BinaryOperatorKind::BO_Shr) && + C.isNegative(B->getRHS())) { + OS << "The result of the " + << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left" + : "right") + << " shift is undefined because the right operand is negative"; + Ex = B->getRHS(); + } else if ((B->getOpcode() == BinaryOperatorKind::BO_Shl || + B->getOpcode() == BinaryOperatorKind::BO_Shr) && + isShiftOverflow(B, C)) { + + OS << "The result of the " + << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left" + : "right") + << " shift is undefined due to shifting by "; + Ex = B->getRHS(); + + SValBuilder &SB = C.getSValBuilder(); + const llvm::APSInt *I = + SB.getKnownValue(C.getState(), C.getSVal(B->getRHS())); + if (!I) + OS << "a value that is"; + else if (I->isUnsigned()) + OS << '\'' << I->getZExtValue() << "\', which is"; + else + OS << '\'' << I->getSExtValue() << "\', which is"; + + OS << " greater or equal to the width of type '" + << B->getLHS()->getType().getAsString() << "'."; + } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl && + C.isNegative(B->getLHS())) { + OS << "The result of the left shift is undefined because the left " + "operand is negative"; + Ex = B->getLHS(); + } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl && + isLeftShiftResultUnrepresentable(B, C)) { + ProgramStateRef State = C.getState(); + SValBuilder &SB = C.getSValBuilder(); + const llvm::APSInt *LHS = + SB.getKnownValue(State, C.getSVal(B->getLHS())); + const llvm::APSInt *RHS = + SB.getKnownValue(State, C.getSVal(B->getRHS())); + OS << "The result of the left shift is undefined due to shifting \'" + << LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue() + << "\', which is unrepresentable in the unsigned version of " + << "the return type \'" << B->getLHS()->getType().getAsString() + << "\'"; + Ex = B->getLHS(); + } else { + OS << "The result of the '" + << BinaryOperator::getOpcodeStr(B->getOpcode()) + << "' expression is undefined"; + } + } + auto report = llvm::make_unique<BugReport>(*BT, OS.str(), N); + if (Ex) { + report->addRange(Ex->getSourceRange()); + bugreporter::trackExpressionValue(N, Ex, *report); + } + else + bugreporter::trackExpressionValue(N, B, *report); + + C.emitReport(std::move(report)); + } +} + +void ento::registerUndefResultChecker(CheckerManager &mgr) { + mgr.registerChecker<UndefResultChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp new file mode 100644 index 000000000000..1d78d7cebd67 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -0,0 +1,64 @@ +//===--- UndefinedArraySubscriptChecker.h ----------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedArraySubscriptChecker, a builtin check in ExprEngine +// that performs checks for undefined array subscripts. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/DeclCXX.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefinedArraySubscriptChecker + : public Checker< check::PreStmt<ArraySubscriptExpr> > { + mutable std::unique_ptr<BugType> BT; + +public: + void checkPreStmt(const ArraySubscriptExpr *A, CheckerContext &C) const; +}; +} // end anonymous namespace + +void +UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A, + CheckerContext &C) const { + const Expr *Index = A->getIdx(); + if (!C.getSVal(Index).isUndef()) + return; + + // Sema generates anonymous array variables for copying array struct fields. + // Don't warn if we're in an implicitly-generated constructor. + const Decl *D = C.getLocationContext()->getDecl(); + if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D)) + if (Ctor->isDefaulted()) + return; + + ExplodedNode *N = C.generateErrorNode(); + if (!N) + return; + if (!BT) + BT.reset(new BuiltinBug(this, "Array subscript is undefined")); + + // Generate a report for this bug. + auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N); + R->addRange(A->getIdx()->getSourceRange()); + bugreporter::trackExpressionValue(N, A->getIdx(), *R); + C.emitReport(std::move(R)); +} + +void ento::registerUndefinedArraySubscriptChecker(CheckerManager &mgr) { + mgr.registerChecker<UndefinedArraySubscriptChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp new file mode 100644 index 000000000000..8e10bfdd2f3c --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -0,0 +1,122 @@ +//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedAssignmentChecker, a builtin check in ExprEngine that +// checks for assigning undefined values. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefinedAssignmentChecker + : public Checker<check::Bind> { + mutable std::unique_ptr<BugType> BT; + +public: + void checkBind(SVal location, SVal val, const Stmt *S, + CheckerContext &C) const; +}; +} + +void UndefinedAssignmentChecker::checkBind(SVal location, SVal val, + const Stmt *StoreE, + CheckerContext &C) const { + if (!val.isUndef()) + return; + + // Do not report assignments of uninitialized values inside swap functions. + // This should allow to swap partially uninitialized structs + // (radar://14129997) + if (const FunctionDecl *EnclosingFunctionDecl = + dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl())) + if (C.getCalleeName(EnclosingFunctionDecl) == "swap") + return; + + ExplodedNode *N = C.generateErrorNode(); + + if (!N) + return; + + static const char *const DefaultMsg = + "Assigned value is garbage or undefined"; + if (!BT) + BT.reset(new BuiltinBug(this, DefaultMsg)); + + // Generate a report for this bug. + llvm::SmallString<128> Str; + llvm::raw_svector_ostream OS(Str); + + const Expr *ex = nullptr; + + while (StoreE) { + if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) { + OS << "The expression is an uninitialized value. " + "The computed value will also be garbage"; + + ex = U->getSubExpr(); + break; + } + + if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) { + if (B->isCompoundAssignmentOp()) { + if (C.getSVal(B->getLHS()).isUndef()) { + OS << "The left expression of the compound assignment is an " + "uninitialized value. The computed value will also be garbage"; + ex = B->getLHS(); + break; + } + } + + ex = B->getRHS(); + break; + } + + if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) { + const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + ex = VD->getInit(); + } + + if (const auto *CD = + dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) { + if (CD->isImplicit()) { + for (auto I : CD->inits()) { + if (I->getInit()->IgnoreImpCasts() == StoreE) { + OS << "Value assigned to field '" << I->getMember()->getName() + << "' in implicit constructor is garbage or undefined"; + break; + } + } + } + } + + break; + } + + if (OS.str().empty()) + OS << DefaultMsg; + + auto R = llvm::make_unique<BugReport>(*BT, OS.str(), N); + if (ex) { + R->addRange(ex->getSourceRange()); + bugreporter::trackExpressionValue(N, ex, *R); + } + C.emitReport(std::move(R)); +} + +void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) { + mgr.registerChecker<UndefinedAssignmentChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h new file mode 100644 index 000000000000..c3291a21c164 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h @@ -0,0 +1,349 @@ +//===----- UninitializedObject.h ---------------------------------*- 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 helper classes for UninitializedObjectChecker and +// documentation about the logic of it. +// +// The checker reports uninitialized fields in objects created after a +// constructor call. +// +// This checker has several options: +// - "Pedantic" (boolean). If its not set or is set to false, the checker +// won't emit warnings for objects that don't have at least one initialized +// field. This may be set with +// +// `-analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true`. +// +// - "NotesAsWarnings" (boolean). If set to true, the checker will emit a +// warning for each uninitialized field, as opposed to emitting one warning +// per constructor call, and listing the uninitialized fields that belongs +// to it in notes. Defaults to false. +// +// `-analyzer-config \ +// alpha.cplusplus.UninitializedObject:NotesAsWarnings=true`. +// +// - "CheckPointeeInitialization" (boolean). If set to false, the checker will +// not analyze the pointee of pointer/reference fields, and will only check +// whether the object itself is initialized. Defaults to false. +// +// `-analyzer-config \ +// alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=true`. +// +// - "IgnoreRecordsWithField" (string). If supplied, the checker will not +// analyze structures that have a field with a name or type name that +// matches the given pattern. Defaults to "". +// +// `-analyzer-config \ +// alpha.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"`. +// +// TODO: With some clever heuristics, some pointers should be dereferenced +// by default. For example, if the pointee is constructed within the +// constructor call, it's reasonable to say that no external object +// references it, and we wouldn't generate multiple report on the same +// pointee. +// +// Most of the following methods as well as the checker itself is defined in +// UninitializedObjectChecker.cpp. +// +// Some methods are implemented in UninitializedPointee.cpp, to reduce the +// complexity of the main checker file. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H +#define LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +namespace clang { +namespace ento { + +struct UninitObjCheckerOptions { + bool IsPedantic = false; + bool ShouldConvertNotesToWarnings = false; + bool CheckPointeeInitialization = false; + std::string IgnoredRecordsWithFieldPattern; +}; + +/// A lightweight polymorphic wrapper around FieldRegion *. We'll use this +/// interface to store addinitional information about fields. As described +/// later, a list of these objects (i.e. "fieldchain") will be constructed and +/// used for printing note messages should an uninitialized value be found. +class FieldNode { +protected: + const FieldRegion *FR; + + /// FieldNodes are never meant to be created on the heap, see + /// FindUninitializedFields::addFieldToUninits(). + /* non-virtual */ ~FieldNode() = default; + +public: + FieldNode(const FieldRegion *FR) : FR(FR) {} + + // We'll delete all of these special member functions to force the users of + // this interface to only store references to FieldNode objects in containers. + FieldNode() = delete; + FieldNode(const FieldNode &) = delete; + FieldNode(FieldNode &&) = delete; + FieldNode &operator=(const FieldNode &) = delete; + FieldNode &operator=(const FieldNode &&) = delete; + + void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(this); } + + /// Helper method for uniqueing. + bool isSameRegion(const FieldRegion *OtherFR) const { + // Special FieldNode descendants may wrap nullpointers (for example if they + // describe a special relationship between two elements of the fieldchain) + // -- we wouldn't like to unique these objects. + if (FR == nullptr) + return false; + + return FR == OtherFR; + } + + const FieldRegion *getRegion() const { return FR; } + const FieldDecl *getDecl() const { + assert(FR); + return FR->getDecl(); + } + + // When a fieldchain is printed, it will have the following format (without + // newline, indices are in order of insertion, from 1 to n): + // + // <note_message_n>'<prefix_n><prefix_n-1>...<prefix_1> + // this-><node_1><separator_1><node_2><separator_2>...<node_n>' + + /// If this is the last element of the fieldchain, this method will print the + /// note message associated with it. + /// The note message should state something like "uninitialized field" or + /// "uninitialized pointee" etc. + virtual void printNoteMsg(llvm::raw_ostream &Out) const = 0; + + /// Print any prefixes before the fieldchain. Could contain casts, etc. + virtual void printPrefix(llvm::raw_ostream &Out) const = 0; + + /// Print the node. Should contain the name of the field stored in FR. + virtual void printNode(llvm::raw_ostream &Out) const = 0; + + /// Print the separator. For example, fields may be separated with '.' or + /// "->". + virtual void printSeparator(llvm::raw_ostream &Out) const = 0; + + virtual bool isBase() const { return false; } +}; + +/// Returns with Field's name. This is a helper function to get the correct name +/// even if Field is a captured lambda variable. +std::string getVariableName(const FieldDecl *Field); + +/// Represents a field chain. A field chain is a list of fields where the first +/// element of the chain is the object under checking (not stored), and every +/// other element is a field, and the element that precedes it is the object +/// that contains it. +/// +/// Note that this class is immutable (essentially a wrapper around an +/// ImmutableList), new FieldChainInfo objects may be created by member +/// functions such as add() and replaceHead(). +class FieldChainInfo { +public: + using FieldChain = llvm::ImmutableList<const FieldNode &>; + +private: + FieldChain::Factory &ChainFactory; + FieldChain Chain; + + FieldChainInfo(FieldChain::Factory &F, FieldChain NewChain) + : FieldChainInfo(F) { + Chain = NewChain; + } + +public: + FieldChainInfo() = delete; + FieldChainInfo(FieldChain::Factory &F) : ChainFactory(F) {} + FieldChainInfo(const FieldChainInfo &Other) = default; + + /// Constructs a new FieldChainInfo object with \p FN appended. + template <class FieldNodeT> FieldChainInfo add(const FieldNodeT &FN); + + /// Constructs a new FieldChainInfo object with \p FN as the new head of the + /// list. + template <class FieldNodeT> FieldChainInfo replaceHead(const FieldNodeT &FN); + + bool contains(const FieldRegion *FR) const; + bool isEmpty() const { return Chain.isEmpty(); } + + const FieldNode &getHead() const { return Chain.getHead(); } + const FieldRegion *getUninitRegion() const { return getHead().getRegion(); } + + void printNoteMsg(llvm::raw_ostream &Out) const; +}; + +using UninitFieldMap = std::map<const FieldRegion *, llvm::SmallString<50>>; + +/// Searches for and stores uninitialized fields in a non-union object. +class FindUninitializedFields { + ProgramStateRef State; + const TypedValueRegion *const ObjectR; + + const UninitObjCheckerOptions Opts; + bool IsAnyFieldInitialized = false; + + FieldChainInfo::FieldChain::Factory ChainFactory; + + /// A map for assigning uninitialized regions to note messages. For example, + /// + /// struct A { + /// int x; + /// }; + /// + /// A a; + /// + /// After analyzing `a`, the map will contain a pair for `a.x`'s region and + /// the note message "uninitialized field 'this->x'. + UninitFieldMap UninitFields; + +public: + /// Constructs the FindUninitializedField object, searches for and stores + /// uninitialized fields in R. + FindUninitializedFields(ProgramStateRef State, + const TypedValueRegion *const R, + const UninitObjCheckerOptions &Opts); + + /// Returns with the modified state and a map of (uninitialized region, + /// note message) pairs. + std::pair<ProgramStateRef, const UninitFieldMap &> getResults() { + return {State, UninitFields}; + } + + /// Returns whether the analyzed region contains at least one initialized + /// field. Note that this includes subfields as well, not just direct ones, + /// and will return false if an uninitialized pointee is found with + /// CheckPointeeInitialization enabled. + bool isAnyFieldInitialized() { return IsAnyFieldInitialized; } + +private: + // For the purposes of this checker, we'll regard the analyzed region as a + // directed tree, where + // * the root is the object under checking + // * every node is an object that is + // - a union + // - a non-union record + // - dereferenceable (see isDereferencableType()) + // - an array + // - of a primitive type (see isPrimitiveType()) + // * the parent of each node is the object that contains it + // * every leaf is an array, a primitive object, a nullptr or an undefined + // pointer. + // + // Example: + // + // struct A { + // struct B { + // int x, y = 0; + // }; + // B b; + // int *iptr = new int; + // B* bptr; + // + // A() {} + // }; + // + // The directed tree: + // + // ->x + // / + // ->b--->y + // / + // A-->iptr->(int value) + // \ + // ->bptr + // + // From this we'll construct a vector of fieldchains, where each fieldchain + // represents an uninitialized field. An uninitialized field may be a + // primitive object, a pointer, a pointee or a union without a single + // initialized field. + // In the above example, for the default constructor call we'll end up with + // these fieldchains: + // + // this->b.x + // this->iptr (pointee uninit) + // this->bptr (pointer uninit) + // + // We'll traverse each node of the above graph with the appropriate one of + // these methods: + + /// Checks the region of a union object, and returns true if no field is + /// initialized within the region. + bool isUnionUninit(const TypedValueRegion *R); + + /// Checks a region of a non-union object, and returns true if an + /// uninitialized field is found within the region. + bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain); + + /// Checks a region of a pointer or reference object, and returns true if the + /// ptr/ref object itself or any field within the pointee's region is + /// uninitialized. + bool isDereferencableUninit(const FieldRegion *FR, FieldChainInfo LocalChain); + + /// Returns true if the value of a primitive object is uninitialized. + bool isPrimitiveUninit(const SVal &V); + + // Note that we don't have a method for arrays -- the elements of an array are + // often left uninitialized intentionally even when it is of a C++ record + // type, so we'll assume that an array is always initialized. + // TODO: Add a support for nonloc::LocAsInteger. + + /// Processes LocalChain and attempts to insert it into UninitFields. Returns + /// true on success. Also adds the head of the list and \p PointeeR (if + /// supplied) to the GDM as already analyzed objects. + /// + /// Since this class analyzes regions with recursion, we'll only store + /// references to temporary FieldNode objects created on the stack. This means + /// that after analyzing a leaf of the directed tree described above, the + /// elements LocalChain references will be destructed, so we can't store it + /// directly. + bool addFieldToUninits(FieldChainInfo LocalChain, + const MemRegion *PointeeR = nullptr); +}; + +/// Returns true if T is a primitive type. An object of a primitive type only +/// needs to be analyzed as much as checking whether their value is undefined. +inline bool isPrimitiveType(const QualType &T) { + return T->isBuiltinType() || T->isEnumeralType() || + T->isMemberPointerType() || T->isBlockPointerType() || + T->isFunctionType(); +} + +inline bool isDereferencableType(const QualType &T) { + return T->isAnyPointerType() || T->isReferenceType(); +} + +// Template method definitions. + +template <class FieldNodeT> +inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) { + assert(!contains(FN.getRegion()) && + "Can't add a field that is already a part of the " + "fieldchain! Is this a cyclic reference?"); + + FieldChainInfo NewChain = *this; + NewChain.Chain = ChainFactory.add(FN, Chain); + return NewChain; +} + +template <class FieldNodeT> +inline FieldChainInfo FieldChainInfo::replaceHead(const FieldNodeT &FN) { + FieldChainInfo NewChain(ChainFactory, Chain.getTail()); + return NewChain.add(FN); +} + +} // end of namespace ento +} // end of namespace clang + +#endif // LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp new file mode 100644 index 000000000000..208e303e8295 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp @@ -0,0 +1,538 @@ +//===----- UninitializedObjectChecker.cpp ------------------------*- 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 a checker that reports uninitialized fields in objects +// created after a constructor call. +// +// To read about command line options and how the checker works, refer to the +// top of the file and inline comments in UninitializedObject.h. +// +// Some of the logic is implemented in UninitializedPointee.cpp, to reduce the +// complexity of this file. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "UninitializedObject.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" + +using namespace clang; +using namespace clang::ento; + +/// We'll mark fields (and pointee of fields) that are confirmed to be +/// uninitialized as already analyzed. +REGISTER_SET_WITH_PROGRAMSTATE(AnalyzedRegions, const MemRegion *) + +namespace { + +class UninitializedObjectChecker + : public Checker<check::EndFunction, check::DeadSymbols> { + std::unique_ptr<BuiltinBug> BT_uninitField; + +public: + // The fields of this struct will be initialized when registering the checker. + UninitObjCheckerOptions Opts; + + UninitializedObjectChecker() + : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {} + + void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; +}; + +/// A basic field type, that is not a pointer or a reference, it's dynamic and +/// static type is the same. +class RegularField final : public FieldNode { +public: + RegularField(const FieldRegion *FR) : FieldNode(FR) {} + + virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + Out << "uninitialized field "; + } + + virtual void printPrefix(llvm::raw_ostream &Out) const override {} + + virtual void printNode(llvm::raw_ostream &Out) const override { + Out << getVariableName(getDecl()); + } + + virtual void printSeparator(llvm::raw_ostream &Out) const override { + Out << '.'; + } +}; + +/// Represents that the FieldNode that comes after this is declared in a base +/// of the previous FieldNode. As such, this descendant doesn't wrap a +/// FieldRegion, and is purely a tool to describe a relation between two other +/// FieldRegion wrapping descendants. +class BaseClass final : public FieldNode { + const QualType BaseClassT; + +public: + BaseClass(const QualType &T) : FieldNode(nullptr), BaseClassT(T) { + assert(!T.isNull()); + assert(T->getAsCXXRecordDecl()); + } + + virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + llvm_unreachable("This node can never be the final node in the " + "fieldchain!"); + } + + virtual void printPrefix(llvm::raw_ostream &Out) const override {} + + virtual void printNode(llvm::raw_ostream &Out) const override { + Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::"; + } + + virtual void printSeparator(llvm::raw_ostream &Out) const override {} + + virtual bool isBase() const override { return true; } +}; + +} // end of anonymous namespace + +// Utility function declarations. + +/// Returns the region that was constructed by CtorDecl, or nullptr if that +/// isn't possible. +static const TypedValueRegion * +getConstructedRegion(const CXXConstructorDecl *CtorDecl, + CheckerContext &Context); + +/// Checks whether the object constructed by \p Ctor will be analyzed later +/// (e.g. if the object is a field of another object, in which case we'd check +/// it multiple times). +static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor, + CheckerContext &Context); + +/// Checks whether RD contains a field with a name or type name that matches +/// \p Pattern. +static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern); + +//===----------------------------------------------------------------------===// +// Methods for UninitializedObjectChecker. +//===----------------------------------------------------------------------===// + +void UninitializedObjectChecker::checkEndFunction( + const ReturnStmt *RS, CheckerContext &Context) const { + + const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>( + Context.getLocationContext()->getDecl()); + if (!CtorDecl) + return; + + if (!CtorDecl->isUserProvided()) + return; + + if (CtorDecl->getParent()->isUnion()) + return; + + // This avoids essentially the same error being reported multiple times. + if (willObjectBeAnalyzedLater(CtorDecl, Context)) + return; + + const TypedValueRegion *R = getConstructedRegion(CtorDecl, Context); + if (!R) + return; + + FindUninitializedFields F(Context.getState(), R, Opts); + + std::pair<ProgramStateRef, const UninitFieldMap &> UninitInfo = + F.getResults(); + + ProgramStateRef UpdatedState = UninitInfo.first; + const UninitFieldMap &UninitFields = UninitInfo.second; + + if (UninitFields.empty()) { + Context.addTransition(UpdatedState); + return; + } + + // There are uninitialized fields in the record. + + ExplodedNode *Node = Context.generateNonFatalErrorNode(UpdatedState); + if (!Node) + return; + + PathDiagnosticLocation LocUsedForUniqueing; + const Stmt *CallSite = Context.getStackFrame()->getCallSite(); + if (CallSite) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin( + CallSite, Context.getSourceManager(), Node->getLocationContext()); + + // For Plist consumers that don't support notes just yet, we'll convert notes + // to warnings. + if (Opts.ShouldConvertNotesToWarnings) { + for (const auto &Pair : UninitFields) { + + auto Report = llvm::make_unique<BugReport>( + *BT_uninitField, Pair.second, Node, LocUsedForUniqueing, + Node->getLocationContext()->getDecl()); + Context.emitReport(std::move(Report)); + } + return; + } + + SmallString<100> WarningBuf; + llvm::raw_svector_ostream WarningOS(WarningBuf); + WarningOS << UninitFields.size() << " uninitialized field" + << (UninitFields.size() == 1 ? "" : "s") + << " at the end of the constructor call"; + + auto Report = llvm::make_unique<BugReport>( + *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing, + Node->getLocationContext()->getDecl()); + + for (const auto &Pair : UninitFields) { + Report->addNote(Pair.second, + PathDiagnosticLocation::create(Pair.first->getDecl(), + Context.getSourceManager())); + } + Context.emitReport(std::move(Report)); +} + +void UninitializedObjectChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + for (const MemRegion *R : State->get<AnalyzedRegions>()) { + if (!SR.isLiveRegion(R)) + State = State->remove<AnalyzedRegions>(R); + } +} + +//===----------------------------------------------------------------------===// +// Methods for FindUninitializedFields. +//===----------------------------------------------------------------------===// + +FindUninitializedFields::FindUninitializedFields( + ProgramStateRef State, const TypedValueRegion *const R, + const UninitObjCheckerOptions &Opts) + : State(State), ObjectR(R), Opts(Opts) { + + isNonUnionUninit(ObjectR, FieldChainInfo(ChainFactory)); + + // In non-pedantic mode, if ObjectR doesn't contain a single initialized + // field, we'll assume that Object was intentionally left uninitialized. + if (!Opts.IsPedantic && !isAnyFieldInitialized()) + UninitFields.clear(); +} + +bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain, + const MemRegion *PointeeR) { + const FieldRegion *FR = Chain.getUninitRegion(); + + assert((PointeeR || !isDereferencableType(FR->getDecl()->getType())) && + "One must also pass the pointee region as a parameter for " + "dereferenceable fields!"); + + if (State->contains<AnalyzedRegions>(FR)) + return false; + + if (PointeeR) { + if (State->contains<AnalyzedRegions>(PointeeR)) { + return false; + } + State = State->add<AnalyzedRegions>(PointeeR); + } + + State = State->add<AnalyzedRegions>(FR); + + if (State->getStateManager().getContext().getSourceManager().isInSystemHeader( + FR->getDecl()->getLocation())) + return false; + + UninitFieldMap::mapped_type NoteMsgBuf; + llvm::raw_svector_ostream OS(NoteMsgBuf); + Chain.printNoteMsg(OS); + return UninitFields.insert({FR, std::move(NoteMsgBuf)}).second; +} + +bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R, + FieldChainInfo LocalChain) { + assert(R->getValueType()->isRecordType() && + !R->getValueType()->isUnionType() && + "This method only checks non-union record objects!"); + + const RecordDecl *RD = R->getValueType()->getAsRecordDecl()->getDefinition(); + + if (!RD) { + IsAnyFieldInitialized = true; + return true; + } + + if (!Opts.IgnoredRecordsWithFieldPattern.empty() && + shouldIgnoreRecord(RD, Opts.IgnoredRecordsWithFieldPattern)) { + IsAnyFieldInitialized = true; + return false; + } + + bool ContainsUninitField = false; + + // Are all of this non-union's fields initialized? + for (const FieldDecl *I : RD->fields()) { + + const auto FieldVal = + State->getLValue(I, loc::MemRegionVal(R)).castAs<loc::MemRegionVal>(); + const auto *FR = FieldVal.getRegionAs<FieldRegion>(); + QualType T = I->getType(); + + // If LocalChain already contains FR, then we encountered a cyclic + // reference. In this case, region FR is already under checking at an + // earlier node in the directed tree. + if (LocalChain.contains(FR)) + return false; + + if (T->isStructureOrClassType()) { + if (isNonUnionUninit(FR, LocalChain.add(RegularField(FR)))) + ContainsUninitField = true; + continue; + } + + if (T->isUnionType()) { + if (isUnionUninit(FR)) { + if (addFieldToUninits(LocalChain.add(RegularField(FR)))) + ContainsUninitField = true; + } else + IsAnyFieldInitialized = true; + continue; + } + + if (T->isArrayType()) { + IsAnyFieldInitialized = true; + continue; + } + + SVal V = State->getSVal(FieldVal); + + if (isDereferencableType(T) || V.getAs<nonloc::LocAsInteger>()) { + if (isDereferencableUninit(FR, LocalChain)) + ContainsUninitField = true; + continue; + } + + if (isPrimitiveType(T)) { + if (isPrimitiveUninit(V)) { + if (addFieldToUninits(LocalChain.add(RegularField(FR)))) + ContainsUninitField = true; + } + continue; + } + + llvm_unreachable("All cases are handled!"); + } + + // Checking bases. The checker will regard inherited data members as direct + // fields. + const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD); + if (!CXXRD) + return ContainsUninitField; + + for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) { + const auto *BaseRegion = State->getLValue(BaseSpec, R) + .castAs<loc::MemRegionVal>() + .getRegionAs<TypedValueRegion>(); + + // If the head of the list is also a BaseClass, we'll overwrite it to avoid + // note messages like 'this->A::B::x'. + if (!LocalChain.isEmpty() && LocalChain.getHead().isBase()) { + if (isNonUnionUninit(BaseRegion, LocalChain.replaceHead( + BaseClass(BaseSpec.getType())))) + ContainsUninitField = true; + } else { + if (isNonUnionUninit(BaseRegion, + LocalChain.add(BaseClass(BaseSpec.getType())))) + ContainsUninitField = true; + } + } + + return ContainsUninitField; +} + +bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) { + assert(R->getValueType()->isUnionType() && + "This method only checks union objects!"); + // TODO: Implement support for union fields. + return false; +} + +bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) { + if (V.isUndef()) + return true; + + IsAnyFieldInitialized = true; + return false; +} + +//===----------------------------------------------------------------------===// +// Methods for FieldChainInfo. +//===----------------------------------------------------------------------===// + +bool FieldChainInfo::contains(const FieldRegion *FR) const { + for (const FieldNode &Node : Chain) { + if (Node.isSameRegion(FR)) + return true; + } + return false; +} + +/// Prints every element except the last to `Out`. Since ImmutableLists store +/// elements in reverse order, and have no reverse iterators, we use a +/// recursive function to print the fieldchain correctly. The last element in +/// the chain is to be printed by `FieldChainInfo::print`. +static void printTail(llvm::raw_ostream &Out, + const FieldChainInfo::FieldChain L); + +// FIXME: This function constructs an incorrect string in the following case: +// +// struct Base { int x; }; +// struct D1 : Base {}; struct D2 : Base {}; +// +// struct MostDerived : D1, D2 { +// MostDerived() {} +// } +// +// A call to MostDerived::MostDerived() will cause two notes that say +// "uninitialized field 'this->x'", but we can't refer to 'x' directly, +// we need an explicit namespace resolution whether the uninit field was +// 'D1::x' or 'D2::x'. +void FieldChainInfo::printNoteMsg(llvm::raw_ostream &Out) const { + if (Chain.isEmpty()) + return; + + const FieldNode &LastField = getHead(); + + LastField.printNoteMsg(Out); + Out << '\''; + + for (const FieldNode &Node : Chain) + Node.printPrefix(Out); + + Out << "this->"; + printTail(Out, Chain.getTail()); + LastField.printNode(Out); + Out << '\''; +} + +static void printTail(llvm::raw_ostream &Out, + const FieldChainInfo::FieldChain L) { + if (L.isEmpty()) + return; + + printTail(Out, L.getTail()); + + L.getHead().printNode(Out); + L.getHead().printSeparator(Out); +} + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static const TypedValueRegion * +getConstructedRegion(const CXXConstructorDecl *CtorDecl, + CheckerContext &Context) { + + Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl, + Context.getStackFrame()); + + SVal ObjectV = Context.getState()->getSVal(ThisLoc); + + auto *R = ObjectV.getAsRegion()->getAs<TypedValueRegion>(); + if (R && !R->getValueType()->getAsCXXRecordDecl()) + return nullptr; + + return R; +} + +static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor, + CheckerContext &Context) { + + const TypedValueRegion *CurrRegion = getConstructedRegion(Ctor, Context); + if (!CurrRegion) + return false; + + const LocationContext *LC = Context.getLocationContext(); + while ((LC = LC->getParent())) { + + // If \p Ctor was called by another constructor. + const auto *OtherCtor = dyn_cast<CXXConstructorDecl>(LC->getDecl()); + if (!OtherCtor) + continue; + + const TypedValueRegion *OtherRegion = + getConstructedRegion(OtherCtor, Context); + if (!OtherRegion) + continue; + + // If the CurrRegion is a subregion of OtherRegion, it will be analyzed + // during the analysis of OtherRegion. + if (CurrRegion->isSubRegionOf(OtherRegion)) + return true; + } + + return false; +} + +static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) { + llvm::Regex R(Pattern); + + for (const FieldDecl *FD : RD->fields()) { + if (R.match(FD->getType().getAsString())) + return true; + if (R.match(FD->getName())) + return true; + } + + return false; +} + +std::string clang::ento::getVariableName(const FieldDecl *Field) { + // If Field is a captured lambda variable, Field->getName() will return with + // an empty string. We can however acquire it's name from the lambda's + // captures. + const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent()); + + if (CXXParent && CXXParent->isLambda()) { + assert(CXXParent->captures_begin()); + auto It = CXXParent->captures_begin() + Field->getFieldIndex(); + + if (It->capturesVariable()) + return llvm::Twine("/*captured variable*/" + + It->getCapturedVar()->getName()) + .str(); + + if (It->capturesThis()) + return "/*'this' capture*/"; + + llvm_unreachable("No other capture type is expected!"); + } + + return Field->getName(); +} + +void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) { + auto Chk = Mgr.registerChecker<UninitializedObjectChecker>(); + + AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions(); + UninitObjCheckerOptions &ChOpts = Chk->Opts; + + ChOpts.IsPedantic = + AnOpts.getCheckerBooleanOption("Pedantic", /*DefaultVal*/ false, Chk); + ChOpts.ShouldConvertNotesToWarnings = + AnOpts.getCheckerBooleanOption("NotesAsWarnings", /*DefaultVal*/ false, Chk); + ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption( + "CheckPointeeInitialization", /*DefaultVal*/ false, Chk); + ChOpts.IgnoredRecordsWithFieldPattern = + AnOpts.getCheckerStringOption("IgnoreRecordsWithField", + /*DefaultVal*/ "", Chk); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp new file mode 100644 index 000000000000..aead59c7bf87 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp @@ -0,0 +1,282 @@ +//===----- UninitializedPointee.cpp ------------------------------*- 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 functions and methods for handling pointers and references +// to reduce the size and complexity of UninitializedObjectChecker.cpp. +// +// To read about command line options and documentation about how the checker +// works, refer to UninitializedObjectChecker.h. +// +//===----------------------------------------------------------------------===// + +#include "UninitializedObject.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" + +using namespace clang; +using namespace clang::ento; + +namespace { + +/// Represents a pointer or a reference field. +class LocField final : public FieldNode { + /// We'll store whether the pointee or the pointer itself is uninitialited. + const bool IsDereferenced; + +public: + LocField(const FieldRegion *FR, const bool IsDereferenced = true) + : FieldNode(FR), IsDereferenced(IsDereferenced) {} + + virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + if (IsDereferenced) + Out << "uninitialized pointee "; + else + Out << "uninitialized pointer "; + } + + virtual void printPrefix(llvm::raw_ostream &Out) const override {} + + virtual void printNode(llvm::raw_ostream &Out) const override { + Out << getVariableName(getDecl()); + } + + virtual void printSeparator(llvm::raw_ostream &Out) const override { + if (getDecl()->getType()->isPointerType()) + Out << "->"; + else + Out << '.'; + } +}; + +/// Represents a nonloc::LocAsInteger or void* field, that point to objects, but +/// needs to be casted back to its dynamic type for a correct note message. +class NeedsCastLocField final : public FieldNode { + QualType CastBackType; + +public: + NeedsCastLocField(const FieldRegion *FR, const QualType &T) + : FieldNode(FR), CastBackType(T) {} + + virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + Out << "uninitialized pointee "; + } + + virtual void printPrefix(llvm::raw_ostream &Out) const override { + // If this object is a nonloc::LocAsInteger. + if (getDecl()->getType()->isIntegerType()) + Out << "reinterpret_cast"; + // If this pointer's dynamic type is different then it's static type. + else + Out << "static_cast"; + Out << '<' << CastBackType.getAsString() << ">("; + } + + virtual void printNode(llvm::raw_ostream &Out) const override { + Out << getVariableName(getDecl()) << ')'; + } + + virtual void printSeparator(llvm::raw_ostream &Out) const override { + Out << "->"; + } +}; + +/// Represents a Loc field that points to itself. +class CyclicLocField final : public FieldNode { + +public: + CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {} + + virtual void printNoteMsg(llvm::raw_ostream &Out) const override { + Out << "object references itself "; + } + + virtual void printPrefix(llvm::raw_ostream &Out) const override {} + + virtual void printNode(llvm::raw_ostream &Out) const override { + Out << getVariableName(getDecl()); + } + + virtual void printSeparator(llvm::raw_ostream &Out) const override { + llvm_unreachable("CyclicLocField objects must be the last node of the " + "fieldchain!"); + } +}; + +} // end of anonymous namespace + +// Utility function declarations. + +struct DereferenceInfo { + const TypedValueRegion *R; + const bool NeedsCastBack; + const bool IsCyclic; + DereferenceInfo(const TypedValueRegion *R, bool NCB, bool IC) + : R(R), NeedsCastBack(NCB), IsCyclic(IC) {} +}; + +/// Dereferences \p FR and returns with the pointee's region, and whether it +/// needs to be casted back to it's location type. If for whatever reason +/// dereferencing fails, returns with None. +static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, + const FieldRegion *FR); + +/// Returns whether \p T can be (transitively) dereferenced to a void pointer +/// type (void*, void**, ...). +static bool isVoidPointer(QualType T); + +//===----------------------------------------------------------------------===// +// Methods for FindUninitializedFields. +//===----------------------------------------------------------------------===// + +bool FindUninitializedFields::isDereferencableUninit( + const FieldRegion *FR, FieldChainInfo LocalChain) { + + SVal V = State->getSVal(FR); + + assert((isDereferencableType(FR->getDecl()->getType()) || + V.getAs<nonloc::LocAsInteger>()) && + "This method only checks dereferenceable objects!"); + + if (V.isUnknown() || V.getAs<loc::ConcreteInt>()) { + IsAnyFieldInitialized = true; + return false; + } + + if (V.isUndef()) { + return addFieldToUninits( + LocalChain.add(LocField(FR, /*IsDereferenced*/ false)), FR); + } + + if (!Opts.CheckPointeeInitialization) { + IsAnyFieldInitialized = true; + return false; + } + + // At this point the pointer itself is initialized and points to a valid + // location, we'll now check the pointee. + llvm::Optional<DereferenceInfo> DerefInfo = dereference(State, FR); + if (!DerefInfo) { + IsAnyFieldInitialized = true; + return false; + } + + if (DerefInfo->IsCyclic) + return addFieldToUninits(LocalChain.add(CyclicLocField(FR)), FR); + + const TypedValueRegion *R = DerefInfo->R; + const bool NeedsCastBack = DerefInfo->NeedsCastBack; + + QualType DynT = R->getLocationType(); + QualType PointeeT = DynT->getPointeeType(); + + if (PointeeT->isStructureOrClassType()) { + if (NeedsCastBack) + return isNonUnionUninit(R, LocalChain.add(NeedsCastLocField(FR, DynT))); + return isNonUnionUninit(R, LocalChain.add(LocField(FR))); + } + + if (PointeeT->isUnionType()) { + if (isUnionUninit(R)) { + if (NeedsCastBack) + return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)), + R); + return addFieldToUninits(LocalChain.add(LocField(FR)), R); + } else { + IsAnyFieldInitialized = true; + return false; + } + } + + if (PointeeT->isArrayType()) { + IsAnyFieldInitialized = true; + return false; + } + + assert((isPrimitiveType(PointeeT) || isDereferencableType(PointeeT)) && + "At this point FR must either have a primitive dynamic type, or it " + "must be a null, undefined, unknown or concrete pointer!"); + + SVal PointeeV = State->getSVal(R); + + if (isPrimitiveUninit(PointeeV)) { + if (NeedsCastBack) + return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)), R); + return addFieldToUninits(LocalChain.add(LocField(FR)), R); + } + + IsAnyFieldInitialized = true; + return false; +} + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State, + const FieldRegion *FR) { + + llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions; + + SVal V = State->getSVal(FR); + assert(V.getAsRegion() && "V must have an underlying region!"); + + // If the static type of the field is a void pointer, or it is a + // nonloc::LocAsInteger, we need to cast it back to the dynamic type before + // dereferencing. + bool NeedsCastBack = isVoidPointer(FR->getDecl()->getType()) || + V.getAs<nonloc::LocAsInteger>(); + + // The region we'd like to acquire. + const auto *R = V.getAsRegion()->getAs<TypedValueRegion>(); + if (!R) + return None; + + VisitedRegions.insert(R); + + // We acquire the dynamic type of R, + QualType DynT = R->getLocationType(); + + while (const MemRegion *Tmp = State->getSVal(R, DynT).getAsRegion()) { + + R = Tmp->getAs<TypedValueRegion>(); + if (!R) + return None; + + // We found a cyclic pointer, like int *ptr = (int *)&ptr. + if (!VisitedRegions.insert(R).second) + return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ true}; + + DynT = R->getLocationType(); + // In order to ensure that this loop terminates, we're also checking the + // dynamic type of R, since type hierarchy is finite. + if (isDereferencableType(DynT->getPointeeType())) + break; + } + + while (R->getAs<CXXBaseObjectRegion>()) { + NeedsCastBack = true; + + if (!isa<TypedValueRegion>(R->getSuperRegion())) + break; + R = R->getSuperRegion()->getAs<TypedValueRegion>(); + } + + return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ false}; +} + +static bool isVoidPointer(QualType T) { + while (!T.isNull()) { + if (T->isVoidPointerType()) + return true; + T = T->getPointeeType(); + } + return false; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp new file mode 100644 index 000000000000..bab0c12704fa --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -0,0 +1,479 @@ +//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UnixAPIChecker, which is an assortment of checks on calls +// to various, widely used UNIX/Posix functions. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/raw_ostream.h" +#include <fcntl.h> + +using namespace clang; +using namespace ento; + +enum class OpenVariant { + /// The standard open() call: + /// int open(const char *path, int oflag, ...); + Open, + + /// The variant taking a directory file descriptor and a relative path: + /// int openat(int fd, const char *path, int oflag, ...); + OpenAt +}; + +namespace { +class UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > { + mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero; + mutable Optional<uint64_t> Val_O_CREAT; + +public: + DefaultBool CheckMisuse, CheckPortability; + + void checkPreStmt(const CallExpr *CE, CheckerContext &C) const; + + void CheckOpen(CheckerContext &C, const CallExpr *CE) const; + void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const; + + void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const; + void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const; + void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const; + void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const; + void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const; + void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const; + void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const; + void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const; + + typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &, + const CallExpr *) const; +private: + + void CheckOpenVariant(CheckerContext &C, + const CallExpr *CE, OpenVariant Variant) const; + + bool ReportZeroByteAllocation(CheckerContext &C, + ProgramStateRef falseState, + const Expr *arg, + const char *fn_name) const; + void BasicAllocationCheck(CheckerContext &C, + const CallExpr *CE, + const unsigned numArgs, + const unsigned sizeArg, + const char *fn) const; + void LazyInitialize(std::unique_ptr<BugType> &BT, const char *name) const { + if (BT) + return; + BT.reset(new BugType(this, name, categories::UnixAPI)); + } + void ReportOpenBug(CheckerContext &C, + ProgramStateRef State, + const char *Msg, + SourceRange SR) const; +}; +} //end anonymous namespace + +//===----------------------------------------------------------------------===// +// "open" (man 2 open) +//===----------------------------------------------------------------------===// + +void UnixAPIChecker::ReportOpenBug(CheckerContext &C, + ProgramStateRef State, + const char *Msg, + SourceRange SR) const { + ExplodedNode *N = C.generateErrorNode(State); + if (!N) + return; + + LazyInitialize(BT_open, "Improper use of 'open'"); + + auto Report = llvm::make_unique<BugReport>(*BT_open, Msg, N); + Report->addRange(SR); + C.emitReport(std::move(Report)); +} + +void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const { + CheckOpenVariant(C, CE, OpenVariant::Open); +} + +void UnixAPIChecker::CheckOpenAt(CheckerContext &C, const CallExpr *CE) const { + CheckOpenVariant(C, CE, OpenVariant::OpenAt); +} + +void UnixAPIChecker::CheckOpenVariant(CheckerContext &C, + const CallExpr *CE, + OpenVariant Variant) const { + // The index of the argument taking the flags open flags (O_RDONLY, + // O_WRONLY, O_CREAT, etc.), + unsigned int FlagsArgIndex; + const char *VariantName; + switch (Variant) { + case OpenVariant::Open: + FlagsArgIndex = 1; + VariantName = "open"; + break; + case OpenVariant::OpenAt: + FlagsArgIndex = 2; + VariantName = "openat"; + break; + }; + + // All calls should at least provide arguments up to the 'flags' parameter. + unsigned int MinArgCount = FlagsArgIndex + 1; + + // If the flags has O_CREAT set then open/openat() require an additional + // argument specifying the file mode (permission bits) for the created file. + unsigned int CreateModeArgIndex = FlagsArgIndex + 1; + + // The create mode argument should be the last argument. + unsigned int MaxArgCount = CreateModeArgIndex + 1; + + ProgramStateRef state = C.getState(); + + if (CE->getNumArgs() < MinArgCount) { + // The frontend should issue a warning for this case, so this is a sanity + // check. + return; + } else if (CE->getNumArgs() == MaxArgCount) { + const Expr *Arg = CE->getArg(CreateModeArgIndex); + QualType QT = Arg->getType(); + if (!QT->isIntegerType()) { + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "The " << CreateModeArgIndex + 1 + << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) + << " argument to '" << VariantName << "' is not an integer"; + + ReportOpenBug(C, state, + SBuf.c_str(), + Arg->getSourceRange()); + return; + } + } else if (CE->getNumArgs() > MaxArgCount) { + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Call to '" << VariantName << "' with more than " << MaxArgCount + << " arguments"; + + ReportOpenBug(C, state, + SBuf.c_str(), + CE->getArg(MaxArgCount)->getSourceRange()); + return; + } + + // The definition of O_CREAT is platform specific. We need a better way + // of querying this information from the checking environment. + if (!Val_O_CREAT.hasValue()) { + if (C.getASTContext().getTargetInfo().getTriple().getVendor() + == llvm::Triple::Apple) + Val_O_CREAT = 0x0200; + else { + // FIXME: We need a more general way of getting the O_CREAT value. + // We could possibly grovel through the preprocessor state, but + // that would require passing the Preprocessor object to the ExprEngine. + // See also: MallocChecker.cpp / M_ZERO. + return; + } + } + + // Now check if oflags has O_CREAT set. + const Expr *oflagsEx = CE->getArg(FlagsArgIndex); + const SVal V = C.getSVal(oflagsEx); + if (!V.getAs<NonLoc>()) { + // The case where 'V' can be a location can only be due to a bad header, + // so in this case bail out. + return; + } + NonLoc oflags = V.castAs<NonLoc>(); + NonLoc ocreateFlag = C.getSValBuilder() + .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>(); + SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, + oflags, ocreateFlag, + oflagsEx->getType()); + if (maskedFlagsUC.isUnknownOrUndef()) + return; + DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>(); + + // Check if maskedFlags is non-zero. + ProgramStateRef trueState, falseState; + std::tie(trueState, falseState) = state->assume(maskedFlags); + + // Only emit an error if the value of 'maskedFlags' is properly + // constrained; + if (!(trueState && !falseState)) + return; + + if (CE->getNumArgs() < MaxArgCount) { + SmallString<256> SBuf; + llvm::raw_svector_ostream OS(SBuf); + OS << "Call to '" << VariantName << "' requires a " + << CreateModeArgIndex + 1 + << llvm::getOrdinalSuffix(CreateModeArgIndex + 1) + << " argument when the 'O_CREAT' flag is set"; + ReportOpenBug(C, trueState, + SBuf.c_str(), + oflagsEx->getSourceRange()); + } +} + +//===----------------------------------------------------------------------===// +// pthread_once +//===----------------------------------------------------------------------===// + +void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C, + const CallExpr *CE) const { + + // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. + // They can possibly be refactored. + + if (CE->getNumArgs() < 1) + return; + + // Check if the first argument is stack allocated. If so, issue a warning + // because that's likely to be bad news. + ProgramStateRef state = C.getState(); + const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion(); + if (!R || !isa<StackSpaceRegion>(R->getMemorySpace())) + return; + + ExplodedNode *N = C.generateErrorNode(state); + if (!N) + return; + + SmallString<256> S; + llvm::raw_svector_ostream os(S); + os << "Call to 'pthread_once' uses"; + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) + os << " the local variable '" << VR->getDecl()->getName() << '\''; + else + os << " stack allocated memory"; + os << " for the \"control\" value. Using such transient memory for " + "the control value is potentially dangerous."; + if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace())) + os << " Perhaps you intended to declare the variable as 'static'?"; + + LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'"); + + auto report = llvm::make_unique<BugReport>(*BT_pthreadOnce, os.str(), N); + report->addRange(CE->getArg(0)->getSourceRange()); + C.emitReport(std::move(report)); +} + +//===----------------------------------------------------------------------===// +// "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc" +// with allocation size 0 +//===----------------------------------------------------------------------===// +// FIXME: Eventually these should be rolled into the MallocChecker, but right now +// they're more basic and valuable for widespread use. + +// Returns true if we try to do a zero byte allocation, false otherwise. +// Fills in trueState and falseState. +static bool IsZeroByteAllocation(ProgramStateRef state, + const SVal argVal, + ProgramStateRef *trueState, + ProgramStateRef *falseState) { + std::tie(*trueState, *falseState) = + state->assume(argVal.castAs<DefinedSVal>()); + + return (*falseState && !*trueState); +} + +// Generates an error report, indicating that the function whose name is given +// will perform a zero byte allocation. +// Returns false if an error occurred, true otherwise. +bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C, + ProgramStateRef falseState, + const Expr *arg, + const char *fn_name) const { + ExplodedNode *N = C.generateErrorNode(falseState); + if (!N) + return false; + + LazyInitialize(BT_mallocZero, + "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)"); + + SmallString<256> S; + llvm::raw_svector_ostream os(S); + os << "Call to '" << fn_name << "' has an allocation size of 0 bytes"; + auto report = llvm::make_unique<BugReport>(*BT_mallocZero, os.str(), N); + + report->addRange(arg->getSourceRange()); + bugreporter::trackExpressionValue(N, arg, *report); + C.emitReport(std::move(report)); + + return true; +} + +// Does a basic check for 0-sized allocations suitable for most of the below +// functions (modulo "calloc") +void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C, + const CallExpr *CE, + const unsigned numArgs, + const unsigned sizeArg, + const char *fn) const { + // Sanity check for the correct number of arguments + if (CE->getNumArgs() != numArgs) + return; + + // Check if the allocation size is 0. + ProgramStateRef state = C.getState(); + ProgramStateRef trueState = nullptr, falseState = nullptr; + const Expr *arg = CE->getArg(sizeArg); + SVal argVal = C.getSVal(arg); + + if (argVal.isUnknownOrUndef()) + return; + + // Is the value perfectly constrained to zero? + if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { + (void) ReportZeroByteAllocation(C, falseState, arg, fn); + return; + } + // Assume the value is non-zero going forward. + assert(trueState); + if (trueState != state) + C.addTransition(trueState); +} + +void UnixAPIChecker::CheckCallocZero(CheckerContext &C, + const CallExpr *CE) const { + unsigned int nArgs = CE->getNumArgs(); + if (nArgs != 2) + return; + + ProgramStateRef state = C.getState(); + ProgramStateRef trueState = nullptr, falseState = nullptr; + + unsigned int i; + for (i = 0; i < nArgs; i++) { + const Expr *arg = CE->getArg(i); + SVal argVal = C.getSVal(arg); + if (argVal.isUnknownOrUndef()) { + if (i == 0) + continue; + else + return; + } + + if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) { + if (ReportZeroByteAllocation(C, falseState, arg, "calloc")) + return; + else if (i == 0) + continue; + else + return; + } + } + + // Assume the value is non-zero going forward. + assert(trueState); + if (trueState != state) + C.addTransition(trueState); +} + +void UnixAPIChecker::CheckMallocZero(CheckerContext &C, + const CallExpr *CE) const { + BasicAllocationCheck(C, CE, 1, 0, "malloc"); +} + +void UnixAPIChecker::CheckReallocZero(CheckerContext &C, + const CallExpr *CE) const { + BasicAllocationCheck(C, CE, 2, 1, "realloc"); +} + +void UnixAPIChecker::CheckReallocfZero(CheckerContext &C, + const CallExpr *CE) const { + BasicAllocationCheck(C, CE, 2, 1, "reallocf"); +} + +void UnixAPIChecker::CheckAllocaZero(CheckerContext &C, + const CallExpr *CE) const { + BasicAllocationCheck(C, CE, 1, 0, "alloca"); +} + +void UnixAPIChecker::CheckAllocaWithAlignZero(CheckerContext &C, + const CallExpr *CE) const { + BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align"); +} + +void UnixAPIChecker::CheckVallocZero(CheckerContext &C, + const CallExpr *CE) const { + BasicAllocationCheck(C, CE, 1, 0, "valloc"); +} + + +//===----------------------------------------------------------------------===// +// Central dispatch function. +//===----------------------------------------------------------------------===// + +void UnixAPIChecker::checkPreStmt(const CallExpr *CE, + CheckerContext &C) const { + const FunctionDecl *FD = C.getCalleeDecl(CE); + if (!FD || FD->getKind() != Decl::Function) + return; + + // Don't treat functions in namespaces with the same name a Unix function + // as a call to the Unix function. + const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext(); + if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx)) + return; + + StringRef FName = C.getCalleeName(FD); + if (FName.empty()) + return; + + if (CheckMisuse) { + if (SubChecker SC = + llvm::StringSwitch<SubChecker>(FName) + .Case("open", &UnixAPIChecker::CheckOpen) + .Case("openat", &UnixAPIChecker::CheckOpenAt) + .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce) + .Default(nullptr)) { + (this->*SC)(C, CE); + } + } + if (CheckPortability) { + if (SubChecker SC = + llvm::StringSwitch<SubChecker>(FName) + .Case("calloc", &UnixAPIChecker::CheckCallocZero) + .Case("malloc", &UnixAPIChecker::CheckMallocZero) + .Case("realloc", &UnixAPIChecker::CheckReallocZero) + .Case("reallocf", &UnixAPIChecker::CheckReallocfZero) + .Cases("alloca", "__builtin_alloca", + &UnixAPIChecker::CheckAllocaZero) + .Case("__builtin_alloca_with_align", + &UnixAPIChecker::CheckAllocaWithAlignZero) + .Case("valloc", &UnixAPIChecker::CheckVallocZero) + .Default(nullptr)) { + (this->*SC)(C, CE); + } + } +} + +//===----------------------------------------------------------------------===// +// Registration. +//===----------------------------------------------------------------------===// + +#define REGISTER_CHECKER(Name) \ + void ento::registerUnixAPI##Name##Checker(CheckerManager &mgr) { \ + mgr.registerChecker<UnixAPIChecker>()->Check##Name = true; \ + } + +REGISTER_CHECKER(Misuse) +REGISTER_CHECKER(Portability) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp new file mode 100644 index 000000000000..16b4d5e925ba --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -0,0 +1,259 @@ +//==- UnreachableCodeChecker.cpp - Generalized dead code checker -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This file implements a generalized unreachable code checker using a +// path-sensitive analysis. We mark any path visited, and then walk the CFG as a +// post-analysis to determine what was never visited. +// +// A similar flow-sensitive only check exists in Analysis/ReachableCode.cpp +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/SmallSet.h" + +using namespace clang; +using namespace ento; + +namespace { +class UnreachableCodeChecker : public Checker<check::EndAnalysis> { +public: + void checkEndAnalysis(ExplodedGraph &G, BugReporter &B, + ExprEngine &Eng) const; +private: + typedef llvm::SmallSet<unsigned, 32> CFGBlocksSet; + + static inline const Stmt *getUnreachableStmt(const CFGBlock *CB); + static void FindUnreachableEntryPoints(const CFGBlock *CB, + CFGBlocksSet &reachable, + CFGBlocksSet &visited); + static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM); + static inline bool isEmptyCFGBlock(const CFGBlock *CB); +}; +} + +void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, + BugReporter &B, + ExprEngine &Eng) const { + CFGBlocksSet reachable, visited; + + if (Eng.hasWorkRemaining()) + return; + + const Decl *D = nullptr; + CFG *C = nullptr; + ParentMap *PM = nullptr; + const LocationContext *LC = nullptr; + // Iterate over ExplodedGraph + for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end(); + I != E; ++I) { + const ProgramPoint &P = I->getLocation(); + LC = P.getLocationContext(); + if (!LC->inTopFrame()) + continue; + + if (!D) + D = LC->getAnalysisDeclContext()->getDecl(); + + // Save the CFG if we don't have it already + if (!C) + C = LC->getAnalysisDeclContext()->getUnoptimizedCFG(); + if (!PM) + PM = &LC->getParentMap(); + + if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { + const CFGBlock *CB = BE->getBlock(); + reachable.insert(CB->getBlockID()); + } + } + + // Bail out if we didn't get the CFG or the ParentMap. + if (!D || !C || !PM) + return; + + // Don't do anything for template instantiations. Proving that code + // in a template instantiation is unreachable means proving that it is + // unreachable in all instantiations. + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) + if (FD->isTemplateInstantiation()) + return; + + // Find CFGBlocks that were not covered by any node + for (CFG::const_iterator I = C->begin(), E = C->end(); I != E; ++I) { + const CFGBlock *CB = *I; + // Check if the block is unreachable + if (reachable.count(CB->getBlockID())) + continue; + + // Check if the block is empty (an artificial block) + if (isEmptyCFGBlock(CB)) + continue; + + // Find the entry points for this block + if (!visited.count(CB->getBlockID())) + FindUnreachableEntryPoints(CB, reachable, visited); + + // This block may have been pruned; check if we still want to report it + if (reachable.count(CB->getBlockID())) + continue; + + // Check for false positives + if (isInvalidPath(CB, *PM)) + continue; + + // It is good practice to always have a "default" label in a "switch", even + // if we should never get there. It can be used to detect errors, for + // instance. Unreachable code directly under a "default" label is therefore + // likely to be a false positive. + if (const Stmt *label = CB->getLabel()) + if (label->getStmtClass() == Stmt::DefaultStmtClass) + continue; + + // Special case for __builtin_unreachable. + // FIXME: This should be extended to include other unreachable markers, + // such as llvm_unreachable. + if (!CB->empty()) { + bool foundUnreachable = false; + for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end(); + ci != ce; ++ci) { + if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>()) + if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) { + if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable || + CE->isBuiltinAssumeFalse(Eng.getContext())) { + foundUnreachable = true; + break; + } + } + } + if (foundUnreachable) + continue; + } + + // We found a block that wasn't covered - find the statement to report + SourceRange SR; + PathDiagnosticLocation DL; + SourceLocation SL; + if (const Stmt *S = getUnreachableStmt(CB)) { + // In macros, 'do {...} while (0)' is often used. Don't warn about the + // condition 0 when it is unreachable. + if (S->getBeginLoc().isMacroID()) + if (const auto *I = dyn_cast<IntegerLiteral>(S)) + if (I->getValue() == 0ULL) + if (const Stmt *Parent = PM->getParent(S)) + if (isa<DoStmt>(Parent)) + continue; + SR = S->getSourceRange(); + DL = PathDiagnosticLocation::createBegin(S, B.getSourceManager(), LC); + SL = DL.asLocation(); + if (SR.isInvalid() || !SL.isValid()) + continue; + } + else + continue; + + // Check if the SourceLocation is in a system header + const SourceManager &SM = B.getSourceManager(); + if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL)) + continue; + + B.EmitBasicReport(D, this, "Unreachable code", "Dead code", + "This statement is never executed", DL, SR); + } +} + +// Recursively finds the entry point(s) for this dead CFGBlock. +void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB, + CFGBlocksSet &reachable, + CFGBlocksSet &visited) { + visited.insert(CB->getBlockID()); + + for (CFGBlock::const_pred_iterator I = CB->pred_begin(), E = CB->pred_end(); + I != E; ++I) { + if (!*I) + continue; + + if (!reachable.count((*I)->getBlockID())) { + // If we find an unreachable predecessor, mark this block as reachable so + // we don't report this block + reachable.insert(CB->getBlockID()); + if (!visited.count((*I)->getBlockID())) + // If we haven't previously visited the unreachable predecessor, recurse + FindUnreachableEntryPoints(*I, reachable, visited); + } + } +} + +// Find the Stmt* in a CFGBlock for reporting a warning +const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) { + for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) { + if (Optional<CFGStmt> S = I->getAs<CFGStmt>()) { + if (!isa<DeclStmt>(S->getStmt())) + return S->getStmt(); + } + } + if (const Stmt *S = CB->getTerminator()) + return S; + else + return nullptr; +} + +// Determines if the path to this CFGBlock contained an element that infers this +// block is a false positive. We assume that FindUnreachableEntryPoints has +// already marked only the entry points to any dead code, so we need only to +// find the condition that led to this block (the predecessor of this block.) +// There will never be more than one predecessor. +bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB, + const ParentMap &PM) { + // We only expect a predecessor size of 0 or 1. If it is >1, then an external + // condition has broken our assumption (for example, a sink being placed by + // another check). In these cases, we choose not to report. + if (CB->pred_size() > 1) + return true; + + // If there are no predecessors, then this block is trivially unreachable + if (CB->pred_size() == 0) + return false; + + const CFGBlock *pred = *CB->pred_begin(); + if (!pred) + return false; + + // Get the predecessor block's terminator condition + const Stmt *cond = pred->getTerminatorCondition(); + + //assert(cond && "CFGBlock's predecessor has a terminator condition"); + // The previous assertion is invalid in some cases (eg do/while). Leaving + // reporting of these situations on at the moment to help triage these cases. + if (!cond) + return false; + + // Run each of the checks on the conditions + return containsMacro(cond) || containsEnum(cond) || + containsStaticLocal(cond) || containsBuiltinOffsetOf(cond) || + containsStmt<UnaryExprOrTypeTraitExpr>(cond); +} + +// Returns true if the given CFGBlock is empty +bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) { + return CB->getLabel() == nullptr // No labels + && CB->size() == 0 // No statements + && !CB->getTerminator(); // No terminator +} + +void ento::registerUnreachableCodeChecker(CheckerManager &mgr) { + mgr.registerChecker<UnreachableCodeChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp new file mode 100644 index 000000000000..e458e0554ee2 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -0,0 +1,185 @@ +//=== VLASizeChecker.cpp - Undefined dereference checker --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines VLASizeChecker, a builtin check in ExprEngine that +// performs checks for declaration of VLA of undefined or zero size. +// In addition, VLASizeChecker is responsible for defining the extent +// of the MemRegion that represents a VLA. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/CharUnits.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > { + mutable std::unique_ptr<BugType> BT; + enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Tainted, VLA_Negative }; + + void reportBug(VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State, + CheckerContext &C, + std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const; + +public: + void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const; +}; +} // end anonymous namespace + +void VLASizeChecker::reportBug( + VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State, + CheckerContext &C, std::unique_ptr<BugReporterVisitor> Visitor) const { + // Generate an error node. + ExplodedNode *N = C.generateErrorNode(State); + if (!N) + return; + + if (!BT) + BT.reset(new BuiltinBug( + this, "Dangerous variable-length array (VLA) declaration")); + + SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + os << "Declared variable-length array (VLA) "; + switch (Kind) { + case VLA_Garbage: + os << "uses a garbage value as its size"; + break; + case VLA_Zero: + os << "has zero size"; + break; + case VLA_Tainted: + os << "has tainted size"; + break; + case VLA_Negative: + os << "has negative size"; + break; + } + + auto report = llvm::make_unique<BugReport>(*BT, os.str(), N); + report->addVisitor(std::move(Visitor)); + report->addRange(SizeE->getSourceRange()); + bugreporter::trackExpressionValue(N, SizeE, *report); + C.emitReport(std::move(report)); +} + +void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const { + if (!DS->isSingleDecl()) + return; + + const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl()); + if (!VD) + return; + + ASTContext &Ctx = C.getASTContext(); + const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType()); + if (!VLA) + return; + + // FIXME: Handle multi-dimensional VLAs. + const Expr *SE = VLA->getSizeExpr(); + ProgramStateRef state = C.getState(); + SVal sizeV = C.getSVal(SE); + + if (sizeV.isUndef()) { + reportBug(VLA_Garbage, SE, state, C); + return; + } + + // See if the size value is known. It can't be undefined because we would have + // warned about that already. + if (sizeV.isUnknown()) + return; + + // Check if the size is tainted. + if (state->isTainted(sizeV)) { + reportBug(VLA_Tainted, SE, nullptr, C, + llvm::make_unique<TaintBugVisitor>(sizeV)); + return; + } + + // Check if the size is zero. + DefinedSVal sizeD = sizeV.castAs<DefinedSVal>(); + + ProgramStateRef stateNotZero, stateZero; + std::tie(stateNotZero, stateZero) = state->assume(sizeD); + + if (stateZero && !stateNotZero) { + reportBug(VLA_Zero, SE, stateZero, C); + return; + } + + // From this point on, assume that the size is not zero. + state = stateNotZero; + + // VLASizeChecker is responsible for defining the extent of the array being + // declared. We do this by multiplying the array length by the element size, + // then matching that with the array region's extent symbol. + + // Check if the size is negative. + SValBuilder &svalBuilder = C.getSValBuilder(); + + QualType Ty = SE->getType(); + DefinedOrUnknownSVal Zero = svalBuilder.makeZeroVal(Ty); + + SVal LessThanZeroVal = svalBuilder.evalBinOp(state, BO_LT, sizeD, Zero, Ty); + if (Optional<DefinedSVal> LessThanZeroDVal = + LessThanZeroVal.getAs<DefinedSVal>()) { + ConstraintManager &CM = C.getConstraintManager(); + ProgramStateRef StatePos, StateNeg; + + std::tie(StateNeg, StatePos) = CM.assumeDual(state, *LessThanZeroDVal); + if (StateNeg && !StatePos) { + reportBug(VLA_Negative, SE, state, C); + return; + } + state = StatePos; + } + + // Convert the array length to size_t. + QualType SizeTy = Ctx.getSizeType(); + NonLoc ArrayLength = + svalBuilder.evalCast(sizeD, SizeTy, SE->getType()).castAs<NonLoc>(); + + // Get the element size. + CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType()); + SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy); + + // Multiply the array length by the element size. + SVal ArraySizeVal = svalBuilder.evalBinOpNN( + state, BO_Mul, ArrayLength, EleSizeVal.castAs<NonLoc>(), SizeTy); + + // Finally, assume that the array's extent matches the given size. + const LocationContext *LC = C.getLocationContext(); + DefinedOrUnknownSVal Extent = + state->getRegion(VD, LC)->getExtent(svalBuilder); + DefinedOrUnknownSVal ArraySize = ArraySizeVal.castAs<DefinedOrUnknownSVal>(); + DefinedOrUnknownSVal sizeIsKnown = + svalBuilder.evalEQ(state, Extent, ArraySize); + state = state->assume(sizeIsKnown, true); + + // Assume should not fail at this point. + assert(state); + + // Remember our assumptions! + C.addTransition(state); +} + +void ento::registerVLASizeChecker(CheckerManager &mgr) { + mgr.registerChecker<VLASizeChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp new file mode 100644 index 000000000000..748b226b7a1e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp @@ -0,0 +1,412 @@ +//== ValistChecker.cpp - stdarg.h macro usage checker -----------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines checkers which detect usage of uninitialized va_list values +// and va_start calls with no matching va_end. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" + +using namespace clang; +using namespace ento; + +REGISTER_SET_WITH_PROGRAMSTATE(InitializedVALists, const MemRegion *) + +namespace { +typedef SmallVector<const MemRegion *, 2> RegionVector; + +class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>, + check::DeadSymbols> { + mutable std::unique_ptr<BugType> BT_leakedvalist, BT_uninitaccess; + + struct VAListAccepter { + CallDescription Func; + int VAListPos; + }; + static const SmallVector<VAListAccepter, 15> VAListAccepters; + static const CallDescription VaStart, VaEnd, VaCopy; + +public: + enum CheckKind { + CK_Uninitialized, + CK_Unterminated, + CK_CopyToSelf, + CK_NumCheckKinds + }; + + DefaultBool ChecksEnabled[CK_NumCheckKinds]; + CheckName CheckNames[CK_NumCheckKinds]; + + void checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const; + +private: + const MemRegion *getVAListAsRegion(SVal SV, const Expr *VAExpr, + bool &IsSymbolic, CheckerContext &C) const; + const ExplodedNode *getStartCallSite(const ExplodedNode *N, + const MemRegion *Reg) const; + + void reportUninitializedAccess(const MemRegion *VAList, StringRef Msg, + CheckerContext &C) const; + void reportLeakedVALists(const RegionVector &LeakedVALists, StringRef Msg1, + StringRef Msg2, CheckerContext &C, ExplodedNode *N, + bool ReportUninit = false) const; + + void checkVAListStartCall(const CallEvent &Call, CheckerContext &C, + bool IsCopy) const; + void checkVAListEndCall(const CallEvent &Call, CheckerContext &C) const; + + class ValistBugVisitor : public BugReporterVisitor { + public: + ValistBugVisitor(const MemRegion *Reg, bool IsLeak = false) + : Reg(Reg), IsLeak(IsLeak) {} + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(Reg); + } + std::shared_ptr<PathDiagnosticPiece> + getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode, + BugReport &BR) override { + if (!IsLeak) + return nullptr; + + PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath( + EndPathNode, BRC.getSourceManager()); + // Do not add the statement itself as a range in case of leak. + return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(), false); + } + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + + private: + const MemRegion *Reg; + bool IsLeak; + }; +}; + +const SmallVector<ValistChecker::VAListAccepter, 15> + ValistChecker::VAListAccepters = { + {{"vfprintf", 3}, 2}, + {{"vfscanf", 3}, 2}, + {{"vprintf", 2}, 1}, + {{"vscanf", 2}, 1}, + {{"vsnprintf", 4}, 3}, + {{"vsprintf", 3}, 2}, + {{"vsscanf", 3}, 2}, + {{"vfwprintf", 3}, 2}, + {{"vfwscanf", 3}, 2}, + {{"vwprintf", 2}, 1}, + {{"vwscanf", 2}, 1}, + {{"vswprintf", 4}, 3}, + // vswprintf is the wide version of vsnprintf, + // vsprintf has no wide version + {{"vswscanf", 3}, 2}}; +const CallDescription ValistChecker::VaStart("__builtin_va_start", 2), + ValistChecker::VaCopy("__builtin_va_copy", 2), + ValistChecker::VaEnd("__builtin_va_end", 1); +} // end anonymous namespace + +void ValistChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + if (!Call.isGlobalCFunction()) + return; + if (Call.isCalled(VaStart)) + checkVAListStartCall(Call, C, false); + else if (Call.isCalled(VaCopy)) + checkVAListStartCall(Call, C, true); + else if (Call.isCalled(VaEnd)) + checkVAListEndCall(Call, C); + else { + for (auto FuncInfo : VAListAccepters) { + if (!Call.isCalled(FuncInfo.Func)) + continue; + bool Symbolic; + const MemRegion *VAList = + getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos), + Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C); + if (!VAList) + return; + + if (C.getState()->contains<InitializedVALists>(VAList)) + return; + + // We did not see va_start call, but the source of the region is unknown. + // Be conservative and assume the best. + if (Symbolic) + return; + + SmallString<80> Errmsg("Function '"); + Errmsg += FuncInfo.Func.getFunctionName(); + Errmsg += "' is called with an uninitialized va_list argument"; + reportUninitializedAccess(VAList, Errmsg.c_str(), C); + break; + } + } +} + +const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E, + bool &IsSymbolic, + CheckerContext &C) const { + const MemRegion *Reg = SV.getAsRegion(); + if (!Reg) + return nullptr; + // TODO: In the future this should be abstracted away by the analyzer. + bool VaListModelledAsArray = false; + if (const auto *Cast = dyn_cast<CastExpr>(E)) { + QualType Ty = Cast->getType(); + VaListModelledAsArray = + Ty->isPointerType() && Ty->getPointeeType()->isRecordType(); + } + if (const auto *DeclReg = Reg->getAs<DeclRegion>()) { + if (isa<ParmVarDecl>(DeclReg->getDecl())) + Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion(); + } + IsSymbolic = Reg && Reg->getAs<SymbolicRegion>(); + // Some VarRegion based VA lists reach here as ElementRegions. + const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg); + return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg; +} + +void ValistChecker::checkPreStmt(const VAArgExpr *VAA, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + const Expr *VASubExpr = VAA->getSubExpr(); + SVal VAListSVal = C.getSVal(VASubExpr); + bool Symbolic; + const MemRegion *VAList = + getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C); + if (!VAList) + return; + if (Symbolic) + return; + if (!State->contains<InitializedVALists>(VAList)) + reportUninitializedAccess( + VAList, "va_arg() is called on an uninitialized va_list", C); +} + +void ValistChecker::checkDeadSymbols(SymbolReaper &SR, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + InitializedVAListsTy TrackedVALists = State->get<InitializedVALists>(); + RegionVector LeakedVALists; + for (auto Reg : TrackedVALists) { + if (SR.isLiveRegion(Reg)) + continue; + LeakedVALists.push_back(Reg); + State = State->remove<InitializedVALists>(Reg); + } + if (ExplodedNode *N = C.addTransition(State)) + reportLeakedVALists(LeakedVALists, "Initialized va_list", " is leaked", C, + N); +} + +// This function traverses the exploded graph backwards and finds the node where +// the va_list is initialized. That node is used for uniquing the bug paths. +// It is not likely that there are several different va_lists that belongs to +// different stack frames, so that case is not yet handled. +const ExplodedNode * +ValistChecker::getStartCallSite(const ExplodedNode *N, + const MemRegion *Reg) const { + const LocationContext *LeakContext = N->getLocationContext(); + const ExplodedNode *StartCallNode = N; + + bool FoundInitializedState = false; + + while (N) { + ProgramStateRef State = N->getState(); + if (!State->contains<InitializedVALists>(Reg)) { + if (FoundInitializedState) + break; + } else { + FoundInitializedState = true; + } + const LocationContext *NContext = N->getLocationContext(); + if (NContext == LeakContext || NContext->isParentOf(LeakContext)) + StartCallNode = N; + N = N->pred_empty() ? nullptr : *(N->pred_begin()); + } + + return StartCallNode; +} + +void ValistChecker::reportUninitializedAccess(const MemRegion *VAList, + StringRef Msg, + CheckerContext &C) const { + if (!ChecksEnabled[CK_Uninitialized]) + return; + if (ExplodedNode *N = C.generateErrorNode()) { + if (!BT_uninitaccess) + BT_uninitaccess.reset(new BugType(CheckNames[CK_Uninitialized], + "Uninitialized va_list", + categories::MemoryError)); + auto R = llvm::make_unique<BugReport>(*BT_uninitaccess, Msg, N); + R->markInteresting(VAList); + R->addVisitor(llvm::make_unique<ValistBugVisitor>(VAList)); + C.emitReport(std::move(R)); + } +} + +void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists, + StringRef Msg1, StringRef Msg2, + CheckerContext &C, ExplodedNode *N, + bool ReportUninit) const { + if (!(ChecksEnabled[CK_Unterminated] || + (ChecksEnabled[CK_Uninitialized] && ReportUninit))) + return; + for (auto Reg : LeakedVALists) { + if (!BT_leakedvalist) { + // FIXME: maybe creating a new check name for this type of bug is a better + // solution. + BT_leakedvalist.reset( + new BugType(CheckNames[CK_Unterminated].getName().empty() + ? CheckNames[CK_Uninitialized] + : CheckNames[CK_Unterminated], + "Leaked va_list", categories::MemoryError)); + BT_leakedvalist->setSuppressOnSink(true); + } + + const ExplodedNode *StartNode = getStartCallSite(N, Reg); + PathDiagnosticLocation LocUsedForUniqueing; + + if (const Stmt *StartCallStmt = PathDiagnosticLocation::getStmt(StartNode)) + LocUsedForUniqueing = PathDiagnosticLocation::createBegin( + StartCallStmt, C.getSourceManager(), StartNode->getLocationContext()); + + SmallString<100> Buf; + llvm::raw_svector_ostream OS(Buf); + OS << Msg1; + std::string VariableName = Reg->getDescriptiveName(); + if (!VariableName.empty()) + OS << " " << VariableName; + OS << Msg2; + + auto R = llvm::make_unique<BugReport>( + *BT_leakedvalist, OS.str(), N, LocUsedForUniqueing, + StartNode->getLocationContext()->getDecl()); + R->markInteresting(Reg); + R->addVisitor(llvm::make_unique<ValistBugVisitor>(Reg, true)); + C.emitReport(std::move(R)); + } +} + +void ValistChecker::checkVAListStartCall(const CallEvent &Call, + CheckerContext &C, bool IsCopy) const { + bool Symbolic; + const MemRegion *VAList = + getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C); + if (!VAList) + return; + + ProgramStateRef State = C.getState(); + + if (IsCopy) { + const MemRegion *Arg2 = + getVAListAsRegion(Call.getArgSVal(1), Call.getArgExpr(1), Symbolic, C); + if (Arg2) { + if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) { + RegionVector LeakedVALists{VAList}; + if (ExplodedNode *N = C.addTransition(State)) + reportLeakedVALists(LeakedVALists, "va_list", + " is copied onto itself", C, N, true); + return; + } else if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) { + if (State->contains<InitializedVALists>(VAList)) { + State = State->remove<InitializedVALists>(VAList); + RegionVector LeakedVALists{VAList}; + if (ExplodedNode *N = C.addTransition(State)) + reportLeakedVALists(LeakedVALists, "Initialized va_list", + " is overwritten by an uninitialized one", C, N, + true); + } else { + reportUninitializedAccess(Arg2, "Uninitialized va_list is copied", C); + } + return; + } + } + } + if (State->contains<InitializedVALists>(VAList)) { + RegionVector LeakedVALists{VAList}; + if (ExplodedNode *N = C.addTransition(State)) + reportLeakedVALists(LeakedVALists, "Initialized va_list", + " is initialized again", C, N); + return; + } + + State = State->add<InitializedVALists>(VAList); + C.addTransition(State); +} + +void ValistChecker::checkVAListEndCall(const CallEvent &Call, + CheckerContext &C) const { + bool Symbolic; + const MemRegion *VAList = + getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C); + if (!VAList) + return; + + // We did not see va_start call, but the source of the region is unknown. + // Be conservative and assume the best. + if (Symbolic) + return; + + if (!C.getState()->contains<InitializedVALists>(VAList)) { + reportUninitializedAccess( + VAList, "va_end() is called on an uninitialized va_list", C); + return; + } + ProgramStateRef State = C.getState(); + State = State->remove<InitializedVALists>(VAList); + C.addTransition(State); +} + +std::shared_ptr<PathDiagnosticPiece> ValistChecker::ValistBugVisitor::VisitNode( + const ExplodedNode *N, BugReporterContext &BRC, + BugReport &) { + ProgramStateRef State = N->getState(); + ProgramStateRef StatePrev = N->getFirstPred()->getState(); + + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + + StringRef Msg; + if (State->contains<InitializedVALists>(Reg) && + !StatePrev->contains<InitializedVALists>(Reg)) + Msg = "Initialized va_list"; + else if (!State->contains<InitializedVALists>(Reg) && + StatePrev->contains<InitializedVALists>(Reg)) + Msg = "Ended va_list"; + + if (Msg.empty()) + return nullptr; + + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true); +} + +#define REGISTER_CHECKER(name) \ + void ento::register##name##Checker(CheckerManager &mgr) { \ + ValistChecker *checker = mgr.registerChecker<ValistChecker>(); \ + checker->ChecksEnabled[ValistChecker::CK_##name] = true; \ + checker->CheckNames[ValistChecker::CK_##name] = mgr.getCurrentCheckName(); \ + } + +REGISTER_CHECKER(Uninitialized) +REGISTER_CHECKER(Unterminated) +REGISTER_CHECKER(CopyToSelf) diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp new file mode 100644 index 000000000000..3ee9f1a07fa2 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp @@ -0,0 +1,218 @@ +//===- VforkChecker.cpp -------- Vfork usage checks --------------*- 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 vfork checker which checks for dangerous uses of vfork. +// Vforked process shares memory (including stack) with parent so it's +// range of actions is significantly limited: can't write variables, +// can't call functions not in whitelist, etc. For more details, see +// http://man7.org/linux/man-pages/man2/vfork.2.html +// +// This checker checks for prohibited constructs in vforked process. +// The state transition diagram: +// PARENT ---(vfork() == 0)--> CHILD +// | +// --(*p = ...)--> bug +// | +// --foo()--> bug +// | +// --return--> bug +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/AST/ParentMap.h" + +using namespace clang; +using namespace ento; + +namespace { + +class VforkChecker : public Checker<check::PreCall, check::PostCall, + check::Bind, check::PreStmt<ReturnStmt>> { + mutable std::unique_ptr<BuiltinBug> BT; + mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkWhitelist; + mutable const IdentifierInfo *II_vfork; + + static bool isChildProcess(const ProgramStateRef State); + + bool isVforkCall(const Decl *D, CheckerContext &C) const; + bool isCallWhitelisted(const IdentifierInfo *II, CheckerContext &C) const; + + void reportBug(const char *What, CheckerContext &C, + const char *Details = nullptr) const; + +public: + VforkChecker() : II_vfork(nullptr) {} + + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + void checkPostCall(const CallEvent &Call, CheckerContext &C) const; + void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const; + void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const; +}; + +} // end anonymous namespace + +// This trait holds region of variable that is assigned with vfork's +// return value (this is the only region child is allowed to write). +// VFORK_RESULT_INVALID means that we are in parent process. +// VFORK_RESULT_NONE means that vfork's return value hasn't been assigned. +// Other values point to valid regions. +REGISTER_TRAIT_WITH_PROGRAMSTATE(VforkResultRegion, const void *) +#define VFORK_RESULT_INVALID 0 +#define VFORK_RESULT_NONE ((void *)(uintptr_t)1) + +bool VforkChecker::isChildProcess(const ProgramStateRef State) { + return State->get<VforkResultRegion>() != VFORK_RESULT_INVALID; +} + +bool VforkChecker::isVforkCall(const Decl *D, CheckerContext &C) const { + auto FD = dyn_cast_or_null<FunctionDecl>(D); + if (!FD || !C.isCLibraryFunction(FD)) + return false; + + if (!II_vfork) { + ASTContext &AC = C.getASTContext(); + II_vfork = &AC.Idents.get("vfork"); + } + + return FD->getIdentifier() == II_vfork; +} + +// Returns true iff ok to call function after successful vfork. +bool VforkChecker::isCallWhitelisted(const IdentifierInfo *II, + CheckerContext &C) const { + if (VforkWhitelist.empty()) { + // According to manpage. + const char *ids[] = { + "_exit", + "_Exit", + "execl", + "execlp", + "execle", + "execv", + "execvp", + "execvpe", + nullptr + }; + + ASTContext &AC = C.getASTContext(); + for (const char **id = ids; *id; ++id) + VforkWhitelist.insert(&AC.Idents.get(*id)); + } + + return VforkWhitelist.count(II); +} + +void VforkChecker::reportBug(const char *What, CheckerContext &C, + const char *Details) const { + if (ExplodedNode *N = C.generateErrorNode(C.getState())) { + if (!BT) + BT.reset(new BuiltinBug(this, + "Dangerous construct in a vforked process")); + + SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + + os << What << " is prohibited after a successful vfork"; + + if (Details) + os << "; " << Details; + + auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N); + // TODO: mark vfork call in BugReportVisitor + C.emitReport(std::move(Report)); + } +} + +// Detect calls to vfork and split execution appropriately. +void VforkChecker::checkPostCall(const CallEvent &Call, + CheckerContext &C) const { + // We can't call vfork in child so don't bother + // (corresponding warning has already been emitted in checkPreCall). + ProgramStateRef State = C.getState(); + if (isChildProcess(State)) + return; + + if (!isVforkCall(Call.getDecl(), C)) + return; + + // Get return value of vfork. + SVal VforkRetVal = Call.getReturnValue(); + Optional<DefinedOrUnknownSVal> DVal = + VforkRetVal.getAs<DefinedOrUnknownSVal>(); + if (!DVal) + return; + + // Get assigned variable. + const ParentMap &PM = C.getLocationContext()->getParentMap(); + const Stmt *P = PM.getParentIgnoreParenCasts(Call.getOriginExpr()); + const VarDecl *LhsDecl; + std::tie(LhsDecl, std::ignore) = parseAssignment(P); + + // Get assigned memory region. + MemRegionManager &M = C.getStoreManager().getRegionManager(); + const MemRegion *LhsDeclReg = + LhsDecl + ? M.getVarRegion(LhsDecl, C.getLocationContext()) + : (const MemRegion *)VFORK_RESULT_NONE; + + // Parent branch gets nonzero return value (according to manpage). + ProgramStateRef ParentState, ChildState; + std::tie(ParentState, ChildState) = C.getState()->assume(*DVal); + C.addTransition(ParentState); + ChildState = ChildState->set<VforkResultRegion>(LhsDeclReg); + C.addTransition(ChildState); +} + +// Prohibit calls to non-whitelist functions in child process. +void VforkChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (isChildProcess(State) + && !isCallWhitelisted(Call.getCalleeIdentifier(), C)) + reportBug("This function call", C); +} + +// Prohibit writes in child process (except for vfork's lhs). +void VforkChecker::checkBind(SVal L, SVal V, const Stmt *S, + CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (!isChildProcess(State)) + return; + + const MemRegion *VforkLhs = + static_cast<const MemRegion *>(State->get<VforkResultRegion>()); + const MemRegion *MR = L.getAsRegion(); + + // Child is allowed to modify only vfork's lhs. + if (!MR || MR == VforkLhs) + return; + + reportBug("This assignment", C); +} + +// Prohibit return from function in child process. +void VforkChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const { + ProgramStateRef State = C.getState(); + if (isChildProcess(State)) + reportBug("Return", C, "call _exit() instead"); +} + +void ento::registerVforkChecker(CheckerManager &mgr) { + mgr.registerChecker<VforkChecker>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp new file mode 100644 index 000000000000..567063197405 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp @@ -0,0 +1,285 @@ +//=======- VirtualCallChecker.cpp --------------------------------*- 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 a checker that checks virtual function calls during +// construction or destruction of C++ objects. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/AST/DeclCXX.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" + +using namespace clang; +using namespace ento; + +namespace { +enum class ObjectState : bool { CtorCalled, DtorCalled }; +} // end namespace + // FIXME: Ascending over StackFrameContext maybe another method. + +namespace llvm { +template <> struct FoldingSetTrait<ObjectState> { + static inline void Profile(ObjectState X, FoldingSetNodeID &ID) { + ID.AddInteger(static_cast<int>(X)); + } +}; +} // end namespace llvm + +namespace { +class VirtualCallChecker + : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> { + mutable std::unique_ptr<BugType> BT; + +public: + // The flag to determine if pure virtual functions should be issued only. + DefaultBool IsPureOnly; + + void checkBeginFunction(CheckerContext &C) const; + void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const; + void checkPreCall(const CallEvent &Call, CheckerContext &C) const; + +private: + void registerCtorDtorCallInState(bool IsBeginFunction, + CheckerContext &C) const; + void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg, + CheckerContext &C) const; + + class VirtualBugVisitor : public BugReporterVisitor { + private: + const MemRegion *ObjectRegion; + bool Found; + + public: + VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int X = 0; + ID.AddPointer(&X); + ID.AddPointer(ObjectRegion); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override; + }; +}; +} // end namespace + +// GDM (generic data map) to the memregion of this for the ctor and dtor. +REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState) + +std::shared_ptr<PathDiagnosticPiece> +VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &) { + // We need the last ctor/dtor which call the virtual function. + // The visitor walks the ExplodedGraph backwards. + if (Found) + return nullptr; + + ProgramStateRef State = N->getState(); + const LocationContext *LCtx = N->getLocationContext(); + const CXXConstructorDecl *CD = + dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl()); + const CXXDestructorDecl *DD = + dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl()); + + if (!CD && !DD) + return nullptr; + + ProgramStateManager &PSM = State->getStateManager(); + auto &SVB = PSM.getSValBuilder(); + const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl()); + if (!MD) + return nullptr; + auto ThiSVal = + State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); + const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion(); + if (!Reg) + return nullptr; + if (Reg != ObjectRegion) + return nullptr; + + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) + return nullptr; + Found = true; + + std::string InfoText; + if (CD) + InfoText = "This constructor of an object of type '" + + CD->getNameAsString() + + "' has not returned when the virtual method was called"; + else + InfoText = "This destructor of an object of type '" + + DD->getNameAsString() + + "' has not returned when the virtual method was called"; + + // Generate the extra diagnostic. + PathDiagnosticLocation Pos(S, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true); +} + +// The function to check if a callexpr is a virtual function. +static bool isVirtualCall(const CallExpr *CE) { + bool CallIsNonVirtual = false; + + if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) { + // The member access is fully qualified (i.e., X::F). + // Treat this as a non-virtual call and do not warn. + if (CME->getQualifier()) + CallIsNonVirtual = true; + + if (const Expr *Base = CME->getBase()) { + // The most derived class is marked final. + if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>()) + CallIsNonVirtual = true; + } + } + + const CXXMethodDecl *MD = + dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee()); + if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() && + !MD->getParent()->hasAttr<FinalAttr>()) + return true; + return false; +} + +// The BeginFunction callback when enter a constructor or a destructor. +void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const { + registerCtorDtorCallInState(true, C); +} + +// The EndFunction callback when leave a constructor or a destructor. +void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS, + CheckerContext &C) const { + registerCtorDtorCallInState(false, C); +} + +void VirtualCallChecker::checkPreCall(const CallEvent &Call, + CheckerContext &C) const { + const auto MC = dyn_cast<CXXMemberCall>(&Call); + if (!MC) + return; + + const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl()); + if (!MD) + return; + ProgramStateRef State = C.getState(); + const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr()); + + if (IsPureOnly && !MD->isPure()) + return; + if (!isVirtualCall(CE)) + return; + + const MemRegion *Reg = MC->getCXXThisVal().getAsRegion(); + const ObjectState *ObState = State->get<CtorDtorMap>(Reg); + if (!ObState) + return; + // Check if a virtual method is called. + // The GDM of constructor and destructor should be true. + if (*ObState == ObjectState::CtorCalled) { + if (IsPureOnly && MD->isPure()) + reportBug("Call to pure virtual function during construction", true, Reg, + C); + else if (!MD->isPure()) + reportBug("Call to virtual function during construction", false, Reg, C); + else + reportBug("Call to pure virtual function during construction", false, Reg, + C); + } + + if (*ObState == ObjectState::DtorCalled) { + if (IsPureOnly && MD->isPure()) + reportBug("Call to pure virtual function during destruction", true, Reg, + C); + else if (!MD->isPure()) + reportBug("Call to virtual function during destruction", false, Reg, C); + else + reportBug("Call to pure virtual function during construction", false, Reg, + C); + } +} + +void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction, + CheckerContext &C) const { + const auto *LCtx = C.getLocationContext(); + const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl()); + if (!MD) + return; + + ProgramStateRef State = C.getState(); + auto &SVB = C.getSValBuilder(); + + // Enter a constructor, set the corresponding memregion be true. + if (isa<CXXConstructorDecl>(MD)) { + auto ThiSVal = + State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); + const MemRegion *Reg = ThiSVal.getAsRegion(); + if (IsBeginFunction) + State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled); + else + State = State->remove<CtorDtorMap>(Reg); + + C.addTransition(State); + return; + } + + // Enter a Destructor, set the corresponding memregion be true. + if (isa<CXXDestructorDecl>(MD)) { + auto ThiSVal = + State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame())); + const MemRegion *Reg = ThiSVal.getAsRegion(); + if (IsBeginFunction) + State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled); + else + State = State->remove<CtorDtorMap>(Reg); + + C.addTransition(State); + return; + } +} + +void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink, + const MemRegion *Reg, + CheckerContext &C) const { + ExplodedNode *N; + if (IsSink) + N = C.generateErrorNode(); + else + N = C.generateNonFatalErrorNode(); + + if (!N) + return; + if (!BT) + BT.reset(new BugType( + this, "Call to virtual function during construction or destruction", + "C++ Object Lifecycle")); + + auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N); + Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg)); + C.emitReport(std::move(Reporter)); +} + +void ento::registerVirtualCallChecker(CheckerManager &mgr) { + VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>(); + + checker->IsPureOnly = + mgr.getAnalyzerOptions().getCheckerBooleanOption("PureOnly", false, + checker); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/APSIntType.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/APSIntType.cpp new file mode 100644 index 000000000000..c7e95268213e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/APSIntType.cpp @@ -0,0 +1,49 @@ +//===--- 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. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" + +using namespace clang; +using namespace ento; + +APSIntType::RangeTestResultKind +APSIntType::testInRange(const llvm::APSInt &Value, + bool AllowSignConversions) const { + + // Negative numbers cannot be losslessly converted to unsigned type. + if (IsUnsigned && !AllowSignConversions && + Value.isSigned() && Value.isNegative()) + return RTR_Below; + + unsigned MinBits; + if (AllowSignConversions) { + if (Value.isSigned() && !IsUnsigned) + MinBits = Value.getMinSignedBits(); + else + MinBits = Value.getActiveBits(); + + } else { + // Signed integers can be converted to signed integers of the same width + // or (if positive) unsigned integers with one fewer bit. + // Unsigned integers can be converted to unsigned integers of the same width + // or signed integers with one more bit. + if (Value.isSigned()) + MinBits = Value.getMinSignedBits() - IsUnsigned; + else + MinBits = Value.getActiveBits() + !IsUnsigned; + } + + if (MinBits <= BitWidth) + return RTR_Within; + + if (Value.isSigned() && Value.isNegative()) + return RTR_Below; + else + return RTR_Above; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp new file mode 100644 index 000000000000..7fb1c09ca049 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp @@ -0,0 +1,62 @@ +//===-- AnalysisManager.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/AnalysisManager.h" + +using namespace clang; +using namespace ento; + +void AnalysisManager::anchor() { } + +AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags, + const PathDiagnosticConsumers &PDC, + StoreManagerCreator storemgr, + ConstraintManagerCreator constraintmgr, + CheckerManager *checkerMgr, + AnalyzerOptions &Options, + CodeInjector *injector) + : AnaCtxMgr( + ASTCtx, Options.UnoptimizedCFG, + Options.ShouldIncludeImplicitDtorsInCFG, + /*AddInitializers=*/true, + Options.ShouldIncludeTemporaryDtorsInCFG, + Options.ShouldIncludeLifetimeInCFG, + // Adding LoopExit elements to the CFG is a requirement for loop + // unrolling. + Options.ShouldIncludeLoopExitInCFG || + Options.ShouldUnrollLoops, + Options.ShouldIncludeScopesInCFG, + Options.ShouldSynthesizeBodies, + Options.ShouldConditionalizeStaticInitializers, + /*addCXXNewAllocator=*/true, + Options.ShouldIncludeRichConstructorsInCFG, + Options.ShouldElideConstructors, injector), + Ctx(ASTCtx), Diags(diags), LangOpts(ASTCtx.getLangOpts()), + PathConsumers(PDC), CreateStoreMgr(storemgr), + CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr), + options(Options) { + AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd(); +} + +AnalysisManager::~AnalysisManager() { + FlushDiagnostics(); + for (PathDiagnosticConsumers::iterator I = PathConsumers.begin(), + E = PathConsumers.end(); I != E; ++I) { + delete *I; + } +} + +void AnalysisManager::FlushDiagnostics() { + PathDiagnosticConsumer::FilesMade filesMade; + for (PathDiagnosticConsumers::iterator I = PathConsumers.begin(), + E = PathConsumers.end(); + I != E; ++I) { + (*I)->FlushDiagnostics(&filesMade); + } +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp new file mode 100644 index 000000000000..0588c2bd3d35 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp @@ -0,0 +1,156 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file contains special accessors for analyzer configuration options +// with string representations. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cstddef> +#include <utility> +#include <vector> + +using namespace clang; +using namespace ento; +using namespace llvm; + +std::vector<StringRef> +AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) { + static const StringRef StaticAnalyzerChecks[] = { +#define GET_CHECKERS +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ + FULLNAME, +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER +#undef GET_CHECKERS + }; + std::vector<StringRef> Result; + for (StringRef CheckName : StaticAnalyzerChecks) { + if (!CheckName.startswith("debug.") && + (IncludeExperimental || !CheckName.startswith("alpha."))) + Result.push_back(CheckName); + } + return Result; +} + +ExplorationStrategyKind +AnalyzerOptions::getExplorationStrategy() const { + auto K = + llvm::StringSwitch<llvm::Optional<ExplorationStrategyKind>>( + ExplorationStrategy) + .Case("dfs", ExplorationStrategyKind::DFS) + .Case("bfs", ExplorationStrategyKind::BFS) + .Case("unexplored_first", + ExplorationStrategyKind::UnexploredFirst) + .Case("unexplored_first_queue", + ExplorationStrategyKind::UnexploredFirstQueue) + .Case("unexplored_first_location_queue", + ExplorationStrategyKind::UnexploredFirstLocationQueue) + .Case("bfs_block_dfs_contents", + ExplorationStrategyKind::BFSBlockDFSContents) + .Default(None); + assert(K.hasValue() && "User mode is invalid."); + return K.getValue(); +} + +IPAKind AnalyzerOptions::getIPAMode() const { + auto K = llvm::StringSwitch<llvm::Optional<IPAKind>>(IPAMode) + .Case("none", IPAK_None) + .Case("basic-inlining", IPAK_BasicInlining) + .Case("inlining", IPAK_Inlining) + .Case("dynamic", IPAK_DynamicDispatch) + .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate) + .Default(None); + assert(K.hasValue() && "IPA Mode is invalid."); + + return K.getValue(); +} + +bool +AnalyzerOptions::mayInlineCXXMemberFunction( + CXXInlineableMemberKind Param) const { + if (getIPAMode() < IPAK_Inlining) + return false; + + auto K = + llvm::StringSwitch<llvm::Optional<CXXInlineableMemberKind>>( + CXXMemberInliningMode) + .Case("constructors", CIMK_Constructors) + .Case("destructors", CIMK_Destructors) + .Case("methods", CIMK_MemberFunctions) + .Case("none", CIMK_None) + .Default(None); + + assert(K.hasValue() && "Invalid c++ member function inlining mode."); + + return *K >= Param; +} + +StringRef AnalyzerOptions::getCheckerStringOption(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(); + + 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 = + Config.find((Twine(CheckerName) + ":" + OptionName).str()); + if (I != E) + return StringRef(I->getValue()); + size_t Pos = CheckerName.rfind('.'); + if (Pos == StringRef::npos) + return DefaultVal; + CheckerName = CheckerName.substr(0, Pos); + } while (!CheckerName.empty() && SearchInParents); + return DefaultVal; +} + +bool AnalyzerOptions::getCheckerBooleanOption(StringRef Name, bool DefaultVal, + const CheckerBase *C, + 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, + SearchInParents)) + .Case("true", true) + .Case("false", false) + .Default(DefaultVal); +} + +int AnalyzerOptions::getCheckerIntegerOption(StringRef Name, int DefaultVal, + const CheckerBase *C, + bool SearchInParents) const { + int Ret = DefaultVal; + bool HasFailed = getCheckerStringOption(Name, std::to_string(DefaultVal), C, + SearchInParents) + .getAsInteger(10, Ret); + assert(!HasFailed && "analyzer-config option should be numeric"); + (void)HasFailed; + return Ret; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp new file mode 100644 index 000000000000..d8ed6942de81 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -0,0 +1,350 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicValueFactory, a class that manages the lifetime +// of APSInt objects and symbolic constraints used by ExprEngine +// and related classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/STLExtras.h" +#include <cassert> +#include <cstdint> +#include <utility> + +using namespace clang; +using namespace ento; + +void CompoundValData::Profile(llvm::FoldingSetNodeID& ID, QualType T, + llvm::ImmutableList<SVal> L) { + T.Profile(ID); + ID.AddPointer(L.getInternalPointer()); +} + +void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID, + const StoreRef &store, + const TypedValueRegion *region) { + ID.AddPointer(store.getStore()); + ID.AddPointer(region); +} + +void PointerToMemberData::Profile( + llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D, + llvm::ImmutableList<const CXXBaseSpecifier *> L) { + ID.AddPointer(D); + ID.AddPointer(L.getInternalPointer()); +} + +using SValData = std::pair<SVal, uintptr_t>; +using SValPair = std::pair<SVal, SVal>; + +namespace llvm { + +template<> struct FoldingSetTrait<SValData> { + static inline void Profile(const SValData& X, llvm::FoldingSetNodeID& ID) { + X.first.Profile(ID); + ID.AddPointer( (void*) X.second); + } +}; + +template<> struct FoldingSetTrait<SValPair> { + static inline void Profile(const SValPair& X, llvm::FoldingSetNodeID& ID) { + X.first.Profile(ID); + X.second.Profile(ID); + } +}; + +} // namespace llvm + +using PersistentSValsTy = + llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValData>>; + +using PersistentSValPairsTy = + llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValPair>>; + +BasicValueFactory::~BasicValueFactory() { + // Note that the dstor for the contents of APSIntSet will never be called, + // so we iterate over the set and invoke the dstor for each APSInt. This + // frees an aux. memory allocated to represent very large constants. + for (const auto &I : APSIntSet) + I.getValue().~APSInt(); + + delete (PersistentSValsTy*) PersistentSVals; + delete (PersistentSValPairsTy*) PersistentSValPairs; +} + +const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) { + llvm::FoldingSetNodeID ID; + void *InsertPos; + + using FoldNodeTy = llvm::FoldingSetNodeWrapper<llvm::APSInt>; + + X.Profile(ID); + FoldNodeTy* P = APSIntSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!P) { + P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>(); + new (P) FoldNodeTy(X); + APSIntSet.InsertNode(P, InsertPos); + } + + return *P; +} + +const llvm::APSInt& BasicValueFactory::getValue(const llvm::APInt& X, + bool isUnsigned) { + llvm::APSInt V(X, isUnsigned); + return getValue(V); +} + +const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, unsigned BitWidth, + bool isUnsigned) { + llvm::APSInt V(BitWidth, isUnsigned); + V = X; + return getValue(V); +} + +const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, QualType T) { + return getValue(getAPSIntType(T).getValue(X)); +} + +const CompoundValData* +BasicValueFactory::getCompoundValData(QualType T, + llvm::ImmutableList<SVal> Vals) { + llvm::FoldingSetNodeID ID; + CompoundValData::Profile(ID, T, Vals); + void *InsertPos; + + CompoundValData* D = CompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!D) { + D = (CompoundValData*) BPAlloc.Allocate<CompoundValData>(); + new (D) CompoundValData(T, Vals); + CompoundValDataSet.InsertNode(D, InsertPos); + } + + return D; +} + +const LazyCompoundValData* +BasicValueFactory::getLazyCompoundValData(const StoreRef &store, + const TypedValueRegion *region) { + llvm::FoldingSetNodeID ID; + LazyCompoundValData::Profile(ID, store, region); + void *InsertPos; + + LazyCompoundValData *D = + LazyCompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!D) { + D = (LazyCompoundValData*) BPAlloc.Allocate<LazyCompoundValData>(); + new (D) LazyCompoundValData(store, region); + LazyCompoundValDataSet.InsertNode(D, InsertPos); + } + + return D; +} + +const PointerToMemberData *BasicValueFactory::getPointerToMemberData( + const DeclaratorDecl *DD, llvm::ImmutableList<const CXXBaseSpecifier *> L) { + llvm::FoldingSetNodeID ID; + PointerToMemberData::Profile(ID, DD, L); + void *InsertPos; + + PointerToMemberData *D = + PointerToMemberDataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!D) { + D = (PointerToMemberData*) BPAlloc.Allocate<PointerToMemberData>(); + new (D) PointerToMemberData(DD, L); + PointerToMemberDataSet.InsertNode(D, InsertPos); + } + + return D; +} + +const PointerToMemberData *BasicValueFactory::accumCXXBase( + llvm::iterator_range<CastExpr::path_const_iterator> PathRange, + const nonloc::PointerToMember &PTM) { + nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData(); + const DeclaratorDecl *DD = nullptr; + llvm::ImmutableList<const CXXBaseSpecifier *> PathList; + + if (PTMDT.isNull() || PTMDT.is<const DeclaratorDecl *>()) { + if (PTMDT.is<const DeclaratorDecl *>()) + DD = PTMDT.get<const DeclaratorDecl *>(); + + PathList = CXXBaseListFactory.getEmptyList(); + } else { // const PointerToMemberData * + const PointerToMemberData *PTMD = + PTMDT.get<const PointerToMemberData *>(); + DD = PTMD->getDeclaratorDecl(); + + PathList = PTMD->getCXXBaseList(); + } + + for (const auto &I : llvm::reverse(PathRange)) + PathList = prependCXXBase(I, PathList); + return getPointerToMemberData(DD, PathList); +} + +const llvm::APSInt* +BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, + const llvm::APSInt& V1, const llvm::APSInt& V2) { + switch (Op) { + default: + llvm_unreachable("Invalid Opcode."); + + case BO_Mul: + return &getValue( V1 * V2 ); + + case BO_Div: + if (V2 == 0) // Avoid division by zero + return nullptr; + return &getValue( V1 / V2 ); + + case BO_Rem: + if (V2 == 0) // Avoid division by zero + return nullptr; + return &getValue( V1 % V2 ); + + case BO_Add: + return &getValue( V1 + V2 ); + + case BO_Sub: + return &getValue( V1 - V2 ); + + case BO_Shl: { + // FIXME: This logic should probably go higher up, where we can + // test these conditions symbolically. + + if (V1.isSigned() && V1.isNegative()) + return nullptr; + + if (V2.isSigned() && V2.isNegative()) + return nullptr; + + uint64_t Amt = V2.getZExtValue(); + + if (Amt >= V1.getBitWidth()) + return nullptr; + + if (V1.isSigned() && Amt > V1.countLeadingZeros()) + return nullptr; + + return &getValue( V1.operator<<( (unsigned) Amt )); + } + + case BO_Shr: { + // FIXME: This logic should probably go higher up, where we can + // test these conditions symbolically. + + if (V2.isSigned() && V2.isNegative()) + return nullptr; + + uint64_t Amt = V2.getZExtValue(); + + if (Amt >= V1.getBitWidth()) + return nullptr; + + return &getValue( V1.operator>>( (unsigned) Amt )); + } + + case BO_LT: + return &getTruthValue( V1 < V2 ); + + case BO_GT: + return &getTruthValue( V1 > V2 ); + + case BO_LE: + return &getTruthValue( V1 <= V2 ); + + case BO_GE: + return &getTruthValue( V1 >= V2 ); + + case BO_EQ: + return &getTruthValue( V1 == V2 ); + + case BO_NE: + return &getTruthValue( V1 != V2 ); + + // Note: LAnd, LOr, Comma are handled specially by higher-level logic. + + case BO_And: + return &getValue( V1 & V2 ); + + case BO_Or: + return &getValue( V1 | V2 ); + + case BO_Xor: + return &getValue( V1 ^ V2 ); + } +} + +const std::pair<SVal, uintptr_t>& +BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) { + // Lazily create the folding set. + if (!PersistentSVals) PersistentSVals = new PersistentSValsTy(); + + llvm::FoldingSetNodeID ID; + void *InsertPos; + V.Profile(ID); + ID.AddPointer((void*) Data); + + PersistentSValsTy& Map = *((PersistentSValsTy*) PersistentSVals); + + using FoldNodeTy = llvm::FoldingSetNodeWrapper<SValData>; + + FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); + + if (!P) { + P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>(); + new (P) FoldNodeTy(std::make_pair(V, Data)); + Map.InsertNode(P, InsertPos); + } + + return P->getValue(); +} + +const std::pair<SVal, SVal>& +BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) { + // Lazily create the folding set. + if (!PersistentSValPairs) PersistentSValPairs = new PersistentSValPairsTy(); + + llvm::FoldingSetNodeID ID; + void *InsertPos; + V1.Profile(ID); + V2.Profile(ID); + + PersistentSValPairsTy& Map = *((PersistentSValPairsTy*) PersistentSValPairs); + + using FoldNodeTy = llvm::FoldingSetNodeWrapper<SValPair>; + + FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); + + if (!P) { + P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>(); + new (P) FoldNodeTy(std::make_pair(V1, V2)); + Map.InsertNode(P, InsertPos); + } + + return P->getValue(); +} + +const SVal* BasicValueFactory::getPersistentSVal(SVal X) { + return &getPersistentSValWithData(X, 0).first; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp new file mode 100644 index 000000000000..8c99bd808494 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp @@ -0,0 +1,85 @@ +//==- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines BlockCounter, an abstract data type used to count +// the number of times a given block has been visited along a path +// analyzed by CoreEngine. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" +#include "llvm/ADT/ImmutableMap.h" + +using namespace clang; +using namespace ento; + +namespace { + +class CountKey { + const StackFrameContext *CallSite; + unsigned BlockID; + +public: + CountKey(const StackFrameContext *CS, unsigned ID) + : CallSite(CS), BlockID(ID) {} + + bool operator==(const CountKey &RHS) const { + return (CallSite == RHS.CallSite) && (BlockID == RHS.BlockID); + } + + bool operator<(const CountKey &RHS) const { + return std::tie(CallSite, BlockID) < std::tie(RHS.CallSite, RHS.BlockID); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(CallSite); + ID.AddInteger(BlockID); + } +}; + +} + +typedef llvm::ImmutableMap<CountKey, unsigned> CountMap; + +static inline CountMap GetMap(void *D) { + return CountMap(static_cast<CountMap::TreeTy*>(D)); +} + +static inline CountMap::Factory& GetFactory(void *F) { + return *static_cast<CountMap::Factory*>(F); +} + +unsigned BlockCounter::getNumVisited(const StackFrameContext *CallSite, + unsigned BlockID) const { + CountMap M = GetMap(Data); + CountMap::data_type* T = M.lookup(CountKey(CallSite, BlockID)); + return T ? *T : 0; +} + +BlockCounter::Factory::Factory(llvm::BumpPtrAllocator& Alloc) { + F = new CountMap::Factory(Alloc); +} + +BlockCounter::Factory::~Factory() { + delete static_cast<CountMap::Factory*>(F); +} + +BlockCounter +BlockCounter::Factory::IncrementCount(BlockCounter BC, + const StackFrameContext *CallSite, + unsigned BlockID) { + return BlockCounter(GetFactory(F).add(GetMap(BC.Data), + CountKey(CallSite, BlockID), + BC.getNumVisited(CallSite, BlockID)+1).getRoot()); +} + +BlockCounter +BlockCounter::Factory::GetEmptyCounter() { + return BlockCounter(GetFactory(F).getEmptyMap().getRoot()); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp new file mode 100644 index 000000000000..fd7f53210490 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp @@ -0,0 +1,3142 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines BugReporter, a utility class for generating +// PathDiagnostics. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtObjC.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <iterator> +#include <memory> +#include <queue> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +using namespace clang; +using namespace ento; + +#define DEBUG_TYPE "BugReporter" + +STATISTIC(MaxBugClassSize, + "The maximum number of bug reports in the same equivalence class"); +STATISTIC(MaxValidBugClassSize, + "The maximum number of bug reports in the same equivalence class " + "where at least one report is valid (not suppressed)"); + +BugReporterVisitor::~BugReporterVisitor() = default; + +void BugReporterContext::anchor() {} + +//===----------------------------------------------------------------------===// +// Helper routines for walking the ExplodedGraph and fetching statements. +//===----------------------------------------------------------------------===// + +static const Stmt *GetPreviousStmt(const ExplodedNode *N) { + for (N = N->getFirstPred(); N; N = N->getFirstPred()) + if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) + return S; + + return nullptr; +} + +static inline const Stmt* +GetCurrentOrPreviousStmt(const ExplodedNode *N) { + if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) + return S; + + return GetPreviousStmt(N); +} + +//===----------------------------------------------------------------------===// +// Diagnostic cleanup. +//===----------------------------------------------------------------------===// + +static PathDiagnosticEventPiece * +eventsDescribeSameCondition(PathDiagnosticEventPiece *X, + PathDiagnosticEventPiece *Y) { + // Prefer diagnostics that come from ConditionBRVisitor over + // those that came from TrackConstraintBRVisitor, + // unless the one from ConditionBRVisitor is + // its generic fallback diagnostic. + const void *tagPreferred = ConditionBRVisitor::getTag(); + const void *tagLesser = TrackConstraintBRVisitor::getTag(); + + if (X->getLocation() != Y->getLocation()) + return nullptr; + + if (X->getTag() == tagPreferred && Y->getTag() == tagLesser) + return ConditionBRVisitor::isPieceMessageGeneric(X) ? Y : X; + + if (Y->getTag() == tagPreferred && X->getTag() == tagLesser) + return ConditionBRVisitor::isPieceMessageGeneric(Y) ? X : Y; + + return nullptr; +} + +/// An optimization pass over PathPieces that removes redundant diagnostics +/// generated by both ConditionBRVisitor and TrackConstraintBRVisitor. Both +/// BugReporterVisitors use different methods to generate diagnostics, with +/// one capable of emitting diagnostics in some cases but not in others. This +/// can lead to redundant diagnostic pieces at the same point in a path. +static void removeRedundantMsgs(PathPieces &path) { + unsigned N = path.size(); + if (N < 2) + return; + // NOTE: this loop intentionally is not using an iterator. Instead, we + // are streaming the path and modifying it in place. This is done by + // grabbing the front, processing it, and if we decide to keep it append + // it to the end of the path. The entire path is processed in this way. + for (unsigned i = 0; i < N; ++i) { + auto piece = std::move(path.front()); + path.pop_front(); + + switch (piece->getKind()) { + case PathDiagnosticPiece::Call: + removeRedundantMsgs(cast<PathDiagnosticCallPiece>(*piece).path); + break; + case PathDiagnosticPiece::Macro: + removeRedundantMsgs(cast<PathDiagnosticMacroPiece>(*piece).subPieces); + break; + case PathDiagnosticPiece::ControlFlow: + break; + case PathDiagnosticPiece::Event: { + if (i == N-1) + break; + + if (auto *nextEvent = + dyn_cast<PathDiagnosticEventPiece>(path.front().get())) { + auto *event = cast<PathDiagnosticEventPiece>(piece.get()); + // Check to see if we should keep one of the two pieces. If we + // come up with a preference, record which piece to keep, and consume + // another piece from the path. + if (auto *pieceToKeep = + eventsDescribeSameCondition(event, nextEvent)) { + piece = std::move(pieceToKeep == event ? piece : path.front()); + path.pop_front(); + ++i; + } + } + break; + } + case PathDiagnosticPiece::Note: + break; + } + path.push_back(std::move(piece)); + } +} + +/// A map from PathDiagnosticPiece to the LocationContext of the inlined +/// function call it represents. +using LocationContextMap = + llvm::DenseMap<const PathPieces *, const LocationContext *>; + +/// Recursively scan through a path and prune out calls and macros pieces +/// that aren't needed. Return true if afterwards the path contains +/// "interesting stuff" which means it shouldn't be pruned from the parent path. +static bool removeUnneededCalls(PathPieces &pieces, BugReport *R, + LocationContextMap &LCM, + bool IsInteresting = false) { + bool containsSomethingInteresting = IsInteresting; + const unsigned N = pieces.size(); + + for (unsigned i = 0 ; i < N ; ++i) { + // Remove the front piece from the path. If it is still something we + // want to keep once we are done, we will push it back on the end. + auto piece = std::move(pieces.front()); + pieces.pop_front(); + + switch (piece->getKind()) { + case PathDiagnosticPiece::Call: { + auto &call = cast<PathDiagnosticCallPiece>(*piece); + // Check if the location context is interesting. + assert(LCM.count(&call.path)); + if (!removeUnneededCalls(call.path, R, LCM, + R->isInteresting(LCM[&call.path]))) + continue; + + containsSomethingInteresting = true; + break; + } + case PathDiagnosticPiece::Macro: { + auto ¯o = cast<PathDiagnosticMacroPiece>(*piece); + if (!removeUnneededCalls(macro.subPieces, R, LCM, IsInteresting)) + continue; + containsSomethingInteresting = true; + break; + } + case PathDiagnosticPiece::Event: { + auto &event = cast<PathDiagnosticEventPiece>(*piece); + + // We never throw away an event, but we do throw it away wholesale + // as part of a path if we throw the entire path away. + containsSomethingInteresting |= !event.isPrunable(); + break; + } + case PathDiagnosticPiece::ControlFlow: + break; + + case PathDiagnosticPiece::Note: + break; + } + + pieces.push_back(std::move(piece)); + } + + return containsSomethingInteresting; +} + +/// Returns true if the given decl has been implicitly given a body, either by +/// the analyzer or by the compiler proper. +static bool hasImplicitBody(const Decl *D) { + assert(D); + return D->isImplicit() || !D->hasBody(); +} + +/// Recursively scan through a path and make sure that all call pieces have +/// valid locations. +static void +adjustCallLocations(PathPieces &Pieces, + PathDiagnosticLocation *LastCallLocation = nullptr) { + for (const auto &I : Pieces) { + auto *Call = dyn_cast<PathDiagnosticCallPiece>(I.get()); + + if (!Call) + continue; + + if (LastCallLocation) { + bool CallerIsImplicit = hasImplicitBody(Call->getCaller()); + if (CallerIsImplicit || !Call->callEnter.asLocation().isValid()) + Call->callEnter = *LastCallLocation; + if (CallerIsImplicit || !Call->callReturn.asLocation().isValid()) + Call->callReturn = *LastCallLocation; + } + + // Recursively clean out the subclass. Keep this call around if + // it contains any informative diagnostics. + PathDiagnosticLocation *ThisCallLocation; + if (Call->callEnterWithin.asLocation().isValid() && + !hasImplicitBody(Call->getCallee())) + ThisCallLocation = &Call->callEnterWithin; + else + ThisCallLocation = &Call->callEnter; + + assert(ThisCallLocation && "Outermost call has an invalid location"); + adjustCallLocations(Call->path, ThisCallLocation); + } +} + +/// Remove edges in and out of C++ default initializer expressions. These are +/// for fields that have in-class initializers, as opposed to being initialized +/// explicitly in a constructor or braced list. +static void removeEdgesToDefaultInitializers(PathPieces &Pieces) { + for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) { + if (auto *C = dyn_cast<PathDiagnosticCallPiece>(I->get())) + removeEdgesToDefaultInitializers(C->path); + + if (auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get())) + removeEdgesToDefaultInitializers(M->subPieces); + + if (auto *CF = dyn_cast<PathDiagnosticControlFlowPiece>(I->get())) { + const Stmt *Start = CF->getStartLocation().asStmt(); + const Stmt *End = CF->getEndLocation().asStmt(); + if (Start && isa<CXXDefaultInitExpr>(Start)) { + I = Pieces.erase(I); + continue; + } else if (End && isa<CXXDefaultInitExpr>(End)) { + PathPieces::iterator Next = std::next(I); + if (Next != E) { + if (auto *NextCF = + dyn_cast<PathDiagnosticControlFlowPiece>(Next->get())) { + NextCF->setStartLocation(CF->getStartLocation()); + } + } + I = Pieces.erase(I); + continue; + } + } + + I++; + } +} + +/// Remove all pieces with invalid locations as these cannot be serialized. +/// We might have pieces with invalid locations as a result of inlining Body +/// Farm generated functions. +static void removePiecesWithInvalidLocations(PathPieces &Pieces) { + for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) { + if (auto *C = dyn_cast<PathDiagnosticCallPiece>(I->get())) + removePiecesWithInvalidLocations(C->path); + + if (auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get())) + removePiecesWithInvalidLocations(M->subPieces); + + if (!(*I)->getLocation().isValid() || + !(*I)->getLocation().asLocation().isValid()) { + I = Pieces.erase(I); + continue; + } + I++; + } +} + +//===----------------------------------------------------------------------===// +// PathDiagnosticBuilder and its associated routines and helper objects. +//===----------------------------------------------------------------------===// + +namespace { + +class PathDiagnosticBuilder : public BugReporterContext { + BugReport *R; + PathDiagnosticConsumer *PDC; + +public: + const LocationContext *LC; + + PathDiagnosticBuilder(GRBugReporter &br, + BugReport *r, InterExplodedGraphMap &Backmap, + PathDiagnosticConsumer *pdc) + : BugReporterContext(br, Backmap), R(r), PDC(pdc), + LC(r->getErrorNode()->getLocationContext()) {} + + PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N); + + PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream &os, + const ExplodedNode *N); + + BugReport *getBugReport() { return R; } + + Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); } + + ParentMap& getParentMap() { return LC->getParentMap(); } + + const Stmt *getParent(const Stmt *S) { + return getParentMap().getParent(S); + } + + PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S); + + PathDiagnosticConsumer::PathGenerationScheme getGenerationScheme() const { + return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Minimal; + } + + bool supportsLogicalOpControlFlow() const { + return PDC ? PDC->supportsLogicalOpControlFlow() : true; + } +}; + +} // namespace + +PathDiagnosticLocation +PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) { + if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N)) + return PathDiagnosticLocation(S, getSourceManager(), LC); + + return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(), + getSourceManager()); +} + +PathDiagnosticLocation +PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os, + const ExplodedNode *N) { + // Slow, but probably doesn't matter. + if (os.str().empty()) + os << ' '; + + const PathDiagnosticLocation &Loc = ExecutionContinues(N); + + if (Loc.asStmt()) + os << "Execution continues on line " + << getSourceManager().getExpansionLineNumber(Loc.asLocation()) + << '.'; + else { + os << "Execution jumps to the end of the "; + const Decl *D = N->getLocationContext()->getDecl(); + if (isa<ObjCMethodDecl>(D)) + os << "method"; + else if (isa<FunctionDecl>(D)) + os << "function"; + else { + assert(isa<BlockDecl>(D)); + os << "anonymous block"; + } + os << '.'; + } + + return Loc; +} + +static const Stmt *getEnclosingParent(const Stmt *S, const ParentMap &PM) { + if (isa<Expr>(S) && PM.isConsumedExpr(cast<Expr>(S))) + return PM.getParentIgnoreParens(S); + + const Stmt *Parent = PM.getParentIgnoreParens(S); + if (!Parent) + return nullptr; + + switch (Parent->getStmtClass()) { + case Stmt::ForStmtClass: + case Stmt::DoStmtClass: + case Stmt::WhileStmtClass: + case Stmt::ObjCForCollectionStmtClass: + case Stmt::CXXForRangeStmtClass: + return Parent; + default: + break; + } + + return nullptr; +} + +static PathDiagnosticLocation +getEnclosingStmtLocation(const Stmt *S, SourceManager &SMgr, const ParentMap &P, + const LocationContext *LC, bool allowNestedContexts) { + if (!S) + return {}; + + while (const Stmt *Parent = getEnclosingParent(S, P)) { + switch (Parent->getStmtClass()) { + case Stmt::BinaryOperatorClass: { + const auto *B = cast<BinaryOperator>(Parent); + if (B->isLogicalOp()) + return PathDiagnosticLocation(allowNestedContexts ? B : S, SMgr, LC); + break; + } + case Stmt::CompoundStmtClass: + case Stmt::StmtExprClass: + return PathDiagnosticLocation(S, SMgr, LC); + case Stmt::ChooseExprClass: + // Similar to '?' if we are referring to condition, just have the edge + // point to the entire choose expression. + if (allowNestedContexts || cast<ChooseExpr>(Parent)->getCond() == S) + return PathDiagnosticLocation(Parent, SMgr, LC); + else + return PathDiagnosticLocation(S, SMgr, LC); + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: + // For '?', if we are referring to condition, just have the edge point + // to the entire '?' expression. + if (allowNestedContexts || + cast<AbstractConditionalOperator>(Parent)->getCond() == S) + return PathDiagnosticLocation(Parent, SMgr, LC); + else + return PathDiagnosticLocation(S, SMgr, LC); + case Stmt::CXXForRangeStmtClass: + if (cast<CXXForRangeStmt>(Parent)->getBody() == S) + return PathDiagnosticLocation(S, SMgr, LC); + break; + case Stmt::DoStmtClass: + return PathDiagnosticLocation(S, SMgr, LC); + case Stmt::ForStmtClass: + if (cast<ForStmt>(Parent)->getBody() == S) + return PathDiagnosticLocation(S, SMgr, LC); + break; + case Stmt::IfStmtClass: + if (cast<IfStmt>(Parent)->getCond() != S) + return PathDiagnosticLocation(S, SMgr, LC); + break; + case Stmt::ObjCForCollectionStmtClass: + if (cast<ObjCForCollectionStmt>(Parent)->getBody() == S) + return PathDiagnosticLocation(S, SMgr, LC); + break; + case Stmt::WhileStmtClass: + if (cast<WhileStmt>(Parent)->getCond() != S) + return PathDiagnosticLocation(S, SMgr, LC); + break; + default: + break; + } + + S = Parent; + } + + assert(S && "Cannot have null Stmt for PathDiagnosticLocation"); + + return PathDiagnosticLocation(S, SMgr, LC); +} + +PathDiagnosticLocation +PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { + assert(S && "Null Stmt passed to getEnclosingStmtLocation"); + return ::getEnclosingStmtLocation(S, getSourceManager(), getParentMap(), LC, + /*allowNestedContexts=*/false); +} + +//===----------------------------------------------------------------------===// +// "Minimal" path diagnostic generation algorithm. +//===----------------------------------------------------------------------===// +using StackDiagPair = + std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>; +using StackDiagVector = SmallVector<StackDiagPair, 6>; + +static void updateStackPiecesWithMessage(PathDiagnosticPiece &P, + StackDiagVector &CallStack) { + // If the piece contains a special message, add it to all the call + // pieces on the active stack. + if (auto *ep = dyn_cast<PathDiagnosticEventPiece>(&P)) { + if (ep->hasCallStackHint()) + for (const auto &I : CallStack) { + PathDiagnosticCallPiece *CP = I.first; + const ExplodedNode *N = I.second; + std::string stackMsg = ep->getCallStackMessage(N); + + // The last message on the path to final bug is the most important + // one. Since we traverse the path backwards, do not add the message + // if one has been previously added. + if (!CP->hasCallStackMessage()) + CP->setCallStackMessage(stackMsg); + } + } +} + +static void CompactMacroExpandedPieces(PathPieces &path, + const SourceManager& SM); + + +std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP( + const ExplodedNode *N, + const CFGBlock *Dst, + const SourceManager &SM, + const LocationContext *LC, + PathDiagnosticBuilder &PDB, + PathDiagnosticLocation &Start + ) { + // Figure out what case arm we took. + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + PathDiagnosticLocation End; + + if (const Stmt *S = Dst->getLabel()) { + End = PathDiagnosticLocation(S, SM, LC); + + switch (S->getStmtClass()) { + default: + os << "No cases match in the switch statement. " + "Control jumps to line " + << End.asLocation().getExpansionLineNumber(); + break; + case Stmt::DefaultStmtClass: + os << "Control jumps to the 'default' case at line " + << End.asLocation().getExpansionLineNumber(); + break; + + case Stmt::CaseStmtClass: { + os << "Control jumps to 'case "; + const auto *Case = cast<CaseStmt>(S); + const Expr *LHS = Case->getLHS()->IgnoreParenCasts(); + + // Determine if it is an enum. + bool GetRawInt = true; + + if (const auto *DR = dyn_cast<DeclRefExpr>(LHS)) { + // FIXME: Maybe this should be an assertion. Are there cases + // were it is not an EnumConstantDecl? + const auto *D = dyn_cast<EnumConstantDecl>(DR->getDecl()); + + if (D) { + GetRawInt = false; + os << *D; + } + } + + if (GetRawInt) + os << LHS->EvaluateKnownConstInt(PDB.getASTContext()); + + os << ":' at line " << End.asLocation().getExpansionLineNumber(); + break; + } + } + } else { + os << "'Default' branch taken. "; + End = PDB.ExecutionContinues(os, N); + } + return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, + os.str()); +} + + +std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForGotoOP( + const Stmt *S, + PathDiagnosticBuilder &PDB, + PathDiagnosticLocation &Start) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); + os << "Control jumps to line " << End.asLocation().getExpansionLineNumber(); + return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()); + +} + +std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP( + const ExplodedNode *N, + const Stmt *T, + const CFGBlock *Src, + const CFGBlock *Dst, + const SourceManager &SM, + PathDiagnosticBuilder &PDB, + const LocationContext *LC) { + const auto *B = cast<BinaryOperator>(T); + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Left side of '"; + PathDiagnosticLocation Start, End; + + if (B->getOpcode() == BO_LAnd) { + os << "&&" + << "' is "; + + if (*(Src->succ_begin() + 1) == Dst) { + os << "false"; + End = PathDiagnosticLocation(B->getLHS(), SM, LC); + Start = + PathDiagnosticLocation::createOperatorLoc(B, SM); + } else { + os << "true"; + Start = PathDiagnosticLocation(B->getLHS(), SM, LC); + End = PDB.ExecutionContinues(N); + } + } else { + assert(B->getOpcode() == BO_LOr); + os << "||" + << "' is "; + + if (*(Src->succ_begin() + 1) == Dst) { + os << "false"; + Start = PathDiagnosticLocation(B->getLHS(), SM, LC); + End = PDB.ExecutionContinues(N); + } else { + os << "true"; + End = PathDiagnosticLocation(B->getLHS(), SM, LC); + Start = + PathDiagnosticLocation::createOperatorLoc(B, SM); + } + } + return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, + os.str()); +} + +void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE, + const SourceManager &SM, + PathDiagnosticBuilder &PDB, + PathDiagnostic &PD) { + const LocationContext *LC = N->getLocationContext(); + const CFGBlock *Src = BE.getSrc(); + const CFGBlock *Dst = BE.getDst(); + const Stmt *T = Src->getTerminator(); + if (!T) + return; + + auto Start = PathDiagnosticLocation::createBegin(T, SM, LC); + switch (T->getStmtClass()) { + default: + break; + + case Stmt::GotoStmtClass: + case Stmt::IndirectGotoStmtClass: { + if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N)) + PD.getActivePath().push_front(generateDiagForGotoOP(S, PDB, Start)); + break; + } + + case Stmt::SwitchStmtClass: { + PD.getActivePath().push_front( + generateDiagForSwitchOP(N, Dst, SM, LC, PDB, Start)); + break; + } + + case Stmt::BreakStmtClass: + case Stmt::ContinueStmtClass: { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); + break; + } + + // Determine control-flow for ternary '?'. + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "'?' condition is "; + + if (*(Src->succ_begin() + 1) == Dst) + os << "false"; + else + os << "true"; + + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str())); + break; + } + + // Determine control-flow for short-circuited '&&' and '||'. + case Stmt::BinaryOperatorClass: { + if (!PDB.supportsLogicalOpControlFlow()) + break; + + std::shared_ptr<PathDiagnosticControlFlowPiece> Diag = + generateDiagForBinaryOP(N, T, Src, Dst, SM, PDB, LC); + PD.getActivePath().push_front(Diag); + break; + } + + case Stmt::DoStmtClass: + if (*(Src->succ_begin()) == Dst) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "Loop condition is true. "; + PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, + os.str())); + } else { + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>( + Start, End, "Loop condition is false. Exiting loop")); + } + break; + + case Stmt::WhileStmtClass: + case Stmt::ForStmtClass: + if (*(Src->succ_begin() + 1) == Dst) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "Loop condition is false. "; + PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, + os.str())); + } else { + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>( + Start, End, "Loop condition is true. Entering loop body")); + } + + break; + + case Stmt::IfStmtClass: { + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + if (*(Src->succ_begin() + 1) == Dst) + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>( + Start, End, "Taking false branch")); + else + PD.getActivePath().push_front( + std::make_shared<PathDiagnosticControlFlowPiece>( + Start, End, "Taking true branch")); + + break; + } + } +} + +// Cone-of-influence: support the reverse propagation of "interesting" symbols +// and values by tracing interesting calculations backwards through evaluated +// expressions along a path. This is probably overly complicated, but the idea +// is that if an expression computed an "interesting" value, the child +// expressions are also likely to be "interesting" as well (which then +// propagates to the values they in turn compute). This reverse propagation +// is needed to track interesting correlations across function call boundaries, +// where formal arguments bind to actual arguments, etc. This is also needed +// because the constraint solver sometimes simplifies certain symbolic values +// into constants when appropriate, and this complicates reasoning about +// interesting values. +using InterestingExprs = llvm::DenseSet<const Expr *>; + +static void reversePropagateIntererstingSymbols(BugReport &R, + InterestingExprs &IE, + const ProgramState *State, + const Expr *Ex, + const LocationContext *LCtx) { + SVal V = State->getSVal(Ex, LCtx); + if (!(R.isInteresting(V) || IE.count(Ex))) + return; + + switch (Ex->getStmtClass()) { + default: + if (!isa<CastExpr>(Ex)) + break; + LLVM_FALLTHROUGH; + case Stmt::BinaryOperatorClass: + case Stmt::UnaryOperatorClass: { + for (const Stmt *SubStmt : Ex->children()) { + if (const auto *child = dyn_cast_or_null<Expr>(SubStmt)) { + IE.insert(child); + SVal ChildV = State->getSVal(child, LCtx); + R.markInteresting(ChildV); + } + } + break; + } + } + + R.markInteresting(V); +} + +static void reversePropagateInterestingSymbols(BugReport &R, + InterestingExprs &IE, + const ProgramState *State, + const LocationContext *CalleeCtx) +{ + // FIXME: Handle non-CallExpr-based CallEvents. + const StackFrameContext *Callee = CalleeCtx->getStackFrame(); + const Stmt *CallSite = Callee->getCallSite(); + if (const auto *CE = dyn_cast_or_null<CallExpr>(CallSite)) { + if (const auto *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) { + FunctionDecl::param_const_iterator PI = FD->param_begin(), + PE = FD->param_end(); + CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); + for (; AI != AE && PI != PE; ++AI, ++PI) { + if (const Expr *ArgE = *AI) { + if (const ParmVarDecl *PD = *PI) { + Loc LV = State->getLValue(PD, CalleeCtx); + if (R.isInteresting(LV) || R.isInteresting(State->getRawSVal(LV))) + IE.insert(ArgE); + } + } + } + } + } +} + +//===----------------------------------------------------------------------===// +// Functions for determining if a loop was executed 0 times. +//===----------------------------------------------------------------------===// + +static bool isLoop(const Stmt *Term) { + switch (Term->getStmtClass()) { + case Stmt::ForStmtClass: + case Stmt::WhileStmtClass: + case Stmt::ObjCForCollectionStmtClass: + case Stmt::CXXForRangeStmtClass: + return true; + default: + // Note that we intentionally do not include do..while here. + return false; + } +} + +static bool isJumpToFalseBranch(const BlockEdge *BE) { + const CFGBlock *Src = BE->getSrc(); + assert(Src->succ_size() == 2); + return (*(Src->succ_begin()+1) == BE->getDst()); +} + +static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) { + while (SubS) { + if (SubS == S) + return true; + SubS = PM.getParent(SubS); + } + return false; +} + +static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term, + const ExplodedNode *N) { + while (N) { + Optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>(); + if (SP) { + const Stmt *S = SP->getStmt(); + if (!isContainedByStmt(PM, Term, S)) + return S; + } + N = N->getFirstPred(); + } + return nullptr; +} + +static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) { + const Stmt *LoopBody = nullptr; + switch (Term->getStmtClass()) { + case Stmt::CXXForRangeStmtClass: { + const auto *FR = cast<CXXForRangeStmt>(Term); + if (isContainedByStmt(PM, FR->getInc(), S)) + return true; + if (isContainedByStmt(PM, FR->getLoopVarStmt(), S)) + return true; + LoopBody = FR->getBody(); + break; + } + case Stmt::ForStmtClass: { + const auto *FS = cast<ForStmt>(Term); + if (isContainedByStmt(PM, FS->getInc(), S)) + return true; + LoopBody = FS->getBody(); + break; + } + case Stmt::ObjCForCollectionStmtClass: { + const auto *FC = cast<ObjCForCollectionStmt>(Term); + LoopBody = FC->getBody(); + break; + } + case Stmt::WhileStmtClass: + LoopBody = cast<WhileStmt>(Term)->getBody(); + break; + default: + return false; + } + return isContainedByStmt(PM, LoopBody, S); +} + +/// Adds a sanitized control-flow diagnostic edge to a path. +static void addEdgeToPath(PathPieces &path, + PathDiagnosticLocation &PrevLoc, + PathDiagnosticLocation NewLoc) { + if (!NewLoc.isValid()) + return; + + SourceLocation NewLocL = NewLoc.asLocation(); + if (NewLocL.isInvalid()) + return; + + if (!PrevLoc.isValid() || !PrevLoc.asLocation().isValid()) { + PrevLoc = NewLoc; + return; + } + + // Ignore self-edges, which occur when there are multiple nodes at the same + // statement. + if (NewLoc.asStmt() && NewLoc.asStmt() == PrevLoc.asStmt()) + return; + + path.push_front( + std::make_shared<PathDiagnosticControlFlowPiece>(NewLoc, PrevLoc)); + PrevLoc = NewLoc; +} + +/// A customized wrapper for CFGBlock::getTerminatorCondition() +/// which returns the element for ObjCForCollectionStmts. +static const Stmt *getTerminatorCondition(const CFGBlock *B) { + const Stmt *S = B->getTerminatorCondition(); + if (const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(S)) + return FS->getElement(); + return S; +} + +static const char StrEnteringLoop[] = "Entering loop body"; +static const char StrLoopBodyZero[] = "Loop body executed 0 times"; +static const char StrLoopRangeEmpty[] = + "Loop body skipped when range is empty"; +static const char StrLoopCollectionEmpty[] = + "Loop body skipped when collection is empty"; + +static std::unique_ptr<FilesToLineNumsMap> +findExecutedLines(SourceManager &SM, const ExplodedNode *N); + +/// Generate diagnostics for the node \p N, +/// and write it into \p PD. +/// \p AddPathEdges Whether diagnostic consumer can generate path arrows +/// showing both row and column. +static void generatePathDiagnosticsForNode(const ExplodedNode *N, + PathDiagnostic &PD, + PathDiagnosticLocation &PrevLoc, + PathDiagnosticBuilder &PDB, + LocationContextMap &LCM, + StackDiagVector &CallStack, + InterestingExprs &IE, + bool AddPathEdges) { + ProgramPoint P = N->getLocation(); + const SourceManager& SM = PDB.getSourceManager(); + + // Have we encountered an entrance to a call? It may be + // the case that we have not encountered a matching + // call exit before this point. This means that the path + // terminated within the call itself. + if (auto CE = P.getAs<CallEnter>()) { + + if (AddPathEdges) { + // Add an edge to the start of the function. + const StackFrameContext *CalleeLC = CE->getCalleeContext(); + const Decl *D = CalleeLC->getDecl(); + // Add the edge only when the callee has body. We jump to the beginning + // of the *declaration*, however we expect it to be followed by the + // body. This isn't the case for autosynthesized property accessors in + // Objective-C. No need for a similar extra check for CallExit points + // because the exit edge comes from a statement (i.e. return), + // not from declaration. + if (D->hasBody()) + addEdgeToPath(PD.getActivePath(), PrevLoc, + PathDiagnosticLocation::createBegin(D, SM)); + } + + // Did we visit an entire call? + bool VisitedEntireCall = PD.isWithinCall(); + PD.popActivePath(); + + PathDiagnosticCallPiece *C; + if (VisitedEntireCall) { + C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front().get()); + } else { + const Decl *Caller = CE->getLocationContext()->getDecl(); + C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller); + + if (AddPathEdges) { + // Since we just transferred the path over to the call piece, + // reset the mapping from active to location context. + assert(PD.getActivePath().size() == 1 && + PD.getActivePath().front().get() == C); + LCM[&PD.getActivePath()] = nullptr; + } + + // Record the location context mapping for the path within + // the call. + assert(LCM[&C->path] == nullptr || + LCM[&C->path] == CE->getCalleeContext()); + LCM[&C->path] = CE->getCalleeContext(); + + // If this is the first item in the active path, record + // the new mapping from active path to location context. + const LocationContext *&NewLC = LCM[&PD.getActivePath()]; + if (!NewLC) + NewLC = N->getLocationContext(); + + PDB.LC = NewLC; + } + C->setCallee(*CE, SM); + + // Update the previous location in the active path. + PrevLoc = C->getLocation(); + + if (!CallStack.empty()) { + assert(CallStack.back().first == C); + CallStack.pop_back(); + } + return; + } + + + if (AddPathEdges) { + // Query the location context here and the previous location + // as processing CallEnter may change the active path. + PDB.LC = N->getLocationContext(); + + // Record the mapping from the active path to the location + // context. + assert(!LCM[&PD.getActivePath()] || LCM[&PD.getActivePath()] == PDB.LC); + LCM[&PD.getActivePath()] = PDB.LC; + } + + // Have we encountered an exit from a function call? + if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) { + + // We are descending into a call (backwards). Construct + // a new call piece to contain the path pieces for that call. + auto C = PathDiagnosticCallPiece::construct(*CE, SM); + // Record the mapping from call piece to LocationContext. + LCM[&C->path] = CE->getCalleeContext(); + + if (AddPathEdges) { + const Stmt *S = CE->getCalleeContext()->getCallSite(); + // Propagate the interesting symbols accordingly. + if (const auto *Ex = dyn_cast_or_null<Expr>(S)) { + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().get(), Ex, + N->getLocationContext()); + } + // Add the edge to the return site. + addEdgeToPath(PD.getActivePath(), PrevLoc, C->callReturn); + PrevLoc.invalidate(); + } + + auto *P = C.get(); + PD.getActivePath().push_front(std::move(C)); + + // Make the contents of the call the active path for now. + PD.pushActivePath(&P->path); + CallStack.push_back(StackDiagPair(P, N)); + return; + } + + if (auto PS = P.getAs<PostStmt>()) { + if (!AddPathEdges) + return; + + // For expressions, make sure we propagate the + // interesting symbols correctly. + if (const Expr *Ex = PS->getStmtAs<Expr>()) + reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE, + N->getState().get(), Ex, + N->getLocationContext()); + + // Add an edge. If this is an ObjCForCollectionStmt do + // not add an edge here as it appears in the CFG both + // as a terminator and as a terminator condition. + if (!isa<ObjCForCollectionStmt>(PS->getStmt())) { + PathDiagnosticLocation L = + PathDiagnosticLocation(PS->getStmt(), SM, PDB.LC); + addEdgeToPath(PD.getActivePath(), PrevLoc, L); + } + + } else if (auto BE = P.getAs<BlockEdge>()) { + + if (!AddPathEdges) { + generateMinimalDiagForBlockEdge(N, *BE, SM, PDB, PD); + return; + } + + // Does this represent entering a call? If so, look at propagating + // interesting symbols across call boundaries. + if (const ExplodedNode *NextNode = N->getFirstPred()) { + const LocationContext *CallerCtx = NextNode->getLocationContext(); + const LocationContext *CalleeCtx = PDB.LC; + if (CallerCtx != CalleeCtx && AddPathEdges) { + reversePropagateInterestingSymbols(*PDB.getBugReport(), IE, + N->getState().get(), CalleeCtx); + } + } + + // Are we jumping to the head of a loop? Add a special diagnostic. + if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) { + PathDiagnosticLocation L(Loop, SM, PDB.LC); + const Stmt *Body = nullptr; + + if (const auto *FS = dyn_cast<ForStmt>(Loop)) + Body = FS->getBody(); + else if (const auto *WS = dyn_cast<WhileStmt>(Loop)) + Body = WS->getBody(); + else if (const auto *OFS = dyn_cast<ObjCForCollectionStmt>(Loop)) { + Body = OFS->getBody(); + } else if (const auto *FRS = dyn_cast<CXXForRangeStmt>(Loop)) { + Body = FRS->getBody(); + } + // do-while statements are explicitly excluded here + + auto p = std::make_shared<PathDiagnosticEventPiece>( + L, "Looping back to the head " + "of the loop"); + p->setPrunable(true); + + addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation()); + PD.getActivePath().push_front(std::move(p)); + + if (const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) { + addEdgeToPath(PD.getActivePath(), PrevLoc, + PathDiagnosticLocation::createEndBrace(CS, SM)); + } + } + + const CFGBlock *BSrc = BE->getSrc(); + ParentMap &PM = PDB.getParentMap(); + + if (const Stmt *Term = BSrc->getTerminator()) { + // Are we jumping past the loop body without ever executing the + // loop (because the condition was false)? + if (isLoop(Term)) { + const Stmt *TermCond = getTerminatorCondition(BSrc); + bool IsInLoopBody = + isInLoopBody(PM, getStmtBeforeCond(PM, TermCond, N), Term); + + const char *str = nullptr; + + if (isJumpToFalseBranch(&*BE)) { + if (!IsInLoopBody) { + if (isa<ObjCForCollectionStmt>(Term)) { + str = StrLoopCollectionEmpty; + } else if (isa<CXXForRangeStmt>(Term)) { + str = StrLoopRangeEmpty; + } else { + str = StrLoopBodyZero; + } + } + } else { + str = StrEnteringLoop; + } + + if (str) { + PathDiagnosticLocation L(TermCond ? TermCond : Term, SM, PDB.LC); + auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str); + PE->setPrunable(true); + addEdgeToPath(PD.getActivePath(), PrevLoc, + PE->getLocation()); + PD.getActivePath().push_front(std::move(PE)); + } + } else if (isa<BreakStmt>(Term) || isa<ContinueStmt>(Term) || + isa<GotoStmt>(Term)) { + PathDiagnosticLocation L(Term, SM, PDB.LC); + addEdgeToPath(PD.getActivePath(), PrevLoc, L); + } + } + } +} + +static std::unique_ptr<PathDiagnostic> +generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) { + BugType &BT = R->getBugType(); + return llvm::make_unique<PathDiagnostic>( + R->getBugType().getCheckName(), R->getDeclWithIssue(), + R->getBugType().getName(), R->getDescription(), + R->getShortDescription(/*Fallback=*/false), BT.getCategory(), + R->getUniqueingLocation(), R->getUniqueingDecl(), + findExecutedLines(SM, R->getErrorNode())); +} + +static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) { + if (!S) + return nullptr; + + while (true) { + S = PM.getParentIgnoreParens(S); + + if (!S) + break; + + if (isa<FullExpr>(S) || + isa<CXXBindTemporaryExpr>(S) || + isa<SubstNonTypeTemplateParmExpr>(S)) + continue; + + break; + } + + return S; +} + +static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) { + switch (S->getStmtClass()) { + case Stmt::BinaryOperatorClass: { + const auto *BO = cast<BinaryOperator>(S); + if (!BO->isLogicalOp()) + return false; + return BO->getLHS() == Cond || BO->getRHS() == Cond; + } + case Stmt::IfStmtClass: + return cast<IfStmt>(S)->getCond() == Cond; + case Stmt::ForStmtClass: + return cast<ForStmt>(S)->getCond() == Cond; + case Stmt::WhileStmtClass: + return cast<WhileStmt>(S)->getCond() == Cond; + case Stmt::DoStmtClass: + return cast<DoStmt>(S)->getCond() == Cond; + case Stmt::ChooseExprClass: + return cast<ChooseExpr>(S)->getCond() == Cond; + case Stmt::IndirectGotoStmtClass: + return cast<IndirectGotoStmt>(S)->getTarget() == Cond; + case Stmt::SwitchStmtClass: + return cast<SwitchStmt>(S)->getCond() == Cond; + case Stmt::BinaryConditionalOperatorClass: + return cast<BinaryConditionalOperator>(S)->getCond() == Cond; + case Stmt::ConditionalOperatorClass: { + const auto *CO = cast<ConditionalOperator>(S); + return CO->getCond() == Cond || + CO->getLHS() == Cond || + CO->getRHS() == Cond; + } + case Stmt::ObjCForCollectionStmtClass: + return cast<ObjCForCollectionStmt>(S)->getElement() == Cond; + case Stmt::CXXForRangeStmtClass: { + const auto *FRS = cast<CXXForRangeStmt>(S); + return FRS->getCond() == Cond || FRS->getRangeInit() == Cond; + } + default: + return false; + } +} + +static bool isIncrementOrInitInForLoop(const Stmt *S, const Stmt *FL) { + if (const auto *FS = dyn_cast<ForStmt>(FL)) + return FS->getInc() == S || FS->getInit() == S; + if (const auto *FRS = dyn_cast<CXXForRangeStmt>(FL)) + return FRS->getInc() == S || FRS->getRangeStmt() == S || + FRS->getLoopVarStmt() || FRS->getRangeInit() == S; + return false; +} + +using OptimizedCallsSet = llvm::DenseSet<const PathDiagnosticCallPiece *>; + +/// Adds synthetic edges from top-level statements to their subexpressions. +/// +/// This avoids a "swoosh" effect, where an edge from a top-level statement A +/// points to a sub-expression B.1 that's not at the start of B. In these cases, +/// we'd like to see an edge from A to B, then another one from B to B.1. +static void addContextEdges(PathPieces &pieces, SourceManager &SM, + const ParentMap &PM, const LocationContext *LCtx) { + PathPieces::iterator Prev = pieces.end(); + for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E; + Prev = I, ++I) { + auto *Piece = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); + + if (!Piece) + continue; + + PathDiagnosticLocation SrcLoc = Piece->getStartLocation(); + SmallVector<PathDiagnosticLocation, 4> SrcContexts; + + PathDiagnosticLocation NextSrcContext = SrcLoc; + const Stmt *InnerStmt = nullptr; + while (NextSrcContext.isValid() && NextSrcContext.asStmt() != InnerStmt) { + SrcContexts.push_back(NextSrcContext); + InnerStmt = NextSrcContext.asStmt(); + NextSrcContext = getEnclosingStmtLocation(InnerStmt, SM, PM, LCtx, + /*allowNested=*/true); + } + + // Repeatedly split the edge as necessary. + // This is important for nested logical expressions (||, &&, ?:) where we + // want to show all the levels of context. + while (true) { + const Stmt *Dst = Piece->getEndLocation().getStmtOrNull(); + + // We are looking at an edge. Is the destination within a larger + // expression? + PathDiagnosticLocation DstContext = + getEnclosingStmtLocation(Dst, SM, PM, LCtx, /*allowNested=*/true); + if (!DstContext.isValid() || DstContext.asStmt() == Dst) + break; + + // If the source is in the same context, we're already good. + if (std::find(SrcContexts.begin(), SrcContexts.end(), DstContext) != + SrcContexts.end()) + break; + + // Update the subexpression node to point to the context edge. + Piece->setStartLocation(DstContext); + + // Try to extend the previous edge if it's at the same level as the source + // context. + if (Prev != E) { + auto *PrevPiece = dyn_cast<PathDiagnosticControlFlowPiece>(Prev->get()); + + if (PrevPiece) { + if (const Stmt *PrevSrc = + PrevPiece->getStartLocation().getStmtOrNull()) { + const Stmt *PrevSrcParent = getStmtParent(PrevSrc, PM); + if (PrevSrcParent == + getStmtParent(DstContext.getStmtOrNull(), PM)) { + PrevPiece->setEndLocation(DstContext); + break; + } + } + } + } + + // Otherwise, split the current edge into a context edge and a + // subexpression edge. Note that the context statement may itself have + // context. + auto P = + std::make_shared<PathDiagnosticControlFlowPiece>(SrcLoc, DstContext); + Piece = P.get(); + I = pieces.insert(I, std::move(P)); + } + } +} + +/// Move edges from a branch condition to a branch target +/// when the condition is simple. +/// +/// This restructures some of the work of addContextEdges. That function +/// creates edges this may destroy, but they work together to create a more +/// aesthetically set of edges around branches. After the call to +/// addContextEdges, we may have (1) an edge to the branch, (2) an edge from +/// the branch to the branch condition, and (3) an edge from the branch +/// condition to the branch target. We keep (1), but may wish to remove (2) +/// and move the source of (3) to the branch if the branch condition is simple. +static void simplifySimpleBranches(PathPieces &pieces) { + for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) { + const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); + + if (!PieceI) + continue; + + const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull(); + const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull(); + + if (!s1Start || !s1End) + continue; + + PathPieces::iterator NextI = I; ++NextI; + if (NextI == E) + break; + + PathDiagnosticControlFlowPiece *PieceNextI = nullptr; + + while (true) { + if (NextI == E) + break; + + const auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get()); + if (EV) { + StringRef S = EV->getString(); + if (S == StrEnteringLoop || S == StrLoopBodyZero || + S == StrLoopCollectionEmpty || S == StrLoopRangeEmpty) { + ++NextI; + continue; + } + break; + } + + PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get()); + break; + } + + if (!PieceNextI) + continue; + + const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull(); + const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull(); + + if (!s2Start || !s2End || s1End != s2Start) + continue; + + // We only perform this transformation for specific branch kinds. + // We don't want to do this for do..while, for example. + if (!(isa<ForStmt>(s1Start) || isa<WhileStmt>(s1Start) || + isa<IfStmt>(s1Start) || isa<ObjCForCollectionStmt>(s1Start) || + isa<CXXForRangeStmt>(s1Start))) + continue; + + // Is s1End the branch condition? + if (!isConditionForTerminator(s1Start, s1End)) + continue; + + // Perform the hoisting by eliminating (2) and changing the start + // location of (3). + PieceNextI->setStartLocation(PieceI->getStartLocation()); + I = pieces.erase(I); + } +} + +/// Returns the number of bytes in the given (character-based) SourceRange. +/// +/// If the locations in the range are not on the same line, returns None. +/// +/// Note that this does not do a precise user-visible character or column count. +static Optional<size_t> getLengthOnSingleLine(SourceManager &SM, + SourceRange Range) { + SourceRange ExpansionRange(SM.getExpansionLoc(Range.getBegin()), + SM.getExpansionRange(Range.getEnd()).getEnd()); + + FileID FID = SM.getFileID(ExpansionRange.getBegin()); + if (FID != SM.getFileID(ExpansionRange.getEnd())) + return None; + + bool Invalid; + const llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, &Invalid); + if (Invalid) + return None; + + unsigned BeginOffset = SM.getFileOffset(ExpansionRange.getBegin()); + unsigned EndOffset = SM.getFileOffset(ExpansionRange.getEnd()); + StringRef Snippet = Buffer->getBuffer().slice(BeginOffset, EndOffset); + + // We're searching the raw bytes of the buffer here, which might include + // escaped newlines and such. That's okay; we're trying to decide whether the + // SourceRange is covering a large or small amount of space in the user's + // editor. + if (Snippet.find_first_of("\r\n") != StringRef::npos) + return None; + + // This isn't Unicode-aware, but it doesn't need to be. + return Snippet.size(); +} + +/// \sa getLengthOnSingleLine(SourceManager, SourceRange) +static Optional<size_t> getLengthOnSingleLine(SourceManager &SM, + const Stmt *S) { + return getLengthOnSingleLine(SM, S->getSourceRange()); +} + +/// Eliminate two-edge cycles created by addContextEdges(). +/// +/// Once all the context edges are in place, there are plenty of cases where +/// there's a single edge from a top-level statement to a subexpression, +/// followed by a single path note, and then a reverse edge to get back out to +/// the top level. If the statement is simple enough, the subexpression edges +/// just add noise and make it harder to understand what's going on. +/// +/// This function only removes edges in pairs, because removing only one edge +/// might leave other edges dangling. +/// +/// This will not remove edges in more complicated situations: +/// - if there is more than one "hop" leading to or from a subexpression. +/// - if there is an inlined call between the edges instead of a single event. +/// - if the whole statement is large enough that having subexpression arrows +/// might be helpful. +static void removeContextCycles(PathPieces &Path, SourceManager &SM) { + for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) { + // Pattern match the current piece and its successor. + const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); + + if (!PieceI) { + ++I; + continue; + } + + const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull(); + const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull(); + + PathPieces::iterator NextI = I; ++NextI; + if (NextI == E) + break; + + const auto *PieceNextI = + dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get()); + + if (!PieceNextI) { + if (isa<PathDiagnosticEventPiece>(NextI->get())) { + ++NextI; + if (NextI == E) + break; + PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get()); + } + + if (!PieceNextI) { + ++I; + continue; + } + } + + const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull(); + const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull(); + + if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) { + const size_t MAX_SHORT_LINE_LENGTH = 80; + Optional<size_t> s1Length = getLengthOnSingleLine(SM, s1Start); + if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) { + Optional<size_t> s2Length = getLengthOnSingleLine(SM, s2Start); + if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) { + Path.erase(I); + I = Path.erase(NextI); + continue; + } + } + } + + ++I; + } +} + +/// Return true if X is contained by Y. +static bool lexicalContains(ParentMap &PM, const Stmt *X, const Stmt *Y) { + while (X) { + if (X == Y) + return true; + X = PM.getParent(X); + } + return false; +} + +// Remove short edges on the same line less than 3 columns in difference. +static void removePunyEdges(PathPieces &path, SourceManager &SM, + ParentMap &PM) { + bool erased = false; + + for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; + erased ? I : ++I) { + erased = false; + + const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); + + if (!PieceI) + continue; + + const Stmt *start = PieceI->getStartLocation().getStmtOrNull(); + const Stmt *end = PieceI->getEndLocation().getStmtOrNull(); + + if (!start || !end) + continue; + + const Stmt *endParent = PM.getParent(end); + if (!endParent) + continue; + + if (isConditionForTerminator(end, endParent)) + continue; + + SourceLocation FirstLoc = start->getBeginLoc(); + SourceLocation SecondLoc = end->getBeginLoc(); + + if (!SM.isWrittenInSameFile(FirstLoc, SecondLoc)) + continue; + if (SM.isBeforeInTranslationUnit(SecondLoc, FirstLoc)) + std::swap(SecondLoc, FirstLoc); + + SourceRange EdgeRange(FirstLoc, SecondLoc); + Optional<size_t> ByteWidth = getLengthOnSingleLine(SM, EdgeRange); + + // If the statements are on different lines, continue. + if (!ByteWidth) + continue; + + const size_t MAX_PUNY_EDGE_LENGTH = 2; + if (*ByteWidth <= MAX_PUNY_EDGE_LENGTH) { + // FIXME: There are enough /bytes/ between the endpoints of the edge, but + // there might not be enough /columns/. A proper user-visible column count + // is probably too expensive, though. + I = path.erase(I); + erased = true; + continue; + } + } +} + +static void removeIdenticalEvents(PathPieces &path) { + for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ++I) { + const auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get()); + + if (!PieceI) + continue; + + PathPieces::iterator NextI = I; ++NextI; + if (NextI == E) + return; + + const auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get()); + + if (!PieceNextI) + continue; + + // Erase the second piece if it has the same exact message text. + if (PieceI->getString() == PieceNextI->getString()) { + path.erase(NextI); + } + } +} + +static bool optimizeEdges(PathPieces &path, SourceManager &SM, + OptimizedCallsSet &OCS, + LocationContextMap &LCM) { + bool hasChanges = false; + const LocationContext *LC = LCM[&path]; + assert(LC); + ParentMap &PM = LC->getParentMap(); + + for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) { + // Optimize subpaths. + if (auto *CallI = dyn_cast<PathDiagnosticCallPiece>(I->get())) { + // Record the fact that a call has been optimized so we only do the + // effort once. + if (!OCS.count(CallI)) { + while (optimizeEdges(CallI->path, SM, OCS, LCM)) {} + OCS.insert(CallI); + } + ++I; + continue; + } + + // Pattern match the current piece and its successor. + auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get()); + + if (!PieceI) { + ++I; + continue; + } + + const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull(); + const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull(); + const Stmt *level1 = getStmtParent(s1Start, PM); + const Stmt *level2 = getStmtParent(s1End, PM); + + PathPieces::iterator NextI = I; ++NextI; + if (NextI == E) + break; + + const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get()); + + if (!PieceNextI) { + ++I; + continue; + } + + const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull(); + const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull(); + const Stmt *level3 = getStmtParent(s2Start, PM); + const Stmt *level4 = getStmtParent(s2End, PM); + + // Rule I. + // + // If we have two consecutive control edges whose end/begin locations + // are at the same level (e.g. statements or top-level expressions within + // a compound statement, or siblings share a single ancestor expression), + // then merge them if they have no interesting intermediate event. + // + // For example: + // + // (1.1 -> 1.2) -> (1.2 -> 1.3) becomes (1.1 -> 1.3) because the common + // parent is '1'. Here 'x.y.z' represents the hierarchy of statements. + // + // NOTE: this will be limited later in cases where we add barriers + // to prevent this optimization. + if (level1 && level1 == level2 && level1 == level3 && level1 == level4) { + PieceI->setEndLocation(PieceNextI->getEndLocation()); + path.erase(NextI); + hasChanges = true; + continue; + } + + // Rule II. + // + // Eliminate edges between subexpressions and parent expressions + // when the subexpression is consumed. + // + // NOTE: this will be limited later in cases where we add barriers + // to prevent this optimization. + if (s1End && s1End == s2Start && level2) { + bool removeEdge = false; + // Remove edges into the increment or initialization of a + // loop that have no interleaving event. This means that + // they aren't interesting. + if (isIncrementOrInitInForLoop(s1End, level2)) + removeEdge = true; + // Next only consider edges that are not anchored on + // the condition of a terminator. This are intermediate edges + // that we might want to trim. + else if (!isConditionForTerminator(level2, s1End)) { + // Trim edges on expressions that are consumed by + // the parent expression. + if (isa<Expr>(s1End) && PM.isConsumedExpr(cast<Expr>(s1End))) { + removeEdge = true; + } + // Trim edges where a lexical containment doesn't exist. + // For example: + // + // X -> Y -> Z + // + // If 'Z' lexically contains Y (it is an ancestor) and + // 'X' does not lexically contain Y (it is a descendant OR + // it has no lexical relationship at all) then trim. + // + // This can eliminate edges where we dive into a subexpression + // and then pop back out, etc. + else if (s1Start && s2End && + lexicalContains(PM, s2Start, s2End) && + !lexicalContains(PM, s1End, s1Start)) { + removeEdge = true; + } + // Trim edges from a subexpression back to the top level if the + // subexpression is on a different line. + // + // A.1 -> A -> B + // becomes + // A.1 -> B + // + // These edges just look ugly and don't usually add anything. + else if (s1Start && s2End && + lexicalContains(PM, s1Start, s1End)) { + SourceRange EdgeRange(PieceI->getEndLocation().asLocation(), + PieceI->getStartLocation().asLocation()); + if (!getLengthOnSingleLine(SM, EdgeRange).hasValue()) + removeEdge = true; + } + } + + if (removeEdge) { + PieceI->setEndLocation(PieceNextI->getEndLocation()); + path.erase(NextI); + hasChanges = true; + continue; + } + } + + // Optimize edges for ObjC fast-enumeration loops. + // + // (X -> collection) -> (collection -> element) + // + // becomes: + // + // (X -> element) + if (s1End == s2Start) { + const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(level3); + if (FS && FS->getCollection()->IgnoreParens() == s2Start && + s2End == FS->getElement()) { + PieceI->setEndLocation(PieceNextI->getEndLocation()); + path.erase(NextI); + hasChanges = true; + continue; + } + } + + // No changes at this index? Move to the next one. + ++I; + } + + if (!hasChanges) { + // Adjust edges into subexpressions to make them more uniform + // and aesthetically pleasing. + addContextEdges(path, SM, PM, LC); + // Remove "cyclical" edges that include one or more context edges. + removeContextCycles(path, SM); + // Hoist edges originating from branch conditions to branches + // for simple branches. + simplifySimpleBranches(path); + // Remove any puny edges left over after primary optimization pass. + removePunyEdges(path, SM, PM); + // Remove identical events. + removeIdenticalEvents(path); + } + + return hasChanges; +} + +/// Drop the very first edge in a path, which should be a function entry edge. +/// +/// If the first edge is not a function entry edge (say, because the first +/// statement had an invalid source location), this function does nothing. +// FIXME: We should just generate invalid edges anyway and have the optimizer +// deal with them. +static void dropFunctionEntryEdge(PathPieces &Path, LocationContextMap &LCM, + SourceManager &SM) { + const auto *FirstEdge = + dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get()); + if (!FirstEdge) + return; + + const Decl *D = LCM[&Path]->getDecl(); + PathDiagnosticLocation EntryLoc = PathDiagnosticLocation::createBegin(D, SM); + if (FirstEdge->getStartLocation() != EntryLoc) + return; + + Path.pop_front(); +} + +using VisitorsDiagnosticsTy = llvm::DenseMap<const ExplodedNode *, + std::vector<std::shared_ptr<PathDiagnosticPiece>>>; + +/// Populate executes lines with lines containing at least one diagnostics. +static void updateExecutedLinesWithDiagnosticPieces( + PathDiagnostic &PD) { + + PathPieces path = PD.path.flatten(/*ShouldFlattenMacros=*/true); + FilesToLineNumsMap &ExecutedLines = PD.getExecutedLines(); + + for (const auto &P : path) { + FullSourceLoc Loc = P->getLocation().asLocation().getExpansionLoc(); + FileID FID = Loc.getFileID(); + unsigned LineNo = Loc.getLineNumber(); + assert(FID.isValid()); + ExecutedLines[FID].insert(LineNo); + } +} + +/// This function is responsible for generating diagnostic pieces that are +/// *not* provided by bug report visitors. +/// These diagnostics may differ depending on the consumer's settings, +/// and are therefore constructed separately for each consumer. +/// +/// There are two path diagnostics generation modes: with adding edges (used +/// for plists) and without (used for HTML and text). +/// When edges are added (\p ActiveScheme is Extensive), +/// the path is modified to insert artificially generated +/// edges. +/// Otherwise, more detailed diagnostics is emitted for block edges, explaining +/// the transitions in words. +static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer( + PathDiagnosticConsumer::PathGenerationScheme ActiveScheme, + PathDiagnosticBuilder &PDB, + const ExplodedNode *ErrorNode, + const VisitorsDiagnosticsTy &VisitorsDiagnostics) { + + bool GenerateDiagnostics = (ActiveScheme != PathDiagnosticConsumer::None); + bool AddPathEdges = (ActiveScheme == PathDiagnosticConsumer::Extensive); + SourceManager &SM = PDB.getSourceManager(); + BugReport *R = PDB.getBugReport(); + AnalyzerOptions &Opts = PDB.getBugReporter().getAnalyzerOptions(); + StackDiagVector CallStack; + InterestingExprs IE; + LocationContextMap LCM; + std::unique_ptr<PathDiagnostic> PD = generateEmptyDiagnosticForReport(R, SM); + + if (GenerateDiagnostics) { + auto EndNotes = VisitorsDiagnostics.find(ErrorNode); + std::shared_ptr<PathDiagnosticPiece> LastPiece; + if (EndNotes != VisitorsDiagnostics.end()) { + assert(!EndNotes->second.empty()); + LastPiece = EndNotes->second[0]; + } else { + LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, ErrorNode, *R); + } + PD->setEndOfPath(LastPiece); + } + + PathDiagnosticLocation PrevLoc = PD->getLocation(); + const ExplodedNode *NextNode = ErrorNode->getFirstPred(); + while (NextNode) { + if (GenerateDiagnostics) + generatePathDiagnosticsForNode( + NextNode, *PD, PrevLoc, PDB, LCM, CallStack, IE, AddPathEdges); + + auto VisitorNotes = VisitorsDiagnostics.find(NextNode); + NextNode = NextNode->getFirstPred(); + if (!GenerateDiagnostics || VisitorNotes == VisitorsDiagnostics.end()) + continue; + + // This is a workaround due to inability to put shared PathDiagnosticPiece + // into a FoldingSet. + std::set<llvm::FoldingSetNodeID> DeduplicationSet; + + // Add pieces from custom visitors. + for (const auto &Note : VisitorNotes->second) { + llvm::FoldingSetNodeID ID; + Note->Profile(ID); + auto P = DeduplicationSet.insert(ID); + if (!P.second) + continue; + + if (AddPathEdges) + addEdgeToPath(PD->getActivePath(), PrevLoc, Note->getLocation()); + updateStackPiecesWithMessage(*Note, CallStack); + PD->getActivePath().push_front(Note); + } + } + + if (AddPathEdges) { + // Add an edge to the start of the function. + // We'll prune it out later, but it helps make diagnostics more uniform. + const StackFrameContext *CalleeLC = PDB.LC->getStackFrame(); + const Decl *D = CalleeLC->getDecl(); + addEdgeToPath(PD->getActivePath(), PrevLoc, + PathDiagnosticLocation::createBegin(D, SM)); + } + + + // Finally, prune the diagnostic path of uninteresting stuff. + if (!PD->path.empty()) { + if (R->shouldPrunePath() && Opts.ShouldPrunePaths) { + bool stillHasNotes = + removeUnneededCalls(PD->getMutablePieces(), R, LCM); + assert(stillHasNotes); + (void)stillHasNotes; + } + + // Redirect all call pieces to have valid locations. + adjustCallLocations(PD->getMutablePieces()); + removePiecesWithInvalidLocations(PD->getMutablePieces()); + + if (AddPathEdges) { + + // Reduce the number of edges from a very conservative set + // to an aesthetically pleasing subset that conveys the + // necessary information. + OptimizedCallsSet OCS; + while (optimizeEdges(PD->getMutablePieces(), SM, OCS, LCM)) {} + + // Drop the very first function-entry edge. It's not really necessary + // for top-level functions. + dropFunctionEntryEdge(PD->getMutablePieces(), LCM, SM); + } + + // Remove messages that are basically the same, and edges that may not + // make sense. + // We have to do this after edge optimization in the Extensive mode. + removeRedundantMsgs(PD->getMutablePieces()); + removeEdgesToDefaultInitializers(PD->getMutablePieces()); + } + + if (GenerateDiagnostics && Opts.ShouldDisplayMacroExpansions) + CompactMacroExpandedPieces(PD->getMutablePieces(), SM); + + return PD; +} + + +//===----------------------------------------------------------------------===// +// Methods for BugType and subclasses. +//===----------------------------------------------------------------------===// + +void BugType::anchor() {} + +void BuiltinBug::anchor() {} + +//===----------------------------------------------------------------------===// +// Methods for BugReport and subclasses. +//===----------------------------------------------------------------------===// + +void BugReport::NodeResolver::anchor() {} + +void BugReport::addVisitor(std::unique_ptr<BugReporterVisitor> visitor) { + if (!visitor) + return; + + llvm::FoldingSetNodeID ID; + visitor->Profile(ID); + + void *InsertPos = nullptr; + if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) { + return; + } + + Callbacks.push_back(std::move(visitor)); +} + +void BugReport::clearVisitors() { + Callbacks.clear(); +} + +BugReport::~BugReport() { + while (!interestingSymbols.empty()) { + popInterestingSymbolsAndRegions(); + } +} + +const Decl *BugReport::getDeclWithIssue() const { + if (DeclWithIssue) + return DeclWithIssue; + + const ExplodedNode *N = getErrorNode(); + if (!N) + return nullptr; + + const LocationContext *LC = N->getLocationContext(); + return LC->getStackFrame()->getDecl(); +} + +void BugReport::Profile(llvm::FoldingSetNodeID& hash) const { + hash.AddPointer(&BT); + hash.AddString(Description); + PathDiagnosticLocation UL = getUniqueingLocation(); + if (UL.isValid()) { + UL.Profile(hash); + } else if (Location.isValid()) { + Location.Profile(hash); + } else { + assert(ErrorNode); + hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode)); + } + + for (SourceRange range : Ranges) { + if (!range.isValid()) + continue; + hash.AddInteger(range.getBegin().getRawEncoding()); + hash.AddInteger(range.getEnd().getRawEncoding()); + } +} + +void BugReport::markInteresting(SymbolRef sym) { + if (!sym) + return; + + getInterestingSymbols().insert(sym); + + if (const auto *meta = dyn_cast<SymbolMetadata>(sym)) + getInterestingRegions().insert(meta->getRegion()); +} + +void BugReport::markInteresting(const MemRegion *R) { + if (!R) + return; + + R = R->getBaseRegion(); + getInterestingRegions().insert(R); + + if (const auto *SR = dyn_cast<SymbolicRegion>(R)) + getInterestingSymbols().insert(SR->getSymbol()); +} + +void BugReport::markInteresting(SVal V) { + markInteresting(V.getAsRegion()); + markInteresting(V.getAsSymbol()); +} + +void BugReport::markInteresting(const LocationContext *LC) { + if (!LC) + return; + InterestingLocationContexts.insert(LC); +} + +bool BugReport::isInteresting(SVal V) { + return isInteresting(V.getAsRegion()) || isInteresting(V.getAsSymbol()); +} + +bool BugReport::isInteresting(SymbolRef sym) { + if (!sym) + return false; + // We don't currently consider metadata symbols to be interesting + // even if we know their region is interesting. Is that correct behavior? + return getInterestingSymbols().count(sym); +} + +bool BugReport::isInteresting(const MemRegion *R) { + if (!R) + return false; + R = R->getBaseRegion(); + bool b = getInterestingRegions().count(R); + if (b) + return true; + if (const auto *SR = dyn_cast<SymbolicRegion>(R)) + return getInterestingSymbols().count(SR->getSymbol()); + return false; +} + +bool BugReport::isInteresting(const LocationContext *LC) { + if (!LC) + return false; + return InterestingLocationContexts.count(LC); +} + +void BugReport::lazyInitializeInterestingSets() { + if (interestingSymbols.empty()) { + interestingSymbols.push_back(new Symbols()); + interestingRegions.push_back(new Regions()); + } +} + +BugReport::Symbols &BugReport::getInterestingSymbols() { + lazyInitializeInterestingSets(); + return *interestingSymbols.back(); +} + +BugReport::Regions &BugReport::getInterestingRegions() { + lazyInitializeInterestingSets(); + return *interestingRegions.back(); +} + +void BugReport::pushInterestingSymbolsAndRegions() { + interestingSymbols.push_back(new Symbols(getInterestingSymbols())); + interestingRegions.push_back(new Regions(getInterestingRegions())); +} + +void BugReport::popInterestingSymbolsAndRegions() { + delete interestingSymbols.pop_back_val(); + delete interestingRegions.pop_back_val(); +} + +const Stmt *BugReport::getStmt() const { + if (!ErrorNode) + return nullptr; + + ProgramPoint ProgP = ErrorNode->getLocation(); + const Stmt *S = nullptr; + + if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) { + CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); + if (BE->getBlock() == &Exit) + S = GetPreviousStmt(ErrorNode); + } + if (!S) + S = PathDiagnosticLocation::getStmt(ErrorNode); + + return S; +} + +llvm::iterator_range<BugReport::ranges_iterator> BugReport::getRanges() { + // If no custom ranges, add the range of the statement corresponding to + // the error node. + if (Ranges.empty()) { + if (const auto *E = dyn_cast_or_null<Expr>(getStmt())) + addRange(E->getSourceRange()); + else + return llvm::make_range(ranges_iterator(), ranges_iterator()); + } + + // User-specified absence of range info. + if (Ranges.size() == 1 && !Ranges.begin()->isValid()) + return llvm::make_range(ranges_iterator(), ranges_iterator()); + + return llvm::make_range(Ranges.begin(), Ranges.end()); +} + +PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const { + if (ErrorNode) { + assert(!Location.isValid() && + "Either Location or ErrorNode should be specified but not both."); + return PathDiagnosticLocation::createEndOfPath(ErrorNode, SM); + } + + assert(Location.isValid()); + return Location; +} + +//===----------------------------------------------------------------------===// +// Methods for BugReporter and subclasses. +//===----------------------------------------------------------------------===// + +BugReportEquivClass::~BugReportEquivClass() = default; + +GRBugReporter::~GRBugReporter() = default; + +BugReporterData::~BugReporterData() = default; + +ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); } + +ProgramStateManager& +GRBugReporter::getStateManager() { return Eng.getStateManager(); } + +BugReporter::~BugReporter() { + FlushReports(); + + // Free the bug reports we are tracking. + for (const auto I : EQClassesVector) + delete I; +} + +void BugReporter::FlushReports() { + if (BugTypes.isEmpty()) + return; + + // We need to flush reports in deterministic order to ensure the order + // of the reports is consistent between runs. + for (const auto EQ : EQClassesVector) + FlushReport(*EQ); + + // BugReporter owns and deletes only BugTypes created implicitly through + // EmitBasicReport. + // FIXME: There are leaks from checkers that assume that the BugTypes they + // create will be destroyed by the BugReporter. + llvm::DeleteContainerSeconds(StrBugTypes); + + // Remove all references to the BugType objects. + BugTypes = F.getEmptySet(); +} + +//===----------------------------------------------------------------------===// +// PathDiagnostics generation. +//===----------------------------------------------------------------------===// + +namespace { + +/// A wrapper around a report graph, which contains only a single path, and its +/// node maps. +class ReportGraph { +public: + InterExplodedGraphMap BackMap; + std::unique_ptr<ExplodedGraph> Graph; + const ExplodedNode *ErrorNode; + size_t Index; +}; + +/// A wrapper around a trimmed graph and its node maps. +class TrimmedGraph { + InterExplodedGraphMap InverseMap; + + using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>; + + PriorityMapTy PriorityMap; + + using NodeIndexPair = std::pair<const ExplodedNode *, size_t>; + + SmallVector<NodeIndexPair, 32> ReportNodes; + + std::unique_ptr<ExplodedGraph> G; + + /// A helper class for sorting ExplodedNodes by priority. + template <bool Descending> + class PriorityCompare { + const PriorityMapTy &PriorityMap; + + public: + PriorityCompare(const PriorityMapTy &M) : PriorityMap(M) {} + + bool operator()(const ExplodedNode *LHS, const ExplodedNode *RHS) const { + PriorityMapTy::const_iterator LI = PriorityMap.find(LHS); + PriorityMapTy::const_iterator RI = PriorityMap.find(RHS); + PriorityMapTy::const_iterator E = PriorityMap.end(); + + if (LI == E) + return Descending; + if (RI == E) + return !Descending; + + return Descending ? LI->second > RI->second + : LI->second < RI->second; + } + + bool operator()(const NodeIndexPair &LHS, const NodeIndexPair &RHS) const { + return (*this)(LHS.first, RHS.first); + } + }; + +public: + TrimmedGraph(const ExplodedGraph *OriginalGraph, + ArrayRef<const ExplodedNode *> Nodes); + + bool popNextReportGraph(ReportGraph &GraphWrapper); +}; + +} // namespace + +TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph, + ArrayRef<const ExplodedNode *> Nodes) { + // The trimmed graph is created in the body of the constructor to ensure + // that the DenseMaps have been initialized already. + InterExplodedGraphMap ForwardMap; + G = OriginalGraph->trim(Nodes, &ForwardMap, &InverseMap); + + // Find the (first) error node in the trimmed graph. We just need to consult + // the node map which maps from nodes in the original graph to nodes + // in the new graph. + llvm::SmallPtrSet<const ExplodedNode *, 32> RemainingNodes; + + for (unsigned i = 0, count = Nodes.size(); i < count; ++i) { + if (const ExplodedNode *NewNode = ForwardMap.lookup(Nodes[i])) { + ReportNodes.push_back(std::make_pair(NewNode, i)); + RemainingNodes.insert(NewNode); + } + } + + assert(!RemainingNodes.empty() && "No error node found in the trimmed graph"); + + // Perform a forward BFS to find all the shortest paths. + std::queue<const ExplodedNode *> WS; + + assert(G->num_roots() == 1); + WS.push(*G->roots_begin()); + unsigned Priority = 0; + + while (!WS.empty()) { + const ExplodedNode *Node = WS.front(); + WS.pop(); + + PriorityMapTy::iterator PriorityEntry; + bool IsNew; + std::tie(PriorityEntry, IsNew) = + PriorityMap.insert(std::make_pair(Node, Priority)); + ++Priority; + + if (!IsNew) { + assert(PriorityEntry->second <= Priority); + continue; + } + + if (RemainingNodes.erase(Node)) + if (RemainingNodes.empty()) + break; + + for (ExplodedNode::const_pred_iterator I = Node->succ_begin(), + E = Node->succ_end(); + I != E; ++I) + WS.push(*I); + } + + // Sort the error paths from longest to shortest. + llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap)); +} + +bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) { + if (ReportNodes.empty()) + return false; + + const ExplodedNode *OrigN; + std::tie(OrigN, GraphWrapper.Index) = ReportNodes.pop_back_val(); + assert(PriorityMap.find(OrigN) != PriorityMap.end() && + "error node not accessible from root"); + + // Create a new graph with a single path. This is the graph + // that will be returned to the caller. + auto GNew = llvm::make_unique<ExplodedGraph>(); + GraphWrapper.BackMap.clear(); + + // Now walk from the error node up the BFS path, always taking the + // predeccessor with the lowest number. + ExplodedNode *Succ = nullptr; + while (true) { + // Create the equivalent node in the new graph with the same state + // and location. + ExplodedNode *NewN = GNew->createUncachedNode(OrigN->getLocation(), OrigN->getState(), + OrigN->isSink()); + + // Store the mapping to the original node. + InterExplodedGraphMap::const_iterator IMitr = InverseMap.find(OrigN); + assert(IMitr != InverseMap.end() && "No mapping to original node."); + GraphWrapper.BackMap[NewN] = IMitr->second; + + // Link up the new node with the previous node. + if (Succ) + Succ->addPredecessor(NewN, *GNew); + else + GraphWrapper.ErrorNode = NewN; + + Succ = NewN; + + // Are we at the final node? + if (OrigN->pred_empty()) { + GNew->addRoot(NewN); + break; + } + + // Find the next predeccessor node. We choose the node that is marked + // with the lowest BFS number. + OrigN = *std::min_element(OrigN->pred_begin(), OrigN->pred_end(), + PriorityCompare<false>(PriorityMap)); + } + + GraphWrapper.Graph = std::move(GNew); + + return true; +} + +/// CompactMacroExpandedPieces - This function postprocesses a PathDiagnostic +/// object and collapses PathDiagosticPieces that are expanded by macros. +static void CompactMacroExpandedPieces(PathPieces &path, + const SourceManager& SM) { + using MacroStackTy = + std::vector< + std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>; + + using PiecesTy = std::vector<std::shared_ptr<PathDiagnosticPiece>>; + + MacroStackTy MacroStack; + PiecesTy Pieces; + + for (PathPieces::const_iterator I = path.begin(), E = path.end(); + I != E; ++I) { + const auto &piece = *I; + + // Recursively compact calls. + if (auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) { + CompactMacroExpandedPieces(call->path, SM); + } + + // Get the location of the PathDiagnosticPiece. + const FullSourceLoc Loc = piece->getLocation().asLocation(); + + // Determine the instantiation location, which is the location we group + // related PathDiagnosticPieces. + SourceLocation InstantiationLoc = Loc.isMacroID() ? + SM.getExpansionLoc(Loc) : + SourceLocation(); + + if (Loc.isFileID()) { + MacroStack.clear(); + Pieces.push_back(piece); + continue; + } + + assert(Loc.isMacroID()); + + // Is the PathDiagnosticPiece within the same macro group? + if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) { + MacroStack.back().first->subPieces.push_back(piece); + continue; + } + + // We aren't in the same group. Are we descending into a new macro + // or are part of an old one? + std::shared_ptr<PathDiagnosticMacroPiece> MacroGroup; + + SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ? + SM.getExpansionLoc(Loc) : + SourceLocation(); + + // Walk the entire macro stack. + while (!MacroStack.empty()) { + if (InstantiationLoc == MacroStack.back().second) { + MacroGroup = MacroStack.back().first; + break; + } + + if (ParentInstantiationLoc == MacroStack.back().second) { + MacroGroup = MacroStack.back().first; + break; + } + + MacroStack.pop_back(); + } + + if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) { + // Create a new macro group and add it to the stack. + auto NewGroup = std::make_shared<PathDiagnosticMacroPiece>( + PathDiagnosticLocation::createSingleLocation(piece->getLocation())); + + if (MacroGroup) + MacroGroup->subPieces.push_back(NewGroup); + else { + assert(InstantiationLoc.isFileID()); + Pieces.push_back(NewGroup); + } + + MacroGroup = NewGroup; + MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc)); + } + + // Finally, add the PathDiagnosticPiece to the group. + MacroGroup->subPieces.push_back(piece); + } + + // Now take the pieces and construct a new PathDiagnostic. + path.clear(); + + path.insert(path.end(), Pieces.begin(), Pieces.end()); +} + +/// Generate notes from all visitors. +/// Notes associated with {@code ErrorNode} are generated using +/// {@code getEndPath}, and the rest are generated with {@code VisitNode}. +static std::unique_ptr<VisitorsDiagnosticsTy> +generateVisitorsDiagnostics(BugReport *R, const ExplodedNode *ErrorNode, + BugReporterContext &BRC) { + auto Notes = llvm::make_unique<VisitorsDiagnosticsTy>(); + BugReport::VisitorList visitors; + + // Run visitors on all nodes starting from the node *before* the last one. + // The last node is reserved for notes generated with {@code getEndPath}. + const ExplodedNode *NextNode = ErrorNode->getFirstPred(); + while (NextNode) { + + // At each iteration, move all visitors from report to visitor list. + for (BugReport::visitor_iterator I = R->visitor_begin(), + E = R->visitor_end(); + I != E; ++I) { + visitors.push_back(std::move(*I)); + } + R->clearVisitors(); + + const ExplodedNode *Pred = NextNode->getFirstPred(); + if (!Pred) { + std::shared_ptr<PathDiagnosticPiece> LastPiece; + for (auto &V : visitors) { + V->finalizeVisitor(BRC, ErrorNode, *R); + + if (auto Piece = V->getEndPath(BRC, ErrorNode, *R)) { + assert(!LastPiece && + "There can only be one final piece in a diagnostic."); + LastPiece = std::move(Piece); + (*Notes)[ErrorNode].push_back(LastPiece); + } + } + break; + } + + for (auto &V : visitors) { + auto P = V->VisitNode(NextNode, BRC, *R); + if (P) + (*Notes)[NextNode].push_back(std::move(P)); + } + + if (!R->isValid()) + break; + + NextNode = Pred; + } + + return Notes; +} + +/// Find a non-invalidated report for a given equivalence class, +/// and return together with a cache of visitors notes. +/// If none found, return a nullptr paired with an empty cache. +static +std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport( + TrimmedGraph &TrimG, + ReportGraph &ErrorGraph, + ArrayRef<BugReport *> &bugReports, + AnalyzerOptions &Opts, + GRBugReporter &Reporter) { + + while (TrimG.popNextReportGraph(ErrorGraph)) { + // Find the BugReport with the original location. + assert(ErrorGraph.Index < bugReports.size()); + BugReport *R = bugReports[ErrorGraph.Index]; + assert(R && "No original report found for sliced graph."); + assert(R->isValid() && "Report selected by trimmed graph marked invalid."); + const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode; + + // Register refutation visitors first, if they mark the bug invalid no + // further analysis is required + R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>()); + + // Register additional node visitors. + R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>()); + R->addVisitor(llvm::make_unique<ConditionBRVisitor>()); + R->addVisitor(llvm::make_unique<CXXSelfAssignmentBRVisitor>()); + + BugReporterContext BRC(Reporter, ErrorGraph.BackMap); + + // Run all visitors on a given graph, once. + std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes = + generateVisitorsDiagnostics(R, ErrorNode, BRC); + + if (R->isValid()) { + if (Opts.ShouldCrosscheckWithZ3) { + // If crosscheck is enabled, remove all visitors, add the refutation + // visitor and check again + R->clearVisitors(); + R->addVisitor(llvm::make_unique<FalsePositiveRefutationBRVisitor>()); + + // We don't overrite the notes inserted by other visitors because the + // refutation manager does not add any new note to the path + generateVisitorsDiagnostics(R, ErrorGraph.ErrorNode, BRC); + } + + // Check if the bug is still valid + if (R->isValid()) + return std::make_pair(R, std::move(visitorNotes)); + } + } + + return std::make_pair(nullptr, llvm::make_unique<VisitorsDiagnosticsTy>()); +} + +std::unique_ptr<DiagnosticForConsumerMapTy> +GRBugReporter::generatePathDiagnostics( + ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> &bugReports) { + assert(!bugReports.empty()); + + auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>(); + bool HasValid = false; + SmallVector<const ExplodedNode *, 32> errorNodes; + for (const auto I : bugReports) { + if (I->isValid()) { + HasValid = true; + errorNodes.push_back(I->getErrorNode()); + } else { + // Keep the errorNodes list in sync with the bugReports list. + errorNodes.push_back(nullptr); + } + } + + // If all the reports have been marked invalid by a previous path generation, + // we're done. + if (!HasValid) + return Out; + + TrimmedGraph TrimG(&getGraph(), errorNodes); + ReportGraph ErrorGraph; + auto ReportInfo = findValidReport(TrimG, ErrorGraph, bugReports, + getAnalyzerOptions(), *this); + BugReport *R = ReportInfo.first; + + if (R && R->isValid()) { + const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode; + for (PathDiagnosticConsumer *PC : consumers) { + PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, PC); + std::unique_ptr<PathDiagnostic> PD = generatePathDiagnosticForConsumer( + PC->getGenerationScheme(), PDB, ErrorNode, *ReportInfo.second); + (*Out)[PC] = std::move(PD); + } + } + + return Out; +} + +void BugReporter::Register(BugType *BT) { + BugTypes = F.add(BugTypes, BT); +} + +void BugReporter::emitReport(std::unique_ptr<BugReport> R) { + if (const ExplodedNode *E = R->getErrorNode()) { + // An error node must either be a sink or have a tag, otherwise + // it could get reclaimed before the path diagnostic is created. + assert((E->isSink() || E->getLocation().getTag()) && + "Error node must either be a sink or have a tag"); + + const AnalysisDeclContext *DeclCtx = + E->getLocationContext()->getAnalysisDeclContext(); + // The source of autosynthesized body can be handcrafted AST or a model + // file. The locations from handcrafted ASTs have no valid source locations + // and have to be discarded. Locations from model files should be preserved + // for processing and reporting. + if (DeclCtx->isBodyAutosynthesized() && + !DeclCtx->isBodyAutosynthesizedFromModelFile()) + return; + } + + bool ValidSourceLoc = R->getLocation(getSourceManager()).isValid(); + assert(ValidSourceLoc); + // If we mess up in a release build, we'd still prefer to just drop the bug + // instead of trying to go on. + if (!ValidSourceLoc) + return; + + // Compute the bug report's hash to determine its equivalence class. + llvm::FoldingSetNodeID ID; + R->Profile(ID); + + // Lookup the equivance class. If there isn't one, create it. + BugType& BT = R->getBugType(); + Register(&BT); + void *InsertPos; + BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos); + + if (!EQ) { + EQ = new BugReportEquivClass(std::move(R)); + EQClasses.InsertNode(EQ, InsertPos); + EQClassesVector.push_back(EQ); + } else + EQ->AddReport(std::move(R)); +} + +//===----------------------------------------------------------------------===// +// Emitting reports in equivalence classes. +//===----------------------------------------------------------------------===// + +namespace { + +struct FRIEC_WLItem { + const ExplodedNode *N; + ExplodedNode::const_succ_iterator I, E; + + FRIEC_WLItem(const ExplodedNode *n) + : N(n), I(N->succ_begin()), E(N->succ_end()) {} +}; + +} // namespace + +static const CFGBlock *findBlockForNode(const ExplodedNode *N) { + ProgramPoint P = N->getLocation(); + if (auto BEP = P.getAs<BlockEntrance>()) + return BEP->getBlock(); + + // Find the node's current statement in the CFG. + if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) + return N->getLocationContext()->getAnalysisDeclContext() + ->getCFGStmtMap()->getBlock(S); + + return nullptr; +} + +// Returns true if by simply looking at the block, we can be sure that it +// results in a sink during analysis. This is useful to know when the analysis +// was interrupted, and we try to figure out if it would sink eventually. +// There may be many more reasons why a sink would appear during analysis +// (eg. checkers may generate sinks arbitrarily), but here we only consider +// sinks that would be obvious by looking at the CFG. +static bool isImmediateSinkBlock(const CFGBlock *Blk) { + if (Blk->hasNoReturnElement()) + return true; + + // FIXME: Throw-expressions are currently generating sinks during analysis: + // they're not supported yet, and also often used for actually terminating + // the program. So we should treat them as sinks in this analysis as well, + // at least for now, but once we have better support for exceptions, + // we'd need to carefully handle the case when the throw is being + // immediately caught. + if (std::any_of(Blk->begin(), Blk->end(), [](const CFGElement &Elm) { + if (Optional<CFGStmt> StmtElm = Elm.getAs<CFGStmt>()) + if (isa<CXXThrowExpr>(StmtElm->getStmt())) + return true; + return false; + })) + return true; + + return false; +} + +// Returns true if by looking at the CFG surrounding the node's program +// point, we can be sure that any analysis starting from this point would +// eventually end with a sink. We scan the child CFG blocks in a depth-first +// manner and see if all paths eventually end up in an immediate sink block. +static bool isInevitablySinking(const ExplodedNode *N) { + const CFG &Cfg = N->getCFG(); + + const CFGBlock *StartBlk = findBlockForNode(N); + if (!StartBlk) + return false; + if (isImmediateSinkBlock(StartBlk)) + return true; + + llvm::SmallVector<const CFGBlock *, 32> DFSWorkList; + llvm::SmallPtrSet<const CFGBlock *, 32> Visited; + + DFSWorkList.push_back(StartBlk); + while (!DFSWorkList.empty()) { + const CFGBlock *Blk = DFSWorkList.back(); + DFSWorkList.pop_back(); + Visited.insert(Blk); + + // If at least one path reaches the CFG exit, it means that control is + // returned to the caller. For now, say that we are not sure what + // happens next. If necessary, this can be improved to analyze + // the parent StackFrameContext's call site in a similar manner. + if (Blk == &Cfg.getExit()) + return false; + + for (const auto &Succ : Blk->succs()) { + if (const CFGBlock *SuccBlk = Succ.getReachableBlock()) { + if (!isImmediateSinkBlock(SuccBlk) && !Visited.count(SuccBlk)) { + // If the block has reachable child blocks that aren't no-return, + // add them to the worklist. + DFSWorkList.push_back(SuccBlk); + } + } + } + } + + // Nothing reached the exit. It can only mean one thing: there's no return. + return true; +} + +static BugReport * +FindReportInEquivalenceClass(BugReportEquivClass& EQ, + SmallVectorImpl<BugReport*> &bugReports) { + BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); + assert(I != E); + 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 + // to 'Nodes'. Any of the reports will serve as a "representative" report. + if (!BT.isSuppressOnSink()) { + BugReport *R = &*I; + for (auto &I : EQ) { + const ExplodedNode *N = I.getErrorNode(); + if (N) { + R = &I; + bugReports.push_back(R); + } + } + return R; + } + + // For bug reports that should be suppressed when all paths are post-dominated + // by a sink node, iterate through the reports in the equivalence class + // until we find one that isn't post-dominated (if one exists). We use a + // DFS traversal of the ExplodedGraph to find a non-sink node. We could write + // this as a recursive function, but we don't want to risk blowing out the + // stack for very long paths. + BugReport *exampleReport = nullptr; + + for (; I != E; ++I) { + const ExplodedNode *errorNode = I->getErrorNode(); + + if (!errorNode) + continue; + if (errorNode->isSink()) { + llvm_unreachable( + "BugType::isSuppressSink() should not be 'true' for sink end nodes"); + } + // No successors? By definition this nodes isn't post-dominated by a sink. + if (errorNode->succ_empty()) { + bugReports.push_back(&*I); + if (!exampleReport) + exampleReport = &*I; + continue; + } + + // See if we are in a no-return CFG block. If so, treat this similarly + // to being post-dominated by a sink. This works better when the analysis + // is incomplete and we have never reached the no-return function call(s) + // that we'd inevitably bump into on this path. + if (isInevitablySinking(errorNode)) + continue; + + // At this point we know that 'N' is not a sink and it has at least one + // successor. Use a DFS worklist to find a non-sink end-of-path node. + using WLItem = FRIEC_WLItem; + using DFSWorkList = SmallVector<WLItem, 10>; + + llvm::DenseMap<const ExplodedNode *, unsigned> Visited; + + DFSWorkList WL; + WL.push_back(errorNode); + Visited[errorNode] = 1; + + while (!WL.empty()) { + WLItem &WI = WL.back(); + assert(!WI.N->succ_empty()); + + for (; WI.I != WI.E; ++WI.I) { + const ExplodedNode *Succ = *WI.I; + // End-of-path node? + if (Succ->succ_empty()) { + // If we found an end-of-path node that is not a sink. + if (!Succ->isSink()) { + bugReports.push_back(&*I); + if (!exampleReport) + exampleReport = &*I; + WL.clear(); + break; + } + // Found a sink? Continue on to the next successor. + continue; + } + // Mark the successor as visited. If it hasn't been explored, + // enqueue it to the DFS worklist. + unsigned &mark = Visited[Succ]; + if (!mark) { + mark = 1; + WL.push_back(Succ); + break; + } + } + + // The worklist may have been cleared at this point. First + // check if it is empty before checking the last item. + if (!WL.empty() && &WL.back() == &WI) + WL.pop_back(); + } + } + + // ExampleReport will be NULL if all the nodes in the equivalence class + // were post-dominated by sinks. + return exampleReport; +} + +void BugReporter::FlushReport(BugReportEquivClass& EQ) { + SmallVector<BugReport*, 10> bugReports; + BugReport *report = FindReportInEquivalenceClass(EQ, bugReports); + if (!report) + return; + + ArrayRef<PathDiagnosticConsumer*> Consumers = getPathDiagnosticConsumers(); + std::unique_ptr<DiagnosticForConsumerMapTy> Diagnostics = + generateDiagnosticForConsumerMap(report, Consumers, bugReports); + + for (auto &P : *Diagnostics) { + PathDiagnosticConsumer *Consumer = P.first; + std::unique_ptr<PathDiagnostic> &PD = P.second; + + // If the path is empty, generate a single step path with the location + // of the issue. + if (PD->path.empty()) { + PathDiagnosticLocation L = report->getLocation(getSourceManager()); + auto piece = llvm::make_unique<PathDiagnosticEventPiece>( + L, report->getDescription()); + for (SourceRange Range : report->getRanges()) + piece->addRange(Range); + PD->setEndOfPath(std::move(piece)); + } + + PathPieces &Pieces = PD->getMutablePieces(); + if (getAnalyzerOptions().ShouldDisplayNotesAsEvents) { + // For path diagnostic consumers that don't support extra notes, + // we may optionally convert those to path notes. + for (auto I = report->getNotes().rbegin(), + E = report->getNotes().rend(); I != E; ++I) { + PathDiagnosticNotePiece *Piece = I->get(); + auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>( + Piece->getLocation(), Piece->getString()); + for (const auto &R: Piece->getRanges()) + ConvertedPiece->addRange(R); + + Pieces.push_front(std::move(ConvertedPiece)); + } + } else { + for (auto I = report->getNotes().rbegin(), + E = report->getNotes().rend(); I != E; ++I) + Pieces.push_front(*I); + } + + // Get the meta data. + const BugReport::ExtraTextList &Meta = report->getExtraText(); + for (const auto &i : Meta) + PD->addMeta(i); + + updateExecutedLinesWithDiagnosticPieces(*PD); + Consumer->HandlePathDiagnostic(std::move(PD)); + } +} + +/// Insert all lines participating in the function signature \p Signature +/// into \p ExecutedLines. +static void populateExecutedLinesWithFunctionSignature( + const Decl *Signature, SourceManager &SM, + FilesToLineNumsMap &ExecutedLines) { + SourceRange SignatureSourceRange; + const Stmt* Body = Signature->getBody(); + if (const auto FD = dyn_cast<FunctionDecl>(Signature)) { + SignatureSourceRange = FD->getSourceRange(); + } else if (const auto OD = dyn_cast<ObjCMethodDecl>(Signature)) { + SignatureSourceRange = OD->getSourceRange(); + } else { + return; + } + SourceLocation Start = SignatureSourceRange.getBegin(); + SourceLocation End = Body ? Body->getSourceRange().getBegin() + : SignatureSourceRange.getEnd(); + if (!Start.isValid() || !End.isValid()) + return; + unsigned StartLine = SM.getExpansionLineNumber(Start); + unsigned EndLine = SM.getExpansionLineNumber(End); + + FileID FID = SM.getFileID(SM.getExpansionLoc(Start)); + for (unsigned Line = StartLine; Line <= EndLine; Line++) + ExecutedLines[FID].insert(Line); +} + +static void populateExecutedLinesWithStmt( + const Stmt *S, SourceManager &SM, + FilesToLineNumsMap &ExecutedLines) { + SourceLocation Loc = S->getSourceRange().getBegin(); + if (!Loc.isValid()) + return; + SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc); + FileID FID = SM.getFileID(ExpansionLoc); + unsigned LineNo = SM.getExpansionLineNumber(ExpansionLoc); + ExecutedLines[FID].insert(LineNo); +} + +/// \return all executed lines including function signatures on the path +/// starting from \p N. +static std::unique_ptr<FilesToLineNumsMap> +findExecutedLines(SourceManager &SM, const ExplodedNode *N) { + auto ExecutedLines = llvm::make_unique<FilesToLineNumsMap>(); + + while (N) { + if (N->getFirstPred() == nullptr) { + // First node: show signature of the entrance point. + const Decl *D = N->getLocationContext()->getDecl(); + populateExecutedLinesWithFunctionSignature(D, SM, *ExecutedLines); + } else if (auto CE = N->getLocationAs<CallEnter>()) { + // Inlined function: show signature. + const Decl* D = CE->getCalleeContext()->getDecl(); + populateExecutedLinesWithFunctionSignature(D, SM, *ExecutedLines); + } else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) { + populateExecutedLinesWithStmt(S, SM, *ExecutedLines); + + // Show extra context for some parent kinds. + const Stmt *P = N->getParentMap().getParent(S); + + // The path exploration can die before the node with the associated + // return statement is generated, but we do want to show the whole + // return. + if (const auto *RS = dyn_cast_or_null<ReturnStmt>(P)) { + populateExecutedLinesWithStmt(RS, SM, *ExecutedLines); + P = N->getParentMap().getParent(RS); + } + + if (P && (isa<SwitchCase>(P) || isa<LabelStmt>(P))) + populateExecutedLinesWithStmt(P, SM, *ExecutedLines); + } + + N = N->getFirstPred(); + } + return ExecutedLines; +} + +std::unique_ptr<DiagnosticForConsumerMapTy> +BugReporter::generateDiagnosticForConsumerMap( + BugReport *report, ArrayRef<PathDiagnosticConsumer *> consumers, + ArrayRef<BugReport *> bugReports) { + + if (!report->isPathSensitive()) { + auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>(); + for (auto *Consumer : consumers) + (*Out)[Consumer] = generateEmptyDiagnosticForReport(report, + getSourceManager()); + return Out; + } + + // Generate the full path sensitive diagnostic, using the generation scheme + // specified by the PathDiagnosticConsumer. Note that we have to generate + // path diagnostics even for consumers which do not support paths, because + // the BugReporterVisitors may mark this bug as a false positive. + assert(!bugReports.empty()); + MaxBugClassSize.updateMax(bugReports.size()); + std::unique_ptr<DiagnosticForConsumerMapTy> Out = + generatePathDiagnostics(consumers, bugReports); + + if (Out->empty()) + return Out; + + MaxValidBugClassSize.updateMax(bugReports.size()); + + // Examine the report and see if the last piece is in a header. Reset the + // report location to the last piece in the main source file. + AnalyzerOptions &Opts = getAnalyzerOptions(); + for (auto const &P : *Out) + if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.AnalyzeAll) + P.second->resetDiagnosticLocationToMainFile(); + + return Out; +} + +void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, + const CheckerBase *Checker, + StringRef Name, StringRef Category, + StringRef Str, PathDiagnosticLocation Loc, + ArrayRef<SourceRange> Ranges) { + EmitBasicReport(DeclWithIssue, Checker->getCheckName(), Name, Category, Str, + Loc, Ranges); +} + +void BugReporter::EmitBasicReport(const Decl *DeclWithIssue, + CheckName CheckName, + StringRef name, StringRef category, + StringRef str, PathDiagnosticLocation Loc, + ArrayRef<SourceRange> Ranges) { + // 'BT' is owned by BugReporter. + BugType *BT = getBugTypeForName(CheckName, name, category); + auto R = llvm::make_unique<BugReport>(*BT, str, Loc); + R->setDeclWithIssue(DeclWithIssue); + for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end(); + I != E; ++I) + R->addRange(*I); + emitReport(std::move(R)); +} + +BugType *BugReporter::getBugTypeForName(CheckName CheckName, StringRef name, + StringRef category) { + SmallString<136> fullDesc; + llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name + << ":" << category; + BugType *&BT = StrBugTypes[fullDesc]; + if (!BT) + BT = new BugType(CheckName, name, category); + return BT; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp new file mode 100644 index 000000000000..da94b6eb21e9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -0,0 +1,2479 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a set of BugReporter "visitors" which can be used to +// enhance the diagnostics reported for a bug. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <deque> +#include <memory> +#include <string> +#include <utility> + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static const Expr *peelOffPointerArithmetic(const BinaryOperator *B) { + if (B->isAdditiveOp() && B->getType()->isPointerType()) { + if (B->getLHS()->getType()->isPointerType()) { + return B->getLHS(); + } else if (B->getRHS()->getType()->isPointerType()) { + return B->getRHS(); + } + } + return nullptr; +} + +/// Given that expression S represents a pointer that would be dereferenced, +/// try to find a sub-expression from which the pointer came from. +/// This is used for tracking down origins of a null or undefined value: +/// "this is null because that is null because that is null" etc. +/// We wipe away field and element offsets because they merely add offsets. +/// We also wipe away all casts except lvalue-to-rvalue casts, because the +/// latter represent an actual pointer dereference; however, we remove +/// the final lvalue-to-rvalue cast before returning from this function +/// because it demonstrates more clearly from where the pointer rvalue was +/// loaded. Examples: +/// x->y.z ==> x (lvalue) +/// foo()->y.z ==> foo() (rvalue) +const Expr *bugreporter::getDerefExpr(const Stmt *S) { + const auto *E = dyn_cast<Expr>(S); + if (!E) + return nullptr; + + while (true) { + if (const auto *CE = dyn_cast<CastExpr>(E)) { + if (CE->getCastKind() == CK_LValueToRValue) { + // This cast represents the load we're looking for. + break; + } + E = CE->getSubExpr(); + } else if (const auto *B = dyn_cast<BinaryOperator>(E)) { + // Pointer arithmetic: '*(x + 2)' -> 'x') etc. + if (const Expr *Inner = peelOffPointerArithmetic(B)) { + E = Inner; + } else { + // Probably more arithmetic can be pattern-matched here, + // but for now give up. + break; + } + } else if (const auto *U = dyn_cast<UnaryOperator>(E)) { + if (U->getOpcode() == UO_Deref || U->getOpcode() == UO_AddrOf || + (U->isIncrementDecrementOp() && U->getType()->isPointerType())) { + // Operators '*' and '&' don't actually mean anything. + // We look at casts instead. + E = U->getSubExpr(); + } else { + // Probably more arithmetic can be pattern-matched here, + // but for now give up. + break; + } + } + // Pattern match for a few useful cases: a[0], p->f, *p etc. + else if (const auto *ME = dyn_cast<MemberExpr>(E)) { + E = ME->getBase(); + } else if (const auto *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) { + E = IvarRef->getBase(); + } else if (const auto *AE = dyn_cast<ArraySubscriptExpr>(E)) { + E = AE->getBase(); + } else if (const auto *PE = dyn_cast<ParenExpr>(E)) { + E = PE->getSubExpr(); + } else if (const auto *FE = dyn_cast<FullExpr>(E)) { + E = FE->getSubExpr(); + } else { + // Other arbitrary stuff. + break; + } + } + + // Special case: remove the final lvalue-to-rvalue cast, but do not recurse + // deeper into the sub-expression. This way we return the lvalue from which + // our pointer rvalue was loaded. + if (const auto *CE = dyn_cast<ImplicitCastExpr>(E)) + if (CE->getCastKind() == CK_LValueToRValue) + E = CE->getSubExpr(); + + return E; +} + +//===----------------------------------------------------------------------===// +// Definitions for bug reporter visitors. +//===----------------------------------------------------------------------===// + +std::shared_ptr<PathDiagnosticPiece> +BugReporterVisitor::getEndPath(BugReporterContext &, + const ExplodedNode *, BugReport &) { + return nullptr; +} + +void +BugReporterVisitor::finalizeVisitor(BugReporterContext &, + const ExplodedNode *, BugReport &) {} + +std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath( + BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { + PathDiagnosticLocation L = + PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager()); + + const auto &Ranges = BR.getRanges(); + + // Only add the statement itself as a range if we didn't specify any + // special ranges for this report. + auto P = std::make_shared<PathDiagnosticEventPiece>( + L, BR.getDescription(), Ranges.begin() == Ranges.end()); + for (SourceRange Range : Ranges) + P->addRange(Range); + + return P; +} + +/// \return name of the macro inside the location \p Loc. +static StringRef getMacroName(SourceLocation Loc, + BugReporterContext &BRC) { + return Lexer::getImmediateMacroName( + Loc, + BRC.getSourceManager(), + BRC.getASTContext().getLangOpts()); +} + +/// \return Whether given spelling location corresponds to an expansion +/// of a function-like macro. +static bool isFunctionMacroExpansion(SourceLocation Loc, + const SourceManager &SM) { + if (!Loc.isMacroID()) + return false; + while (SM.isMacroArgExpansion(Loc)) + Loc = SM.getImmediateExpansionRange(Loc).getBegin(); + std::pair<FileID, unsigned> TLInfo = SM.getDecomposedLoc(Loc); + SrcMgr::SLocEntry SE = SM.getSLocEntry(TLInfo.first); + const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion(); + return EInfo.isFunctionMacroExpansion(); +} + +/// \return Whether \c RegionOfInterest was modified at \p N, +/// where \p ReturnState is a state associated with the return +/// from the current frame. +static bool wasRegionOfInterestModifiedAt( + const SubRegion *RegionOfInterest, + const ExplodedNode *N, + SVal ValueAfter) { + ProgramStateRef State = N->getState(); + ProgramStateManager &Mgr = N->getState()->getStateManager(); + + if (!N->getLocationAs<PostStore>() + && !N->getLocationAs<PostInitializer>() + && !N->getLocationAs<PostStmt>()) + return false; + + // Writing into region of interest. + if (auto PS = N->getLocationAs<PostStmt>()) + if (auto *BO = PS->getStmtAs<BinaryOperator>()) + if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf( + N->getSVal(BO->getLHS()).getAsRegion())) + return true; + + // SVal after the state is possibly different. + SVal ValueAtN = N->getState()->getSVal(RegionOfInterest); + if (!Mgr.getSValBuilder().areEqual(State, ValueAtN, ValueAfter).isConstrainedTrue() && + (!ValueAtN.isUndef() || !ValueAfter.isUndef())) + return true; + + return false; +} + + +namespace { + +/// Put a diagnostic on return statement of all inlined functions +/// for which the region of interest \p RegionOfInterest was passed into, +/// but not written inside, and it has caused an undefined read or a null +/// pointer dereference outside. +class NoStoreFuncVisitor final : public BugReporterVisitor { + const SubRegion *RegionOfInterest; + MemRegionManager &MmrMgr; + const SourceManager &SM; + const PrintingPolicy &PP; + + /// Recursion limit for dereferencing fields when looking for the + /// region of interest. + /// The limit of two indicates that we will dereference fields only once. + static const unsigned DEREFERENCE_LIMIT = 2; + + /// Frames writing into \c RegionOfInterest. + /// This visitor generates a note only if a function does not write into + /// a region of interest. This information is not immediately available + /// by looking at the node associated with the exit from the function + /// (usually the return statement). To avoid recomputing the same information + /// many times (going up the path for each node and checking whether the + /// region was written into) we instead lazily compute the + /// stack frames along the path which write into the region of interest. + llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingRegion; + llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated; + + using RegionVector = SmallVector<const MemRegion *, 5>; +public: + NoStoreFuncVisitor(const SubRegion *R) + : RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()), + SM(MmrMgr.getContext().getSourceManager()), + PP(MmrMgr.getContext().getPrintingPolicy()) {} + + void Profile(llvm::FoldingSetNodeID &ID) const override { + static int Tag = 0; + ID.AddPointer(&Tag); + ID.AddPointer(RegionOfInterest); + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BR, + BugReport &) override { + + const LocationContext *Ctx = N->getLocationContext(); + const StackFrameContext *SCtx = Ctx->getStackFrame(); + ProgramStateRef State = N->getState(); + auto CallExitLoc = N->getLocationAs<CallExitBegin>(); + + // No diagnostic if region was modified inside the frame. + if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N)) + return nullptr; + + 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)) { + if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) { + const MemRegion *SelfRegion = MC->getReceiverSVal().getAsRegion(); + if (RegionOfInterest->isSubRegionOf(SelfRegion) && + potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(), + IvarR->getDecl())) + return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, SelfRegion, + "self", /*FirstIsReferenceType=*/false, + 1); + } + } + + if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) { + const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion(); + if (RegionOfInterest->isSubRegionOf(ThisR) + && !CCall->getDecl()->isImplicit()) + return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, ThisR, + "this", + /*FirstIsReferenceType=*/false, 1); + + // Do not generate diagnostics for not modified parameters in + // constructors. + return nullptr; + } + + 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); + 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); + + 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); + + S = State->getSVal(R, PT); + T = PT; + IndirectionLevel++; + } + } + + return nullptr; + } + +private: + /// Attempts to find the region of interest in a given CXX decl, + /// by either following the base classes or fields. + /// Dereferences fields up to a given recursion limit. + /// Note that \p Vec is passed by value, leading to quadratic copying cost, + /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT. + /// \return A chain fields leading to the region of interest or None. + const Optional<RegionVector> + findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State, + const MemRegion *R, + const RegionVector &Vec = {}, + int depth = 0) { + + if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth. + return None; + + if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) + if (!RDX->hasDefinition()) + return None; + + // Recursively examine the base classes. + // Note that following base classes does not increase the recursion depth. + if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD)) + for (const auto II : RDX->bases()) + if (const RecordDecl *RRD = II.getType()->getAsRecordDecl()) + if (auto Out = findRegionOfInterestInRecord(RRD, State, R, Vec, depth)) + return Out; + + for (const FieldDecl *I : RD->fields()) { + QualType FT = I->getType(); + const FieldRegion *FR = MmrMgr.getFieldRegion(I, cast<SubRegion>(R)); + const SVal V = State->getSVal(FR); + const MemRegion *VR = V.getAsRegion(); + + RegionVector VecF = Vec; + VecF.push_back(FR); + + if (RegionOfInterest == VR) + return VecF; + + if (const RecordDecl *RRD = FT->getAsRecordDecl()) + if (auto Out = + findRegionOfInterestInRecord(RRD, State, FR, VecF, depth + 1)) + return Out; + + QualType PT = FT->getPointeeType(); + if (PT.isNull() || PT->isVoidType() || !VR) continue; + + if (const RecordDecl *RRD = PT->getAsRecordDecl()) + if (auto Out = + findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1)) + return Out; + + } + + return None; + } + + /// \return Whether the method declaration \p Parent + /// syntactically has a binary operation writing into the ivar \p Ivar. + bool potentiallyWritesIntoIvar(const Decl *Parent, + const ObjCIvarDecl *Ivar) { + using namespace ast_matchers; + const char * IvarBind = "Ivar"; + if (!Parent || !Parent->hasBody()) + return false; + StatementMatcher WriteIntoIvarM = binaryOperator( + hasOperatorName("="), + hasLHS(ignoringParenImpCasts( + objcIvarRefExpr(hasDeclaration(equalsNode(Ivar))).bind(IvarBind)))); + StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM)); + auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext()); + for (BoundNodes &Match : Matches) { + auto IvarRef = Match.getNodeAs<ObjCIvarRefExpr>(IvarBind); + if (IvarRef->isFreeIvar()) + return true; + + const Expr *Base = IvarRef->getBase(); + if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base)) + Base = ICE->getSubExpr(); + + if (const auto *DRE = dyn_cast<DeclRefExpr>(Base)) + if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl())) + if (ID->getParameterKind() == ImplicitParamDecl::ObjCSelf) + return true; + + return false; + } + return false; + } + + /// Check and lazily calculate whether the region of interest is + /// modified in the stack frame to which \p N belongs. + /// The calculation is cached in FramesModifyingRegion. + bool isRegionOfInterestModifiedInFrame(const ExplodedNode *N) { + const LocationContext *Ctx = N->getLocationContext(); + const StackFrameContext *SCtx = Ctx->getStackFrame(); + if (!FramesModifyingCalculated.count(SCtx)) + findModifyingFrames(N); + return FramesModifyingRegion.count(SCtx); + } + + + /// Write to \c FramesModifyingRegion all stack frames along + /// the path in the current stack frame which modify \c RegionOfInterest. + void findModifyingFrames(const ExplodedNode *N) { + assert(N->getLocationAs<CallExitBegin>()); + ProgramStateRef LastReturnState = N->getState(); + SVal ValueAtReturn = LastReturnState->getSVal(RegionOfInterest); + const LocationContext *Ctx = N->getLocationContext(); + const StackFrameContext *OriginalSCtx = Ctx->getStackFrame(); + + do { + ProgramStateRef State = N->getState(); + auto CallExitLoc = N->getLocationAs<CallExitBegin>(); + if (CallExitLoc) { + LastReturnState = State; + ValueAtReturn = LastReturnState->getSVal(RegionOfInterest); + } + + FramesModifyingCalculated.insert( + N->getLocationContext()->getStackFrame()); + + if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtReturn)) { + const StackFrameContext *SCtx = N->getStackFrame(); + while (!SCtx->inTopFrame()) { + auto p = FramesModifyingRegion.insert(SCtx); + if (!p.second) + break; // Frame and all its parents already inserted. + SCtx = SCtx->getParent()->getStackFrame(); + } + } + + // Stop calculation at the call to the current function. + if (auto CE = N->getLocationAs<CallEnter>()) + if (CE->getCalleeContext() == OriginalSCtx) + break; + + N = N->getFirstPred(); + } while (N); + } + + /// Get parameters associated with runtime definition in order + /// to get the correct parameter name. + ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) { + // Use runtime definition, if available. + RuntimeDefinition RD = Call->getRuntimeDefinition(); + if (const auto *FD = dyn_cast_or_null<FunctionDecl>(RD.getDecl())) + return FD->parameters(); + if (const auto *MD = dyn_cast_or_null<ObjCMethodDecl>(RD.getDecl())) + return MD->parameters(); + + return Call->parameters(); + } + + /// \return whether \p Ty points to a const type, or is a const reference. + bool isPointerToConst(QualType Ty) { + return !Ty->getPointeeType().isNull() && + Ty->getPointeeType().getCanonicalType().isConstQualified(); + } + + /// \return Diagnostics piece for region not modified in the current function. + 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); + } + + SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << "Returning without writing to '"; + + // Do not generate the note if failed to pretty-print. + if (!prettyPrintRegionName(FirstElement, FirstIsReferenceType, + MatchedRegion, FieldChain, IndirectionLevel, os)) + return nullptr; + + os << "'"; + return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); + } + + /// Pretty-print region \p MatchedRegion to \p os. + /// \return Whether printing succeeded. + bool prettyPrintRegionName(StringRef FirstElement, bool FirstIsReferenceType, + const MemRegion *MatchedRegion, + const RegionVector &FieldChain, + int IndirectionLevel, + llvm::raw_svector_ostream &os) { + + if (FirstIsReferenceType) + IndirectionLevel--; + + RegionVector RegionSequence; + + // Add the regions in the reverse order, then reverse the resulting array. + assert(RegionOfInterest->isSubRegionOf(MatchedRegion)); + const MemRegion *R = RegionOfInterest; + while (R != MatchedRegion) { + RegionSequence.push_back(R); + R = cast<SubRegion>(R)->getSuperRegion(); + } + std::reverse(RegionSequence.begin(), RegionSequence.end()); + RegionSequence.append(FieldChain.begin(), FieldChain.end()); + + StringRef Sep; + for (const MemRegion *R : RegionSequence) { + + // Just keep going up to the base region. + // Element regions may appear due to casts. + if (isa<CXXBaseObjectRegion>(R) || isa<CXXTempObjectRegion>(R)) + continue; + + if (Sep.empty()) + Sep = prettyPrintFirstElement(FirstElement, + /*MoreItemsExpected=*/true, + IndirectionLevel, os); + + os << Sep; + + // Can only reasonably pretty-print DeclRegions. + if (!isa<DeclRegion>(R)) + return false; + + const auto *DR = cast<DeclRegion>(R); + Sep = DR->getValueType()->isAnyPointerType() ? "->" : "."; + DR->getDecl()->getDeclName().print(os, PP); + } + + if (Sep.empty()) + prettyPrintFirstElement(FirstElement, + /*MoreItemsExpected=*/false, IndirectionLevel, + os); + return true; + } + + /// Print first item in the chain, return new separator. + StringRef prettyPrintFirstElement(StringRef FirstElement, + bool MoreItemsExpected, + int IndirectionLevel, + llvm::raw_svector_ostream &os) { + StringRef Out = "."; + + if (IndirectionLevel > 0 && MoreItemsExpected) { + IndirectionLevel--; + Out = "->"; + } + + if (IndirectionLevel > 0 && MoreItemsExpected) + os << "("; + + for (int i=0; i<IndirectionLevel; i++) + os << "*"; + os << FirstElement; + + if (IndirectionLevel > 0 && MoreItemsExpected) + os << ")"; + + return Out; + } +}; + +/// Suppress null-pointer-dereference bugs where dereferenced null was returned +/// the macro. +class MacroNullReturnSuppressionVisitor final : public BugReporterVisitor { + const SubRegion *RegionOfInterest; + const SVal ValueAtDereference; + + // Do not invalidate the reports where the value was modified + // after it got assigned to from the macro. + bool WasModified = false; + +public: + MacroNullReturnSuppressionVisitor(const SubRegion *R, + const SVal V) : RegionOfInterest(R), + ValueAtDereference(V) {} + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override { + if (WasModified) + return nullptr; + + auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>(); + if (!BugPoint) + return nullptr; + + const SourceManager &SMgr = BRC.getSourceManager(); + if (auto Loc = matchAssignment(N)) { + if (isFunctionMacroExpansion(*Loc, SMgr)) { + std::string MacroName = getMacroName(*Loc, BRC); + SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc(); + if (!BugLoc.isMacroID() || getMacroName(BugLoc, BRC) != MacroName) + BR.markInvalid(getTag(), MacroName.c_str()); + } + } + + if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtDereference)) + WasModified = true; + + return nullptr; + } + + static void addMacroVisitorIfNecessary( + const ExplodedNode *N, const MemRegion *R, + bool EnableNullFPSuppression, BugReport &BR, + const SVal V) { + AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; + if (EnableNullFPSuppression && + Options.ShouldSuppressNullReturnPaths && V.getAs<Loc>()) + BR.addVisitor(llvm::make_unique<MacroNullReturnSuppressionVisitor>( + R->getAs<SubRegion>(), V)); + } + + void* getTag() const { + static int Tag = 0; + return static_cast<void *>(&Tag); + } + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.AddPointer(getTag()); + } + +private: + /// \return Source location of right hand side of an assignment + /// into \c RegionOfInterest, empty optional if none found. + Optional<SourceLocation> matchAssignment(const ExplodedNode *N) { + const Stmt *S = PathDiagnosticLocation::getStmt(N); + ProgramStateRef State = N->getState(); + auto *LCtx = N->getLocationContext(); + if (!S) + return None; + + if (const auto *DS = dyn_cast<DeclStmt>(S)) { + if (const auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl())) + if (const Expr *RHS = VD->getInit()) + if (RegionOfInterest->isSubRegionOf( + State->getLValue(VD, LCtx).getAsRegion())) + return RHS->getBeginLoc(); + } else if (const auto *BO = dyn_cast<BinaryOperator>(S)) { + const MemRegion *R = N->getSVal(BO->getLHS()).getAsRegion(); + const Expr *RHS = BO->getRHS(); + if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(R)) { + return RHS->getBeginLoc(); + } + } + return None; + } +}; + +/// Emits an extra note at the return statement of an interesting stack frame. +/// +/// The returned value is marked as an interesting value, and if it's null, +/// adds a visitor to track where it became null. +/// +/// This visitor is intended to be used when another visitor discovers that an +/// interesting value comes from an inlined function call. +class ReturnVisitor : public BugReporterVisitor { + const StackFrameContext *StackFrame; + enum { + Initial, + MaybeUnsuppress, + Satisfied + } Mode = Initial; + + bool EnableNullFPSuppression; + bool ShouldInvalidate = true; + AnalyzerOptions& Options; + +public: + ReturnVisitor(const StackFrameContext *Frame, + bool Suppressed, + AnalyzerOptions &Options) + : StackFrame(Frame), EnableNullFPSuppression(Suppressed), + Options(Options) {} + + static void *getTag() { + static int Tag = 0; + return static_cast<void *>(&Tag); + } + + void Profile(llvm::FoldingSetNodeID &ID) const override { + ID.AddPointer(ReturnVisitor::getTag()); + ID.AddPointer(StackFrame); + ID.AddBoolean(EnableNullFPSuppression); + } + + /// Adds a ReturnVisitor if the given statement represents a call that was + /// inlined. + /// + /// This will search back through the ExplodedGraph, starting from the given + /// node, looking for when the given statement was processed. If it turns out + /// the statement is a call that was inlined, we add the visitor to the + /// bug report, so it can print a note later. + static void addVisitorIfNecessary(const ExplodedNode *Node, const Stmt *S, + BugReport &BR, + bool InEnableNullFPSuppression) { + if (!CallEvent::isCallStmt(S)) + return; + + // First, find when we processed the statement. + do { + if (auto CEE = Node->getLocationAs<CallExitEnd>()) + if (CEE->getCalleeContext()->getCallSite() == S) + break; + if (auto SP = Node->getLocationAs<StmtPoint>()) + if (SP->getStmt() == S) + break; + + Node = Node->getFirstPred(); + } while (Node); + + // Next, step over any post-statement checks. + while (Node && Node->getLocation().getAs<PostStmt>()) + Node = Node->getFirstPred(); + if (!Node) + return; + + // Finally, see if we inlined the call. + Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>(); + if (!CEE) + return; + + const StackFrameContext *CalleeContext = CEE->getCalleeContext(); + if (CalleeContext->getCallSite() != S) + return; + + // Check the return value. + ProgramStateRef State = Node->getState(); + SVal RetVal = Node->getSVal(S); + + // Handle cases where a reference is returned and then immediately used. + if (cast<Expr>(S)->isGLValue()) + if (Optional<Loc> LValue = RetVal.getAs<Loc>()) + RetVal = State->getSVal(*LValue); + + // See if the return value is NULL. If so, suppress the report. + AnalyzerOptions &Options = State->getAnalysisManager().options; + + bool EnableNullFPSuppression = false; + if (InEnableNullFPSuppression && + Options.ShouldSuppressNullReturnPaths) + if (Optional<Loc> RetLoc = RetVal.getAs<Loc>()) + EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue(); + + BR.markInteresting(CalleeContext); + BR.addVisitor(llvm::make_unique<ReturnVisitor>(CalleeContext, + EnableNullFPSuppression, + Options)); + } + + std::shared_ptr<PathDiagnosticPiece> + visitNodeInitial(const ExplodedNode *N, + BugReporterContext &BRC, BugReport &BR) { + // Only print a message at the interesting return statement. + if (N->getLocationContext() != StackFrame) + return nullptr; + + Optional<StmtPoint> SP = N->getLocationAs<StmtPoint>(); + if (!SP) + return nullptr; + + const auto *Ret = dyn_cast<ReturnStmt>(SP->getStmt()); + if (!Ret) + return nullptr; + + // Okay, we're at the right return statement, but do we have the return + // value available? + ProgramStateRef State = N->getState(); + SVal V = State->getSVal(Ret, StackFrame); + if (V.isUnknownOrUndef()) + return nullptr; + + // Don't print any more notes after this one. + Mode = Satisfied; + + const Expr *RetE = Ret->getRetValue(); + assert(RetE && "Tracking a return value for a void function"); + + // Handle cases where a reference is returned and then immediately used. + Optional<Loc> LValue; + if (RetE->isGLValue()) { + if ((LValue = V.getAs<Loc>())) { + SVal RValue = State->getRawSVal(*LValue, RetE->getType()); + if (RValue.getAs<DefinedSVal>()) + V = RValue; + } + } + + // Ignore aggregate rvalues. + if (V.getAs<nonloc::LazyCompoundVal>() || + V.getAs<nonloc::CompoundVal>()) + return nullptr; + + RetE = RetE->IgnoreParenCasts(); + + // If we're returning 0, we should track where that 0 came from. + bugreporter::trackExpressionValue(N, RetE, BR, EnableNullFPSuppression); + + // Build an appropriate message based on the return value. + SmallString<64> Msg; + llvm::raw_svector_ostream Out(Msg); + + if (State->isNull(V).isConstrainedTrue()) { + if (V.getAs<Loc>()) { + + // If we have counter-suppression enabled, make sure we keep visiting + // future nodes. We want to emit a path note as well, in case + // the report is resurrected as valid later on. + if (EnableNullFPSuppression && + Options.ShouldAvoidSuppressingNullArgumentPaths) + Mode = MaybeUnsuppress; + + if (RetE->getType()->isObjCObjectPointerType()) { + Out << "Returning nil"; + } else { + Out << "Returning null pointer"; + } + } else { + Out << "Returning zero"; + } + + } else { + if (auto CI = V.getAs<nonloc::ConcreteInt>()) { + Out << "Returning the value " << CI->getValue(); + } else if (V.getAs<Loc>()) { + Out << "Returning pointer"; + } else { + Out << "Returning value"; + } + } + + if (LValue) { + if (const MemRegion *MR = LValue->getAsRegion()) { + if (MR->canPrintPretty()) { + Out << " (reference to "; + MR->printPretty(Out); + Out << ")"; + } + } + } else { + // FIXME: We should have a more generalized location printing mechanism. + if (const auto *DR = dyn_cast<DeclRefExpr>(RetE)) + if (const auto *DD = dyn_cast<DeclaratorDecl>(DR->getDecl())) + Out << " (loaded from '" << *DD << "')"; + } + + PathDiagnosticLocation L(Ret, BRC.getSourceManager(), StackFrame); + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + return std::make_shared<PathDiagnosticEventPiece>(L, Out.str()); + } + + std::shared_ptr<PathDiagnosticPiece> + visitNodeMaybeUnsuppress(const ExplodedNode *N, + BugReporterContext &BRC, BugReport &BR) { +#ifndef NDEBUG + assert(Options.ShouldAvoidSuppressingNullArgumentPaths); +#endif + + // Are we at the entry node for this call? + Optional<CallEnter> CE = N->getLocationAs<CallEnter>(); + if (!CE) + return nullptr; + + if (CE->getCalleeContext() != StackFrame) + return nullptr; + + Mode = Satisfied; + + // Don't automatically suppress a report if one of the arguments is + // known to be a null pointer. Instead, start tracking /that/ null + // value back to its origin. + ProgramStateManager &StateMgr = BRC.getStateManager(); + CallEventManager &CallMgr = StateMgr.getCallEventManager(); + + ProgramStateRef State = N->getState(); + CallEventRef<> Call = CallMgr.getCaller(StackFrame, State); + for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) { + Optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>(); + if (!ArgV) + continue; + + const Expr *ArgE = Call->getArgExpr(I); + if (!ArgE) + continue; + + // Is it possible for this argument to be non-null? + if (!State->isNull(*ArgV).isConstrainedTrue()) + continue; + + if (bugreporter::trackExpressionValue(N, ArgE, BR, EnableNullFPSuppression)) + ShouldInvalidate = false; + + // If we /can't/ track the null pointer, we should err on the side of + // false negatives, and continue towards marking this report invalid. + // (We will still look at the other arguments, though.) + } + + return nullptr; + } + + std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, + BugReport &BR) override { + switch (Mode) { + case Initial: + return visitNodeInitial(N, BRC, BR); + case MaybeUnsuppress: + return visitNodeMaybeUnsuppress(N, BRC, BR); + case Satisfied: + return nullptr; + } + + llvm_unreachable("Invalid visit mode!"); + } + + void finalizeVisitor(BugReporterContext &, const ExplodedNode *, + BugReport &BR) override { + if (EnableNullFPSuppression && ShouldInvalidate) + BR.markInvalid(ReturnVisitor::getTag(), StackFrame); + } +}; + +} // namespace + +void FindLastStoreBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { + static int tag = 0; + ID.AddPointer(&tag); + ID.AddPointer(R); + ID.Add(V); + ID.AddBoolean(EnableNullFPSuppression); +} + +/// Returns true if \p N represents the DeclStmt declaring and initializing +/// \p VR. +static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) { + Optional<PostStmt> P = N->getLocationAs<PostStmt>(); + if (!P) + return false; + + const DeclStmt *DS = P->getStmtAs<DeclStmt>(); + if (!DS) + return false; + + if (DS->getSingleDecl() != VR->getDecl()) + return false; + + const MemSpaceRegion *VarSpace = VR->getMemorySpace(); + const auto *FrameSpace = dyn_cast<StackSpaceRegion>(VarSpace); + if (!FrameSpace) { + // If we ever directly evaluate global DeclStmts, this assertion will be + // invalid, but this still seems preferable to silently accepting an + // initialization that may be for a path-sensitive variable. + assert(VR->getDecl()->isStaticLocal() && "non-static stackless VarRegion"); + return true; + } + + assert(VR->getDecl()->hasLocalStorage()); + const LocationContext *LCtx = N->getLocationContext(); + return FrameSpace->getStackFrame() == LCtx->getStackFrame(); +} + +/// Show diagnostics for initializing or declaring a region \p R with a bad value. +static void showBRDiagnostics(const char *action, llvm::raw_svector_ostream &os, + const MemRegion *R, SVal V, const DeclStmt *DS) { + if (R->canPrintPretty()) { + R->printPretty(os); + os << " "; + } + + if (V.getAs<loc::ConcreteInt>()) { + bool b = false; + if (R->isBoundable()) { + if (const auto *TR = dyn_cast<TypedValueRegion>(R)) { + if (TR->getValueType()->isObjCObjectPointerType()) { + os << action << "nil"; + b = true; + } + } + } + if (!b) + os << action << "a null pointer value"; + + } else if (auto CVal = V.getAs<nonloc::ConcreteInt>()) { + os << action << CVal->getValue(); + } else if (DS) { + if (V.isUndef()) { + if (isa<VarRegion>(R)) { + const auto *VD = cast<VarDecl>(DS->getSingleDecl()); + if (VD->getInit()) { + os << (R->canPrintPretty() ? "initialized" : "Initializing") + << " to a garbage value"; + } else { + os << (R->canPrintPretty() ? "declared" : "Declaring") + << " without an initial value"; + } + } + } else { + os << (R->canPrintPretty() ? "initialized" : "Initialized") + << " here"; + } + } +} + +/// Display diagnostics for passing bad region as a parameter. +static void showBRParamDiagnostics(llvm::raw_svector_ostream& os, + const VarRegion *VR, + SVal V) { + const auto *Param = cast<ParmVarDecl>(VR->getDecl()); + + os << "Passing "; + + if (V.getAs<loc::ConcreteInt>()) { + if (Param->getType()->isObjCObjectPointerType()) + os << "nil object reference"; + else + os << "null pointer value"; + } else if (V.isUndef()) { + os << "uninitialized value"; + } else if (auto CI = V.getAs<nonloc::ConcreteInt>()) { + os << "the value " << CI->getValue(); + } else { + os << "value"; + } + + // Printed parameter indexes are 1-based, not 0-based. + unsigned Idx = Param->getFunctionScopeIndex() + 1; + os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter"; + if (VR->canPrintPretty()) { + os << " "; + VR->printPretty(os); + } +} + +/// Show default diagnostics for storing bad region. +static void showBRDefaultDiagnostics(llvm::raw_svector_ostream& os, + const MemRegion *R, + SVal V) { + if (V.getAs<loc::ConcreteInt>()) { + bool b = false; + if (R->isBoundable()) { + if (const auto *TR = dyn_cast<TypedValueRegion>(R)) { + if (TR->getValueType()->isObjCObjectPointerType()) { + os << "nil object reference stored"; + b = true; + } + } + } + if (!b) { + if (R->canPrintPretty()) + os << "Null pointer value stored"; + else + os << "Storing null pointer value"; + } + + } else if (V.isUndef()) { + if (R->canPrintPretty()) + os << "Uninitialized value stored"; + else + os << "Storing uninitialized value"; + + } else if (auto CV = V.getAs<nonloc::ConcreteInt>()) { + if (R->canPrintPretty()) + os << "The value " << CV->getValue() << " is assigned"; + else + os << "Assigning " << CV->getValue(); + + } else { + if (R->canPrintPretty()) + os << "Value assigned"; + else + os << "Assigning value"; + } + + if (R->canPrintPretty()) { + os << " to "; + R->printPretty(os); + } +} + +std::shared_ptr<PathDiagnosticPiece> +FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, BugReport &BR) { + if (Satisfied) + return nullptr; + + const ExplodedNode *StoreSite = nullptr; + const ExplodedNode *Pred = Succ->getFirstPred(); + const Expr *InitE = nullptr; + bool IsParam = false; + + // First see if we reached the declaration of the region. + if (const auto *VR = dyn_cast<VarRegion>(R)) { + if (isInitializationOfVar(Pred, VR)) { + StoreSite = Pred; + InitE = VR->getDecl()->getInit(); + } + } + + // If this is a post initializer expression, initializing the region, we + // should track the initializer expression. + if (Optional<PostInitializer> PIP = Pred->getLocationAs<PostInitializer>()) { + const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue(); + if (FieldReg && FieldReg == R) { + StoreSite = Pred; + InitE = PIP->getInitializer()->getInit(); + } + } + + // Otherwise, see if this is the store site: + // (1) Succ has this binding and Pred does not, i.e. this is + // where the binding first occurred. + // (2) Succ has this binding and is a PostStore node for this region, i.e. + // the same binding was re-assigned here. + if (!StoreSite) { + if (Succ->getState()->getSVal(R) != V) + return nullptr; + + if (Pred->getState()->getSVal(R) == V) { + Optional<PostStore> PS = Succ->getLocationAs<PostStore>(); + if (!PS || PS->getLocationValue() != R) + return nullptr; + } + + StoreSite = Succ; + + // If this is an assignment expression, we can track the value + // being assigned. + if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>()) + if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) + if (BO->isAssignmentOp()) + InitE = BO->getRHS(); + + // If this is a call entry, the variable should be a parameter. + // FIXME: Handle CXXThisRegion as well. (This is not a priority because + // 'this' should never be NULL, but this visitor isn't just for NULL and + // 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(); + CallEventManager &CallMgr = StateMgr.getCallEventManager(); + + CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(), + Succ->getState()); + InitE = Call->getArgExpr(Param->getFunctionScopeIndex()); + IsParam = true; + } + } + + // If this is a CXXTempObjectRegion, the Expr responsible for its creation + // is wrapped inside of it. + if (const auto *TmpR = dyn_cast<CXXTempObjectRegion>(R)) + InitE = TmpR->getExpr(); + } + + if (!StoreSite) + return nullptr; + Satisfied = true; + + // If we have an expression that provided the value, try to track where it + // came from. + if (InitE) { + if (V.isUndef() || + V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { + if (!IsParam) + InitE = InitE->IgnoreParenCasts(); + bugreporter::trackExpressionValue(StoreSite, InitE, BR, + EnableNullFPSuppression); + } + ReturnVisitor::addVisitorIfNecessary(StoreSite, InitE->IgnoreParenCasts(), + BR, EnableNullFPSuppression); + } + + // Okay, we've found the binding. Emit an appropriate message. + SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); + + if (Optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) { + const Stmt *S = PS->getStmt(); + const char *action = nullptr; + const auto *DS = dyn_cast<DeclStmt>(S); + const auto *VR = dyn_cast<VarRegion>(R); + + if (DS) { + action = R->canPrintPretty() ? "initialized to " : + "Initializing to "; + } else if (isa<BlockExpr>(S)) { + action = R->canPrintPretty() ? "captured by block as " : + "Captured by block as "; + if (VR) { + // See if we can get the BlockVarRegion. + ProgramStateRef State = StoreSite->getState(); + SVal V = StoreSite->getSVal(S); + if (const auto *BDR = + dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) { + if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) { + if (auto KV = State->getSVal(OriginalR).getAs<KnownSVal>()) + BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + *KV, OriginalR, EnableNullFPSuppression)); + } + } + } + } + if (action) + showBRDiagnostics(action, os, R, V, DS); + + } else if (StoreSite->getLocation().getAs<CallEnter>()) { + if (const auto *VR = dyn_cast<VarRegion>(R)) + showBRParamDiagnostics(os, VR, V); + } + + if (os.str().empty()) + showBRDefaultDiagnostics(os, R, V); + + // Construct a new PathDiagnosticPiece. + ProgramPoint P = StoreSite->getLocation(); + PathDiagnosticLocation L; + if (P.getAs<CallEnter>() && InitE) + L = PathDiagnosticLocation(InitE, BRC.getSourceManager(), + P.getLocationContext()); + + if (!L.isValid() || !L.asLocation().isValid()) + L = PathDiagnosticLocation::create(P, BRC.getSourceManager()); + + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + return std::make_shared<PathDiagnosticEventPiece>(L, os.str()); +} + +void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const { + static int tag = 0; + ID.AddPointer(&tag); + ID.AddBoolean(Assumption); + ID.Add(Constraint); +} + +/// Return the tag associated with this visitor. This tag will be used +/// to make all PathDiagnosticPieces created by this visitor. +const char *TrackConstraintBRVisitor::getTag() { + return "TrackConstraintBRVisitor"; +} + +bool TrackConstraintBRVisitor::isUnderconstrained(const ExplodedNode *N) const { + if (IsZeroCheck) + return N->getState()->isNull(Constraint).isUnderconstrained(); + return (bool)N->getState()->assume(Constraint, !Assumption); +} + +std::shared_ptr<PathDiagnosticPiece> +TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, BugReport &) { + const ExplodedNode *PrevN = N->getFirstPred(); + if (IsSatisfied) + return nullptr; + + // Start tracking after we see the first state in which the value is + // constrained. + if (!IsTrackingTurnedOn) + if (!isUnderconstrained(N)) + IsTrackingTurnedOn = true; + if (!IsTrackingTurnedOn) + return nullptr; + + // Check if in the previous state it was feasible for this constraint + // to *not* be true. + if (isUnderconstrained(PrevN)) { + IsSatisfied = true; + + // As a sanity check, make sure that the negation of the constraint + // was infeasible in the current state. If it is feasible, we somehow + // missed the transition point. + assert(!isUnderconstrained(N)); + + // We found the transition point for the constraint. We now need to + // pretty-print the constraint. (work-in-progress) + SmallString<64> sbuf; + llvm::raw_svector_ostream os(sbuf); + + if (Constraint.getAs<Loc>()) { + os << "Assuming pointer value is "; + os << (Assumption ? "non-null" : "null"); + } + + if (os.str().empty()) + return nullptr; + + // Construct a new PathDiagnosticPiece. + ProgramPoint P = N->getLocation(); + PathDiagnosticLocation L = + PathDiagnosticLocation::create(P, BRC.getSourceManager()); + if (!L.isValid()) + return nullptr; + + auto X = std::make_shared<PathDiagnosticEventPiece>(L, os.str()); + X->setTag(getTag()); + return std::move(X); + } + + return nullptr; +} + +SuppressInlineDefensiveChecksVisitor:: +SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N) + : V(Value) { + // Check if the visitor is disabled. + AnalyzerOptions &Options = N->getState()->getAnalysisManager().options; + if (!Options.ShouldSuppressInlinedDefensiveChecks) + IsSatisfied = true; + + assert(N->getState()->isNull(V).isConstrainedTrue() && + "The visitor only tracks the cases where V is constrained to 0"); +} + +void SuppressInlineDefensiveChecksVisitor::Profile( + llvm::FoldingSetNodeID &ID) const { + static int id = 0; + ID.AddPointer(&id); + ID.Add(V); +} + +const char *SuppressInlineDefensiveChecksVisitor::getTag() { + return "IDCVisitor"; +} + +std::shared_ptr<PathDiagnosticPiece> +SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, + BugReport &BR) { + const ExplodedNode *Pred = Succ->getFirstPred(); + if (IsSatisfied) + return nullptr; + + // Start tracking after we see the first state in which the value is null. + if (!IsTrackingTurnedOn) + if (Succ->getState()->isNull(V).isConstrainedTrue()) + IsTrackingTurnedOn = true; + if (!IsTrackingTurnedOn) + return nullptr; + + // Check if in the previous state it was feasible for this value + // to *not* be null. + if (!Pred->getState()->isNull(V).isConstrainedTrue()) { + IsSatisfied = true; + + assert(Succ->getState()->isNull(V).isConstrainedTrue()); + + // Check if this is inlined defensive checks. + const LocationContext *CurLC =Succ->getLocationContext(); + const LocationContext *ReportLC = BR.getErrorNode()->getLocationContext(); + if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC)) { + BR.markInvalid("Suppress IDC", CurLC); + return nullptr; + } + + // Treat defensive checks in function-like macros as if they were an inlined + // defensive check. If the bug location is not in a macro and the + // terminator for the current location is in a macro then suppress the + // warning. + auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>(); + + if (!BugPoint) + return nullptr; + + ProgramPoint CurPoint = Succ->getLocation(); + const Stmt *CurTerminatorStmt = nullptr; + if (auto BE = CurPoint.getAs<BlockEdge>()) { + CurTerminatorStmt = BE->getSrc()->getTerminator().getStmt(); + } else if (auto SP = CurPoint.getAs<StmtPoint>()) { + const Stmt *CurStmt = SP->getStmt(); + if (!CurStmt->getBeginLoc().isMacroID()) + return nullptr; + + CFGStmtMap *Map = CurLC->getAnalysisDeclContext()->getCFGStmtMap(); + CurTerminatorStmt = Map->getBlock(CurStmt)->getTerminator(); + } else { + return nullptr; + } + + if (!CurTerminatorStmt) + return nullptr; + + SourceLocation TerminatorLoc = CurTerminatorStmt->getBeginLoc(); + if (TerminatorLoc.isMacroID()) { + SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc(); + + // Suppress reports unless we are in that same macro. + if (!BugLoc.isMacroID() || + getMacroName(BugLoc, BRC) != getMacroName(TerminatorLoc, BRC)) { + BR.markInvalid("Suppress Macro IDC", CurLC); + } + return nullptr; + } + } + return nullptr; +} + +static const MemRegion *getLocationRegionIfReference(const Expr *E, + const ExplodedNode *N) { + if (const auto *DR = dyn_cast<DeclRefExpr>(E)) { + if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) { + if (!VD->getType()->isReferenceType()) + return nullptr; + ProgramStateManager &StateMgr = N->getState()->getStateManager(); + MemRegionManager &MRMgr = StateMgr.getRegionManager(); + return MRMgr.getVarRegion(VD, N->getLocationContext()); + } + } + + // FIXME: This does not handle other kinds of null references, + // for example, references from FieldRegions: + // struct Wrapper { int &ref; }; + // Wrapper w = { *(int *)0 }; + // w.ref = 1; + + return nullptr; +} + +/// \return A subexpression of {@code Ex} which represents the +/// expression-of-interest. +static const Expr *peelOffOuterExpr(const Expr *Ex, + const ExplodedNode *N) { + Ex = Ex->IgnoreParenCasts(); + if (const auto *FE = dyn_cast<FullExpr>(Ex)) + return peelOffOuterExpr(FE->getSubExpr(), N); + if (const auto *OVE = dyn_cast<OpaqueValueExpr>(Ex)) + return peelOffOuterExpr(OVE->getSourceExpr(), N); + if (const auto *POE = dyn_cast<PseudoObjectExpr>(Ex)) { + const auto *PropRef = dyn_cast<ObjCPropertyRefExpr>(POE->getSyntacticForm()); + if (PropRef && PropRef->isMessagingGetter()) { + const Expr *GetterMessageSend = + POE->getSemanticExpr(POE->getNumSemanticExprs() - 1); + assert(isa<ObjCMessageExpr>(GetterMessageSend->IgnoreParenCasts())); + return peelOffOuterExpr(GetterMessageSend, N); + } + } + + // Peel off the ternary operator. + if (const auto *CO = dyn_cast<ConditionalOperator>(Ex)) { + // Find a node where the branching occurred and find out which branch + // we took (true/false) by looking at the ExplodedGraph. + const ExplodedNode *NI = N; + do { + ProgramPoint ProgPoint = NI->getLocation(); + if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) { + const CFGBlock *srcBlk = BE->getSrc(); + if (const Stmt *term = srcBlk->getTerminator()) { + if (term == CO) { + bool TookTrueBranch = (*(srcBlk->succ_begin()) == BE->getDst()); + if (TookTrueBranch) + return peelOffOuterExpr(CO->getTrueExpr(), N); + else + return peelOffOuterExpr(CO->getFalseExpr(), N); + } + } + } + NI = NI->getFirstPred(); + } while (NI); + } + + if (auto *BO = dyn_cast<BinaryOperator>(Ex)) + if (const Expr *SubEx = peelOffPointerArithmetic(BO)) + return peelOffOuterExpr(SubEx, N); + + if (auto *UO = dyn_cast<UnaryOperator>(Ex)) { + if (UO->getOpcode() == UO_LNot) + return peelOffOuterExpr(UO->getSubExpr(), N); + + // FIXME: There's a hack in our Store implementation that always computes + // field offsets around null pointers as if they are always equal to 0. + // The idea here is to report accesses to fields as null dereferences + // even though the pointer value that's being dereferenced is actually + // the offset of the field rather than exactly 0. + // See the FIXME in StoreManager's getLValueFieldOrIvar() method. + // This code interacts heavily with this hack; otherwise the value + // would not be null at all for most fields, so we'd be unable to track it. + if (UO->getOpcode() == UO_AddrOf && UO->getSubExpr()->isLValue()) + if (const Expr *DerefEx = bugreporter::getDerefExpr(UO->getSubExpr())) + return peelOffOuterExpr(DerefEx, N); + } + + return Ex; +} + +/// Find the ExplodedNode where the lvalue (the value of 'Ex') +/// was computed. +static const ExplodedNode* findNodeForExpression(const ExplodedNode *N, + const Expr *Inner) { + while (N) { + if (PathDiagnosticLocation::getStmt(N) == Inner) + return N; + N = N->getFirstPred(); + } + return N; +} + +bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode, + const Expr *E, BugReport &report, + bool EnableNullFPSuppression) { + if (!E || !InputNode) + return false; + + const Expr *Inner = peelOffOuterExpr(E, InputNode); + const ExplodedNode *LVNode = findNodeForExpression(InputNode, Inner); + if (!LVNode) + return false; + + ProgramStateRef LVState = LVNode->getState(); + + // The message send could be nil due to the receiver being nil. + // At this point in the path, the receiver should be live since we are at the + // message send expr. If it is nil, start tracking it. + if (const Expr *Receiver = NilReceiverBRVisitor::getNilReceiver(Inner, LVNode)) + trackExpressionValue(LVNode, Receiver, report, EnableNullFPSuppression); + + // See if the expression we're interested refers to a variable. + // If so, we can track both its contents and constraints on its value. + if (ExplodedGraph::isInterestingLValueExpr(Inner)) { + SVal LVal = LVNode->getSVal(Inner); + + const MemRegion *RR = getLocationRegionIfReference(Inner, LVNode); + bool LVIsNull = LVState->isNull(LVal).isConstrainedTrue(); + + // If this is a C++ reference to a null pointer, we are tracking the + // pointer. In addition, we should find the store at which the reference + // got initialized. + if (RR && !LVIsNull) + if (auto KV = LVal.getAs<KnownSVal>()) + report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + *KV, RR, EnableNullFPSuppression)); + + // In case of C++ references, we want to differentiate between a null + // reference and reference to null pointer. + // If the LVal is null, check if we are dealing with null reference. + // For those, we want to track the location of the reference. + const MemRegion *R = (RR && LVIsNull) ? RR : + LVNode->getSVal(Inner).getAsRegion(); + + if (R) { + + // Mark both the variable region and its contents as interesting. + SVal V = LVState->getRawSVal(loc::MemRegionVal(R)); + report.addVisitor( + llvm::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R))); + + MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary( + LVNode, R, EnableNullFPSuppression, report, V); + + report.markInteresting(V); + report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(R)); + + // If the contents are symbolic, find out when they became null. + if (V.getAsLocSymbol(/*IncludeBaseRegions*/ true)) + report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>( + V.castAs<DefinedSVal>(), false)); + + // Add visitor, which will suppress inline defensive checks. + if (auto DV = V.getAs<DefinedSVal>()) + if (!DV->isZeroConstant() && LVState->isNull(*DV).isConstrainedTrue() && + EnableNullFPSuppression) + report.addVisitor( + llvm::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV, + LVNode)); + + if (auto KV = V.getAs<KnownSVal>()) + report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + *KV, R, EnableNullFPSuppression)); + return true; + } + } + + // If the expression is not an "lvalue expression", we can still + // track the constraints on its contents. + SVal V = LVState->getSValAsScalarOrLoc(Inner, LVNode->getLocationContext()); + + ReturnVisitor::addVisitorIfNecessary( + LVNode, Inner, report, EnableNullFPSuppression); + + // Is it a symbolic value? + if (auto L = V.getAs<loc::MemRegionVal>()) { + report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(L->getRegion())); + + // FIXME: this is a hack for fixing a later crash when attempting to + // dereference a void* pointer. + // We should not try to dereference pointers at all when we don't care + // what is written inside the pointer. + bool CanDereference = true; + if (const auto *SR = dyn_cast<SymbolicRegion>(L->getRegion())) + if (SR->getSymbol()->getType()->getPointeeType()->isVoidType()) + CanDereference = false; + + // At this point we are dealing with the region's LValue. + // However, if the rvalue is a symbolic region, we should track it as well. + // Try to use the correct type when looking up the value. + SVal RVal; + if (ExplodedGraph::isInterestingLValueExpr(Inner)) { + RVal = LVState->getRawSVal(L.getValue(), Inner->getType()); + } else if (CanDereference) { + RVal = LVState->getSVal(L->getRegion()); + } + + if (CanDereference) + if (auto KV = RVal.getAs<KnownSVal>()) + report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + *KV, L->getRegion(), EnableNullFPSuppression)); + + const MemRegion *RegionRVal = RVal.getAsRegion(); + if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) { + report.markInteresting(RegionRVal); + report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>( + loc::MemRegionVal(RegionRVal), /*assumption=*/false)); + } + } + return true; +} + +const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S, + const ExplodedNode *N) { + const auto *ME = dyn_cast<ObjCMessageExpr>(S); + if (!ME) + return nullptr; + if (const Expr *Receiver = ME->getInstanceReceiver()) { + ProgramStateRef state = N->getState(); + SVal V = N->getSVal(Receiver); + if (state->isNull(V).isConstrainedTrue()) + return Receiver; + } + return nullptr; +} + +std::shared_ptr<PathDiagnosticPiece> +NilReceiverBRVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, BugReport &BR) { + Optional<PreStmt> P = N->getLocationAs<PreStmt>(); + if (!P) + return nullptr; + + const Stmt *S = P->getStmt(); + const Expr *Receiver = getNilReceiver(S, N); + if (!Receiver) + return nullptr; + + llvm::SmallString<256> Buf; + llvm::raw_svector_ostream OS(Buf); + + if (const auto *ME = dyn_cast<ObjCMessageExpr>(S)) { + OS << "'"; + ME->getSelector().print(OS); + OS << "' not called"; + } + else { + OS << "No method is called"; + } + OS << " because the receiver is nil"; + + // The receiver was nil, and hence the method was skipped. + // Register a BugReporterVisitor to issue a message telling us how + // the receiver was null. + bugreporter::trackExpressionValue(N, Receiver, BR, + /*EnableNullFPSuppression*/ false); + // Issue a message saying that the method was skipped. + PathDiagnosticLocation L(Receiver, BRC.getSourceManager(), + N->getLocationContext()); + return std::make_shared<PathDiagnosticEventPiece>(L, OS.str()); +} + +// Registers every VarDecl inside a Stmt with a last store visitor. +void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR, + const Stmt *S, + bool EnableNullFPSuppression) { + const ExplodedNode *N = BR.getErrorNode(); + std::deque<const Stmt *> WorkList; + WorkList.push_back(S); + + while (!WorkList.empty()) { + const Stmt *Head = WorkList.front(); + WorkList.pop_front(); + + ProgramStateManager &StateMgr = N->getState()->getStateManager(); + + if (const auto *DR = dyn_cast<DeclRefExpr>(Head)) { + if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) { + const VarRegion *R = + StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); + + // What did we load? + SVal V = N->getSVal(S); + + if (V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) { + // Register a new visitor with the BugReport. + BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>( + V.castAs<KnownSVal>(), R, EnableNullFPSuppression)); + } + } + } + + for (const Stmt *SubStmt : Head->children()) + WorkList.push_back(SubStmt); + } +} + +//===----------------------------------------------------------------------===// +// Visitor that tries to report interesting diagnostics from conditions. +//===----------------------------------------------------------------------===// + +/// Return the tag associated with this visitor. This tag will be used +/// to make all PathDiagnosticPieces created by this visitor. +const char *ConditionBRVisitor::getTag() { + return "ConditionBRVisitor"; +} + +std::shared_ptr<PathDiagnosticPiece> +ConditionBRVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, BugReport &BR) { + auto piece = VisitNodeImpl(N, BRC, BR); + if (piece) { + piece->setTag(getTag()); + if (auto *ev = dyn_cast<PathDiagnosticEventPiece>(piece.get())) + ev->setPrunable(true, /* override */ false); + } + return piece; +} + +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. + if (Optional<BlockEdge> BE = progPoint.getAs<BlockEdge>()) { + const CFGBlock *srcBlk = BE->getSrc(); + if (const Stmt *term = srcBlk->getTerminator()) + return VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC); + return nullptr; + } + + if (Optional<PostStmt> PS = progPoint.getAs<PostStmt>()) { + const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags = + ExprEngine::geteagerlyAssumeBinOpBifurcationTags(); + + const ProgramPointTag *tag = PS->getTag(); + if (tag == tags.first) + return VisitTrueTest(cast<Expr>(PS->getStmt()), true, + BRC, BR, N); + if (tag == tags.second) + return VisitTrueTest(cast<Expr>(PS->getStmt()), false, + BRC, BR, N); + + return nullptr; + } + + return nullptr; +} + +std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator( + const Stmt *Term, const ExplodedNode *N, const CFGBlock *srcBlk, + const CFGBlock *dstBlk, BugReport &R, BugReporterContext &BRC) { + const Expr *Cond = nullptr; + + // In the code below, Term is a CFG terminator and Cond is a branch condition + // expression upon which the decision is made on this terminator. + // + // For example, in "if (x == 0)", the "if (x == 0)" statement is a terminator, + // and "x == 0" is the respective condition. + // + // Another example: in "if (x && y)", we've got two terminators and two + // conditions due to short-circuit nature of operator "&&": + // 1. The "if (x && y)" statement is a terminator, + // and "y" is the respective condition. + // 2. Also "x && ..." is another terminator, + // and "x" is its condition. + + switch (Term->getStmtClass()) { + // FIXME: Stmt::SwitchStmtClass is worth handling, however it is a bit + // more tricky because there are more than two branches to account for. + default: + return nullptr; + case Stmt::IfStmtClass: + Cond = cast<IfStmt>(Term)->getCond(); + break; + case Stmt::ConditionalOperatorClass: + Cond = cast<ConditionalOperator>(Term)->getCond(); + break; + case Stmt::BinaryOperatorClass: + // When we encounter a logical operator (&& or ||) as a CFG terminator, + // then the condition is actually its LHS; otherwise, we'd encounter + // the parent, such as if-statement, as a terminator. + const auto *BO = cast<BinaryOperator>(Term); + assert(BO->isLogicalOp() && + "CFG terminator is not a short-circuit operator!"); + Cond = BO->getLHS(); + break; + } + + // 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. + while (const auto *InnerBO = dyn_cast<BinaryOperator>(Cond)) { + if (!InnerBO->isLogicalOp()) + break; + Cond = InnerBO->getRHS()->IgnoreParens(); + } + + assert(Cond); + assert(srcBlk->succ_size() == 2); + const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk; + return VisitTrueTest(Cond, tookTrue, BRC, R, N); +} + +std::shared_ptr<PathDiagnosticPiece> +ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, + BugReporterContext &BRC, BugReport &R, + const ExplodedNode *N) { + // 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; + bool tookTrueTmp = tookTrue; + + while (true) { + CondTmp = CondTmp->IgnoreParenCasts(); + switch (CondTmp->getStmtClass()) { + default: + break; + case Stmt::BinaryOperatorClass: + if (auto P = VisitTrueTest(Cond, cast<BinaryOperator>(CondTmp), + tookTrueTmp, BRC, R, N)) + return P; + break; + case Stmt::DeclRefExprClass: + if (auto P = VisitTrueTest(Cond, cast<DeclRefExpr>(CondTmp), + tookTrueTmp, BRC, R, N)) + return P; + break; + case Stmt::UnaryOperatorClass: { + const auto *UO = cast<UnaryOperator>(CondTmp); + if (UO->getOpcode() == UO_LNot) { + tookTrueTmp = !tookTrueTmp; + CondTmp = UO->getSubExpr(); + continue; + } + break; + } + } + break; + } + + // 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; + + return std::make_shared<PathDiagnosticEventPiece>( + Loc, tookTrue ? GenericTrueMessage : GenericFalseMessage); +} + +bool ConditionBRVisitor::patternMatch(const Expr *Ex, + const Expr *ParentEx, + raw_ostream &Out, + BugReporterContext &BRC, + BugReport &report, + const ExplodedNode *N, + Optional<bool> &prunable) { + 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); + } + StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics( + Loc, BRC.getSourceManager(), BRC.getASTContext().getLangOpts()); + + // Return the macro name. + Out << MacroName; + return false; + } + } + + if (const auto *DR = dyn_cast<DeclRefExpr>(Ex)) { + const bool quotes = isa<VarDecl>(DR->getDecl()); + if (quotes) { + Out << '\''; + const LocationContext *LCtx = N->getLocationContext(); + const ProgramState *state = N->getState().get(); + if (const MemRegion *R = state->getLValue(cast<VarDecl>(DR->getDecl()), + LCtx).getAsRegion()) { + if (report.isInteresting(R)) + prunable = false; + else { + const ProgramState *state = N->getState().get(); + SVal V = state->getSVal(R); + if (report.isInteresting(V)) + prunable = false; + } + } + } + Out << DR->getDecl()->getDeclName().getAsString(); + if (quotes) + Out << '\''; + return quotes; + } + + if (const auto *IL = dyn_cast<IntegerLiteral>(Ex)) { + QualType OriginalTy = OriginalExpr->getType(); + if (OriginalTy->isPointerType()) { + if (IL->getValue() == 0) { + Out << "null"; + return false; + } + } + else if (OriginalTy->isObjCObjectPointerType()) { + if (IL->getValue() == 0) { + Out << "nil"; + return false; + } + } + + Out << IL->getValue(); + return false; + } + + return false; +} + +std::shared_ptr<PathDiagnosticPiece> +ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr, + const bool tookTrue, BugReporterContext &BRC, + BugReport &R, const ExplodedNode *N) { + bool shouldInvert = false; + Optional<bool> shouldPrune; + + SmallString<128> LhsString, RhsString; + { + llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString); + const bool isVarLHS = patternMatch(BExpr->getLHS(), BExpr, OutLHS, + BRC, R, N, shouldPrune); + const bool isVarRHS = patternMatch(BExpr->getRHS(), BExpr, OutRHS, + BRC, R, N, shouldPrune); + + shouldInvert = !isVarLHS && isVarRHS; + } + + BinaryOperator::Opcode Op = BExpr->getOpcode(); + + if (BinaryOperator::isAssignmentOp(Op)) { + // For assignment operators, all that we care about is that the LHS + // evaluates to "true" or "false". + return VisitConditionVariable(LhsString, BExpr->getLHS(), tookTrue, + BRC, R, N); + } + + // For non-assignment operations, we require that we can understand + // both the LHS and RHS. + if (LhsString.empty() || RhsString.empty() || + !BinaryOperator::isComparisonOp(Op) || Op == BO_Cmp) + return nullptr; + + // Should we invert the strings if the LHS is not a variable name? + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + Out << "Assuming " << (shouldInvert ? RhsString : LhsString) << " is "; + + // Do we need to invert the opcode? + if (shouldInvert) + switch (Op) { + default: break; + case BO_LT: Op = BO_GT; break; + case BO_GT: Op = BO_LT; break; + case BO_LE: Op = BO_GE; break; + case BO_GE: Op = BO_LE; break; + } + + if (!tookTrue) + switch (Op) { + case BO_EQ: Op = BO_NE; break; + case BO_NE: Op = BO_EQ; break; + case BO_LT: Op = BO_GE; break; + case BO_GT: Op = BO_LE; break; + case BO_LE: Op = BO_GT; break; + case BO_GE: Op = BO_LT; break; + default: + return nullptr; + } + + switch (Op) { + case BO_EQ: + Out << "equal to "; + break; + case BO_NE: + Out << "not equal to "; + break; + default: + Out << BinaryOperator::getOpcodeStr(Op) << ' '; + break; + } + + Out << (shouldInvert ? LhsString : RhsString); + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); + if (shouldPrune.hasValue()) + event->setPrunable(shouldPrune.getValue()); + return event; +} + +std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable( + StringRef LhsString, const Expr *CondVarExpr, const bool tookTrue, + BugReporterContext &BRC, BugReport &report, const ExplodedNode *N) { + // FIXME: If there's already a constraint tracker for this variable, + // we shouldn't emit anything here (c.f. the double note in + // test/Analysis/inlining/path-notes.c) + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + Out << "Assuming " << LhsString << " is "; + + QualType Ty = CondVarExpr->getType(); + + if (Ty->isPointerType()) + Out << (tookTrue ? "not null" : "null"); + else if (Ty->isObjCObjectPointerType()) + Out << (tookTrue ? "not nil" : "nil"); + else if (Ty->isBooleanType()) + Out << (tookTrue ? "true" : "false"); + else if (Ty->isIntegralOrEnumerationType()) + Out << (tookTrue ? "non-zero" : "zero"); + else + return nullptr; + + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx); + auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); + + if (const auto *DR = dyn_cast<DeclRefExpr>(CondVarExpr)) { + if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) { + const ProgramState *state = N->getState().get(); + if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { + if (report.isInteresting(R)) + event->setPrunable(false); + } + } + } + + return event; +} + +std::shared_ptr<PathDiagnosticPiece> +ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR, + const bool tookTrue, BugReporterContext &BRC, + BugReport &report, const ExplodedNode *N) { + const auto *VD = dyn_cast<VarDecl>(DR->getDecl()); + if (!VD) + return nullptr; + + SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + + Out << "Assuming '" << VD->getDeclName() << "' is "; + + QualType VDTy = VD->getType(); + + if (VDTy->isPointerType()) + Out << (tookTrue ? "non-null" : "null"); + else if (VDTy->isObjCObjectPointerType()) + Out << (tookTrue ? "non-nil" : "nil"); + else if (VDTy->isScalarType()) + Out << (tookTrue ? "not equal to 0" : "0"); + else + return nullptr; + + const LocationContext *LCtx = N->getLocationContext(); + PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); + auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str()); + + const ProgramState *state = N->getState().get(); + if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) { + if (report.isInteresting(R)) + event->setPrunable(false); + else { + SVal V = state->getSVal(R); + if (report.isInteresting(V)) + event->setPrunable(false); + } + } + return std::move(event); +} + +const char *const ConditionBRVisitor::GenericTrueMessage = + "Assuming the condition is true"; +const char *const ConditionBRVisitor::GenericFalseMessage = + "Assuming the condition is false"; + +bool ConditionBRVisitor::isPieceMessageGeneric( + const PathDiagnosticPiece *Piece) { + return Piece->getString() == GenericTrueMessage || + Piece->getString() == GenericFalseMessage; +} + +void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor( + BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR) { + // Here we suppress false positives coming from system headers. This list is + // based on known issues. + AnalyzerOptions &Options = BRC.getAnalyzerOptions(); + const Decl *D = N->getLocationContext()->getDecl(); + + if (AnalysisDeclContext::isInStdNamespace(D)) { + // Skip reports within the 'std' namespace. Although these can sometimes be + // the user's fault, we currently don't report them very well, and + // Note that this will not help for any other data structure libraries, like + // TR1, Boost, or llvm/ADT. + if (Options.ShouldSuppressFromCXXStandardLibrary) { + BR.markInvalid(getTag(), nullptr); + return; + } else { + // If the complete 'std' suppression is not enabled, suppress reports + // from the 'std' namespace that are known to produce false positives. + + // The analyzer issues a false use-after-free when std::list::pop_front + // or std::list::pop_back are called multiple times because we cannot + // reason about the internal invariants of the data structure. + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { + const CXXRecordDecl *CD = MD->getParent(); + if (CD->getName() == "list") { + BR.markInvalid(getTag(), nullptr); + return; + } + } + + // The analyzer issues a false positive when the constructor of + // std::__independent_bits_engine from algorithms is used. + if (const auto *MD = dyn_cast<CXXConstructorDecl>(D)) { + const CXXRecordDecl *CD = MD->getParent(); + if (CD->getName() == "__independent_bits_engine") { + BR.markInvalid(getTag(), nullptr); + return; + } + } + + for (const LocationContext *LCtx = N->getLocationContext(); LCtx; + LCtx = LCtx->getParent()) { + const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl()); + if (!MD) + continue; + + const CXXRecordDecl *CD = MD->getParent(); + // The analyzer issues a false positive on + // std::basic_string<uint8_t> v; v.push_back(1); + // and + // std::u16string s; s += u'a'; + // because we cannot reason about the internal invariants of the + // data structure. + if (CD->getName() == "basic_string") { + BR.markInvalid(getTag(), nullptr); + return; + } + + // The analyzer issues a false positive on + // std::shared_ptr<int> p(new int(1)); p = nullptr; + // because it does not reason properly about temporary destructors. + if (CD->getName() == "shared_ptr") { + BR.markInvalid(getTag(), nullptr); + return; + } + } + } + } + + // Skip reports within the sys/queue.h macros as we do not have the ability to + // reason about data structure shapes. + SourceManager &SM = BRC.getSourceManager(); + FullSourceLoc Loc = BR.getLocation(SM).asLocation(); + while (Loc.isMacroID()) { + Loc = Loc.getSpellingLoc(); + if (SM.getFilename(Loc).endswith("sys/queue.h")) { + BR.markInvalid(getTag(), nullptr); + return; + } + } +} + +std::shared_ptr<PathDiagnosticPiece> +UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &BRC, BugReport &BR) { + ProgramStateRef State = N->getState(); + ProgramPoint ProgLoc = N->getLocation(); + + // We are only interested in visiting CallEnter nodes. + Optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>(); + if (!CEnter) + return nullptr; + + // Check if one of the arguments is the region the visitor is tracking. + CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager(); + CallEventRef<> Call = CEMgr.getCaller(CEnter->getCalleeContext(), State); + unsigned Idx = 0; + ArrayRef<ParmVarDecl *> parms = Call->parameters(); + + for (const auto ParamDecl : parms) { + const MemRegion *ArgReg = Call->getArgSVal(Idx).getAsRegion(); + ++Idx; + + // Are we tracking the argument or its subregion? + if ( !ArgReg || !R->isSubRegionOf(ArgReg->StripCasts())) + continue; + + // Check the function parameter type. + assert(ParamDecl && "Formal parameter has no decl?"); + QualType T = ParamDecl->getType(); + + if (!(T->isAnyPointerType() || T->isReferenceType())) { + // Function can only change the value passed in by address. + continue; + } + + // If it is a const pointer value, the function does not intend to + // change the value. + if (T->getPointeeType().isConstQualified()) + continue; + + // Mark the call site (LocationContext) as interesting if the value of the + // argument is undefined or '0'/'NULL'. + SVal BoundVal = State->getSVal(R); + if (BoundVal.isUndef() || BoundVal.isZeroConstant()) { + BR.markInteresting(CEnter->getCalleeContext()); + return nullptr; + } + } + return nullptr; +} + +std::shared_ptr<PathDiagnosticPiece> +CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ, + BugReporterContext &BRC, BugReport &) { + if (Satisfied) + return nullptr; + + const auto Edge = Succ->getLocation().getAs<BlockEdge>(); + if (!Edge.hasValue()) + return nullptr; + + auto Tag = Edge->getTag(); + if (!Tag) + return nullptr; + + if (Tag->getTagDescription() != "cplusplus.SelfAssignment") + return nullptr; + + Satisfied = true; + + const auto *Met = + dyn_cast<CXXMethodDecl>(Succ->getCodeDecl().getAsFunction()); + assert(Met && "Not a C++ method."); + assert((Met->isCopyAssignmentOperator() || Met->isMoveAssignmentOperator()) && + "Not a copy/move assignment operator."); + + const auto *LCtx = Edge->getLocationContext(); + + const auto &State = Succ->getState(); + auto &SVB = State->getStateManager().getSValBuilder(); + + const auto Param = + State->getSVal(State->getRegion(Met->getParamDecl(0), LCtx)); + const auto This = + State->getSVal(SVB.getCXXThis(Met, LCtx->getStackFrame())); + + auto L = PathDiagnosticLocation::create(Met, BRC.getSourceManager()); + + if (!L.isValid() || !L.asLocation().isValid()) + return nullptr; + + SmallString<256> Buf; + llvm::raw_svector_ostream Out(Buf); + + Out << "Assuming " << Met->getParamDecl(0)->getName() << + ((Param == This) ? " == " : " != ") << "*this"; + + auto Piece = std::make_shared<PathDiagnosticEventPiece>(L, Out.str()); + Piece->addRange(Met->getSourceRange()); + + 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()) {} + +void FalsePositiveRefutationBRVisitor::finalizeVisitor( + BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) { + // Collect new constraints + VisitNode(EndPathNode, BRC, BR); + + // Create a refutation manager + SMTSolverRef RefutationSolver = CreateZ3Solver(); + ASTContext &Ctx = BRC.getASTContext(); + + // Add constraints to the solver + for (const auto &I : Constraints) { + const SymbolRef Sym = I.first; + auto RangeIt = I.second.begin(); + + SMTExprRef Constraints = SMTConv::getRangeExpr( + RefutationSolver, Ctx, Sym, RangeIt->From(), RangeIt->To(), + /*InRange=*/true); + while ((++RangeIt) != I.second.end()) { + Constraints = RefutationSolver->mkOr( + Constraints, SMTConv::getRangeExpr(RefutationSolver, Ctx, Sym, + RangeIt->From(), RangeIt->To(), + /*InRange=*/true)); + } + + RefutationSolver->addConstraint(Constraints); + } + + // And check for satisfiability + Optional<bool> isSat = RefutationSolver->check(); + if (!isSat.hasValue()) + return; + + if (!isSat.getValue()) + BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext()); +} + +std::shared_ptr<PathDiagnosticPiece> +FalsePositiveRefutationBRVisitor::VisitNode(const ExplodedNode *N, + BugReporterContext &, + BugReport &) { + // Collect new constraints + const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>(); + ConstraintRangeTy::Factory &CF = + N->getState()->get_context<ConstraintRange>(); + + // Add constraints if we don't have them yet + for (auto const &C : NewCs) { + const SymbolRef &Sym = C.first; + if (!Constraints.contains(Sym)) { + Constraints = CF.add(Constraints, Sym, C.second); + } + } + + return nullptr; +} + +void FalsePositiveRefutationBRVisitor::Profile( + llvm::FoldingSetNodeID &ID) const { + static int Tag = 0; + ID.AddPointer(&Tag); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CallEvent.cpp new file mode 100644 index 000000000000..0e7f31502e81 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -0,0 +1,1424 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +/// \file This file defines CallEvent and its subclasses, which represent path- +/// sensitive instances of different kinds of function and method calls +/// (C, C++, and Objective-C). +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/CrossTU/CrossTranslationUnit.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerIntPair.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <utility> + +#define DEBUG_TYPE "static-analyzer-call-event" + +using namespace clang; +using namespace ento; + +QualType CallEvent::getResultType() const { + ASTContext &Ctx = getState()->getStateManager().getContext(); + const Expr *E = getOriginExpr(); + if (!E) + return Ctx.VoidTy; + assert(E); + + QualType ResultTy = E->getType(); + + // A function that returns a reference to 'int' will have a result type + // of simply 'int'. Check the origin expr's value kind to recover the + // proper type. + switch (E->getValueKind()) { + case VK_LValue: + ResultTy = Ctx.getLValueReferenceType(ResultTy); + break; + case VK_XValue: + ResultTy = Ctx.getRValueReferenceType(ResultTy); + break; + case VK_RValue: + // No adjustment is necessary. + break; + } + + return ResultTy; +} + +static bool isCallback(QualType T) { + // If a parameter is a block or a callback, assume it can modify pointer. + if (T->isBlockPointerType() || + T->isFunctionPointerType() || + T->isObjCSelType()) + return true; + + // Check if a callback is passed inside a struct (for both, struct passed by + // reference and by value). Dig just one level into the struct for now. + + if (T->isAnyPointerType() || T->isReferenceType()) + T = T->getPointeeType(); + + if (const RecordType *RT = T->getAsStructureType()) { + const RecordDecl *RD = RT->getDecl(); + for (const auto *I : RD->fields()) { + QualType FieldT = I->getType(); + if (FieldT->isBlockPointerType() || FieldT->isFunctionPointerType()) + return true; + } + } + return false; +} + +static bool isVoidPointerToNonConst(QualType T) { + if (const auto *PT = T->getAs<PointerType>()) { + QualType PointeeTy = PT->getPointeeType(); + if (PointeeTy.isConstQualified()) + return false; + return PointeeTy->isVoidType(); + } else + return false; +} + +bool CallEvent::hasNonNullArgumentsWithType(bool (*Condition)(QualType)) const { + unsigned NumOfArgs = getNumArgs(); + + // If calling using a function pointer, assume the function does not + // satisfy the callback. + // TODO: We could check the types of the arguments here. + if (!getDecl()) + return false; + + unsigned Idx = 0; + for (CallEvent::param_type_iterator I = param_type_begin(), + E = param_type_end(); + I != E && Idx < NumOfArgs; ++I, ++Idx) { + // If the parameter is 0, it's harmless. + if (getArgSVal(Idx).isZeroConstant()) + continue; + + if (Condition(*I)) + return true; + } + return false; +} + +bool CallEvent::hasNonZeroCallbackArg() const { + return hasNonNullArgumentsWithType(isCallback); +} + +bool CallEvent::hasVoidPointerToNonConstArg() const { + return hasNonNullArgumentsWithType(isVoidPointerToNonConst); +} + +bool CallEvent::isGlobalCFunction(StringRef FunctionName) const { + const auto *FD = dyn_cast_or_null<FunctionDecl>(getDecl()); + if (!FD) + return false; + + return CheckerContext::isCLibraryFunction(FD, FunctionName); +} + +AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const { + const Decl *D = getDecl(); + if (!D) + return nullptr; + + // TODO: For now we skip functions without definitions, even if we have + // our own getDecl(), because it's hard to find out which re-declaration + // is going to be used, and usually clients don't really care about this + // situation because there's a loss of precision anyway because we cannot + // inline the call. + RuntimeDefinition RD = getRuntimeDefinition(); + if (!RD.getDecl()) + return nullptr; + + AnalysisDeclContext *ADC = + LCtx->getAnalysisDeclContext()->getManager()->getContext(D); + + // TODO: For now we skip virtual functions, because this also rises + // the problem of which decl to use, but now it's across different classes. + if (RD.mayHaveOtherDefinitions() || RD.getDecl() != ADC->getDecl()) + return nullptr; + + return ADC; +} + +const StackFrameContext *CallEvent::getCalleeStackFrame() const { + AnalysisDeclContext *ADC = getCalleeAnalysisDeclContext(); + if (!ADC) + return nullptr; + + const Expr *E = getOriginExpr(); + if (!E) + return nullptr; + + // Recover CFG block via reverse lookup. + // TODO: If we were to keep CFG element information as part of the CallEvent + // instead of doing this reverse lookup, we would be able to build the stack + // frame for non-expression-based calls, and also we wouldn't need the reverse + // lookup. + CFGStmtMap *Map = LCtx->getAnalysisDeclContext()->getCFGStmtMap(); + const CFGBlock *B = Map->getBlock(E); + assert(B); + + // Also recover CFG index by scanning the CFG block. + unsigned Idx = 0, Sz = B->size(); + for (; Idx < Sz; ++Idx) + if (auto StmtElem = (*B)[Idx].getAs<CFGStmt>()) + if (StmtElem->getStmt() == E) + break; + assert(Idx < Sz); + + return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, Idx); +} + +const VarRegion *CallEvent::getParameterLocation(unsigned Index) const { + const StackFrameContext *SFC = getCalleeStackFrame(); + // We cannot construct a VarRegion without a stack frame. + if (!SFC) + return nullptr; + + // Retrieve parameters of the definition, which are different from + // CallEvent's parameters() because getDecl() isn't necessarily + // the definition. SFC contains the definition that would be used + // during analysis. + const Decl *D = SFC->getDecl(); + + // TODO: Refactor into a virtual method of CallEvent, like parameters(). + const ParmVarDecl *PVD = nullptr; + if (const auto *FD = dyn_cast<FunctionDecl>(D)) + PVD = FD->parameters()[Index]; + else if (const auto *BD = dyn_cast<BlockDecl>(D)) + PVD = BD->parameters()[Index]; + else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) + PVD = MD->parameters()[Index]; + else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D)) + PVD = CD->parameters()[Index]; + assert(PVD && "Unexpected Decl kind!"); + + const VarRegion *VR = + State->getStateManager().getRegionManager().getVarRegion(PVD, SFC); + + // This sanity check would fail if our parameter declaration doesn't + // correspond to the stack frame's function declaration. + assert(VR->getStackFrame() == SFC); + + return VR; +} + +/// Returns true if a type is a pointer-to-const or reference-to-const +/// with no further indirection. +static bool isPointerToConst(QualType Ty) { + QualType PointeeTy = Ty->getPointeeType(); + if (PointeeTy == QualType()) + return false; + if (!PointeeTy.isConstQualified()) + return false; + if (PointeeTy->isAnyPointerType()) + return false; + return true; +} + +// Try to retrieve the function declaration and find the function parameter +// types which are pointers/references to a non-pointer const. +// We will not invalidate the corresponding argument regions. +static void findPtrToConstParams(llvm::SmallSet<unsigned, 4> &PreserveArgs, + const CallEvent &Call) { + unsigned Idx = 0; + for (CallEvent::param_type_iterator I = Call.param_type_begin(), + E = Call.param_type_end(); + I != E; ++I, ++Idx) { + if (isPointerToConst(*I)) + PreserveArgs.insert(Idx); + } +} + +ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount, + ProgramStateRef Orig) const { + ProgramStateRef Result = (Orig ? Orig : getState()); + + // Don't invalidate anything if the callee is marked pure/const. + if (const Decl *callee = getDecl()) + if (callee->hasAttr<PureAttr>() || callee->hasAttr<ConstAttr>()) + return Result; + + SmallVector<SVal, 8> ValuesToInvalidate; + RegionAndSymbolInvalidationTraits ETraits; + + getExtraInvalidatedValues(ValuesToInvalidate, &ETraits); + + // Indexes of arguments whose values will be preserved by the call. + llvm::SmallSet<unsigned, 4> PreserveArgs; + if (!argumentsMayEscape()) + findPtrToConstParams(PreserveArgs, *this); + + for (unsigned Idx = 0, Count = getNumArgs(); Idx != Count; ++Idx) { + // Mark this region for invalidation. We batch invalidate regions + // below for efficiency. + if (PreserveArgs.count(Idx)) + if (const MemRegion *MR = getArgSVal(Idx).getAsRegion()) + ETraits.setTrait(MR->getBaseRegion(), + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + // TODO: Factor this out + handle the lower level const pointers. + + ValuesToInvalidate.push_back(getArgSVal(Idx)); + + // If a function accepts an object by argument (which would of course be a + // temporary that isn't lifetime-extended), invalidate the object itself, + // not only other objects reachable from it. This is necessary because the + // destructor has access to the temporary object after the call. + // TODO: Support placement arguments once we start + // constructing them directly. + // TODO: This is unnecessary when there's no destructor, but that's + // currently hard to figure out. + if (getKind() != CE_CXXAllocator) + if (isArgumentConstructedDirectly(Idx)) + if (auto AdjIdx = getAdjustedParameterIndex(Idx)) + if (const VarRegion *VR = getParameterLocation(*AdjIdx)) + ValuesToInvalidate.push_back(loc::MemRegionVal(VR)); + } + + // Invalidate designated regions using the batch invalidation API. + // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate + // global variables. + return Result->invalidateRegions(ValuesToInvalidate, getOriginExpr(), + BlockCount, getLocationContext(), + /*CausedByPointerEscape*/ true, + /*Symbols=*/nullptr, this, &ETraits); +} + +ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit, + const ProgramPointTag *Tag) const { + if (const Expr *E = getOriginExpr()) { + if (IsPreVisit) + return PreStmt(E, getLocationContext(), Tag); + return PostStmt(E, getLocationContext(), Tag); + } + + const Decl *D = getDecl(); + assert(D && "Cannot get a program point without a statement or decl"); + + SourceLocation Loc = getSourceRange().getBegin(); + if (IsPreVisit) + return PreImplicitCall(D, Loc, getLocationContext(), Tag); + return PostImplicitCall(D, Loc, getLocationContext(), Tag); +} + +bool CallEvent::isCalled(const CallDescription &CD) const { + // FIXME: Add ObjC Message support. + if (getKind() == CE_ObjCMessage) + return false; + if (!CD.IsLookupDone) { + CD.IsLookupDone = true; + CD.II = &getState()->getStateManager().getContext().Idents.get( + CD.getFunctionName()); + } + const IdentifierInfo *II = getCalleeIdentifier(); + if (!II || II != CD.II) + return false; + + const Decl *D = getDecl(); + // If CallDescription provides prefix names, use them to improve matching + // accuracy. + if (CD.QualifiedName.size() > 1 && D) { + const DeclContext *Ctx = D->getDeclContext(); + // See if we'll be able to match them all. + size_t NumUnmatched = CD.QualifiedName.size() - 1; + for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) { + if (NumUnmatched == 0) + break; + + if (const auto *ND = dyn_cast<NamespaceDecl>(Ctx)) { + if (ND->getName() == CD.QualifiedName[NumUnmatched - 1]) + --NumUnmatched; + continue; + } + + if (const auto *RD = dyn_cast<RecordDecl>(Ctx)) { + if (RD->getName() == CD.QualifiedName[NumUnmatched - 1]) + --NumUnmatched; + continue; + } + } + + if (NumUnmatched > 0) + return false; + } + + return (CD.RequiredArgs == CallDescription::NoArgRequirement || + CD.RequiredArgs == getNumArgs()); +} + +SVal CallEvent::getArgSVal(unsigned Index) const { + const Expr *ArgE = getArgExpr(Index); + if (!ArgE) + return UnknownVal(); + return getSVal(ArgE); +} + +SourceRange CallEvent::getArgSourceRange(unsigned Index) const { + const Expr *ArgE = getArgExpr(Index); + if (!ArgE) + return {}; + return ArgE->getSourceRange(); +} + +SVal CallEvent::getReturnValue() const { + const Expr *E = getOriginExpr(); + if (!E) + return UndefinedVal(); + return getSVal(E); +} + +LLVM_DUMP_METHOD void CallEvent::dump() const { dump(llvm::errs()); } + +void CallEvent::dump(raw_ostream &Out) const { + ASTContext &Ctx = getState()->getStateManager().getContext(); + if (const Expr *E = getOriginExpr()) { + E->printPretty(Out, nullptr, Ctx.getPrintingPolicy()); + Out << "\n"; + return; + } + + if (const Decl *D = getDecl()) { + Out << "Call to "; + D->print(Out, Ctx.getPrintingPolicy()); + return; + } + + // FIXME: a string representation of the kind would be nice. + Out << "Unknown call (type " << getKind() << ")"; +} + +bool CallEvent::isCallStmt(const Stmt *S) { + return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S) + || isa<CXXConstructExpr>(S) + || isa<CXXNewExpr>(S); +} + +QualType CallEvent::getDeclaredResultType(const Decl *D) { + assert(D); + if (const auto *FD = dyn_cast<FunctionDecl>(D)) + return FD->getReturnType(); + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) + return MD->getReturnType(); + if (const auto *BD = dyn_cast<BlockDecl>(D)) { + // Blocks are difficult because the return type may not be stored in the + // BlockDecl itself. The AST should probably be enhanced, but for now we + // just do what we can. + // If the block is declared without an explicit argument list, the + // signature-as-written just includes the return type, not the entire + // function type. + // FIXME: All blocks should have signatures-as-written, even if the return + // type is inferred. (That's signified with a dependent result type.) + if (const TypeSourceInfo *TSI = BD->getSignatureAsWritten()) { + QualType Ty = TSI->getType(); + if (const FunctionType *FT = Ty->getAs<FunctionType>()) + Ty = FT->getReturnType(); + if (!Ty->isDependentType()) + return Ty; + } + + return {}; + } + + llvm_unreachable("unknown callable kind"); +} + +bool CallEvent::isVariadic(const Decl *D) { + assert(D); + + if (const auto *FD = dyn_cast<FunctionDecl>(D)) + return FD->isVariadic(); + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) + return MD->isVariadic(); + if (const auto *BD = dyn_cast<BlockDecl>(D)) + return BD->isVariadic(); + + llvm_unreachable("unknown callable kind"); +} + +static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx, + CallEvent::BindingsTy &Bindings, + SValBuilder &SVB, + const CallEvent &Call, + ArrayRef<ParmVarDecl*> parameters) { + MemRegionManager &MRMgr = SVB.getRegionManager(); + + // If the function has fewer parameters than the call has arguments, we simply + // do not bind any values to them. + unsigned NumArgs = Call.getNumArgs(); + unsigned Idx = 0; + ArrayRef<ParmVarDecl*>::iterator I = parameters.begin(), E = parameters.end(); + for (; I != E && Idx < NumArgs; ++I, ++Idx) { + const ParmVarDecl *ParamDecl = *I; + assert(ParamDecl && "Formal parameter has no decl?"); + + // TODO: Support allocator calls. + if (Call.getKind() != CE_CXXAllocator) + if (Call.isArgumentConstructedDirectly(Idx)) + continue; + + // TODO: Allocators should receive the correct size and possibly alignment, + // determined in compile-time but not represented as arg-expressions, + // which makes getArgSVal() fail and return UnknownVal. + SVal ArgVal = Call.getArgSVal(Idx); + if (!ArgVal.isUnknown()) { + Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx)); + Bindings.push_back(std::make_pair(ParamLoc, ArgVal)); + } + } + + // FIXME: Variadic arguments are not handled at all right now. +} + +ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const { + const FunctionDecl *D = getDecl(); + if (!D) + return None; + return D->parameters(); +} + +RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const { + const FunctionDecl *FD = getDecl(); + if (!FD) + return {}; + + // Note that the AnalysisDeclContext will have the FunctionDecl with + // the definition (if one exists). + AnalysisDeclContext *AD = + getLocationContext()->getAnalysisDeclContext()-> + getManager()->getContext(FD); + bool IsAutosynthesized; + Stmt* Body = AD->getBody(IsAutosynthesized); + LLVM_DEBUG({ + if (IsAutosynthesized) + llvm::dbgs() << "Using autosynthesized body for " << FD->getName() + << "\n"; + }); + if (Body) { + const Decl* Decl = AD->getDecl(); + return RuntimeDefinition(Decl); + } + + SubEngine &Engine = getState()->getStateManager().getOwningEngine(); + AnalyzerOptions &Opts = Engine.getAnalysisManager().options; + + // Try to get CTU definition only if CTUDir is provided. + if (!Opts.IsNaiveCTUEnabled) + return {}; + + cross_tu::CrossTranslationUnitContext &CTUCtx = + *Engine.getCrossTranslationUnitContext(); + llvm::Expected<const FunctionDecl *> CTUDeclOrError = + CTUCtx.getCrossTUDefinition(FD, Opts.CTUDir, Opts.CTUIndexName, + Opts.DisplayCTUProgress); + + if (!CTUDeclOrError) { + handleAllErrors(CTUDeclOrError.takeError(), + [&](const cross_tu::IndexError &IE) { + CTUCtx.emitCrossTUDiagnostics(IE); + }); + return {}; + } + + return RuntimeDefinition(*CTUDeclOrError); +} + +void AnyFunctionCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + const auto *D = cast<FunctionDecl>(CalleeCtx->getDecl()); + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, + D->parameters()); +} + +bool AnyFunctionCall::argumentsMayEscape() const { + if (CallEvent::argumentsMayEscape() || hasVoidPointerToNonConstArg()) + return true; + + const FunctionDecl *D = getDecl(); + if (!D) + return true; + + const IdentifierInfo *II = D->getIdentifier(); + if (!II) + return false; + + // This set of "escaping" APIs is + + // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a + // value into thread local storage. The value can later be retrieved with + // 'void *ptheread_getspecific(pthread_key)'. So even thought the + // parameter is 'const void *', the region escapes through the call. + if (II->isStr("pthread_setspecific")) + return true; + + // - xpc_connection_set_context stores a value which can be retrieved later + // with xpc_connection_get_context. + if (II->isStr("xpc_connection_set_context")) + return true; + + // - funopen - sets a buffer for future IO calls. + if (II->isStr("funopen")) + return true; + + // - __cxa_demangle - can reallocate memory and can return the pointer to + // the input buffer. + if (II->isStr("__cxa_demangle")) + return true; + + StringRef FName = II->getName(); + + // - CoreFoundation functions that end with "NoCopy" can free a passed-in + // buffer even if it is const. + if (FName.endswith("NoCopy")) + return true; + + // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can + // be deallocated by NSMapRemove. + if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos)) + return true; + + // - Many CF containers allow objects to escape through custom + // allocators/deallocators upon container construction. (PR12101) + if (FName.startswith("CF") || FName.startswith("CG")) { + return StrInStrNoCase(FName, "InsertValue") != StringRef::npos || + StrInStrNoCase(FName, "AddValue") != StringRef::npos || + StrInStrNoCase(FName, "SetValue") != StringRef::npos || + StrInStrNoCase(FName, "WithData") != StringRef::npos || + StrInStrNoCase(FName, "AppendValue") != StringRef::npos || + StrInStrNoCase(FName, "SetAttribute") != StringRef::npos; + } + + return false; +} + +const FunctionDecl *SimpleFunctionCall::getDecl() const { + const FunctionDecl *D = getOriginExpr()->getDirectCallee(); + if (D) + return D; + + return getSVal(getOriginExpr()->getCallee()).getAsFunctionDecl(); +} + +const FunctionDecl *CXXInstanceCall::getDecl() const { + const auto *CE = cast_or_null<CallExpr>(getOriginExpr()); + if (!CE) + return AnyFunctionCall::getDecl(); + + const FunctionDecl *D = CE->getDirectCallee(); + if (D) + return D; + + return getSVal(CE->getCallee()).getAsFunctionDecl(); +} + +void CXXInstanceCall::getExtraInvalidatedValues( + ValueList &Values, RegionAndSymbolInvalidationTraits *ETraits) const { + SVal ThisVal = getCXXThisVal(); + Values.push_back(ThisVal); + + // Don't invalidate if the method is const and there are no mutable fields. + if (const auto *D = cast_or_null<CXXMethodDecl>(getDecl())) { + if (!D->isConst()) + return; + // Get the record decl for the class of 'This'. D->getParent() may return a + // base class decl, rather than the class of the instance which needs to be + // checked for mutable fields. + // TODO: We might as well look at the dynamic type of the object. + const Expr *Ex = getCXXThisExpr()->ignoreParenBaseCasts(); + QualType T = Ex->getType(); + if (T->isPointerType()) // Arrow or implicit-this syntax? + T = T->getPointeeType(); + const CXXRecordDecl *ParentRecord = T->getAsCXXRecordDecl(); + assert(ParentRecord); + if (ParentRecord->hasMutableFields()) + return; + // Preserve CXXThis. + const MemRegion *ThisRegion = ThisVal.getAsRegion(); + if (!ThisRegion) + return; + + ETraits->setTrait(ThisRegion->getBaseRegion(), + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + } +} + +SVal CXXInstanceCall::getCXXThisVal() const { + const Expr *Base = getCXXThisExpr(); + // FIXME: This doesn't handle an overloaded ->* operator. + if (!Base) + return UnknownVal(); + + SVal ThisVal = getSVal(Base); + assert(ThisVal.isUnknownOrUndef() || ThisVal.getAs<Loc>()); + return ThisVal; +} + +RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const { + // Do we have a decl at all? + const Decl *D = getDecl(); + if (!D) + return {}; + + // If the method is non-virtual, we know we can inline it. + const auto *MD = cast<CXXMethodDecl>(D); + if (!MD->isVirtual()) + return AnyFunctionCall::getRuntimeDefinition(); + + // Do we know the implicit 'this' object being called? + const MemRegion *R = getCXXThisVal().getAsRegion(); + if (!R) + return {}; + + // Do we know anything about the type of 'this'? + DynamicTypeInfo DynType = getDynamicTypeInfo(getState(), R); + if (!DynType.isValid()) + return {}; + + // Is the type a C++ class? (This is mostly a defensive check.) + QualType RegionType = DynType.getType()->getPointeeType(); + assert(!RegionType.isNull() && "DynamicTypeInfo should always be a pointer."); + + const CXXRecordDecl *RD = RegionType->getAsCXXRecordDecl(); + if (!RD || !RD->hasDefinition()) + return {}; + + // Find the decl for this method in that class. + const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD, true); + if (!Result) { + // We might not even get the original statically-resolved method due to + // some particularly nasty casting (e.g. casts to sister classes). + // However, we should at least be able to search up and down our own class + // hierarchy, and some real bugs have been caught by checking this. + assert(!RD->isDerivedFrom(MD->getParent()) && "Couldn't find known method"); + + // FIXME: This is checking that our DynamicTypeInfo is at least as good as + // the static type. However, because we currently don't update + // DynamicTypeInfo when an object is cast, we can't actually be sure the + // DynamicTypeInfo is up to date. This assert should be re-enabled once + // this is fixed. <rdar://problem/12287087> + //assert(!MD->getParent()->isDerivedFrom(RD) && "Bad DynamicTypeInfo"); + + return {}; + } + + // Does the decl that we found have an implementation? + const FunctionDecl *Definition; + if (!Result->hasBody(Definition)) + return {}; + + // We found a definition. If we're not sure that this devirtualization is + // actually what will happen at runtime, make sure to provide the region so + // that ExprEngine can decide what to do with it. + if (DynType.canBeASubClass()) + return RuntimeDefinition(Definition, R->StripCasts()); + return RuntimeDefinition(Definition, /*DispatchRegion=*/nullptr); +} + +void CXXInstanceCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings); + + // Handle the binding of 'this' in the new stack frame. + SVal ThisVal = getCXXThisVal(); + if (!ThisVal.isUnknown()) { + ProgramStateManager &StateMgr = getState()->getStateManager(); + SValBuilder &SVB = StateMgr.getSValBuilder(); + + const auto *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx); + + // If we devirtualized to a different member function, we need to make sure + // we have the proper layering of CXXBaseObjectRegions. + if (MD->getCanonicalDecl() != getDecl()->getCanonicalDecl()) { + ASTContext &Ctx = SVB.getContext(); + const CXXRecordDecl *Class = MD->getParent(); + QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class)); + + // FIXME: CallEvent maybe shouldn't be directly accessing StoreManager. + bool Failed; + ThisVal = StateMgr.getStoreManager().attemptDownCast(ThisVal, Ty, Failed); + if (Failed) { + // We might have suffered some sort of placement new earlier, so + // we're constructing in a completely unexpected storage. + // Fall back to a generic pointer cast for this-value. + const CXXMethodDecl *StaticMD = cast<CXXMethodDecl>(getDecl()); + const CXXRecordDecl *StaticClass = StaticMD->getParent(); + QualType StaticTy = Ctx.getPointerType(Ctx.getRecordType(StaticClass)); + ThisVal = SVB.evalCast(ThisVal, Ty, StaticTy); + } + } + + if (!ThisVal.isUnknown()) + Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); + } +} + +const Expr *CXXMemberCall::getCXXThisExpr() const { + return getOriginExpr()->getImplicitObjectArgument(); +} + +RuntimeDefinition CXXMemberCall::getRuntimeDefinition() const { + // C++11 [expr.call]p1: ...If the selected function is non-virtual, or if the + // id-expression in the class member access expression is a qualified-id, + // that function is called. Otherwise, its final overrider in the dynamic type + // of the object expression is called. + if (const auto *ME = dyn_cast<MemberExpr>(getOriginExpr()->getCallee())) + if (ME->hasQualifier()) + return AnyFunctionCall::getRuntimeDefinition(); + + return CXXInstanceCall::getRuntimeDefinition(); +} + +const Expr *CXXMemberOperatorCall::getCXXThisExpr() const { + return getOriginExpr()->getArg(0); +} + +const BlockDataRegion *BlockCall::getBlockRegion() const { + const Expr *Callee = getOriginExpr()->getCallee(); + const MemRegion *DataReg = getSVal(Callee).getAsRegion(); + + return dyn_cast_or_null<BlockDataRegion>(DataReg); +} + +ArrayRef<ParmVarDecl*> BlockCall::parameters() const { + const BlockDecl *D = getDecl(); + if (!D) + return None; + return D->parameters(); +} + +void BlockCall::getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const { + // FIXME: This also needs to invalidate captured globals. + if (const MemRegion *R = getBlockRegion()) + Values.push_back(loc::MemRegionVal(R)); +} + +void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + ArrayRef<ParmVarDecl*> Params; + if (isConversionFromLambda()) { + auto *LambdaOperatorDecl = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + Params = LambdaOperatorDecl->parameters(); + + // For blocks converted from a C++ lambda, the callee declaration is the + // operator() method on the lambda so we bind "this" to + // the lambda captured by the block. + const VarRegion *CapturedLambdaRegion = getRegionStoringCapturedLambda(); + SVal ThisVal = loc::MemRegionVal(CapturedLambdaRegion); + Loc ThisLoc = SVB.getCXXThis(LambdaOperatorDecl, CalleeCtx); + Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); + } else { + Params = cast<BlockDecl>(CalleeCtx->getDecl())->parameters(); + } + + addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, + Params); +} + +SVal CXXConstructorCall::getCXXThisVal() const { + if (Data) + return loc::MemRegionVal(static_cast<const MemRegion *>(Data)); + return UnknownVal(); +} + +void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values, + RegionAndSymbolInvalidationTraits *ETraits) const { + if (Data) { + loc::MemRegionVal MV(static_cast<const MemRegion *>(Data)); + if (SymbolRef Sym = MV.getAsSymbol(true)) + ETraits->setTrait(Sym, + RegionAndSymbolInvalidationTraits::TK_SuppressEscape); + Values.push_back(MV); + } +} + +void CXXConstructorCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings); + + SVal ThisVal = getCXXThisVal(); + if (!ThisVal.isUnknown()) { + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + const auto *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx); + Bindings.push_back(std::make_pair(ThisLoc, ThisVal)); + } +} + +SVal CXXDestructorCall::getCXXThisVal() const { + if (Data) + return loc::MemRegionVal(DtorDataTy::getFromOpaqueValue(Data).getPointer()); + return UnknownVal(); +} + +RuntimeDefinition CXXDestructorCall::getRuntimeDefinition() const { + // Base destructors are always called non-virtually. + // Skip CXXInstanceCall's devirtualization logic in this case. + if (isBaseDestructor()) + return AnyFunctionCall::getRuntimeDefinition(); + + return CXXInstanceCall::getRuntimeDefinition(); +} + +ArrayRef<ParmVarDecl*> ObjCMethodCall::parameters() const { + const ObjCMethodDecl *D = getDecl(); + if (!D) + return None; + return D->parameters(); +} + +void ObjCMethodCall::getExtraInvalidatedValues( + ValueList &Values, RegionAndSymbolInvalidationTraits *ETraits) const { + + // If the method call is a setter for property known to be backed by + // an instance variable, don't invalidate the entire receiver, just + // the storage for that instance variable. + if (const ObjCPropertyDecl *PropDecl = getAccessedProperty()) { + if (const ObjCIvarDecl *PropIvar = PropDecl->getPropertyIvarDecl()) { + SVal IvarLVal = getState()->getLValue(PropIvar, getReceiverSVal()); + if (const MemRegion *IvarRegion = IvarLVal.getAsRegion()) { + ETraits->setTrait( + IvarRegion, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + ETraits->setTrait( + IvarRegion, + RegionAndSymbolInvalidationTraits::TK_SuppressEscape); + Values.push_back(IvarLVal); + } + return; + } + } + + Values.push_back(getReceiverSVal()); +} + +SVal ObjCMethodCall::getSelfSVal() const { + const LocationContext *LCtx = getLocationContext(); + const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl(); + if (!SelfDecl) + return SVal(); + return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx)); +} + +SVal ObjCMethodCall::getReceiverSVal() const { + // FIXME: Is this the best way to handle class receivers? + if (!isInstanceMessage()) + return UnknownVal(); + + if (const Expr *RecE = getOriginExpr()->getInstanceReceiver()) + return getSVal(RecE); + + // An instance message with no expression means we are sending to super. + // In this case the object reference is the same as 'self'. + assert(getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperInstance); + SVal SelfVal = getSelfSVal(); + assert(SelfVal.isValid() && "Calling super but not in ObjC method"); + return SelfVal; +} + +bool ObjCMethodCall::isReceiverSelfOrSuper() const { + if (getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperInstance || + getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperClass) + return true; + + if (!isInstanceMessage()) + return false; + + SVal RecVal = getSVal(getOriginExpr()->getInstanceReceiver()); + + return (RecVal == getSelfSVal()); +} + +SourceRange ObjCMethodCall::getSourceRange() const { + switch (getMessageKind()) { + case OCM_Message: + return getOriginExpr()->getSourceRange(); + case OCM_PropertyAccess: + case OCM_Subscript: + return getContainingPseudoObjectExpr()->getSourceRange(); + } + llvm_unreachable("unknown message kind"); +} + +using ObjCMessageDataTy = llvm::PointerIntPair<const PseudoObjectExpr *, 2>; + +const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const { + assert(Data && "Lazy lookup not yet performed."); + assert(getMessageKind() != OCM_Message && "Explicit message send."); + return ObjCMessageDataTy::getFromOpaqueValue(Data).getPointer(); +} + +static const Expr * +getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) { + const Expr *Syntactic = POE->getSyntacticForm(); + + // This handles the funny case of assigning to the result of a getter. + // This can happen if the getter returns a non-const reference. + if (const auto *BO = dyn_cast<BinaryOperator>(Syntactic)) + Syntactic = BO->getLHS(); + + return Syntactic; +} + +ObjCMessageKind ObjCMethodCall::getMessageKind() const { + if (!Data) { + // Find the parent, ignoring implicit casts. + ParentMap &PM = getLocationContext()->getParentMap(); + const Stmt *S = PM.getParentIgnoreParenCasts(getOriginExpr()); + + // Check if parent is a PseudoObjectExpr. + if (const auto *POE = dyn_cast_or_null<PseudoObjectExpr>(S)) { + const Expr *Syntactic = getSyntacticFromForPseudoObjectExpr(POE); + + ObjCMessageKind K; + switch (Syntactic->getStmtClass()) { + case Stmt::ObjCPropertyRefExprClass: + K = OCM_PropertyAccess; + break; + case Stmt::ObjCSubscriptRefExprClass: + K = OCM_Subscript; + break; + default: + // FIXME: Can this ever happen? + K = OCM_Message; + break; + } + + if (K != OCM_Message) { + const_cast<ObjCMethodCall *>(this)->Data + = ObjCMessageDataTy(POE, K).getOpaqueValue(); + assert(getMessageKind() == K); + return K; + } + } + + const_cast<ObjCMethodCall *>(this)->Data + = ObjCMessageDataTy(nullptr, 1).getOpaqueValue(); + assert(getMessageKind() == OCM_Message); + return OCM_Message; + } + + ObjCMessageDataTy Info = ObjCMessageDataTy::getFromOpaqueValue(Data); + if (!Info.getPointer()) + return OCM_Message; + return static_cast<ObjCMessageKind>(Info.getInt()); +} + +const ObjCPropertyDecl *ObjCMethodCall::getAccessedProperty() const { + // Look for properties accessed with property syntax (foo.bar = ...) + if ( getMessageKind() == OCM_PropertyAccess) { + const PseudoObjectExpr *POE = getContainingPseudoObjectExpr(); + assert(POE && "Property access without PseudoObjectExpr?"); + + const Expr *Syntactic = getSyntacticFromForPseudoObjectExpr(POE); + auto *RefExpr = cast<ObjCPropertyRefExpr>(Syntactic); + + if (RefExpr->isExplicitProperty()) + return RefExpr->getExplicitProperty(); + } + + // Look for properties accessed with method syntax ([foo setBar:...]). + const ObjCMethodDecl *MD = getDecl(); + if (!MD || !MD->isPropertyAccessor()) + return nullptr; + + // Note: This is potentially quite slow. + return MD->findPropertyDecl(); +} + +bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl, + Selector Sel) const { + assert(IDecl); + AnalysisManager &AMgr = + getState()->getStateManager().getOwningEngine().getAnalysisManager(); + // If the class interface is declared inside the main file, assume it is not + // subcassed. + // TODO: It could actually be subclassed if the subclass is private as well. + // This is probably very rare. + SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc(); + if (InterfLoc.isValid() && AMgr.isInCodeFile(InterfLoc)) + return false; + + // Assume that property accessors are not overridden. + if (getMessageKind() == OCM_PropertyAccess) + return false; + + // We assume that if the method is public (declared outside of main file) or + // has a parent which publicly declares the method, the method could be + // overridden in a subclass. + + // Find the first declaration in the class hierarchy that declares + // the selector. + ObjCMethodDecl *D = nullptr; + while (true) { + D = IDecl->lookupMethod(Sel, true); + + // Cannot find a public definition. + if (!D) + return false; + + // If outside the main file, + if (D->getLocation().isValid() && !AMgr.isInCodeFile(D->getLocation())) + return true; + + if (D->isOverriding()) { + // Search in the superclass on the next iteration. + IDecl = D->getClassInterface(); + if (!IDecl) + return false; + + IDecl = IDecl->getSuperClass(); + if (!IDecl) + return false; + + continue; + } + + return false; + }; + + llvm_unreachable("The while loop should always terminate."); +} + +static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) { + if (!MD) + return MD; + + // Find the redeclaration that defines the method. + if (!MD->hasBody()) { + for (auto I : MD->redecls()) + if (I->hasBody()) + MD = cast<ObjCMethodDecl>(I); + } + return MD; +} + +static bool isCallToSelfClass(const ObjCMessageExpr *ME) { + const Expr* InstRec = ME->getInstanceReceiver(); + if (!InstRec) + return false; + const auto *InstRecIg = dyn_cast<DeclRefExpr>(InstRec->IgnoreParenImpCasts()); + + // Check that receiver is called 'self'. + if (!InstRecIg || !InstRecIg->getFoundDecl() || + !InstRecIg->getFoundDecl()->getName().equals("self")) + return false; + + // Check that the method name is 'class'. + if (ME->getSelector().getNumArgs() != 0 || + !ME->getSelector().getNameForSlot(0).equals("class")) + return false; + + return true; +} + +RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const { + const ObjCMessageExpr *E = getOriginExpr(); + assert(E); + Selector Sel = E->getSelector(); + + if (E->isInstanceMessage()) { + // Find the receiver type. + const ObjCObjectPointerType *ReceiverT = nullptr; + bool CanBeSubClassed = false; + QualType SupersType = E->getSuperType(); + const MemRegion *Receiver = nullptr; + + if (!SupersType.isNull()) { + // The receiver is guaranteed to be 'super' in this case. + // Super always means the type of immediate predecessor to the method + // where the call occurs. + ReceiverT = cast<ObjCObjectPointerType>(SupersType); + } else { + Receiver = getReceiverSVal().getAsRegion(); + if (!Receiver) + return {}; + + DynamicTypeInfo DTI = getDynamicTypeInfo(getState(), Receiver); + if (!DTI.isValid()) { + assert(isa<AllocaRegion>(Receiver) && + "Unhandled untyped region class!"); + return {}; + } + + QualType DynType = DTI.getType(); + CanBeSubClassed = DTI.canBeASubClass(); + ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType()); + + if (ReceiverT && CanBeSubClassed) + if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) + if (!canBeOverridenInSubclass(IDecl, Sel)) + CanBeSubClassed = false; + } + + // Handle special cases of '[self classMethod]' and + // '[[self class] classMethod]', which are treated by the compiler as + // instance (not class) messages. We will statically dispatch to those. + if (auto *PT = dyn_cast_or_null<ObjCObjectPointerType>(ReceiverT)) { + // For [self classMethod], return the compiler visible declaration. + if (PT->getObjectType()->isObjCClass() && + Receiver == getSelfSVal().getAsRegion()) + return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl())); + + // Similarly, handle [[self class] classMethod]. + // TODO: We are currently doing a syntactic match for this pattern with is + // limiting as the test cases in Analysis/inlining/InlineObjCClassMethod.m + // shows. A better way would be to associate the meta type with the symbol + // using the dynamic type info tracking and use it here. We can add a new + // SVal for ObjC 'Class' values that know what interface declaration they + // come from. Then 'self' in a class method would be filled in with + // something meaningful in ObjCMethodCall::getReceiverSVal() and we could + // do proper dynamic dispatch for class methods just like we do for + // instance methods now. + if (E->getInstanceReceiver()) + if (const auto *M = dyn_cast<ObjCMessageExpr>(E->getInstanceReceiver())) + if (isCallToSelfClass(M)) + return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl())); + } + + // Lookup the instance method implementation. + if (ReceiverT) + if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) { + // Repeatedly calling lookupPrivateMethod() is expensive, especially + // when in many cases it returns null. We cache the results so + // that repeated queries on the same ObjCIntefaceDecl and Selector + // don't incur the same cost. On some test cases, we can see the + // same query being issued thousands of times. + // + // NOTE: This cache is essentially a "global" variable, but it + // only gets lazily created when we get here. The value of the + // cache probably comes from it being global across ExprEngines, + // where the same queries may get issued. If we are worried about + // concurrency, or possibly loading/unloading ASTs, etc., we may + // need to revisit this someday. In terms of memory, this table + // stays around until clang quits, which also may be bad if we + // need to release memory. + using PrivateMethodKey = std::pair<const ObjCInterfaceDecl *, Selector>; + using PrivateMethodCache = + llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>; + + static PrivateMethodCache PMC; + Optional<const ObjCMethodDecl *> &Val = PMC[std::make_pair(IDecl, Sel)]; + + // Query lookupPrivateMethod() if the cache does not hit. + if (!Val.hasValue()) { + Val = IDecl->lookupPrivateMethod(Sel); + + // If the method is a property accessor, we should try to "inline" it + // even if we don't actually have an implementation. + if (!*Val) + if (const ObjCMethodDecl *CompileTimeMD = E->getMethodDecl()) + if (CompileTimeMD->isPropertyAccessor()) { + if (!CompileTimeMD->getSelfDecl() && + isa<ObjCCategoryDecl>(CompileTimeMD->getDeclContext())) { + // If the method is an accessor in a category, and it doesn't + // have a self declaration, first + // try to find the method in a class extension. This + // works around a bug in Sema where multiple accessors + // are synthesized for properties in class + // extensions that are redeclared in a category and the + // the implicit parameters are not filled in for + // the method on the category. + // This ensures we find the accessor in the extension, which + // has the implicit parameters filled in. + auto *ID = CompileTimeMD->getClassInterface(); + for (auto *CatDecl : ID->visible_extensions()) { + Val = CatDecl->getMethod(Sel, + CompileTimeMD->isInstanceMethod()); + if (*Val) + break; + } + } + if (!*Val) + Val = IDecl->lookupInstanceMethod(Sel); + } + } + + const ObjCMethodDecl *MD = Val.getValue(); + if (CanBeSubClassed) + return RuntimeDefinition(MD, Receiver); + else + return RuntimeDefinition(MD, nullptr); + } + } else { + // This is a class method. + // If we have type info for the receiver class, we are calling via + // class name. + if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface()) { + // Find/Return the method implementation. + return RuntimeDefinition(IDecl->lookupPrivateClassMethod(Sel)); + } + } + + return {}; +} + +bool ObjCMethodCall::argumentsMayEscape() const { + if (isInSystemHeader() && !isInstanceMessage()) { + Selector Sel = getSelector(); + if (Sel.getNumArgs() == 1 && + Sel.getIdentifierInfoForSlot(0)->isStr("valueWithPointer")) + return true; + } + + return CallEvent::argumentsMayEscape(); +} + +void ObjCMethodCall::getInitialStackFrameContents( + const StackFrameContext *CalleeCtx, + BindingsTy &Bindings) const { + const auto *D = cast<ObjCMethodDecl>(CalleeCtx->getDecl()); + SValBuilder &SVB = getState()->getStateManager().getSValBuilder(); + addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this, + D->parameters()); + + SVal SelfVal = getReceiverSVal(); + if (!SelfVal.isUnknown()) { + const VarDecl *SelfD = CalleeCtx->getAnalysisDeclContext()->getSelfDecl(); + MemRegionManager &MRMgr = SVB.getRegionManager(); + Loc SelfLoc = SVB.makeLoc(MRMgr.getVarRegion(SelfD, CalleeCtx)); + Bindings.push_back(std::make_pair(SelfLoc, SelfVal)); + } +} + +CallEventRef<> +CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State, + const LocationContext *LCtx) { + if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(CE)) + return create<CXXMemberCall>(MCE, State, LCtx); + + if (const auto *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) { + const FunctionDecl *DirectCallee = OpCE->getDirectCallee(); + if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee)) + if (MD->isInstance()) + return create<CXXMemberOperatorCall>(OpCE, State, LCtx); + + } else if (CE->getCallee()->getType()->isBlockPointerType()) { + return create<BlockCall>(CE, State, LCtx); + } + + // Otherwise, it's a normal function call, static member function call, or + // something we can't reason about. + return create<SimpleFunctionCall>(CE, State, LCtx); +} + +CallEventRef<> +CallEventManager::getCaller(const StackFrameContext *CalleeCtx, + ProgramStateRef State) { + const LocationContext *ParentCtx = CalleeCtx->getParent(); + const LocationContext *CallerCtx = ParentCtx->getStackFrame(); + assert(CallerCtx && "This should not be used for top-level stack frames"); + + const Stmt *CallSite = CalleeCtx->getCallSite(); + + if (CallSite) { + if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx)) + return Out; + + // All other cases are handled by getCall. + assert(isa<CXXConstructExpr>(CallSite) && + "This is not an inlineable statement"); + + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + const auto *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl()); + Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx); + SVal ThisVal = State->getSVal(ThisPtr); + + return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite), + ThisVal.getAsRegion(), State, CallerCtx); + } + + // Fall back to the CFG. The only thing we haven't handled yet is + // destructors, though this could change in the future. + const CFGBlock *B = CalleeCtx->getCallSiteBlock(); + CFGElement E = (*B)[CalleeCtx->getIndex()]; + assert((E.getAs<CFGImplicitDtor>() || E.getAs<CFGTemporaryDtor>()) && + "All other CFG elements should have exprs"); + + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + const auto *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl()); + Loc ThisPtr = SVB.getCXXThis(Dtor, CalleeCtx); + SVal ThisVal = State->getSVal(ThisPtr); + + const Stmt *Trigger; + if (Optional<CFGAutomaticObjDtor> AutoDtor = E.getAs<CFGAutomaticObjDtor>()) + Trigger = AutoDtor->getTriggerStmt(); + else if (Optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>()) + Trigger = DeleteDtor->getDeleteExpr(); + else + Trigger = Dtor->getBody(); + + return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(), + E.getAs<CFGBaseDtor>().hasValue(), State, + CallerCtx); +} + +CallEventRef<> CallEventManager::getCall(const Stmt *S, ProgramStateRef State, + const LocationContext *LC) { + if (const auto *CE = dyn_cast<CallExpr>(S)) { + return getSimpleCall(CE, State, LC); + } else if (const auto *NE = dyn_cast<CXXNewExpr>(S)) { + return getCXXAllocatorCall(NE, State, LC); + } else if (const auto *ME = dyn_cast<ObjCMessageExpr>(S)) { + return getObjCMethodCall(ME, State, LC); + } else { + return nullptr; + } +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp new file mode 100644 index 000000000000..72bfd84b40a3 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp @@ -0,0 +1,40 @@ +//== 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines Checker, used to create and register checkers. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/Checker.h" + +using namespace clang; +using namespace ento; + +int ImplicitNullDerefEvent::Tag; + +StringRef CheckerBase::getTagDescription() const { + return getCheckName().getName(); +} + +CheckName CheckerBase::getCheckName() const { return Name; } + +CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName, + StringRef Msg) + : SimpleProgramPointTag(CheckerName, Msg) {} + +CheckerProgramPointTag::CheckerProgramPointTag(const CheckerBase *Checker, + StringRef Msg) + : SimpleProgramPointTag(Checker->getCheckName().getName(), Msg) {} + +raw_ostream& clang::ento::operator<<(raw_ostream &Out, + const CheckerBase &Checker) { + Out << Checker.getCheckName().getName(); + return Out; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp new file mode 100644 index 000000000000..6cf931abbddd --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp @@ -0,0 +1,132 @@ +//== 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines CheckerContext that provides contextual info for +// path-sensitive checkers. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/Basic/Builtins.h" +#include "clang/Lex/Lexer.h" + +using namespace clang; +using namespace ento; + +const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const { + const Expr *Callee = CE->getCallee(); + SVal L = Pred->getSVal(Callee); + return L.getAsFunctionDecl(); +} + +StringRef CheckerContext::getCalleeName(const FunctionDecl *FunDecl) const { + if (!FunDecl) + return StringRef(); + IdentifierInfo *funI = FunDecl->getIdentifier(); + if (!funI) + return StringRef(); + return funI->getName(); +} + +StringRef CheckerContext::getDeclDescription(const Decl *D) { + if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D)) + return "method"; + if (isa<BlockDecl>(D)) + return "anonymous block"; + return "function"; +} + +bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD, + StringRef Name) { + // To avoid false positives (Ex: finding user defined functions with + // similar names), only perform fuzzy name matching when it's a builtin. + // Using a string compare is slow, we might want to switch on BuiltinID here. + unsigned BId = FD->getBuiltinID(); + if (BId != 0) { + if (Name.empty()) + return true; + StringRef BName = FD->getASTContext().BuiltinInfo.getName(BId); + if (BName.find(Name) != StringRef::npos) + return true; + } + + const IdentifierInfo *II = FD->getIdentifier(); + // If this is a special C++ name without IdentifierInfo, it can't be a + // C library function. + if (!II) + return false; + + // Look through 'extern "C"' and anything similar invented in the future. + // If this function is not in TU directly, it is not a C library function. + if (!FD->getDeclContext()->getRedeclContext()->isTranslationUnit()) + return false; + + // If this function is not externally visible, it is not a C library function. + // Note that we make an exception for inline functions, which may be + // declared in header files without external linkage. + if (!FD->isInlined() && !FD->isExternallyVisible()) + return false; + + if (Name.empty()) + return true; + + StringRef FName = II->getName(); + if (FName.equals(Name)) + return true; + + if (FName.startswith("__inline") && (FName.find(Name) != StringRef::npos)) + return true; + + if (FName.startswith("__") && FName.endswith("_chk") && + FName.find(Name) != StringRef::npos) + return true; + + return false; +} + +StringRef CheckerContext::getMacroNameOrSpelling(SourceLocation &Loc) { + if (Loc.isMacroID()) + return Lexer::getImmediateMacroName(Loc, getSourceManager(), + getLangOpts()); + SmallVector<char, 16> buf; + return Lexer::getSpelling(Loc, buf, getSourceManager(), getLangOpts()); +} + +/// Evaluate comparison and return true if it's known that condition is true +static bool evalComparison(SVal LHSVal, BinaryOperatorKind ComparisonOp, + SVal RHSVal, ProgramStateRef State) { + if (LHSVal.isUnknownOrUndef()) + return false; + ProgramStateManager &Mgr = State->getStateManager(); + if (!LHSVal.getAs<NonLoc>()) { + LHSVal = Mgr.getStoreManager().getBinding(State->getStore(), + LHSVal.castAs<Loc>()); + if (LHSVal.isUnknownOrUndef() || !LHSVal.getAs<NonLoc>()) + return false; + } + + SValBuilder &Bldr = Mgr.getSValBuilder(); + SVal Eval = Bldr.evalBinOp(State, ComparisonOp, LHSVal, RHSVal, + Bldr.getConditionType()); + if (Eval.isUnknownOrUndef()) + return false; + ProgramStateRef StTrue, StFalse; + std::tie(StTrue, StFalse) = State->assume(Eval.castAs<DefinedSVal>()); + return StTrue && !StFalse; +} + +bool CheckerContext::isGreaterOrEqual(const Expr *E, unsigned long long Val) { + DefinedSVal V = getSValBuilder().makeIntVal(Val, getASTContext().LongLongTy); + return evalComparison(getSVal(E), BO_GE, V, getState()); +} + +bool CheckerContext::isNegative(const Expr *E) { + DefinedSVal V = getSValBuilder().makeIntVal(0, false); + return evalComparison(getSVal(E), BO_LT, V, getState()); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp new file mode 100644 index 000000000000..e73a22ae3981 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp @@ -0,0 +1,115 @@ +//===---- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines several static functions for use in checkers. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h" +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" + +namespace clang { + +namespace ento { + +// Recursively find any substatements containing macros +bool containsMacro(const Stmt *S) { + if (S->getBeginLoc().isMacroID()) + return true; + + if (S->getEndLoc().isMacroID()) + return true; + + for (const Stmt *Child : S->children()) + if (Child && containsMacro(Child)) + return true; + + return false; +} + +// Recursively find any substatements containing enum constants +bool containsEnum(const Stmt *S) { + const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S); + + if (DR && isa<EnumConstantDecl>(DR->getDecl())) + return true; + + for (const Stmt *Child : S->children()) + if (Child && containsEnum(Child)) + return true; + + return false; +} + +// Recursively find any substatements containing static vars +bool containsStaticLocal(const Stmt *S) { + const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S); + + if (DR) + if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) + if (VD->isStaticLocal()) + return true; + + for (const Stmt *Child : S->children()) + if (Child && containsStaticLocal(Child)) + return true; + + return false; +} + +// Recursively find any substatements containing __builtin_offsetof +bool containsBuiltinOffsetOf(const Stmt *S) { + if (isa<OffsetOfExpr>(S)) + return true; + + for (const Stmt *Child : S->children()) + if (Child && containsBuiltinOffsetOf(Child)) + return true; + + return false; +} + +// Extract lhs and rhs from assignment statement +std::pair<const clang::VarDecl *, const clang::Expr *> +parseAssignment(const Stmt *S) { + const VarDecl *VD = nullptr; + const Expr *RHS = nullptr; + + if (auto Assign = dyn_cast_or_null<BinaryOperator>(S)) { + if (Assign->isAssignmentOp()) { + // Ordinary assignment + RHS = Assign->getRHS(); + if (auto DE = dyn_cast_or_null<DeclRefExpr>(Assign->getLHS())) + VD = dyn_cast_or_null<VarDecl>(DE->getDecl()); + } + } else if (auto PD = dyn_cast_or_null<DeclStmt>(S)) { + // Initialization + assert(PD->isSingleDecl() && "We process decls one by one"); + VD = dyn_cast_or_null<VarDecl>(PD->getSingleDecl()); + RHS = VD->getAnyInitializer(); + } + + return std::make_pair(VD, RHS); +} + +Nullability getNullabilityAnnotation(QualType Type) { + const auto *AttrType = Type->getAs<AttributedType>(); + if (!AttrType) + return Nullability::Unspecified; + if (AttrType->getAttrKind() == attr::TypeNullable) + return Nullability::Nullable; + else if (AttrType->getAttrKind() == attr::TypeNonNull) + return Nullability::Nonnull; + return Nullability::Unspecified; +} + + +} // end namespace ento +} // end namespace clang diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp new file mode 100644 index 000000000000..688c47e984cc --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -0,0 +1,835 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// Defines the Static Analyzer Checker Manager. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/Stmt.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/Checker.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include <cassert> +#include <vector> + +using namespace clang; +using namespace ento; + +bool CheckerManager::hasPathSensitiveCheckers() const { + return !StmtCheckers.empty() || + !PreObjCMessageCheckers.empty() || + !PostObjCMessageCheckers.empty() || + !PreCallCheckers.empty() || + !PostCallCheckers.empty() || + !LocationCheckers.empty() || + !BindCheckers.empty() || + !EndAnalysisCheckers.empty() || + !EndFunctionCheckers.empty() || + !BranchConditionCheckers.empty() || + !LiveSymbolsCheckers.empty() || + !DeadSymbolsCheckers.empty() || + !RegionChangesCheckers.empty() || + !EvalAssumeCheckers.empty() || + !EvalCallCheckers.empty(); +} + +void CheckerManager::finishedCheckerRegistration() { +#ifndef NDEBUG + // Make sure that for every event that has listeners, there is at least + // one dispatcher registered for it. + for (const auto &Event : Events) + assert(Event.second.HasDispatcher && + "No dispatcher registered for an event"); +#endif +} + +//===----------------------------------------------------------------------===// +// Functions for running checkers for AST traversing.. +//===----------------------------------------------------------------------===// + +void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) { + assert(D); + + unsigned DeclKind = D->getKind(); + CachedDeclCheckers *checkers = nullptr; + CachedDeclCheckersMapTy::iterator CCI = CachedDeclCheckersMap.find(DeclKind); + if (CCI != CachedDeclCheckersMap.end()) { + checkers = &(CCI->second); + } else { + // Find the checkers that should run for this Decl and cache them. + checkers = &CachedDeclCheckersMap[DeclKind]; + for (const auto &info : DeclCheckers) + if (info.IsForDeclFn(D)) + checkers->push_back(info.CheckFn); + } + + assert(checkers); + for (const auto checker : *checkers) + checker(D, mgr, BR); +} + +void CheckerManager::runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr, + BugReporter &BR) { + assert(D && D->hasBody()); + + for (const auto BodyChecker : BodyCheckers) + BodyChecker(D, mgr, BR); +} + +//===----------------------------------------------------------------------===// +// Functions for running checkers for path-sensitive checking. +//===----------------------------------------------------------------------===// + +template <typename CHECK_CTX> +static void expandGraphWithCheckers(CHECK_CTX checkCtx, + ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src) { + const NodeBuilderContext &BldrCtx = checkCtx.Eng.getBuilderContext(); + if (Src.empty()) + return; + + typename CHECK_CTX::CheckersTy::const_iterator + I = checkCtx.checkers_begin(), E = checkCtx.checkers_end(); + if (I == E) { + Dst.insert(Src); + return; + } + + ExplodedNodeSet Tmp1, Tmp2; + const ExplodedNodeSet *PrevSet = &Src; + + for (; I != E; ++I) { + ExplodedNodeSet *CurrSet = nullptr; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp1) ? &Tmp2 : &Tmp1; + CurrSet->clear(); + } + + NodeBuilder B(*PrevSet, *CurrSet, BldrCtx); + for (const auto &NI : *PrevSet) + checkCtx.runChecker(*I, B, NI); + + // If all the produced transitions are sinks, stop. + if (CurrSet->empty()) + return; + + // Update which NodeSet is the current one. + PrevSet = CurrSet; + } +} + +namespace { + + struct CheckStmtContext { + using CheckersTy = SmallVectorImpl<CheckerManager::CheckStmtFunc>; + + bool IsPreVisit; + const CheckersTy &Checkers; + const Stmt *S; + ExprEngine &Eng; + bool WasInlined; + + CheckStmtContext(bool isPreVisit, const CheckersTy &checkers, + const Stmt *s, ExprEngine &eng, bool wasInlined = false) + : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng), + WasInlined(wasInlined) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + void runChecker(CheckerManager::CheckStmtFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + // FIXME: Remove respondsToCallback from CheckerContext; + ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind : + ProgramPoint::PostStmtKind; + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K, + Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L, WasInlined); + checkFn(S, C); + } + }; + +} // namespace + +/// Run checkers for visiting Stmts. +void CheckerManager::runCheckersForStmt(bool isPreVisit, + ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + const Stmt *S, + ExprEngine &Eng, + bool WasInlined) { + CheckStmtContext C(isPreVisit, getCachedStmtCheckersFor(S, isPreVisit), + S, Eng, WasInlined); + expandGraphWithCheckers(C, Dst, Src); +} + +namespace { + + struct CheckObjCMessageContext { + using CheckersTy = std::vector<CheckerManager::CheckObjCMessageFunc>; + + ObjCMessageVisitKind Kind; + bool WasInlined; + const CheckersTy &Checkers; + const ObjCMethodCall &Msg; + ExprEngine &Eng; + + CheckObjCMessageContext(ObjCMessageVisitKind visitKind, + const CheckersTy &checkers, + const ObjCMethodCall &msg, ExprEngine &eng, + bool wasInlined) + : Kind(visitKind), WasInlined(wasInlined), Checkers(checkers), Msg(msg), + Eng(eng) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + void runChecker(CheckerManager::CheckObjCMessageFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + bool IsPreVisit; + + switch (Kind) { + case ObjCMessageVisitKind::Pre: + IsPreVisit = true; + break; + case ObjCMessageVisitKind::MessageNil: + case ObjCMessageVisitKind::Post: + IsPreVisit = false; + break; + } + + const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L, WasInlined); + + checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C); + } + }; + +} // namespace + +/// Run checkers for visiting obj-c messages. +void CheckerManager::runCheckersForObjCMessage(ObjCMessageVisitKind visitKind, + ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + const ObjCMethodCall &msg, + ExprEngine &Eng, + bool WasInlined) { + auto &checkers = getObjCMessageCheckers(visitKind); + CheckObjCMessageContext C(visitKind, checkers, msg, Eng, WasInlined); + expandGraphWithCheckers(C, Dst, Src); +} + +const std::vector<CheckerManager::CheckObjCMessageFunc> & +CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) { + switch (Kind) { + case ObjCMessageVisitKind::Pre: + return PreObjCMessageCheckers; + break; + case ObjCMessageVisitKind::Post: + return PostObjCMessageCheckers; + case ObjCMessageVisitKind::MessageNil: + return ObjCMessageNilCheckers; + } + llvm_unreachable("Unknown Kind"); +} + +namespace { + + // FIXME: This has all the same signatures as CheckObjCMessageContext. + // Is there a way we can merge the two? + struct CheckCallContext { + using CheckersTy = std::vector<CheckerManager::CheckCallFunc>; + + bool IsPreVisit, WasInlined; + const CheckersTy &Checkers; + const CallEvent &Call; + ExprEngine &Eng; + + CheckCallContext(bool isPreVisit, const CheckersTy &checkers, + const CallEvent &call, ExprEngine &eng, + bool wasInlined) + : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers), + Call(call), Eng(eng) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + void runChecker(CheckerManager::CheckCallFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L, WasInlined); + + checkFn(*Call.cloneWithState(Pred->getState()), C); + } + }; + +} // namespace + +/// Run checkers for visiting an abstract call event. +void CheckerManager::runCheckersForCallEvent(bool isPreVisit, + ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + const CallEvent &Call, + ExprEngine &Eng, + bool WasInlined) { + CheckCallContext C(isPreVisit, + isPreVisit ? PreCallCheckers + : PostCallCheckers, + Call, Eng, WasInlined); + expandGraphWithCheckers(C, Dst, Src); +} + +namespace { + + struct CheckLocationContext { + using CheckersTy = std::vector<CheckerManager::CheckLocationFunc>; + + const CheckersTy &Checkers; + SVal Loc; + bool IsLoad; + const Stmt *NodeEx; /* Will become a CFGStmt */ + const Stmt *BoundEx; + ExprEngine &Eng; + + CheckLocationContext(const CheckersTy &checkers, + SVal loc, bool isLoad, const Stmt *NodeEx, + const Stmt *BoundEx, + ExprEngine &eng) + : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx), + BoundEx(BoundEx), Eng(eng) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + void runChecker(CheckerManager::CheckLocationFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + ProgramPoint::Kind K = IsLoad ? ProgramPoint::PreLoadKind : + ProgramPoint::PreStoreKind; + const ProgramPoint &L = + ProgramPoint::getProgramPoint(NodeEx, K, + Pred->getLocationContext(), + checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + checkFn(Loc, IsLoad, BoundEx, C); + } + }; + +} // namespace + +/// Run checkers for load/store of a location. + +void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + SVal location, bool isLoad, + const Stmt *NodeEx, + const Stmt *BoundEx, + ExprEngine &Eng) { + CheckLocationContext C(LocationCheckers, location, isLoad, NodeEx, + BoundEx, Eng); + expandGraphWithCheckers(C, Dst, Src); +} + +namespace { + + struct CheckBindContext { + using CheckersTy = std::vector<CheckerManager::CheckBindFunc>; + + const CheckersTy &Checkers; + SVal Loc; + SVal Val; + const Stmt *S; + ExprEngine &Eng; + const ProgramPoint &PP; + + CheckBindContext(const CheckersTy &checkers, + SVal loc, SVal val, const Stmt *s, ExprEngine &eng, + const ProgramPoint &pp) + : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PP(pp) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + void runChecker(CheckerManager::CheckBindFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + const ProgramPoint &L = PP.withTag(checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + + checkFn(Loc, Val, S, C); + } + }; + +} // namespace + +/// Run checkers for binding of a value to a location. +void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + SVal location, SVal val, + const Stmt *S, ExprEngine &Eng, + const ProgramPoint &PP) { + CheckBindContext C(BindCheckers, location, val, S, Eng, PP); + expandGraphWithCheckers(C, Dst, Src); +} + +void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G, + BugReporter &BR, + ExprEngine &Eng) { + for (const auto EndAnalysisChecker : EndAnalysisCheckers) + EndAnalysisChecker(G, BR, Eng); +} + +namespace { + +struct CheckBeginFunctionContext { + using CheckersTy = std::vector<CheckerManager::CheckBeginFunctionFunc>; + + const CheckersTy &Checkers; + ExprEngine &Eng; + const ProgramPoint &PP; + + CheckBeginFunctionContext(const CheckersTy &Checkers, ExprEngine &Eng, + const ProgramPoint &PP) + : Checkers(Checkers), Eng(Eng), PP(PP) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + void runChecker(CheckerManager::CheckBeginFunctionFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + const ProgramPoint &L = PP.withTag(checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + + checkFn(C); + } +}; + +} // namespace + +void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst, + const BlockEdge &L, + ExplodedNode *Pred, + ExprEngine &Eng) { + ExplodedNodeSet Src; + Src.insert(Pred); + CheckBeginFunctionContext C(BeginFunctionCheckers, Eng, L); + expandGraphWithCheckers(C, Dst, Src); +} + +/// Run checkers for end of path. +// Note, We do not chain the checker output (like in expandGraphWithCheckers) +// for this callback since end of path nodes are expected to be final. +void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC, + ExplodedNodeSet &Dst, + ExplodedNode *Pred, + ExprEngine &Eng, + const ReturnStmt *RS) { + // We define the builder outside of the loop because if at least one checker + // creates a successor for Pred, we do not need to generate an + // autotransition for it. + NodeBuilder Bldr(Pred, Dst, BC); + for (const auto checkFn : EndFunctionCheckers) { + const ProgramPoint &L = + FunctionExitPoint(RS, Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + checkFn(RS, C); + } +} + +namespace { + + struct CheckBranchConditionContext { + using CheckersTy = std::vector<CheckerManager::CheckBranchConditionFunc>; + + const CheckersTy &Checkers; + const Stmt *Condition; + ExprEngine &Eng; + + CheckBranchConditionContext(const CheckersTy &checkers, + const Stmt *Cond, ExprEngine &eng) + : Checkers(checkers), Condition(Cond), Eng(eng) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + void runChecker(CheckerManager::CheckBranchConditionFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + ProgramPoint L = PostCondition(Condition, Pred->getLocationContext(), + checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + checkFn(Condition, C); + } + }; + +} // namespace + +/// Run checkers for branch condition. +void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition, + ExplodedNodeSet &Dst, + ExplodedNode *Pred, + ExprEngine &Eng) { + ExplodedNodeSet Src; + Src.insert(Pred); + CheckBranchConditionContext C(BranchConditionCheckers, Condition, Eng); + expandGraphWithCheckers(C, Dst, Src); +} + +namespace { + + struct CheckNewAllocatorContext { + using CheckersTy = std::vector<CheckerManager::CheckNewAllocatorFunc>; + + const CheckersTy &Checkers; + const CXXNewExpr *NE; + SVal Target; + bool WasInlined; + ExprEngine &Eng; + + CheckNewAllocatorContext(const CheckersTy &Checkers, const CXXNewExpr *NE, + SVal Target, bool WasInlined, ExprEngine &Eng) + : Checkers(Checkers), NE(NE), Target(Target), WasInlined(WasInlined), + Eng(Eng) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + void runChecker(CheckerManager::CheckNewAllocatorFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + ProgramPoint L = PostAllocatorCall(NE, Pred->getLocationContext()); + CheckerContext C(Bldr, Eng, Pred, L, WasInlined); + checkFn(NE, Target, C); + } + }; + +} // namespace + +void CheckerManager::runCheckersForNewAllocator( + const CXXNewExpr *NE, SVal Target, ExplodedNodeSet &Dst, ExplodedNode *Pred, + ExprEngine &Eng, bool WasInlined) { + ExplodedNodeSet Src; + Src.insert(Pred); + CheckNewAllocatorContext C(NewAllocatorCheckers, NE, Target, WasInlined, Eng); + expandGraphWithCheckers(C, Dst, Src); +} + +/// Run checkers for live symbols. +void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state, + SymbolReaper &SymReaper) { + for (const auto LiveSymbolsChecker : LiveSymbolsCheckers) + LiveSymbolsChecker(state, SymReaper); +} + +namespace { + + struct CheckDeadSymbolsContext { + using CheckersTy = std::vector<CheckerManager::CheckDeadSymbolsFunc>; + + const CheckersTy &Checkers; + SymbolReaper &SR; + const Stmt *S; + ExprEngine &Eng; + ProgramPoint::Kind ProgarmPointKind; + + CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr, + const Stmt *s, ExprEngine &eng, + ProgramPoint::Kind K) + : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) {} + + CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); } + CheckersTy::const_iterator checkers_end() { return Checkers.end(); } + + void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn, + NodeBuilder &Bldr, ExplodedNode *Pred) { + const ProgramPoint &L = ProgramPoint::getProgramPoint(S, ProgarmPointKind, + Pred->getLocationContext(), checkFn.Checker); + CheckerContext C(Bldr, Eng, Pred, L); + + // Note, do not pass the statement to the checkers without letting them + // differentiate if we ran remove dead bindings before or after the + // statement. + checkFn(SR, C); + } + }; + +} // namespace + +/// Run checkers for dead symbols. +void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + SymbolReaper &SymReaper, + const Stmt *S, + ExprEngine &Eng, + ProgramPoint::Kind K) { + CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K); + expandGraphWithCheckers(C, Dst, Src); +} + +/// Run checkers for region changes. +ProgramStateRef +CheckerManager::runCheckersForRegionChanges(ProgramStateRef state, + const InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call) { + for (const auto RegionChangesChecker : RegionChangesCheckers) { + // If any checker declares the state infeasible (or if it starts that way), + // bail out. + if (!state) + return nullptr; + state = RegionChangesChecker(state, invalidated, ExplicitRegions, Regions, + LCtx, Call); + } + return state; +} + +/// Run checkers to process symbol escape event. +ProgramStateRef +CheckerManager::runCheckersForPointerEscape(ProgramStateRef State, + const InvalidatedSymbols &Escaped, + const CallEvent *Call, + PointerEscapeKind Kind, + RegionAndSymbolInvalidationTraits *ETraits) { + assert((Call != nullptr || + (Kind != PSK_DirectEscapeOnCall && + Kind != PSK_IndirectEscapeOnCall)) && + "Call must not be NULL when escaping on call"); + for (const auto PointerEscapeChecker : PointerEscapeCheckers) { + // If any checker declares the state infeasible (or if it starts that + // way), bail out. + if (!State) + return nullptr; + State = PointerEscapeChecker(State, Escaped, Call, Kind, ETraits); + } + return State; +} + +/// Run checkers for handling assumptions on symbolic values. +ProgramStateRef +CheckerManager::runCheckersForEvalAssume(ProgramStateRef state, + SVal Cond, bool Assumption) { + for (const auto EvalAssumeChecker : EvalAssumeCheckers) { + // If any checker declares the state infeasible (or if it starts that way), + // bail out. + if (!state) + return nullptr; + state = EvalAssumeChecker(state, Cond, Assumption); + } + return state; +} + +/// Run checkers for evaluating a call. +/// Only one checker will evaluate the call. +void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst, + const ExplodedNodeSet &Src, + const CallEvent &Call, + ExprEngine &Eng) { + const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr()); + for (const auto Pred : Src) { + bool anyEvaluated = false; + + ExplodedNodeSet checkDst; + NodeBuilder B(Pred, checkDst, Eng.getBuilderContext()); + + // Check if any of the EvalCall callbacks can evaluate the call. + for (const auto EvalCallChecker : EvalCallCheckers) { + ProgramPoint::Kind K = ProgramPoint::PostStmtKind; + const ProgramPoint &L = + ProgramPoint::getProgramPoint(CE, K, Pred->getLocationContext(), + EvalCallChecker.Checker); + bool evaluated = false; + { // CheckerContext generates transitions(populates checkDest) on + // destruction, so introduce the scope to make sure it gets properly + // populated. + CheckerContext C(B, Eng, Pred, L); + evaluated = EvalCallChecker(CE, C); + } + assert(!(evaluated && anyEvaluated) + && "There are more than one checkers evaluating the call"); + if (evaluated) { + anyEvaluated = true; + Dst.insert(checkDst); +#ifdef NDEBUG + break; // on release don't check that no other checker also evals. +#endif + } + } + + // If none of the checkers evaluated the call, ask ExprEngine to handle it. + if (!anyEvaluated) { + NodeBuilder B(Pred, Dst, Eng.getBuilderContext()); + Eng.defaultEvalCall(B, Pred, Call); + } + } +} + +/// Run checkers for the entire Translation Unit. +void CheckerManager::runCheckersOnEndOfTranslationUnit( + const TranslationUnitDecl *TU, + AnalysisManager &mgr, + BugReporter &BR) { + for (const auto EndOfTranslationUnitChecker : EndOfTranslationUnitCheckers) + EndOfTranslationUnitChecker(TU, mgr, BR); +} + +void CheckerManager::runCheckersForPrintState(raw_ostream &Out, + ProgramStateRef State, + const char *NL, const char *Sep) { + for (const auto &CheckerTag : CheckerTags) + CheckerTag.second->printState(Out, State, NL, Sep); +} + +//===----------------------------------------------------------------------===// +// Internal registration functions for AST traversing. +//===----------------------------------------------------------------------===// + +void CheckerManager::_registerForDecl(CheckDeclFunc checkfn, + HandlesDeclFunc isForDeclFn) { + DeclCheckerInfo info = { checkfn, isForDeclFn }; + DeclCheckers.push_back(info); +} + +void CheckerManager::_registerForBody(CheckDeclFunc checkfn) { + BodyCheckers.push_back(checkfn); +} + +//===----------------------------------------------------------------------===// +// Internal registration functions for path-sensitive checking. +//===----------------------------------------------------------------------===// + +void CheckerManager::_registerForPreStmt(CheckStmtFunc checkfn, + HandlesStmtFunc isForStmtFn) { + StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/true }; + StmtCheckers.push_back(info); +} + +void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn, + HandlesStmtFunc isForStmtFn) { + StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/false }; + StmtCheckers.push_back(info); +} + +void CheckerManager::_registerForPreObjCMessage(CheckObjCMessageFunc checkfn) { + PreObjCMessageCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForObjCMessageNil(CheckObjCMessageFunc checkfn) { + ObjCMessageNilCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) { + PostObjCMessageCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) { + PreCallCheckers.push_back(checkfn); +} +void CheckerManager::_registerForPostCall(CheckCallFunc checkfn) { + PostCallCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) { + LocationCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForBind(CheckBindFunc checkfn) { + BindCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) { + EndAnalysisCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForBeginFunction(CheckBeginFunctionFunc checkfn) { + BeginFunctionCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) { + EndFunctionCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForBranchCondition( + CheckBranchConditionFunc checkfn) { + BranchConditionCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForNewAllocator(CheckNewAllocatorFunc checkfn) { + NewAllocatorCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) { + LiveSymbolsCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForDeadSymbols(CheckDeadSymbolsFunc checkfn) { + DeadSymbolsCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn) { + RegionChangesCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForPointerEscape(CheckPointerEscapeFunc checkfn){ + PointerEscapeCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForConstPointerEscape( + CheckPointerEscapeFunc checkfn) { + PointerEscapeCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForEvalAssume(EvalAssumeFunc checkfn) { + EvalAssumeCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) { + EvalCallCheckers.push_back(checkfn); +} + +void CheckerManager::_registerForEndOfTranslationUnit( + CheckEndOfTranslationUnit checkfn) { + EndOfTranslationUnitCheckers.push_back(checkfn); +} + +//===----------------------------------------------------------------------===// +// Implementation details. +//===----------------------------------------------------------------------===// + +const CheckerManager::CachedStmtCheckers & +CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) { + assert(S); + + unsigned Key = (S->getStmtClass() << 1) | unsigned(isPreVisit); + CachedStmtCheckersMapTy::iterator CCI = CachedStmtCheckersMap.find(Key); + if (CCI != CachedStmtCheckersMap.end()) + return CCI->second; + + // Find the checkers that should run for this Stmt and cache them. + CachedStmtCheckers &Checkers = CachedStmtCheckersMap[Key]; + for (const auto &Info : StmtCheckers) + if (Info.IsPreVisit == isPreVisit && Info.IsForStmtFn(S)) + Checkers.push_back(Info.CheckFn); + return Checkers; +} + +CheckerManager::~CheckerManager() { + for (const auto CheckerDtor : CheckerDtors) + CheckerDtor(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp new file mode 100644 index 000000000000..cdae3ef0116a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp @@ -0,0 +1,21 @@ +//=--- 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. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h" + +// Common strings used for the "category" of many static analyzer issues. +namespace clang { namespace ento { namespace categories { + +const char * const CoreFoundationObjectiveC = "Core Foundation/Objective-C"; +const char * const LogicError = "Logic error"; +const char * const MemoryRefCount = + "Memory (Core Foundation/Objective-C/OSObject)"; +const char * const MemoryError = "Memory error"; +const char * const UnixAPI = "Unix API"; +}}} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp new file mode 100644 index 000000000000..ef9c44c51be4 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp @@ -0,0 +1,44 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defined the interface to manage constraints on symbolic values. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" +#include "clang/AST/Type.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" + +using namespace clang; +using namespace ento; + +ConstraintManager::~ConstraintManager() = default; + +static DefinedSVal getLocFromSymbol(const ProgramStateRef &State, + SymbolRef Sym) { + const MemRegion *R = + State->getStateManager().getRegionManager().getSymbolicRegion(Sym); + return loc::MemRegionVal(R); +} + +ConditionTruthVal ConstraintManager::checkNull(ProgramStateRef State, + SymbolRef Sym) { + QualType Ty = Sym->getType(); + DefinedSVal V = Loc::isLocType(Ty) ? getLocFromSymbol(State, Sym) + : nonloc::SymbolVal(Sym); + const ProgramStatePair &P = assumeDual(State, V); + if (P.first && !P.second) + return ConditionTruthVal(false); + if (!P.first && P.second) + return ConditionTruthVal(true); + return {}; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp new file mode 100644 index 000000000000..196854cb09da --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -0,0 +1,655 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a generic engine for intraprocedural, path-sensitive, +// dataflow analysis via graph reachability engine. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include <algorithm> +#include <cassert> +#include <memory> +#include <utility> + +using namespace clang; +using namespace ento; + +#define DEBUG_TYPE "CoreEngine" + +STATISTIC(NumSteps, + "The # of steps executed."); +STATISTIC(NumReachedMaxSteps, + "The # of times we reached the max number of steps."); +STATISTIC(NumPathsExplored, + "The # of paths explored by the analyzer."); + +//===----------------------------------------------------------------------===// +// Core analysis engine. +//===----------------------------------------------------------------------===// + +static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts, + SubEngine &subengine) { + switch (Opts.getExplorationStrategy()) { + case ExplorationStrategyKind::DFS: + return WorkList::makeDFS(); + case ExplorationStrategyKind::BFS: + return WorkList::makeBFS(); + case ExplorationStrategyKind::BFSBlockDFSContents: + return WorkList::makeBFSBlockDFSContents(); + case ExplorationStrategyKind::UnexploredFirst: + return WorkList::makeUnexploredFirst(); + case ExplorationStrategyKind::UnexploredFirstQueue: + return WorkList::makeUnexploredFirstPriorityQueue(); + case ExplorationStrategyKind::UnexploredFirstLocationQueue: + return WorkList::makeUnexploredFirstPriorityLocationQueue(); + } + llvm_unreachable("Unknown AnalyzerOptions::ExplorationStrategyKind"); +} + +CoreEngine::CoreEngine(SubEngine &subengine, FunctionSummariesTy *FS, + AnalyzerOptions &Opts) + : SubEng(subengine), WList(generateWorkList(Opts, subengine)), + BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {} + +/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. +bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, + ProgramStateRef InitState) { + if (G.num_roots() == 0) { // Initialize the analysis by constructing + // the root if none exists. + + const CFGBlock *Entry = &(L->getCFG()->getEntry()); + + assert(Entry->empty() && "Entry block must be empty."); + + assert(Entry->succ_size() == 1 && "Entry block must have 1 successor."); + + // Mark the entry block as visited. + FunctionSummaries->markVisitedBasicBlock(Entry->getBlockID(), + L->getDecl(), + L->getCFG()->getNumBlockIDs()); + + // Get the solitary successor. + const CFGBlock *Succ = *(Entry->succ_begin()); + + // Construct an edge representing the + // starting location in the function. + BlockEdge StartLoc(Entry, Succ, L); + + // Set the current block counter to being empty. + WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); + + if (!InitState) + InitState = SubEng.getInitialState(L); + + bool IsNew; + ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew); + assert(IsNew); + G.addRoot(Node); + + NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node); + ExplodedNodeSet DstBegin; + SubEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc); + + enqueue(DstBegin); + } + + // Check if we have a steps limit + bool UnlimitedSteps = Steps == 0; + // Cap our pre-reservation in the event that the user specifies + // a very large number of maximum steps. + const unsigned PreReservationCap = 4000000; + if(!UnlimitedSteps) + G.reserve(std::min(Steps,PreReservationCap)); + + while (WList->hasWork()) { + if (!UnlimitedSteps) { + if (Steps == 0) { + NumReachedMaxSteps++; + break; + } + --Steps; + } + + NumSteps++; + + const WorkListUnit& WU = WList->dequeue(); + + // Set the current block counter. + WList->setBlockCounter(WU.getBlockCounter()); + + // Retrieve the node. + ExplodedNode *Node = WU.getNode(); + + dispatchWorkItem(Node, Node->getLocation(), WU); + } + SubEng.processEndWorklist(); + return WList->hasWork(); +} + +void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc, + const WorkListUnit& WU) { + // Dispatch on the location type. + switch (Loc.getKind()) { + case ProgramPoint::BlockEdgeKind: + HandleBlockEdge(Loc.castAs<BlockEdge>(), Pred); + break; + + case ProgramPoint::BlockEntranceKind: + HandleBlockEntrance(Loc.castAs<BlockEntrance>(), Pred); + break; + + case ProgramPoint::BlockExitKind: + assert(false && "BlockExit location never occur in forward analysis."); + break; + + case ProgramPoint::CallEnterKind: + HandleCallEnter(Loc.castAs<CallEnter>(), Pred); + break; + + case ProgramPoint::CallExitBeginKind: + SubEng.processCallExit(Pred); + break; + + case ProgramPoint::EpsilonKind: { + assert(Pred->hasSinglePred() && + "Assume epsilon has exactly one predecessor by construction"); + ExplodedNode *PNode = Pred->getFirstPred(); + dispatchWorkItem(Pred, PNode->getLocation(), WU); + break; + } + default: + assert(Loc.getAs<PostStmt>() || + Loc.getAs<PostInitializer>() || + Loc.getAs<PostImplicitCall>() || + Loc.getAs<CallExitEnd>() || + Loc.getAs<LoopExit>() || + Loc.getAs<PostAllocatorCall>()); + HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred); + break; + } +} + +bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, + unsigned Steps, + ProgramStateRef InitState, + ExplodedNodeSet &Dst) { + bool DidNotFinish = ExecuteWorkList(L, Steps, InitState); + for (ExplodedGraph::eop_iterator I = G.eop_begin(), E = G.eop_end(); I != E; + ++I) { + Dst.Add(*I); + } + return DidNotFinish; +} + +void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) { + const CFGBlock *Blk = L.getDst(); + NodeBuilderContext BuilderCtx(*this, Blk, Pred); + + // Mark this block as visited. + const LocationContext *LC = Pred->getLocationContext(); + FunctionSummaries->markVisitedBasicBlock(Blk->getBlockID(), + LC->getDecl(), + LC->getCFG()->getNumBlockIDs()); + + // Check if we are entering the EXIT block. + if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { + assert(L.getLocationContext()->getCFG()->getExit().empty() && + "EXIT block cannot contain Stmts."); + + // Get return statement.. + const ReturnStmt *RS = nullptr; + if (!L.getSrc()->empty()) { + CFGElement LastElement = L.getSrc()->back(); + if (Optional<CFGStmt> LastStmt = LastElement.getAs<CFGStmt>()) { + RS = dyn_cast<ReturnStmt>(LastStmt->getStmt()); + } else if (Optional<CFGAutomaticObjDtor> AutoDtor = + LastElement.getAs<CFGAutomaticObjDtor>()) { + RS = dyn_cast<ReturnStmt>(AutoDtor->getTriggerStmt()); + } + } + + // Process the final state transition. + SubEng.processEndOfFunction(BuilderCtx, Pred, RS); + + // This path is done. Don't enqueue any more nodes. + return; + } + + // Call into the SubEngine to process entering the CFGBlock. + ExplodedNodeSet dstNodes; + BlockEntrance BE(Blk, Pred->getLocationContext()); + NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE); + SubEng.processCFGBlockEntrance(L, nodeBuilder, Pred); + + // Auto-generate a node. + if (!nodeBuilder.hasGeneratedNodes()) { + nodeBuilder.generateNode(Pred->State, Pred); + } + + // Enqueue nodes onto the worklist. + enqueue(dstNodes); +} + +void CoreEngine::HandleBlockEntrance(const BlockEntrance &L, + ExplodedNode *Pred) { + // Increment the block counter. + const LocationContext *LC = Pred->getLocationContext(); + unsigned BlockId = L.getBlock()->getBlockID(); + BlockCounter Counter = WList->getBlockCounter(); + Counter = BCounterFactory.IncrementCount(Counter, LC->getStackFrame(), + BlockId); + WList->setBlockCounter(Counter); + + // Process the entrance of the block. + if (Optional<CFGElement> E = L.getFirstElement()) { + NodeBuilderContext Ctx(*this, L.getBlock(), Pred); + SubEng.processCFGElement(*E, Pred, 0, &Ctx); + } + else + HandleBlockExit(L.getBlock(), Pred); +} + +void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) { + if (const Stmt *Term = B->getTerminator()) { + switch (Term->getStmtClass()) { + default: + llvm_unreachable("Analysis for this terminator not implemented."); + + case Stmt::CXXBindTemporaryExprClass: + HandleCleanupTemporaryBranch( + cast<CXXBindTemporaryExpr>(B->getTerminator().getStmt()), B, Pred); + return; + + // Model static initializers. + case Stmt::DeclStmtClass: + HandleStaticInit(cast<DeclStmt>(Term), B, Pred); + return; + + case Stmt::BinaryOperatorClass: // '&&' and '||' + HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred); + return; + + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: + HandleBranch(cast<AbstractConditionalOperator>(Term)->getCond(), + Term, B, Pred); + return; + + // FIXME: Use constant-folding in CFG construction to simplify this + // case. + + case Stmt::ChooseExprClass: + HandleBranch(cast<ChooseExpr>(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::CXXTryStmtClass: + // Generate a node for each of the successors. + // Our logic for EH analysis can certainly be improved. + for (CFGBlock::const_succ_iterator it = B->succ_begin(), + et = B->succ_end(); it != et; ++it) { + if (const CFGBlock *succ = *it) { + generateNode(BlockEdge(B, succ, Pred->getLocationContext()), + Pred->State, Pred); + } + } + return; + + case Stmt::DoStmtClass: + HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::CXXForRangeStmtClass: + HandleBranch(cast<CXXForRangeStmt>(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::ForStmtClass: + HandleBranch(cast<ForStmt>(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::ContinueStmtClass: + case Stmt::BreakStmtClass: + case Stmt::GotoStmtClass: + break; + + case Stmt::IfStmtClass: + HandleBranch(cast<IfStmt>(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::IndirectGotoStmtClass: { + // Only 1 successor: the indirect goto dispatch block. + assert(B->succ_size() == 1); + + IndirectGotoNodeBuilder + builder(Pred, B, cast<IndirectGotoStmt>(Term)->getTarget(), + *(B->succ_begin()), this); + + SubEng.processIndirectGoto(builder); + return; + } + + case Stmt::ObjCForCollectionStmtClass: + // In the case of ObjCForCollectionStmt, it appears twice in a CFG: + // + // (1) inside a basic block, which represents the binding of the + // 'element' variable to a value. + // (2) in a terminator, which represents the branch. + // + // For (1), subengines will bind a value (i.e., 0 or 1) indicating + // whether or not collection contains any more elements. We cannot + // just test to see if the element is nil because a container can + // contain nil elements. + HandleBranch(Term, Term, B, Pred); + return; + + case Stmt::SwitchStmtClass: { + SwitchNodeBuilder builder(Pred, B, cast<SwitchStmt>(Term)->getCond(), + this); + + SubEng.processSwitch(builder); + return; + } + + case Stmt::WhileStmtClass: + HandleBranch(cast<WhileStmt>(Term)->getCond(), Term, B, Pred); + return; + } + } + + assert(B->succ_size() == 1 && + "Blocks with no terminator should have at most 1 successor."); + + generateNode(BlockEdge(B, *(B->succ_begin()), Pred->getLocationContext()), + Pred->State, Pred); +} + +void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) { + NodeBuilderContext BuilderCtx(*this, CE.getEntry(), Pred); + SubEng.processCallEnter(BuilderCtx, CE, Pred); +} + +void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term, + const CFGBlock * B, ExplodedNode *Pred) { + assert(B->succ_size() == 2); + NodeBuilderContext Ctx(*this, B, Pred); + ExplodedNodeSet Dst; + SubEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()), + *(B->succ_begin() + 1)); + // Enqueue the new frontier onto the worklist. + enqueue(Dst); +} + +void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, + const CFGBlock *B, + ExplodedNode *Pred) { + assert(B->succ_size() == 2); + NodeBuilderContext Ctx(*this, B, Pred); + ExplodedNodeSet Dst; + SubEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()), + *(B->succ_begin() + 1)); + // Enqueue the new frontier onto the worklist. + enqueue(Dst); +} + +void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B, + ExplodedNode *Pred) { + assert(B->succ_size() == 2); + NodeBuilderContext Ctx(*this, B, Pred); + ExplodedNodeSet Dst; + SubEng.processStaticInitializer(DS, Ctx, Pred, Dst, + *(B->succ_begin()), *(B->succ_begin()+1)); + // Enqueue the new frontier onto the worklist. + enqueue(Dst); +} + +void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx, + ExplodedNode *Pred) { + assert(B); + assert(!B->empty()); + + if (StmtIdx == B->size()) + HandleBlockExit(B, Pred); + else { + NodeBuilderContext Ctx(*this, B, Pred); + SubEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx); + } +} + +/// generateNode - Utility method to generate nodes, hook up successors, +/// and add nodes to the worklist. +void CoreEngine::generateNode(const ProgramPoint &Loc, + ProgramStateRef State, + ExplodedNode *Pred) { + bool IsNew; + ExplodedNode *Node = G.getNode(Loc, State, false, &IsNew); + + if (Pred) + Node->addPredecessor(Pred, G); // Link 'Node' with its predecessor. + else { + assert(IsNew); + G.addRoot(Node); // 'Node' has no predecessor. Make it a root. + } + + // Only add 'Node' to the worklist if it was freshly generated. + if (IsNew) WList->enqueue(Node); +} + +void CoreEngine::enqueueStmtNode(ExplodedNode *N, + const CFGBlock *Block, unsigned Idx) { + assert(Block); + assert(!N->isSink()); + + // Check if this node entered a callee. + if (N->getLocation().getAs<CallEnter>()) { + // Still use the index of the CallExpr. It's needed to create the callee + // StackFrameContext. + WList->enqueue(N, Block, Idx); + return; + } + + // Do not create extra nodes. Move to the next CFG element. + if (N->getLocation().getAs<PostInitializer>() || + N->getLocation().getAs<PostImplicitCall>()|| + N->getLocation().getAs<LoopExit>()) { + WList->enqueue(N, Block, Idx+1); + return; + } + + if (N->getLocation().getAs<EpsilonPoint>()) { + WList->enqueue(N, Block, Idx); + return; + } + + if ((*Block)[Idx].getKind() == CFGElement::NewAllocator) { + WList->enqueue(N, Block, Idx+1); + return; + } + + // At this point, we know we're processing a normal statement. + CFGStmt CS = (*Block)[Idx].castAs<CFGStmt>(); + PostStmt Loc(CS.getStmt(), N->getLocationContext()); + + if (Loc == N->getLocation().withTag(nullptr)) { + // Note: 'N' should be a fresh node because otherwise it shouldn't be + // a member of Deferred. + WList->enqueue(N, Block, Idx+1); + return; + } + + bool IsNew; + ExplodedNode *Succ = G.getNode(Loc, N->getState(), false, &IsNew); + Succ->addPredecessor(N, G); + + if (IsNew) + WList->enqueue(Succ, Block, Idx+1); +} + +ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N, + const ReturnStmt *RS) { + // Create a CallExitBegin node and enqueue it. + const auto *LocCtx = cast<StackFrameContext>(N->getLocationContext()); + + // Use the callee location context. + CallExitBegin Loc(LocCtx, RS); + + bool isNew; + ExplodedNode *Node = G.getNode(Loc, N->getState(), false, &isNew); + Node->addPredecessor(N, G); + return isNew ? Node : nullptr; +} + +void CoreEngine::enqueue(ExplodedNodeSet &Set) { + for (const auto I : Set) + WList->enqueue(I); +} + +void CoreEngine::enqueue(ExplodedNodeSet &Set, + const CFGBlock *Block, unsigned Idx) { + for (const auto I : Set) + enqueueStmtNode(I, Block, Idx); +} + +void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS) { + for (auto I : Set) { + // If we are in an inlined call, generate CallExitBegin node. + if (I->getLocationContext()->getParent()) { + I = generateCallExitBeginNode(I, RS); + if (I) + WList->enqueue(I); + } else { + // TODO: We should run remove dead bindings here. + G.addEndOfPath(I); + NumPathsExplored++; + } + } +} + +void NodeBuilder::anchor() {} + +ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc, + ProgramStateRef State, + ExplodedNode *FromN, + bool MarkAsSink) { + HasGeneratedNodes = true; + bool IsNew; + ExplodedNode *N = C.Eng.G.getNode(Loc, State, MarkAsSink, &IsNew); + N->addPredecessor(FromN, C.Eng.G); + Frontier.erase(FromN); + + if (!IsNew) + return nullptr; + + if (!MarkAsSink) + Frontier.Add(N); + + return N; +} + +void NodeBuilderWithSinks::anchor() {} + +StmtNodeBuilder::~StmtNodeBuilder() { + if (EnclosingBldr) + for (const auto I : Frontier) + EnclosingBldr->addNodes(I); +} + +void BranchNodeBuilder::anchor() {} + +ExplodedNode *BranchNodeBuilder::generateNode(ProgramStateRef State, + bool branch, + ExplodedNode *NodePred) { + // If the branch has been marked infeasible we should not generate a node. + if (!isFeasible(branch)) + return nullptr; + + ProgramPoint Loc = BlockEdge(C.Block, branch ? DstT:DstF, + NodePred->getLocationContext()); + ExplodedNode *Succ = generateNodeImpl(Loc, State, NodePred); + return Succ; +} + +ExplodedNode* +IndirectGotoNodeBuilder::generateNode(const iterator &I, + ProgramStateRef St, + bool IsSink) { + bool IsNew; + ExplodedNode *Succ = + Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), + St, IsSink, &IsNew); + Succ->addPredecessor(Pred, Eng.G); + + if (!IsNew) + return nullptr; + + if (!IsSink) + Eng.WList->enqueue(Succ); + + return Succ; +} + +ExplodedNode* +SwitchNodeBuilder::generateCaseStmtNode(const iterator &I, + ProgramStateRef St) { + bool IsNew; + ExplodedNode *Succ = + Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()), + St, false, &IsNew); + Succ->addPredecessor(Pred, Eng.G); + if (!IsNew) + return nullptr; + + Eng.WList->enqueue(Succ); + return Succ; +} + +ExplodedNode* +SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St, + bool IsSink) { + // Get the block for the default case. + assert(Src->succ_rbegin() != Src->succ_rend()); + CFGBlock *DefaultBlock = *Src->succ_rbegin(); + + // Sanity check for default blocks that are unreachable and not caught + // by earlier stages. + if (!DefaultBlock) + return nullptr; + + bool IsNew; + ExplodedNode *Succ = + Eng.G.getNode(BlockEdge(Src, DefaultBlock, Pred->getLocationContext()), + St, IsSink, &IsNew); + Succ->addPredecessor(Pred, Eng.G); + + if (!IsNew) + return nullptr; + + if (!IsSink) + Eng.WList->enqueue(Succ); + + return Succ; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp new file mode 100644 index 000000000000..da7854df1def --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp @@ -0,0 +1,86 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines APIs that track and query dynamic type information. This +// information can be used to devirtualize calls during the symbolic execution +// or do type checking. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> + +namespace clang { +namespace ento { + +DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, + const MemRegion *Reg) { + Reg = Reg->StripCasts(); + + // Look up the dynamic type in the GDM. + const DynamicTypeInfo *GDMType = State->get<DynamicTypeMap>(Reg); + if (GDMType) + return *GDMType; + + // Otherwise, fall back to what we know about the region. + if (const auto *TR = dyn_cast<TypedRegion>(Reg)) + return DynamicTypeInfo(TR->getLocationType(), /*CanBeSubclass=*/false); + + if (const auto *SR = dyn_cast<SymbolicRegion>(Reg)) { + SymbolRef Sym = SR->getSymbol(); + return DynamicTypeInfo(Sym->getType()); + } + + return {}; +} + +ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg, + DynamicTypeInfo NewTy) { + Reg = Reg->StripCasts(); + ProgramStateRef NewState = State->set<DynamicTypeMap>(Reg, NewTy); + assert(NewState); + return NewState; +} + +void printDynamicTypeInfo(ProgramStateRef State, raw_ostream &Out, + const char *NL, const char *Sep) { + bool First = true; + for (const auto &I : State->get<DynamicTypeMap>()) { + if (First) { + Out << NL << "Dynamic types of regions:" << NL; + First = false; + } + const MemRegion *MR = I.first; + const DynamicTypeInfo &DTI = I.second; + Out << MR << " : "; + if (DTI.isValid()) { + Out << DTI.getType()->getPointeeType().getAsString(); + if (DTI.canBeASubClass()) { + Out << " (or its subclass)"; + } + } else { + Out << "Invalid type info"; + } + Out << NL; + } +} + +void *ProgramStateTrait<DynamicTypeMap>::GDMIndex() { + static int index = 0; + return &index; +} + +} // namespace ento +} // namespace clang diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp new file mode 100644 index 000000000000..b45f93b6dde8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp @@ -0,0 +1,242 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defined the Environment and EnvironmentManager classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/Stmt.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> + +using namespace clang; +using namespace ento; + +static const Expr *ignoreTransparentExprs(const Expr *E) { + E = E->IgnoreParens(); + + switch (E->getStmtClass()) { + case Stmt::OpaqueValueExprClass: + E = cast<OpaqueValueExpr>(E)->getSourceExpr(); + break; + case Stmt::ExprWithCleanupsClass: + E = cast<ExprWithCleanups>(E)->getSubExpr(); + break; + case Stmt::ConstantExprClass: + E = cast<ConstantExpr>(E)->getSubExpr(); + break; + case Stmt::CXXBindTemporaryExprClass: + E = cast<CXXBindTemporaryExpr>(E)->getSubExpr(); + break; + case Stmt::SubstNonTypeTemplateParmExprClass: + E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement(); + break; + default: + // This is the base case: we can't look through more than we already have. + return E; + } + + return ignoreTransparentExprs(E); +} + +static const Stmt *ignoreTransparentExprs(const Stmt *S) { + if (const auto *E = dyn_cast<Expr>(S)) + return ignoreTransparentExprs(E); + return S; +} + +EnvironmentEntry::EnvironmentEntry(const Stmt *S, const LocationContext *L) + : std::pair<const Stmt *, + const StackFrameContext *>(ignoreTransparentExprs(S), + L ? L->getStackFrame() + : nullptr) {} + +SVal Environment::lookupExpr(const EnvironmentEntry &E) const { + const SVal* X = ExprBindings.lookup(E); + if (X) { + SVal V = *X; + return V; + } + return UnknownVal(); +} + +SVal Environment::getSVal(const EnvironmentEntry &Entry, + SValBuilder& svalBuilder) const { + const Stmt *S = Entry.getStmt(); + const LocationContext *LCtx = Entry.getLocationContext(); + + switch (S->getStmtClass()) { + case Stmt::CXXBindTemporaryExprClass: + case Stmt::ExprWithCleanupsClass: + case Stmt::GenericSelectionExprClass: + case Stmt::OpaqueValueExprClass: + case Stmt::ConstantExprClass: + case Stmt::ParenExprClass: + case Stmt::SubstNonTypeTemplateParmExprClass: + llvm_unreachable("Should have been handled by ignoreTransparentExprs"); + + case Stmt::AddrLabelExprClass: + case Stmt::CharacterLiteralClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::CXXScalarValueInitExprClass: + case Stmt::ImplicitValueInitExprClass: + case Stmt::IntegerLiteralClass: + case Stmt::ObjCBoolLiteralExprClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::StringLiteralClass: + case Stmt::TypeTraitExprClass: + // Known constants; defer to SValBuilder. + return svalBuilder.getConstantVal(cast<Expr>(S)).getValue(); + + case Stmt::ReturnStmtClass: { + const auto *RS = cast<ReturnStmt>(S); + if (const Expr *RE = RS->getRetValue()) + return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder); + return UndefinedVal(); + } + + // Handle all other Stmt* using a lookup. + default: + return lookupExpr(EnvironmentEntry(S, LCtx)); + } +} + +Environment EnvironmentManager::bindExpr(Environment Env, + const EnvironmentEntry &E, + SVal V, + bool Invalidate) { + if (V.isUnknown()) { + if (Invalidate) + return Environment(F.remove(Env.ExprBindings, E)); + else + return Env; + } + return Environment(F.add(Env.ExprBindings, E, V)); +} + +namespace { + +class MarkLiveCallback final : public SymbolVisitor { + SymbolReaper &SymReaper; + +public: + MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} + + bool VisitSymbol(SymbolRef sym) override { + SymReaper.markLive(sym); + return true; + } + + bool VisitMemRegion(const MemRegion *R) override { + SymReaper.markLive(R); + return true; + } +}; + +} // namespace + +// removeDeadBindings: +// - Remove subexpression bindings. +// - Remove dead block expression bindings. +// - Keep live block expression bindings: +// - Mark their reachable symbols live in SymbolReaper, +// see ScanReachableSymbols. +// - Mark the region in DRoots if the binding is a loc::MemRegionVal. +Environment +EnvironmentManager::removeDeadBindings(Environment Env, + SymbolReaper &SymReaper, + ProgramStateRef ST) { + // We construct a new Environment object entirely, as this is cheaper than + // individually removing all the subexpression bindings (which will greatly + // outnumber block-level expression bindings). + Environment NewEnv = getInitialEnvironment(); + + MarkLiveCallback CB(SymReaper); + ScanReachableSymbols RSScaner(ST, CB); + + llvm::ImmutableMapRef<EnvironmentEntry, SVal> + EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(), + F.getTreeFactory()); + + // Iterate over the block-expr bindings. + for (Environment::iterator I = Env.begin(), E = Env.end(); + I != E; ++I) { + const EnvironmentEntry &BlkExpr = I.getKey(); + const SVal &X = I.getData(); + + if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) { + // Copy the binding to the new map. + EBMapRef = EBMapRef.add(BlkExpr, X); + + // Mark all symbols in the block expr's value live. + RSScaner.scan(X); + } + } + + NewEnv.ExprBindings = EBMapRef.asImmutableMap(); + return NewEnv; +} + +void Environment::print(raw_ostream &Out, const char *NL, + const char *Sep, + const ASTContext &Context, + const LocationContext *WithLC) const { + if (ExprBindings.isEmpty()) + return; + + if (!WithLC) { + // Find the freshest location context. + llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts; + for (auto I : *this) { + const LocationContext *LC = I.first.getLocationContext(); + if (FoundContexts.count(LC) == 0) { + // This context is fresher than all other contexts so far. + WithLC = LC; + for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent()) + FoundContexts.insert(LCI); + } + } + } + + assert(WithLC); + + PrintingPolicy PP = Context.getPrintingPolicy(); + + Out << NL << "Expressions by stack frame:" << NL; + WithLC->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { + for (auto I : ExprBindings) { + if (I.first.getLocationContext() != LC) + continue; + + const Stmt *S = I.first.getStmt(); + assert(S != nullptr && "Expected non-null Stmt"); + + Out << "(LC" << LC->getID() << ", S" << S->getID(Context) << ") "; + S->printPretty(Out, /*Helper=*/nullptr, PP); + Out << " : " << I.second << NL; + } + }); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp new file mode 100644 index 000000000000..d6bcbb96b55f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp @@ -0,0 +1,441 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines the template classes ExplodedNode and ExplodedGraph, +// which represent a path-sensitive, intra-procedural "exploded graph." +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/Stmt.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Analysis/Support/BumpVector.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include <cassert> +#include <memory> + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Cleanup. +//===----------------------------------------------------------------------===// + +ExplodedGraph::ExplodedGraph() = default; + +ExplodedGraph::~ExplodedGraph() = default; + +//===----------------------------------------------------------------------===// +// Node reclamation. +//===----------------------------------------------------------------------===// + +bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) { + if (!Ex->isLValue()) + return false; + return isa<DeclRefExpr>(Ex) || + isa<MemberExpr>(Ex) || + isa<ObjCIvarRefExpr>(Ex); +} + +bool ExplodedGraph::shouldCollect(const ExplodedNode *node) { + // First, we only consider nodes for reclamation of the following + // conditions apply: + // + // (1) 1 predecessor (that has one successor) + // (2) 1 successor (that has one predecessor) + // + // If a node has no successor it is on the "frontier", while a node + // with no predecessor is a root. + // + // After these prerequisites, we discard all "filler" nodes that + // are used only for intermediate processing, and are not essential + // for analyzer history: + // + // (a) PreStmtPurgeDeadSymbols + // + // We then discard all other nodes where *all* of the following conditions + // apply: + // + // (3) The ProgramPoint is for a PostStmt, but not a PostStore. + // (4) There is no 'tag' for the ProgramPoint. + // (5) The 'store' is the same as the predecessor. + // (6) The 'GDM' is the same as the predecessor. + // (7) The LocationContext is the same as the predecessor. + // (8) Expressions that are *not* lvalue expressions. + // (9) The PostStmt isn't for a non-consumed Stmt or Expr. + // (10) The successor is neither a CallExpr StmtPoint nor a CallEnter or + // PreImplicitCall (so that we would be able to find it when retrying a + // call with no inlining). + // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well. + + // Conditions 1 and 2. + if (node->pred_size() != 1 || node->succ_size() != 1) + return false; + + const ExplodedNode *pred = *(node->pred_begin()); + if (pred->succ_size() != 1) + return false; + + const ExplodedNode *succ = *(node->succ_begin()); + if (succ->pred_size() != 1) + return false; + + // Now reclaim any nodes that are (by definition) not essential to + // analysis history and are not consulted by any client code. + ProgramPoint progPoint = node->getLocation(); + if (progPoint.getAs<PreStmtPurgeDeadSymbols>()) + return !progPoint.getTag(); + + // Condition 3. + if (!progPoint.getAs<PostStmt>() || progPoint.getAs<PostStore>()) + return false; + + // Condition 4. + if (progPoint.getTag()) + return false; + + // Conditions 5, 6, and 7. + ProgramStateRef state = node->getState(); + ProgramStateRef pred_state = pred->getState(); + if (state->store != pred_state->store || state->GDM != pred_state->GDM || + progPoint.getLocationContext() != pred->getLocationContext()) + return false; + + // All further checks require expressions. As per #3, we know that we have + // a PostStmt. + const Expr *Ex = dyn_cast<Expr>(progPoint.castAs<PostStmt>().getStmt()); + if (!Ex) + return false; + + // Condition 8. + // Do not collect nodes for "interesting" lvalue expressions since they are + // used extensively for generating path diagnostics. + if (isInterestingLValueExpr(Ex)) + return false; + + // Condition 9. + // Do not collect nodes for non-consumed Stmt or Expr to ensure precise + // diagnostic generation; specifically, so that we could anchor arrows + // pointing to the beginning of statements (as written in code). + ParentMap &PM = progPoint.getLocationContext()->getParentMap(); + if (!PM.isConsumedExpr(Ex)) + return false; + + // Condition 10. + const ProgramPoint SuccLoc = succ->getLocation(); + if (Optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>()) + if (CallEvent::isCallStmt(SP->getStmt())) + return false; + + // Condition 10, continuation. + if (SuccLoc.getAs<CallEnter>() || SuccLoc.getAs<PreImplicitCall>()) + return false; + + return true; +} + +void ExplodedGraph::collectNode(ExplodedNode *node) { + // Removing a node means: + // (a) changing the predecessors successor to the successor of this node + // (b) changing the successors predecessor to the predecessor of this node + // (c) Putting 'node' onto freeNodes. + assert(node->pred_size() == 1 || node->succ_size() == 1); + ExplodedNode *pred = *(node->pred_begin()); + ExplodedNode *succ = *(node->succ_begin()); + pred->replaceSuccessor(succ); + succ->replacePredecessor(pred); + FreeNodes.push_back(node); + Nodes.RemoveNode(node); + --NumNodes; + node->~ExplodedNode(); +} + +void ExplodedGraph::reclaimRecentlyAllocatedNodes() { + if (ChangedNodes.empty()) + return; + + // Only periodically reclaim nodes so that we can build up a set of + // nodes that meet the reclamation criteria. Freshly created nodes + // by definition have no successor, and thus cannot be reclaimed (see below). + assert(ReclaimCounter > 0); + if (--ReclaimCounter != 0) + return; + ReclaimCounter = ReclaimNodeInterval; + + for (const auto node : ChangedNodes) + if (shouldCollect(node)) + collectNode(node); + ChangedNodes.clear(); +} + +//===----------------------------------------------------------------------===// +// ExplodedNode. +//===----------------------------------------------------------------------===// + +// An NodeGroup's storage type is actually very much like a TinyPtrVector: +// it can be either a pointer to a single ExplodedNode, or a pointer to a +// BumpVector allocated with the ExplodedGraph's allocator. This allows the +// common case of single-node NodeGroups to be implemented with no extra memory. +// +// Consequently, each of the NodeGroup methods have up to four cases to handle: +// 1. The flag is set and this group does not actually contain any nodes. +// 2. The group is empty, in which case the storage value is null. +// 3. The group contains a single node. +// 4. The group contains more than one node. +using ExplodedNodeVector = BumpVector<ExplodedNode *>; +using GroupStorage = llvm::PointerUnion<ExplodedNode *, ExplodedNodeVector *>; + +void ExplodedNode::addPredecessor(ExplodedNode *V, ExplodedGraph &G) { + assert(!V->isSink()); + Preds.addNode(V, G); + V->Succs.addNode(this, G); +} + +void ExplodedNode::NodeGroup::replaceNode(ExplodedNode *node) { + assert(!getFlag()); + + GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P); + assert(Storage.is<ExplodedNode *>()); + Storage = node; + assert(Storage.is<ExplodedNode *>()); +} + +void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) { + assert(!getFlag()); + + GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P); + if (Storage.isNull()) { + Storage = N; + assert(Storage.is<ExplodedNode *>()); + return; + } + + ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>(); + + if (!V) { + // Switch from single-node to multi-node representation. + ExplodedNode *Old = Storage.get<ExplodedNode *>(); + + BumpVectorContext &Ctx = G.getNodeAllocator(); + V = G.getAllocator().Allocate<ExplodedNodeVector>(); + new (V) ExplodedNodeVector(Ctx, 4); + V->push_back(Old, Ctx); + + Storage = V; + assert(!getFlag()); + assert(Storage.is<ExplodedNodeVector *>()); + } + + V->push_back(N, G.getNodeAllocator()); +} + +unsigned ExplodedNode::NodeGroup::size() const { + if (getFlag()) + return 0; + + const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P); + if (Storage.isNull()) + return 0; + if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>()) + return V->size(); + return 1; +} + +ExplodedNode * const *ExplodedNode::NodeGroup::begin() const { + if (getFlag()) + return nullptr; + + const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P); + if (Storage.isNull()) + return nullptr; + if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>()) + return V->begin(); + return Storage.getAddrOfPtr1(); +} + +ExplodedNode * const *ExplodedNode::NodeGroup::end() const { + if (getFlag()) + return nullptr; + + const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P); + if (Storage.isNull()) + return nullptr; + if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>()) + return V->end(); + return Storage.getAddrOfPtr1() + 1; +} + +int64_t ExplodedNode::getID(ExplodedGraph *G) const { + return G->getAllocator().identifyKnownAlignedObject<ExplodedNode>(this); +} + +bool ExplodedNode::isTrivial() const { + return pred_size() == 1 && succ_size() == 1 && + getFirstPred()->getState()->getID() == getState()->getID() && + getFirstPred()->succ_size() == 1; +} + +ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L, + ProgramStateRef State, + bool IsSink, + bool* IsNew) { + // Profile 'State' to determine if we already have an existing node. + llvm::FoldingSetNodeID profile; + void *InsertPos = nullptr; + + NodeTy::Profile(profile, L, State, IsSink); + NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos); + + if (!V) { + if (!FreeNodes.empty()) { + V = FreeNodes.back(); + FreeNodes.pop_back(); + } + else { + // Allocate a new node. + V = (NodeTy*) getAllocator().Allocate<NodeTy>(); + } + + new (V) NodeTy(L, State, IsSink); + + if (ReclaimNodeInterval) + ChangedNodes.push_back(V); + + // Insert the node into the node set and return it. + Nodes.InsertNode(V, InsertPos); + ++NumNodes; + + if (IsNew) *IsNew = true; + } + else + if (IsNew) *IsNew = false; + + return V; +} + +ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L, + ProgramStateRef State, + bool IsSink) { + NodeTy *V = (NodeTy *) getAllocator().Allocate<NodeTy>(); + new (V) NodeTy(L, State, IsSink); + return V; +} + +std::unique_ptr<ExplodedGraph> +ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks, + InterExplodedGraphMap *ForwardMap, + InterExplodedGraphMap *InverseMap) const { + if (Nodes.empty()) + return nullptr; + + using Pass1Ty = llvm::DenseSet<const ExplodedNode *>; + Pass1Ty Pass1; + + using Pass2Ty = InterExplodedGraphMap; + InterExplodedGraphMap Pass2Scratch; + Pass2Ty &Pass2 = ForwardMap ? *ForwardMap : Pass2Scratch; + + SmallVector<const ExplodedNode*, 10> WL1, WL2; + + // ===- Pass 1 (reverse DFS) -=== + for (const auto Sink : Sinks) + if (Sink) + WL1.push_back(Sink); + + // Process the first worklist until it is empty. + while (!WL1.empty()) { + const ExplodedNode *N = WL1.pop_back_val(); + + // Have we already visited this node? If so, continue to the next one. + if (!Pass1.insert(N).second) + continue; + + // If this is a root enqueue it to the second worklist. + if (N->Preds.empty()) { + WL2.push_back(N); + continue; + } + + // Visit our predecessors and enqueue them. + WL1.append(N->Preds.begin(), N->Preds.end()); + } + + // We didn't hit a root? Return with a null pointer for the new graph. + if (WL2.empty()) + return nullptr; + + // Create an empty graph. + std::unique_ptr<ExplodedGraph> G = MakeEmptyGraph(); + + // ===- Pass 2 (forward DFS to construct the new graph) -=== + while (!WL2.empty()) { + const ExplodedNode *N = WL2.pop_back_val(); + + // Skip this node if we have already processed it. + if (Pass2.find(N) != Pass2.end()) + continue; + + // Create the corresponding node in the new graph and record the mapping + // from the old node to the new node. + ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State, N->isSink()); + Pass2[N] = NewN; + + // Also record the reverse mapping from the new node to the old node. + if (InverseMap) (*InverseMap)[NewN] = N; + + // If this node is a root, designate it as such in the graph. + if (N->Preds.empty()) + G->addRoot(NewN); + + // In the case that some of the intended predecessors of NewN have already + // been created, we should hook them up as predecessors. + + // Walk through the predecessors of 'N' and hook up their corresponding + // nodes in the new graph (if any) to the freshly created node. + for (ExplodedNode::pred_iterator I = N->Preds.begin(), E = N->Preds.end(); + I != E; ++I) { + Pass2Ty::iterator PI = Pass2.find(*I); + if (PI == Pass2.end()) + continue; + + NewN->addPredecessor(const_cast<ExplodedNode *>(PI->second), *G); + } + + // In the case that some of the intended successors of NewN have already + // been created, we should hook them up as successors. Otherwise, enqueue + // the new nodes from the original graph that should have nodes created + // in the new graph. + for (ExplodedNode::succ_iterator I = N->Succs.begin(), E = N->Succs.end(); + I != E; ++I) { + Pass2Ty::iterator PI = Pass2.find(*I); + if (PI != Pass2.end()) { + const_cast<ExplodedNode *>(PI->second)->addPredecessor(NewN, *G); + continue; + } + + // Enqueue nodes to the worklist that were marked during pass 1. + if (Pass1.count(*I)) + WL2.push_back(*I); + } + } + + return G; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp new file mode 100644 index 000000000000..151eef56fece --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -0,0 +1,3115 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a meta-engine for path-sensitive dataflow analysis that +// is built on GREngine, but provides the boilerplate to execute transfer +// functions and build the ExplodedGraph at the expression level. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "PrettyStackTraceLocationContext.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/StmtCXX.h" +#include "clang/AST/StmtObjC.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/ConstructionContext.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/PrettyStackTrace.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/DOTGraphTraits.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/GraphWriter.h" +#include "llvm/Support/SaveAndRestore.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cstdint> +#include <memory> +#include <string> +#include <tuple> +#include <utility> +#include <vector> + +using namespace clang; +using namespace ento; + +#define DEBUG_TYPE "ExprEngine" + +STATISTIC(NumRemoveDeadBindings, + "The # of times RemoveDeadBindings is called"); +STATISTIC(NumMaxBlockCountReached, + "The # of aborted paths due to reaching the maximum block count in " + "a top level function"); +STATISTIC(NumMaxBlockCountReachedInInlined, + "The # of aborted paths due to reaching the maximum block count in " + "an inlined function"); +STATISTIC(NumTimesRetriedWithoutInlining, + "The # of times we re-evaluated a call without inlining"); + +//===----------------------------------------------------------------------===// +// Internal program state traits. +//===----------------------------------------------------------------------===// + +namespace { + +// When modeling a C++ constructor, for a variety of reasons we need to track +// the location of the object for the duration of its ConstructionContext. +// ObjectsUnderConstruction maps statements within the construction context +// to the object's location, so that on every such statement the location +// could have been retrieved. + +/// ConstructedObjectKey is used for being able to find the path-sensitive +/// memory region of a freshly constructed object while modeling the AST node +/// that syntactically represents the object that is being constructed. +/// Semantics of such nodes may sometimes require access to the region that's +/// not otherwise present in the program state, or to the very fact that +/// the construction context was present and contained references to these +/// AST nodes. +class ConstructedObjectKey { + typedef std::pair<ConstructionContextItem, const LocationContext *> + ConstructedObjectKeyImpl; + + const ConstructedObjectKeyImpl Impl; + + const void *getAnyASTNodePtr() const { + if (const Stmt *S = getItem().getStmtOrNull()) + return S; + else + return getItem().getCXXCtorInitializer(); + } + +public: + explicit ConstructedObjectKey(const ConstructionContextItem &Item, + const LocationContext *LC) + : Impl(Item, LC) {} + + const ConstructionContextItem &getItem() const { return Impl.first; } + const LocationContext *getLocationContext() const { return Impl.second; } + + ASTContext &getASTContext() const { + return getLocationContext()->getDecl()->getASTContext(); + } + + void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) { + OS << "(LC" << getLocationContext()->getID() << ','; + if (const Stmt *S = getItem().getStmtOrNull()) + OS << 'S' << S->getID(getASTContext()); + else + OS << 'I' << getItem().getCXXCtorInitializer()->getID(getASTContext()); + OS << ',' << getItem().getKindAsString(); + if (getItem().getKind() == ConstructionContextItem::ArgumentKind) + OS << " #" << getItem().getIndex(); + OS << ") "; + if (const Stmt *S = getItem().getStmtOrNull()) { + S->printPretty(OS, Helper, PP); + } else { + const CXXCtorInitializer *I = getItem().getCXXCtorInitializer(); + OS << I->getAnyMember()->getNameAsString(); + } + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.Add(Impl.first); + ID.AddPointer(Impl.second); + } + + bool operator==(const ConstructedObjectKey &RHS) const { + return Impl == RHS.Impl; + } + + bool operator<(const ConstructedObjectKey &RHS) const { + return Impl < RHS.Impl; + } +}; +} // namespace + +typedef llvm::ImmutableMap<ConstructedObjectKey, SVal> + ObjectsUnderConstructionMap; +REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction, + ObjectsUnderConstructionMap) + +//===----------------------------------------------------------------------===// +// Engine construction and deletion. +//===----------------------------------------------------------------------===// + +static const char* TagProviderName = "ExprEngine"; + +ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU, + AnalysisManager &mgr, + SetOfConstDecls *VisitedCalleesIn, + FunctionSummariesTy *FS, + InliningModes HowToInlineIn) + : CTU(CTU), AMgr(mgr), + AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()), + Engine(*this, FS, mgr.getAnalyzerOptions()), G(Engine.getGraph()), + StateMgr(getContext(), mgr.getStoreManagerCreator(), + mgr.getConstraintManagerCreator(), G.getAllocator(), + this), + SymMgr(StateMgr.getSymbolManager()), + svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()), + BR(mgr, *this), + VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) { + unsigned TrimInterval = mgr.options.GraphTrimInterval; + if (TrimInterval != 0) { + // Enable eager node reclamation when constructing the ExplodedGraph. + G.enableNodeReclamation(TrimInterval); + } +} + +ExprEngine::~ExprEngine() { + BR.FlushReports(); +} + +//===----------------------------------------------------------------------===// +// Utility methods. +//===----------------------------------------------------------------------===// + +ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) { + ProgramStateRef state = StateMgr.getInitialState(InitLoc); + const Decl *D = InitLoc->getDecl(); + + // Preconditions. + // FIXME: It would be nice if we had a more general mechanism to add + // such preconditions. Some day. + do { + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { + // Precondition: the first argument of 'main' is an integer guaranteed + // to be > 0. + const IdentifierInfo *II = FD->getIdentifier(); + if (!II || !(II->getName() == "main" && FD->getNumParams() > 0)) + break; + + const ParmVarDecl *PD = FD->getParamDecl(0); + QualType T = PD->getType(); + const auto *BT = dyn_cast<BuiltinType>(T); + if (!BT || !BT->isInteger()) + break; + + const MemRegion *R = state->getRegion(PD, InitLoc); + if (!R) + break; + + SVal V = state->getSVal(loc::MemRegionVal(R)); + SVal Constraint_untested = evalBinOp(state, BO_GT, V, + svalBuilder.makeZeroVal(T), + svalBuilder.getConditionType()); + + Optional<DefinedOrUnknownSVal> Constraint = + Constraint_untested.getAs<DefinedOrUnknownSVal>(); + + if (!Constraint) + break; + + if (ProgramStateRef newState = state->assume(*Constraint, true)) + state = newState; + } + break; + } + while (false); + + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) { + // Precondition: 'self' is always non-null upon entry to an Objective-C + // method. + const ImplicitParamDecl *SelfD = MD->getSelfDecl(); + const MemRegion *R = state->getRegion(SelfD, InitLoc); + SVal V = state->getSVal(loc::MemRegionVal(R)); + + if (Optional<Loc> LV = V.getAs<Loc>()) { + // Assume that the pointer value in 'self' is non-null. + state = state->assume(*LV, true); + assert(state && "'self' cannot be null"); + } + } + + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { + if (!MD->isStatic()) { + // Precondition: 'this' is always non-null upon entry to the + // top-level function. This is our starting assumption for + // analyzing an "open" program. + const StackFrameContext *SFC = InitLoc->getStackFrame(); + if (SFC->getParent() == nullptr) { + loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC); + SVal V = state->getSVal(L); + if (Optional<Loc> LV = V.getAs<Loc>()) { + state = state->assume(*LV, true); + assert(state && "'this' cannot be null"); + } + } + } + } + + return state; +} + +ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded( + ProgramStateRef State, const LocationContext *LC, + const Expr *InitWithAdjustments, const Expr *Result, + const SubRegion **OutRegionWithAdjustments) { + // FIXME: This function is a hack that works around the quirky AST + // we're often having with respect to C++ temporaries. If only we modelled + // the actual execution order of statements properly in the CFG, + // all the hassle with adjustments would not be necessary, + // and perhaps the whole function would be removed. + SVal InitValWithAdjustments = State->getSVal(InitWithAdjustments, LC); + if (!Result) { + // If we don't have an explicit result expression, we're in "if needed" + // mode. Only create a region if the current value is a NonLoc. + if (!InitValWithAdjustments.getAs<NonLoc>()) { + if (OutRegionWithAdjustments) + *OutRegionWithAdjustments = nullptr; + return State; + } + Result = InitWithAdjustments; + } else { + // We need to create a region no matter what. For sanity, make sure we don't + // try to stuff a Loc into a non-pointer temporary region. + assert(!InitValWithAdjustments.getAs<Loc>() || + Loc::isLocType(Result->getType()) || + Result->getType()->isMemberPointerType()); + } + + ProgramStateManager &StateMgr = State->getStateManager(); + MemRegionManager &MRMgr = StateMgr.getRegionManager(); + StoreManager &StoreMgr = StateMgr.getStoreManager(); + + // MaterializeTemporaryExpr may appear out of place, after a few field and + // base-class accesses have been made to the object, even though semantically + // it is the whole object that gets materialized and lifetime-extended. + // + // For example: + // + // `-MaterializeTemporaryExpr + // `-MemberExpr + // `-CXXTemporaryObjectExpr + // + // instead of the more natural + // + // `-MemberExpr + // `-MaterializeTemporaryExpr + // `-CXXTemporaryObjectExpr + // + // Use the usual methods for obtaining the expression of the base object, + // and record the adjustments that we need to make to obtain the sub-object + // that the whole expression 'Ex' refers to. This trick is usual, + // in the sense that CodeGen takes a similar route. + + SmallVector<const Expr *, 2> CommaLHSs; + SmallVector<SubobjectAdjustment, 2> Adjustments; + + const Expr *Init = InitWithAdjustments->skipRValueSubobjectAdjustments( + CommaLHSs, Adjustments); + + // Take the region for Init, i.e. for the whole object. If we do not remember + // the region in which the object originally was constructed, come up with + // a new temporary region out of thin air and copy the contents of the object + // (which are currently present in the Environment, because Init is an rvalue) + // into that region. This is not correct, but it is better than nothing. + const TypedValueRegion *TR = nullptr; + if (const auto *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) { + if (Optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) { + State = finishObjectConstruction(State, MT, LC); + State = State->BindExpr(Result, LC, *V); + return State; + } else { + StorageDuration SD = MT->getStorageDuration(); + // If this object is bound to a reference with static storage duration, we + // put it in a different region to prevent "address leakage" warnings. + if (SD == SD_Static || SD == SD_Thread) { + TR = MRMgr.getCXXStaticTempObjectRegion(Init); + } else { + TR = MRMgr.getCXXTempObjectRegion(Init, LC); + } + } + } else { + TR = MRMgr.getCXXTempObjectRegion(Init, LC); + } + + SVal Reg = loc::MemRegionVal(TR); + SVal BaseReg = Reg; + + // Make the necessary adjustments to obtain the sub-object. + for (auto I = Adjustments.rbegin(), E = Adjustments.rend(); I != E; ++I) { + const SubobjectAdjustment &Adj = *I; + switch (Adj.Kind) { + case SubobjectAdjustment::DerivedToBaseAdjustment: + Reg = StoreMgr.evalDerivedToBase(Reg, Adj.DerivedToBase.BasePath); + break; + case SubobjectAdjustment::FieldAdjustment: + Reg = StoreMgr.getLValueField(Adj.Field, Reg); + break; + case SubobjectAdjustment::MemberPointerAdjustment: + // FIXME: Unimplemented. + State = State->invalidateRegions(Reg, InitWithAdjustments, + currBldrCtx->blockCount(), LC, true, + nullptr, nullptr, nullptr); + return State; + } + } + + // What remains is to copy the value of the object to the new region. + // FIXME: In other words, what we should always do is copy value of the + // Init expression (which corresponds to the bigger object) to the whole + // temporary region TR. However, this value is often no longer present + // in the Environment. If it has disappeared, we instead invalidate TR. + // Still, what we can do is assign the value of expression Ex (which + // corresponds to the sub-object) to the TR's sub-region Reg. At least, + // values inside Reg would be correct. + SVal InitVal = State->getSVal(Init, LC); + if (InitVal.isUnknown()) { + InitVal = getSValBuilder().conjureSymbolVal(Result, LC, Init->getType(), + currBldrCtx->blockCount()); + State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false); + + // Then we'd need to take the value that certainly exists and bind it + // over. + if (InitValWithAdjustments.isUnknown()) { + // Try to recover some path sensitivity in case we couldn't + // compute the value. + InitValWithAdjustments = getSValBuilder().conjureSymbolVal( + Result, LC, InitWithAdjustments->getType(), + currBldrCtx->blockCount()); + } + State = + State->bindLoc(Reg.castAs<Loc>(), InitValWithAdjustments, LC, false); + } else { + State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false); + } + + // The result expression would now point to the correct sub-region of the + // newly created temporary region. Do this last in order to getSVal of Init + // correctly in case (Result == Init). + if (Result->isGLValue()) { + State = State->BindExpr(Result, LC, Reg); + } else { + State = State->BindExpr(Result, LC, InitValWithAdjustments); + } + + // Notify checkers once for two bindLoc()s. + State = processRegionChange(State, TR, LC); + + if (OutRegionWithAdjustments) + *OutRegionWithAdjustments = cast<SubRegion>(Reg.getAsRegion()); + return State; +} + +ProgramStateRef +ExprEngine::addObjectUnderConstruction(ProgramStateRef State, + const ConstructionContextItem &Item, + const LocationContext *LC, SVal V) { + ConstructedObjectKey Key(Item, LC->getStackFrame()); + // FIXME: Currently the state might already contain the marker due to + // incorrect handling of temporaries bound to default parameters. + assert(!State->get<ObjectsUnderConstruction>(Key) || + Key.getItem().getKind() == + ConstructionContextItem::TemporaryDestructorKind); + return State->set<ObjectsUnderConstruction>(Key, V); +} + +Optional<SVal> +ExprEngine::getObjectUnderConstruction(ProgramStateRef State, + const ConstructionContextItem &Item, + const LocationContext *LC) { + ConstructedObjectKey Key(Item, LC->getStackFrame()); + return Optional<SVal>::create(State->get<ObjectsUnderConstruction>(Key)); +} + +ProgramStateRef +ExprEngine::finishObjectConstruction(ProgramStateRef State, + const ConstructionContextItem &Item, + const LocationContext *LC) { + ConstructedObjectKey Key(Item, LC->getStackFrame()); + assert(State->contains<ObjectsUnderConstruction>(Key)); + return State->remove<ObjectsUnderConstruction>(Key); +} + +ProgramStateRef ExprEngine::elideDestructor(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC) { + ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC); + // FIXME: Currently the state might already contain the marker due to + // incorrect handling of temporaries bound to default parameters. + return State->set<ObjectsUnderConstruction>(Key, UnknownVal()); +} + +ProgramStateRef +ExprEngine::cleanupElidedDestructor(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC) { + ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC); + assert(State->contains<ObjectsUnderConstruction>(Key)); + return State->remove<ObjectsUnderConstruction>(Key); +} + +bool ExprEngine::isDestructorElided(ProgramStateRef State, + const CXXBindTemporaryExpr *BTE, + const LocationContext *LC) { + ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC); + return State->contains<ObjectsUnderConstruction>(Key); +} + +bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State, + const LocationContext *FromLC, + const LocationContext *ToLC) { + const LocationContext *LC = FromLC; + while (LC != ToLC) { + assert(LC && "ToLC must be a parent of FromLC!"); + for (auto I : State->get<ObjectsUnderConstruction>()) + if (I.first.getLocationContext() == LC) + return false; + + LC = LC->getParent(); + } + return true; +} + + +//===----------------------------------------------------------------------===// +// Top-level transfer function logic (Dispatcher). +//===----------------------------------------------------------------------===// + +/// evalAssume - Called by ConstraintManager. Used to call checker-specific +/// logic for handling assumptions on symbolic values. +ProgramStateRef ExprEngine::processAssume(ProgramStateRef state, + SVal cond, bool assumption) { + return getCheckerManager().runCheckersForEvalAssume(state, cond, assumption); +} + +ProgramStateRef +ExprEngine::processRegionChanges(ProgramStateRef state, + const InvalidatedSymbols *invalidated, + ArrayRef<const MemRegion *> Explicits, + ArrayRef<const MemRegion *> Regions, + const LocationContext *LCtx, + const CallEvent *Call) { + return getCheckerManager().runCheckersForRegionChanges(state, invalidated, + Explicits, Regions, + LCtx, Call); +} + +static void printObjectsUnderConstructionForContext(raw_ostream &Out, + ProgramStateRef State, + const char *NL, + const LocationContext *LC) { + PrintingPolicy PP = + LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy(); + for (auto I : State->get<ObjectsUnderConstruction>()) { + ConstructedObjectKey Key = I.first; + SVal Value = I.second; + if (Key.getLocationContext() != LC) + continue; + Key.print(Out, nullptr, PP); + Out << " : " << Value << NL; + } +} + +void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State, + const char *NL, const char *Sep, + const LocationContext *LCtx) { + if (LCtx) { + if (!State->get<ObjectsUnderConstruction>().isEmpty()) { + Out << Sep << "Objects under construction:" << NL; + + LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) { + printObjectsUnderConstructionForContext(Out, State, NL, LC); + }); + } + } + + getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep); +} + +void ExprEngine::processEndWorklist() { + getCheckerManager().runCheckersForEndAnalysis(G, BR, *this); +} + +void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred, + unsigned StmtIdx, NodeBuilderContext *Ctx) { + PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); + currStmtIdx = StmtIdx; + currBldrCtx = Ctx; + + switch (E.getKind()) { + case CFGElement::Statement: + case CFGElement::Constructor: + case CFGElement::CXXRecordTypedCall: + ProcessStmt(E.castAs<CFGStmt>().getStmt(), Pred); + return; + case CFGElement::Initializer: + ProcessInitializer(E.castAs<CFGInitializer>(), Pred); + return; + case CFGElement::NewAllocator: + ProcessNewAllocator(E.castAs<CFGNewAllocator>().getAllocatorExpr(), + Pred); + return; + case CFGElement::AutomaticObjectDtor: + case CFGElement::DeleteDtor: + case CFGElement::BaseDtor: + case CFGElement::MemberDtor: + case CFGElement::TemporaryDtor: + ProcessImplicitDtor(E.castAs<CFGImplicitDtor>(), Pred); + return; + case CFGElement::LoopExit: + ProcessLoopExit(E.castAs<CFGLoopExit>().getLoopStmt(), Pred); + return; + case CFGElement::LifetimeEnds: + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: + return; + } +} + +static bool shouldRemoveDeadBindings(AnalysisManager &AMgr, + const Stmt *S, + const ExplodedNode *Pred, + const LocationContext *LC) { + // Are we never purging state values? + if (AMgr.options.AnalysisPurgeOpt == PurgeNone) + return false; + + // Is this the beginning of a basic block? + if (Pred->getLocation().getAs<BlockEntrance>()) + return true; + + // Is this on a non-expression? + if (!isa<Expr>(S)) + return true; + + // Run before processing a call. + if (CallEvent::isCallStmt(S)) + return true; + + // Is this an expression that is consumed by another expression? If so, + // postpone cleaning out the state. + ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap(); + return !PM.isConsumedExpr(cast<Expr>(S)); +} + +void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out, + const Stmt *ReferenceStmt, + const LocationContext *LC, + const Stmt *DiagnosticStmt, + ProgramPoint::Kind K) { + assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind || + ReferenceStmt == nullptr || isa<ReturnStmt>(ReferenceStmt)) + && "PostStmt is not generally supported by the SymbolReaper yet"); + assert(LC && "Must pass the current (or expiring) LocationContext"); + + if (!DiagnosticStmt) { + DiagnosticStmt = ReferenceStmt; + assert(DiagnosticStmt && "Required for clearing a LocationContext"); + } + + NumRemoveDeadBindings++; + ProgramStateRef CleanedState = Pred->getState(); + + // LC is the location context being destroyed, but SymbolReaper wants a + // location context that is still live. (If this is the top-level stack + // frame, this will be null.) + if (!ReferenceStmt) { + assert(K == ProgramPoint::PostStmtPurgeDeadSymbolsKind && + "Use PostStmtPurgeDeadSymbolsKind for clearing a LocationContext"); + LC = LC->getParent(); + } + + const StackFrameContext *SFC = LC ? LC->getStackFrame() : nullptr; + SymbolReaper SymReaper(SFC, ReferenceStmt, SymMgr, getStoreManager()); + + for (auto I : CleanedState->get<ObjectsUnderConstruction>()) { + if (SymbolRef Sym = I.second.getAsSymbol()) + SymReaper.markLive(Sym); + if (const MemRegion *MR = I.second.getAsRegion()) + SymReaper.markLive(MR); + } + + getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper); + + // Create a state in which dead bindings are removed from the environment + // and the store. TODO: The function should just return new env and store, + // not a new state. + CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper); + + // Process any special transfer function for dead symbols. + // A tag to track convenience transitions, which can be removed at cleanup. + static SimpleProgramPointTag cleanupTag(TagProviderName, "Clean Node"); + // Call checkers with the non-cleaned state so that they could query the + // values of the soon to be dead symbols. + ExplodedNodeSet CheckedSet; + getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper, + DiagnosticStmt, *this, K); + + // For each node in CheckedSet, generate CleanedNodes that have the + // environment, the store, and the constraints cleaned up but have the + // user-supplied states as the predecessors. + StmtNodeBuilder Bldr(CheckedSet, Out, *currBldrCtx); + for (const auto I : CheckedSet) { + ProgramStateRef CheckerState = I->getState(); + + // The constraint manager has not been cleaned up yet, so clean up now. + CheckerState = + getConstraintManager().removeDeadBindings(CheckerState, SymReaper); + + assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) && + "Checkers are not allowed to modify the Environment as a part of " + "checkDeadSymbols processing."); + assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) && + "Checkers are not allowed to modify the Store as a part of " + "checkDeadSymbols processing."); + + // Create a state based on CleanedState with CheckerState GDM and + // generate a transition to that state. + ProgramStateRef CleanedCheckerSt = + StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState); + Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K); + } +} + +void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) { + // Reclaim any unnecessary nodes in the ExplodedGraph. + G.reclaimRecentlyAllocatedNodes(); + + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + currStmt->getBeginLoc(), + "Error evaluating statement"); + + // Remove dead bindings and symbols. + ExplodedNodeSet CleanedStates; + if (shouldRemoveDeadBindings(AMgr, currStmt, Pred, + Pred->getLocationContext())) { + removeDead(Pred, CleanedStates, currStmt, + Pred->getLocationContext()); + } else + CleanedStates.Add(Pred); + + // Visit the statement. + ExplodedNodeSet Dst; + for (const auto I : CleanedStates) { + ExplodedNodeSet DstI; + // Visit the statement. + Visit(currStmt, I, DstI); + Dst.insert(DstI); + } + + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); +} + +void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) { + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + S->getBeginLoc(), + "Error evaluating end of the loop"); + ExplodedNodeSet Dst; + Dst.Add(Pred); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + ProgramStateRef NewState = Pred->getState(); + + if(AMgr.options.ShouldUnrollLoops) + NewState = processLoopEnd(S, NewState); + + LoopExit PP(S, Pred->getLocationContext()); + Bldr.generateNode(PP, NewState, Pred); + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); +} + +void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit, + ExplodedNode *Pred) { + const CXXCtorInitializer *BMI = CFGInit.getInitializer(); + const Expr *Init = BMI->getInit()->IgnoreImplicit(); + const LocationContext *LC = Pred->getLocationContext(); + + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + BMI->getSourceLocation(), + "Error evaluating initializer"); + + // We don't clean up dead bindings here. + const auto *stackFrame = cast<StackFrameContext>(Pred->getLocationContext()); + const auto *decl = cast<CXXConstructorDecl>(stackFrame->getDecl()); + + ProgramStateRef State = Pred->getState(); + SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame)); + + ExplodedNodeSet Tmp; + SVal FieldLoc; + + // Evaluate the initializer, if necessary + if (BMI->isAnyMemberInitializer()) { + // Constructors build the object directly in the field, + // but non-objects must be copied in from the initializer. + if (getObjectUnderConstruction(State, BMI, LC)) { + // The field was directly constructed, so there is no need to bind. + // But we still need to stop tracking the object under construction. + State = finishObjectConstruction(State, BMI, LC); + NodeBuilder Bldr(Pred, Tmp, *currBldrCtx); + PostStore PS(Init, LC, /*Loc*/ nullptr, /*tag*/ nullptr); + Bldr.generateNode(PS, State, Pred); + } else { + const ValueDecl *Field; + if (BMI->isIndirectMemberInitializer()) { + Field = BMI->getIndirectMember(); + FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal); + } else { + Field = BMI->getMember(); + FieldLoc = State->getLValue(BMI->getMember(), thisVal); + } + + SVal InitVal; + if (Init->getType()->isArrayType()) { + // Handle arrays of trivial type. We can represent this with a + // primitive load/copy from the base array region. + const ArraySubscriptExpr *ASE; + while ((ASE = dyn_cast<ArraySubscriptExpr>(Init))) + Init = ASE->getBase()->IgnoreImplicit(); + + SVal LValue = State->getSVal(Init, stackFrame); + if (!Field->getType()->isReferenceType()) + if (Optional<Loc> LValueLoc = LValue.getAs<Loc>()) + InitVal = State->getSVal(*LValueLoc); + + // If we fail to get the value for some reason, use a symbolic value. + if (InitVal.isUnknownOrUndef()) { + SValBuilder &SVB = getSValBuilder(); + InitVal = SVB.conjureSymbolVal(BMI->getInit(), stackFrame, + Field->getType(), + currBldrCtx->blockCount()); + } + } else { + InitVal = State->getSVal(BMI->getInit(), stackFrame); + } + + PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); + evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP); + } + } else { + assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer()); + Tmp.insert(Pred); + // We already did all the work when visiting the CXXConstructExpr. + } + + // Construct PostInitializer nodes whether the state changed or not, + // so that the diagnostics don't get confused. + PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame); + ExplodedNodeSet Dst; + NodeBuilder Bldr(Tmp, Dst, *currBldrCtx); + for (const auto I : Tmp) { + ProgramStateRef State = I->getState(); + Bldr.generateNode(PP, State, I); + } + + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); +} + +void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, + ExplodedNode *Pred) { + ExplodedNodeSet Dst; + switch (D.getKind()) { + case CFGElement::AutomaticObjectDtor: + ProcessAutomaticObjDtor(D.castAs<CFGAutomaticObjDtor>(), Pred, Dst); + break; + case CFGElement::BaseDtor: + ProcessBaseDtor(D.castAs<CFGBaseDtor>(), Pred, Dst); + break; + case CFGElement::MemberDtor: + ProcessMemberDtor(D.castAs<CFGMemberDtor>(), Pred, Dst); + break; + case CFGElement::TemporaryDtor: + ProcessTemporaryDtor(D.castAs<CFGTemporaryDtor>(), Pred, Dst); + break; + case CFGElement::DeleteDtor: + ProcessDeleteDtor(D.castAs<CFGDeleteDtor>(), Pred, Dst); + break; + default: + llvm_unreachable("Unexpected dtor kind."); + } + + // Enqueue the new nodes onto the work list. + Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); +} + +void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE, + ExplodedNode *Pred) { + ExplodedNodeSet Dst; + AnalysisManager &AMgr = getAnalysisManager(); + AnalyzerOptions &Opts = AMgr.options; + // TODO: We're not evaluating allocators for all cases just yet as + // we're not handling the return value correctly, which causes false + // positives when the alpha.cplusplus.NewDeleteLeaks check is on. + if (Opts.MayInlineCXXAllocator) + VisitCXXNewAllocatorCall(NE, Pred, Dst); + else { + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + const LocationContext *LCtx = Pred->getLocationContext(); + PostImplicitCall PP(NE->getOperatorNew(), NE->getBeginLoc(), LCtx); + Bldr.generateNode(PP, Pred->getState(), Pred); + } + Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx); +} + +void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const VarDecl *varDecl = Dtor.getVarDecl(); + QualType varType = varDecl->getType(); + + ProgramStateRef state = Pred->getState(); + SVal dest = state->getLValue(varDecl, Pred->getLocationContext()); + const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion(); + + if (varType->isReferenceType()) { + const MemRegion *ValueRegion = state->getSVal(Region).getAsRegion(); + if (!ValueRegion) { + // FIXME: This should not happen. The language guarantees a presence + // of a valid initializer here, so the reference shall not be undefined. + // It seems that we're calling destructors over variables that + // were not initialized yet. + return; + } + Region = ValueRegion->getBaseRegion(); + varType = cast<TypedValueRegion>(Region)->getValueType(); + } + + // FIXME: We need to run the same destructor on every element of the array. + // This workaround will just run the first destructor (which will still + // invalidate the entire array). + EvalCallOptions CallOpts; + Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType, + CallOpts.IsArrayCtorOrDtor).getAsRegion(); + + VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false, + Pred, Dst, CallOpts); +} + +void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ProgramStateRef State = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + const CXXDeleteExpr *DE = Dtor.getDeleteExpr(); + const Stmt *Arg = DE->getArgument(); + QualType DTy = DE->getDestroyedType(); + SVal ArgVal = State->getSVal(Arg, LCtx); + + // If the argument to delete is known to be a null value, + // don't run destructor. + if (State->isNull(ArgVal).isConstrainedTrue()) { + QualType BTy = getContext().getBaseElementType(DTy); + const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl(); + const CXXDestructorDecl *Dtor = RD->getDestructor(); + + PostImplicitCall PP(Dtor, DE->getBeginLoc(), LCtx); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateNode(PP, Pred->getState(), Pred); + return; + } + + EvalCallOptions CallOpts; + const MemRegion *ArgR = ArgVal.getAsRegion(); + if (DE->isArrayForm()) { + // FIXME: We need to run the same destructor on every element of the array. + // This workaround will just run the first destructor (which will still + // invalidate the entire array). + CallOpts.IsArrayCtorOrDtor = true; + // Yes, it may even be a multi-dimensional array. + while (const auto *AT = getContext().getAsArrayType(DTy)) + DTy = AT->getElementType(); + if (ArgR) + ArgR = getStoreManager().GetElementZeroRegion(cast<SubRegion>(ArgR), DTy); + } + + VisitCXXDestructor(DTy, ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts); +} + +void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + const LocationContext *LCtx = Pred->getLocationContext(); + + const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); + Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor, + LCtx->getStackFrame()); + SVal ThisVal = Pred->getState()->getSVal(ThisPtr); + + // Create the base object region. + const CXXBaseSpecifier *Base = D.getBaseSpecifier(); + QualType BaseTy = Base->getType(); + SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy, + Base->isVirtual()); + + VisitCXXDestructor(BaseTy, BaseVal.castAs<loc::MemRegionVal>().getRegion(), + CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst, {}); +} + +void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + const FieldDecl *Member = D.getFieldDecl(); + QualType T = Member->getType(); + ProgramStateRef State = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + + const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl()); + Loc ThisVal = getSValBuilder().getCXXThis(CurDtor, + LCtx->getStackFrame()); + SVal FieldVal = + State->getLValue(Member, State->getSVal(ThisVal).castAs<Loc>()); + + // FIXME: We need to run the same destructor on every element of the array. + // This workaround will just run the first destructor (which will still + // invalidate the entire array). + EvalCallOptions CallOpts; + FieldVal = makeZeroElementRegion(State, FieldVal, T, + CallOpts.IsArrayCtorOrDtor); + + VisitCXXDestructor(T, FieldVal.castAs<loc::MemRegionVal>().getRegion(), + CurDtor->getBody(), /*IsBase=*/false, Pred, Dst, CallOpts); +} + +void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const CXXBindTemporaryExpr *BTE = D.getBindTemporaryExpr(); + ProgramStateRef State = Pred->getState(); + const LocationContext *LC = Pred->getLocationContext(); + const MemRegion *MR = nullptr; + + if (Optional<SVal> V = + getObjectUnderConstruction(State, D.getBindTemporaryExpr(), + Pred->getLocationContext())) { + // FIXME: Currently we insert temporary destructors for default parameters, + // but we don't insert the constructors, so the entry in + // ObjectsUnderConstruction may be missing. + State = finishObjectConstruction(State, D.getBindTemporaryExpr(), + Pred->getLocationContext()); + MR = V->getAsRegion(); + } + + // If copy elision has occurred, and the constructor corresponding to the + // destructor was elided, we need to skip the destructor as well. + if (isDestructorElided(State, BTE, LC)) { + State = cleanupElidedDestructor(State, BTE, LC); + NodeBuilder Bldr(Pred, Dst, *currBldrCtx); + PostImplicitCall PP(D.getDestructorDecl(getContext()), + D.getBindTemporaryExpr()->getBeginLoc(), + Pred->getLocationContext()); + Bldr.generateNode(PP, State, Pred); + return; + } + + ExplodedNodeSet CleanDtorState; + StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx); + StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State); + + QualType T = D.getBindTemporaryExpr()->getSubExpr()->getType(); + // FIXME: Currently CleanDtorState can be empty here due to temporaries being + // bound to default parameters. + assert(CleanDtorState.size() <= 1); + ExplodedNode *CleanPred = + CleanDtorState.empty() ? Pred : *CleanDtorState.begin(); + + EvalCallOptions CallOpts; + CallOpts.IsTemporaryCtorOrDtor = true; + if (!MR) { + CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; + + // If we have no MR, we still need to unwrap the array to avoid destroying + // the whole array at once. Regardless, we'd eventually need to model array + // destructors properly, element-by-element. + while (const ArrayType *AT = getContext().getAsArrayType(T)) { + T = AT->getElementType(); + CallOpts.IsArrayCtorOrDtor = true; + } + } else { + // We'd eventually need to makeZeroElementRegion() trick here, + // but for now we don't have the respective construction contexts, + // so MR would always be null in this case. Do nothing for now. + } + VisitCXXDestructor(T, MR, D.getBindTemporaryExpr(), + /*IsBase=*/false, CleanPred, Dst, CallOpts); +} + +void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE, + NodeBuilderContext &BldCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) { + BranchNodeBuilder TempDtorBuilder(Pred, Dst, BldCtx, DstT, DstF); + ProgramStateRef State = Pred->getState(); + const LocationContext *LC = Pred->getLocationContext(); + if (getObjectUnderConstruction(State, BTE, LC)) { + TempDtorBuilder.markInfeasible(false); + TempDtorBuilder.generateNode(State, true, Pred); + } else { + TempDtorBuilder.markInfeasible(true); + TempDtorBuilder.generateNode(State, false, Pred); + } +} + +void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE, + ExplodedNodeSet &PreVisit, + ExplodedNodeSet &Dst) { + // This is a fallback solution in case we didn't have a construction + // context when we were constructing the temporary. Otherwise the map should + // have been populated there. + if (!getAnalysisManager().options.ShouldIncludeTemporaryDtorsInCFG) { + // In case we don't have temporary destructors in the CFG, do not mark + // the initialization - we would otherwise never clean it up. + Dst = PreVisit; + return; + } + StmtNodeBuilder StmtBldr(PreVisit, Dst, *currBldrCtx); + for (ExplodedNode *Node : PreVisit) { + ProgramStateRef State = Node->getState(); + const LocationContext *LC = Node->getLocationContext(); + if (!getObjectUnderConstruction(State, BTE, LC)) { + // FIXME: Currently the state might also already contain the marker due to + // incorrect handling of temporaries bound to default parameters; for + // those, we currently skip the CXXBindTemporaryExpr but rely on adding + // temporary destructor nodes. + State = addObjectUnderConstruction(State, BTE, LC, UnknownVal()); + } + StmtBldr.generateNode(BTE, Node, State); + } +} + +ProgramStateRef ExprEngine::escapeValue(ProgramStateRef State, SVal V, + PointerEscapeKind K) const { + class CollectReachableSymbolsCallback final : public SymbolVisitor { + InvalidatedSymbols Symbols; + + public: + explicit CollectReachableSymbolsCallback(ProgramStateRef) {} + + const InvalidatedSymbols &getSymbols() const { return Symbols; } + + bool VisitSymbol(SymbolRef Sym) override { + Symbols.insert(Sym); + return true; + } + }; + + const CollectReachableSymbolsCallback &Scanner = + State->scanReachableSymbols<CollectReachableSymbolsCallback>(V); + return getCheckerManager().runCheckersForPointerEscape( + State, Scanner.getSymbols(), /*CallEvent*/ nullptr, K, nullptr); +} + +void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred, + ExplodedNodeSet &DstTop) { + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + S->getBeginLoc(), "Error evaluating statement"); + ExplodedNodeSet Dst; + StmtNodeBuilder Bldr(Pred, DstTop, *currBldrCtx); + + assert(!isa<Expr>(S) || S == cast<Expr>(S)->IgnoreParens()); + + switch (S->getStmtClass()) { + // C++, OpenMP and ARC stuff we don't support yet. + case Expr::ObjCIndirectCopyRestoreExprClass: + case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::CXXInheritedCtorInitExprClass: + case Stmt::CXXTryStmtClass: + case Stmt::CXXTypeidExprClass: + case Stmt::CXXUuidofExprClass: + case Stmt::CXXFoldExprClass: + case Stmt::MSPropertyRefExprClass: + case Stmt::MSPropertySubscriptExprClass: + case Stmt::CXXUnresolvedConstructExprClass: + case Stmt::DependentScopeDeclRefExprClass: + case Stmt::ArrayTypeTraitExprClass: + case Stmt::ExpressionTraitExprClass: + case Stmt::UnresolvedLookupExprClass: + case Stmt::UnresolvedMemberExprClass: + case Stmt::TypoExprClass: + case Stmt::CXXNoexceptExprClass: + case Stmt::PackExpansionExprClass: + case Stmt::SubstNonTypeTemplateParmPackExprClass: + case Stmt::FunctionParmPackExprClass: + case Stmt::CoroutineBodyStmtClass: + case Stmt::CoawaitExprClass: + case Stmt::DependentCoawaitExprClass: + case Stmt::CoreturnStmtClass: + case Stmt::CoyieldExprClass: + case Stmt::SEHTryStmtClass: + case Stmt::SEHExceptStmtClass: + case Stmt::SEHLeaveStmtClass: + case Stmt::SEHFinallyStmtClass: + case Stmt::OMPParallelDirectiveClass: + case Stmt::OMPSimdDirectiveClass: + case Stmt::OMPForDirectiveClass: + case Stmt::OMPForSimdDirectiveClass: + case Stmt::OMPSectionsDirectiveClass: + case Stmt::OMPSectionDirectiveClass: + case Stmt::OMPSingleDirectiveClass: + case Stmt::OMPMasterDirectiveClass: + case Stmt::OMPCriticalDirectiveClass: + case Stmt::OMPParallelForDirectiveClass: + case Stmt::OMPParallelForSimdDirectiveClass: + case Stmt::OMPParallelSectionsDirectiveClass: + case Stmt::OMPTaskDirectiveClass: + case Stmt::OMPTaskyieldDirectiveClass: + case Stmt::OMPBarrierDirectiveClass: + case Stmt::OMPTaskwaitDirectiveClass: + case Stmt::OMPTaskgroupDirectiveClass: + case Stmt::OMPFlushDirectiveClass: + case Stmt::OMPOrderedDirectiveClass: + case Stmt::OMPAtomicDirectiveClass: + case Stmt::OMPTargetDirectiveClass: + case Stmt::OMPTargetDataDirectiveClass: + case Stmt::OMPTargetEnterDataDirectiveClass: + case Stmt::OMPTargetExitDataDirectiveClass: + case Stmt::OMPTargetParallelDirectiveClass: + case Stmt::OMPTargetParallelForDirectiveClass: + case Stmt::OMPTargetUpdateDirectiveClass: + case Stmt::OMPTeamsDirectiveClass: + case Stmt::OMPCancellationPointDirectiveClass: + case Stmt::OMPCancelDirectiveClass: + case Stmt::OMPTaskLoopDirectiveClass: + case Stmt::OMPTaskLoopSimdDirectiveClass: + case Stmt::OMPDistributeDirectiveClass: + case Stmt::OMPDistributeParallelForDirectiveClass: + case Stmt::OMPDistributeParallelForSimdDirectiveClass: + case Stmt::OMPDistributeSimdDirectiveClass: + case Stmt::OMPTargetParallelForSimdDirectiveClass: + case Stmt::OMPTargetSimdDirectiveClass: + case Stmt::OMPTeamsDistributeDirectiveClass: + case Stmt::OMPTeamsDistributeSimdDirectiveClass: + case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass: + case Stmt::OMPTeamsDistributeParallelForDirectiveClass: + case Stmt::OMPTargetTeamsDirectiveClass: + case Stmt::OMPTargetTeamsDistributeDirectiveClass: + case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass: + case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass: + case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass: + case Stmt::CapturedStmtClass: { + const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); + Engine.addAbortedBlock(node, currBldrCtx->getBlock()); + break; + } + + case Stmt::ParenExprClass: + llvm_unreachable("ParenExprs already handled."); + case Stmt::GenericSelectionExprClass: + llvm_unreachable("GenericSelectionExprs already handled."); + // Cases that should never be evaluated simply because they shouldn't + // appear in the CFG. + case Stmt::BreakStmtClass: + case Stmt::CaseStmtClass: + case Stmt::CompoundStmtClass: + case Stmt::ContinueStmtClass: + case Stmt::CXXForRangeStmtClass: + case Stmt::DefaultStmtClass: + case Stmt::DoStmtClass: + case Stmt::ForStmtClass: + case Stmt::GotoStmtClass: + case Stmt::IfStmtClass: + case Stmt::IndirectGotoStmtClass: + case Stmt::LabelStmtClass: + case Stmt::NoStmtClass: + case Stmt::NullStmtClass: + case Stmt::SwitchStmtClass: + case Stmt::WhileStmtClass: + case Expr::MSDependentExistsStmtClass: + llvm_unreachable("Stmt should not be in analyzer evaluation loop"); + + case Stmt::ObjCSubscriptRefExprClass: + case Stmt::ObjCPropertyRefExprClass: + llvm_unreachable("These are handled by PseudoObjectExpr"); + + case Stmt::GNUNullExprClass: { + // GNU __null is a pointer-width integer, not an actual pointer. + ProgramStateRef state = Pred->getState(); + state = state->BindExpr(S, Pred->getLocationContext(), + svalBuilder.makeIntValWithPtrWidth(0, false)); + Bldr.generateNode(S, Pred, state); + break; + } + + case Stmt::ObjCAtSynchronizedStmtClass: + Bldr.takeNodes(Pred); + VisitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Expr::ConstantExprClass: + case Stmt::ExprWithCleanupsClass: + // Handled due to fully linearised CFG. + break; + + case Stmt::CXXBindTemporaryExprClass: { + Bldr.takeNodes(Pred); + ExplodedNodeSet PreVisit; + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + ExplodedNodeSet Next; + VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), PreVisit, Next); + getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this); + Bldr.addNodes(Dst); + break; + } + + // Cases not handled yet; but will handle some day. + case Stmt::DesignatedInitExprClass: + case Stmt::DesignatedInitUpdateExprClass: + case Stmt::ArrayInitLoopExprClass: + case Stmt::ArrayInitIndexExprClass: + case Stmt::ExtVectorElementExprClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::ObjCAtCatchStmtClass: + case Stmt::ObjCAtFinallyStmtClass: + case Stmt::ObjCAtTryStmtClass: + case Stmt::ObjCAutoreleasePoolStmtClass: + case Stmt::ObjCEncodeExprClass: + case Stmt::ObjCIsaExprClass: + case Stmt::ObjCProtocolExprClass: + case Stmt::ObjCSelectorExprClass: + case Stmt::ParenListExprClass: + case Stmt::ShuffleVectorExprClass: + case Stmt::ConvertVectorExprClass: + case Stmt::VAArgExprClass: + case Stmt::CUDAKernelCallExprClass: + case Stmt::OpaqueValueExprClass: + case Stmt::AsTypeExprClass: + // Fall through. + + // Cases we intentionally don't evaluate, since they don't need + // to be explicitly evaluated. + case Stmt::PredefinedExprClass: + case Stmt::AddrLabelExprClass: + case Stmt::AttributedStmtClass: + case Stmt::IntegerLiteralClass: + case Stmt::FixedPointLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::ImplicitValueInitExprClass: + case Stmt::CXXScalarValueInitExprClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::ObjCBoolLiteralExprClass: + case Stmt::ObjCAvailabilityCheckExprClass: + case Stmt::FloatingLiteralClass: + case Stmt::NoInitExprClass: + case Stmt::SizeOfPackExprClass: + case Stmt::StringLiteralClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::CXXPseudoDestructorExprClass: + case Stmt::SubstNonTypeTemplateParmExprClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::OMPArraySectionExprClass: + case Stmt::TypeTraitExprClass: { + Bldr.takeNodes(Pred); + ExplodedNodeSet preVisit; + getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); + getCheckerManager().runCheckersForPostStmt(Dst, preVisit, S, *this); + Bldr.addNodes(Dst); + break; + } + + case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXDefaultInitExprClass: { + Bldr.takeNodes(Pred); + ExplodedNodeSet PreVisit; + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx); + + const Expr *ArgE; + if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S)) + ArgE = DefE->getExpr(); + else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S)) + ArgE = DefE->getExpr(); + else + llvm_unreachable("unknown constant wrapper kind"); + + bool IsTemporary = false; + if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) { + ArgE = MTE->GetTemporaryExpr(); + IsTemporary = true; + } + + Optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE); + if (!ConstantVal) + ConstantVal = UnknownVal(); + + const LocationContext *LCtx = Pred->getLocationContext(); + for (const auto I : PreVisit) { + ProgramStateRef State = I->getState(); + State = State->BindExpr(S, LCtx, *ConstantVal); + if (IsTemporary) + State = createTemporaryRegionIfNeeded(State, LCtx, + cast<Expr>(S), + cast<Expr>(S)); + Bldr2.generateNode(S, I, State); + } + + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); + Bldr.addNodes(Dst); + break; + } + + // Cases we evaluate as opaque expressions, conjuring a symbol. + case Stmt::CXXStdInitializerListExprClass: + case Expr::ObjCArrayLiteralClass: + case Expr::ObjCDictionaryLiteralClass: + case Expr::ObjCBoxedExprClass: { + Bldr.takeNodes(Pred); + + ExplodedNodeSet preVisit; + getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this); + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr2(preVisit, Tmp, *currBldrCtx); + + const auto *Ex = cast<Expr>(S); + QualType resultType = Ex->getType(); + + for (const auto N : preVisit) { + const LocationContext *LCtx = N->getLocationContext(); + SVal result = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx, + resultType, + currBldrCtx->blockCount()); + ProgramStateRef State = N->getState()->BindExpr(Ex, LCtx, result); + + // Escape pointers passed into the list, unless it's an ObjC boxed + // expression which is not a boxable C structure. + if (!(isa<ObjCBoxedExpr>(Ex) && + !cast<ObjCBoxedExpr>(Ex)->getSubExpr() + ->getType()->isRecordType())) + for (auto Child : Ex->children()) { + assert(Child); + SVal Val = State->getSVal(Child, LCtx); + State = escapeValue(State, Val, PSK_EscapeOther); + } + + Bldr2.generateNode(S, N, State); + } + + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); + Bldr.addNodes(Dst); + break; + } + + case Stmt::ArraySubscriptExprClass: + Bldr.takeNodes(Pred); + VisitArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::GCCAsmStmtClass: + Bldr.takeNodes(Pred); + VisitGCCAsmStmt(cast<GCCAsmStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::MSAsmStmtClass: + Bldr.takeNodes(Pred); + VisitMSAsmStmt(cast<MSAsmStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::BlockExprClass: + Bldr.takeNodes(Pred); + VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::LambdaExprClass: + if (AMgr.options.ShouldInlineLambdas) { + Bldr.takeNodes(Pred); + VisitLambdaExpr(cast<LambdaExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + } else { + const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState()); + Engine.addAbortedBlock(node, currBldrCtx->getBlock()); + } + break; + + case Stmt::BinaryOperatorClass: { + const auto *B = cast<BinaryOperator>(S); + if (B->isLogicalOp()) { + Bldr.takeNodes(Pred); + VisitLogicalExpr(B, Pred, Dst); + Bldr.addNodes(Dst); + break; + } + else if (B->getOpcode() == BO_Comma) { + ProgramStateRef state = Pred->getState(); + Bldr.generateNode(B, Pred, + state->BindExpr(B, Pred->getLocationContext(), + state->getSVal(B->getRHS(), + Pred->getLocationContext()))); + break; + } + + Bldr.takeNodes(Pred); + + if (AMgr.options.ShouldEagerlyAssume && + (B->isRelationalOp() || B->isEqualityOp())) { + ExplodedNodeSet Tmp; + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Tmp); + evalEagerlyAssumeBinOpBifurcation(Dst, Tmp, cast<Expr>(S)); + } + else + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + + Bldr.addNodes(Dst); + break; + } + + case Stmt::CXXOperatorCallExprClass: { + const auto *OCE = cast<CXXOperatorCallExpr>(S); + + // For instance method operators, make sure the 'this' argument has a + // valid region. + const Decl *Callee = OCE->getCalleeDecl(); + if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) { + if (MD->isInstance()) { + ProgramStateRef State = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef NewState = + createTemporaryRegionIfNeeded(State, LCtx, OCE->getArg(0)); + if (NewState != State) { + Pred = Bldr.generateNode(OCE, Pred, NewState, /*Tag=*/nullptr, + ProgramPoint::PreStmtKind); + // Did we cache out? + if (!Pred) + break; + } + } + } + // FALLTHROUGH + LLVM_FALLTHROUGH; + } + + case Stmt::CallExprClass: + case Stmt::CXXMemberCallExprClass: + case Stmt::UserDefinedLiteralClass: + Bldr.takeNodes(Pred); + VisitCallExpr(cast<CallExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::CXXCatchStmtClass: + Bldr.takeNodes(Pred); + VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::CXXTemporaryObjectExprClass: + case Stmt::CXXConstructExprClass: + Bldr.takeNodes(Pred); + VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::CXXNewExprClass: { + Bldr.takeNodes(Pred); + + ExplodedNodeSet PreVisit; + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + + ExplodedNodeSet PostVisit; + for (const auto i : PreVisit) + VisitCXXNewExpr(cast<CXXNewExpr>(S), i, PostVisit); + + getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); + Bldr.addNodes(Dst); + break; + } + + case Stmt::CXXDeleteExprClass: { + Bldr.takeNodes(Pred); + ExplodedNodeSet PreVisit; + const auto *CDE = cast<CXXDeleteExpr>(S); + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + + for (const auto i : PreVisit) + VisitCXXDeleteExpr(CDE, i, Dst); + + Bldr.addNodes(Dst); + break; + } + // FIXME: ChooseExpr is really a constant. We need to fix + // the CFG do not model them as explicit control-flow. + + case Stmt::ChooseExprClass: { // __builtin_choose_expr + Bldr.takeNodes(Pred); + const auto *C = cast<ChooseExpr>(S); + VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); + Bldr.addNodes(Dst); + break; + } + + case Stmt::CompoundAssignOperatorClass: + Bldr.takeNodes(Pred); + VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::CompoundLiteralExprClass: + Bldr.takeNodes(Pred); + VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: { // '?' operator + Bldr.takeNodes(Pred); + const auto *C = cast<AbstractConditionalOperator>(S); + VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst); + Bldr.addNodes(Dst); + break; + } + + case Stmt::CXXThisExprClass: + Bldr.takeNodes(Pred); + VisitCXXThisExpr(cast<CXXThisExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::DeclRefExprClass: { + Bldr.takeNodes(Pred); + const auto *DE = cast<DeclRefExpr>(S); + VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst); + Bldr.addNodes(Dst); + break; + } + + case Stmt::DeclStmtClass: + Bldr.takeNodes(Pred); + VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::ImplicitCastExprClass: + case Stmt::CStyleCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::CXXDynamicCastExprClass: + case Stmt::CXXReinterpretCastExprClass: + case Stmt::CXXConstCastExprClass: + case Stmt::CXXFunctionalCastExprClass: + case Stmt::ObjCBridgedCastExprClass: { + Bldr.takeNodes(Pred); + const auto *C = cast<CastExpr>(S); + ExplodedNodeSet dstExpr; + VisitCast(C, C->getSubExpr(), Pred, dstExpr); + + // Handle the postvisit checks. + getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this); + Bldr.addNodes(Dst); + break; + } + + case Expr::MaterializeTemporaryExprClass: { + Bldr.takeNodes(Pred); + const auto *MTE = cast<MaterializeTemporaryExpr>(S); + ExplodedNodeSet dstPrevisit; + getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this); + ExplodedNodeSet dstExpr; + for (const auto i : dstPrevisit) + CreateCXXTemporaryObject(MTE, i, dstExpr); + getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this); + Bldr.addNodes(Dst); + break; + } + + case Stmt::InitListExprClass: + Bldr.takeNodes(Pred); + VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::MemberExprClass: + Bldr.takeNodes(Pred); + VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::AtomicExprClass: + Bldr.takeNodes(Pred); + VisitAtomicExpr(cast<AtomicExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::ObjCIvarRefExprClass: + Bldr.takeNodes(Pred); + VisitLvalObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::ObjCForCollectionStmtClass: + Bldr.takeNodes(Pred); + VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::ObjCMessageExprClass: + Bldr.takeNodes(Pred); + VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::ObjCAtThrowStmtClass: + case Stmt::CXXThrowExprClass: + // FIXME: This is not complete. We basically treat @throw as + // an abort. + Bldr.generateSink(S, Pred, Pred->getState()); + break; + + case Stmt::ReturnStmtClass: + Bldr.takeNodes(Pred); + VisitReturnStmt(cast<ReturnStmt>(S), Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::OffsetOfExprClass: { + Bldr.takeNodes(Pred); + ExplodedNodeSet PreVisit; + getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this); + + ExplodedNodeSet PostVisit; + for (const auto Node : PreVisit) + VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Node, PostVisit); + + getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this); + Bldr.addNodes(Dst); + break; + } + + case Stmt::UnaryExprOrTypeTraitExprClass: + Bldr.takeNodes(Pred); + VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S), + Pred, Dst); + Bldr.addNodes(Dst); + break; + + case Stmt::StmtExprClass: { + const auto *SE = cast<StmtExpr>(S); + + if (SE->getSubStmt()->body_empty()) { + // Empty statement expression. + assert(SE->getType() == getContext().VoidTy + && "Empty statement expression must have void type."); + break; + } + + if (const auto *LastExpr = + dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) { + ProgramStateRef state = Pred->getState(); + Bldr.generateNode(SE, Pred, + state->BindExpr(SE, Pred->getLocationContext(), + state->getSVal(LastExpr, + Pred->getLocationContext()))); + } + break; + } + + case Stmt::UnaryOperatorClass: { + Bldr.takeNodes(Pred); + const auto *U = cast<UnaryOperator>(S); + if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)) { + ExplodedNodeSet Tmp; + VisitUnaryOperator(U, Pred, Tmp); + evalEagerlyAssumeBinOpBifurcation(Dst, Tmp, U); + } + else + VisitUnaryOperator(U, Pred, Dst); + Bldr.addNodes(Dst); + break; + } + + case Stmt::PseudoObjectExprClass: { + Bldr.takeNodes(Pred); + ProgramStateRef state = Pred->getState(); + const auto *PE = cast<PseudoObjectExpr>(S); + if (const Expr *Result = PE->getResultExpr()) { + SVal V = state->getSVal(Result, Pred->getLocationContext()); + Bldr.generateNode(S, Pred, + state->BindExpr(S, Pred->getLocationContext(), V)); + } + else + Bldr.generateNode(S, Pred, + state->BindExpr(S, Pred->getLocationContext(), + UnknownVal())); + + Bldr.addNodes(Dst); + break; + } + } +} + +bool ExprEngine::replayWithoutInlining(ExplodedNode *N, + const LocationContext *CalleeLC) { + const StackFrameContext *CalleeSF = CalleeLC->getStackFrame(); + const StackFrameContext *CallerSF = CalleeSF->getParent()->getStackFrame(); + assert(CalleeSF && CallerSF); + ExplodedNode *BeforeProcessingCall = nullptr; + const Stmt *CE = CalleeSF->getCallSite(); + + // Find the first node before we started processing the call expression. + while (N) { + ProgramPoint L = N->getLocation(); + BeforeProcessingCall = N; + N = N->pred_empty() ? nullptr : *(N->pred_begin()); + + // Skip the nodes corresponding to the inlined code. + if (L.getStackFrame() != CallerSF) + continue; + // We reached the caller. Find the node right before we started + // processing the call. + if (L.isPurgeKind()) + continue; + if (L.getAs<PreImplicitCall>()) + continue; + if (L.getAs<CallEnter>()) + continue; + if (Optional<StmtPoint> SP = L.getAs<StmtPoint>()) + if (SP->getStmt() == CE) + continue; + break; + } + + if (!BeforeProcessingCall) + return false; + + // TODO: Clean up the unneeded nodes. + + // Build an Epsilon node from which we will restart the analyzes. + // Note that CE is permitted to be NULL! + ProgramPoint NewNodeLoc = + EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE); + // Add the special flag to GDM to signal retrying with no inlining. + // Note, changing the state ensures that we are not going to cache out. + ProgramStateRef NewNodeState = BeforeProcessingCall->getState(); + NewNodeState = + NewNodeState->set<ReplayWithoutInlining>(const_cast<Stmt *>(CE)); + + // Make the new node a successor of BeforeProcessingCall. + bool IsNew = false; + ExplodedNode *NewNode = G.getNode(NewNodeLoc, NewNodeState, false, &IsNew); + // We cached out at this point. Caching out is common due to us backtracking + // from the inlined function, which might spawn several paths. + if (!IsNew) + return true; + + NewNode->addPredecessor(BeforeProcessingCall, G); + + // Add the new node to the work list. + Engine.enqueueStmtNode(NewNode, CalleeSF->getCallSiteBlock(), + CalleeSF->getIndex()); + NumTimesRetriedWithoutInlining++; + return true; +} + +/// Block entrance. (Update counters). +void ExprEngine::processCFGBlockEntrance(const BlockEdge &L, + NodeBuilderWithSinks &nodeBuilder, + ExplodedNode *Pred) { + PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); + // If we reach a loop which has a known bound (and meets + // other constraints) then consider completely unrolling it. + if(AMgr.options.ShouldUnrollLoops) { + unsigned maxBlockVisitOnPath = AMgr.options.maxBlockVisitOnPath; + const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); + if (Term) { + ProgramStateRef NewState = updateLoopStack(Term, AMgr.getASTContext(), + Pred, maxBlockVisitOnPath); + if (NewState != Pred->getState()) { + ExplodedNode *UpdatedNode = nodeBuilder.generateNode(NewState, Pred); + if (!UpdatedNode) + return; + Pred = UpdatedNode; + } + } + // Is we are inside an unrolled loop then no need the check the counters. + if(isUnrolledState(Pred->getState())) + return; + } + + // If this block is terminated by a loop and it has already been visited the + // maximum number of times, widen the loop. + unsigned int BlockCount = nodeBuilder.getContext().blockCount(); + if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 && + AMgr.options.ShouldWidenLoops) { + const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator(); + if (!(Term && + (isa<ForStmt>(Term) || isa<WhileStmt>(Term) || isa<DoStmt>(Term)))) + return; + // Widen. + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef WidenedState = + getWidenedLoopState(Pred->getState(), LCtx, BlockCount, Term); + nodeBuilder.generateNode(WidenedState, Pred); + return; + } + + // FIXME: Refactor this into a checker. + if (BlockCount >= AMgr.options.maxBlockVisitOnPath) { + static SimpleProgramPointTag tag(TagProviderName, "Block count exceeded"); + const ExplodedNode *Sink = + nodeBuilder.generateSink(Pred->getState(), Pred, &tag); + + // Check if we stopped at the top level function or not. + // Root node should have the location context of the top most function. + const LocationContext *CalleeLC = Pred->getLocation().getLocationContext(); + const LocationContext *CalleeSF = CalleeLC->getStackFrame(); + const LocationContext *RootLC = + (*G.roots_begin())->getLocation().getLocationContext(); + if (RootLC->getStackFrame() != CalleeSF) { + Engine.FunctionSummaries->markReachedMaxBlockCount(CalleeSF->getDecl()); + + // Re-run the call evaluation without inlining it, by storing the + // no-inlining policy in the state and enqueuing the new work item on + // the list. Replay should almost never fail. Use the stats to catch it + // if it does. + if ((!AMgr.options.NoRetryExhausted && + replayWithoutInlining(Pred, CalleeLC))) + return; + NumMaxBlockCountReachedInInlined++; + } else + NumMaxBlockCountReached++; + + // Make sink nodes as exhausted(for stats) only if retry failed. + Engine.blocksExhausted.push_back(std::make_pair(L, Sink)); + } +} + +//===----------------------------------------------------------------------===// +// Branch processing. +//===----------------------------------------------------------------------===// + +/// RecoverCastedSymbol - A helper function for ProcessBranch that is used +/// to try to recover some path-sensitivity for casts of symbolic +/// integers that promote their values (which are currently not tracked well). +/// This function returns the SVal bound to Condition->IgnoreCasts if all the +// cast(s) did was sign-extend the original value. +static SVal RecoverCastedSymbol(ProgramStateRef state, + const Stmt *Condition, + const LocationContext *LCtx, + ASTContext &Ctx) { + + const auto *Ex = dyn_cast<Expr>(Condition); + if (!Ex) + return UnknownVal(); + + uint64_t bits = 0; + bool bitsInit = false; + + while (const auto *CE = dyn_cast<CastExpr>(Ex)) { + QualType T = CE->getType(); + + if (!T->isIntegralOrEnumerationType()) + return UnknownVal(); + + uint64_t newBits = Ctx.getTypeSize(T); + if (!bitsInit || newBits < bits) { + bitsInit = true; + bits = newBits; + } + + Ex = CE->getSubExpr(); + } + + // We reached a non-cast. Is it a symbolic value? + QualType T = Ex->getType(); + + if (!bitsInit || !T->isIntegralOrEnumerationType() || + Ctx.getTypeSize(T) > bits) + return UnknownVal(); + + return state->getSVal(Ex, LCtx); +} + +#ifndef NDEBUG +static const Stmt *getRightmostLeaf(const Stmt *Condition) { + while (Condition) { + const auto *BO = dyn_cast<BinaryOperator>(Condition); + if (!BO || !BO->isLogicalOp()) { + return Condition; + } + Condition = BO->getRHS()->IgnoreParens(); + } + return nullptr; +} +#endif + +// Returns the condition the branch at the end of 'B' depends on and whose value +// has been evaluated within 'B'. +// In most cases, the terminator condition of 'B' will be evaluated fully in +// the last statement of 'B'; in those cases, the resolved condition is the +// given 'Condition'. +// If the condition of the branch is a logical binary operator tree, the CFG is +// optimized: in that case, we know that the expression formed by all but the +// rightmost leaf of the logical binary operator tree must be true, and thus +// the branch condition is at this point equivalent to the truth value of that +// rightmost leaf; the CFG block thus only evaluates this rightmost leaf +// expression in its final statement. As the full condition in that case was +// not evaluated, and is thus not in the SVal cache, we need to use that leaf +// expression to evaluate the truth value of the condition in the current state +// space. +static const Stmt *ResolveCondition(const Stmt *Condition, + const CFGBlock *B) { + if (const auto *Ex = dyn_cast<Expr>(Condition)) + Condition = Ex->IgnoreParens(); + + const auto *BO = dyn_cast<BinaryOperator>(Condition); + if (!BO || !BO->isLogicalOp()) + return Condition; + + assert(!B->getTerminator().isTemporaryDtorsBranch() && + "Temporary destructor branches handled by processBindTemporary."); + + // For logical operations, we still have the case where some branches + // use the traditional "merge" approach and others sink the branch + // directly into the basic blocks representing the logical operation. + // We need to distinguish between those two cases here. + + // The invariants are still shifting, but it is possible that the + // last element in a CFGBlock is not a CFGStmt. Look for the last + // CFGStmt as the value of the condition. + CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend(); + for (; I != E; ++I) { + CFGElement Elem = *I; + Optional<CFGStmt> CS = Elem.getAs<CFGStmt>(); + if (!CS) + continue; + const Stmt *LastStmt = CS->getStmt(); + assert(LastStmt == Condition || LastStmt == getRightmostLeaf(Condition)); + return LastStmt; + } + llvm_unreachable("could not resolve condition"); +} + +void ExprEngine::processBranch(const Stmt *Condition, + NodeBuilderContext& BldCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) { + assert((!Condition || !isa<CXXBindTemporaryExpr>(Condition)) && + "CXXBindTemporaryExprs are handled by processBindTemporary."); + const LocationContext *LCtx = Pred->getLocationContext(); + PrettyStackTraceLocationContext StackCrashInfo(LCtx); + currBldrCtx = &BldCtx; + + // Check for NULL conditions; e.g. "for(;;)" + if (!Condition) { + BranchNodeBuilder NullCondBldr(Pred, Dst, BldCtx, DstT, DstF); + NullCondBldr.markInfeasible(false); + NullCondBldr.generateNode(Pred->getState(), true, Pred); + return; + } + + if (const auto *Ex = dyn_cast<Expr>(Condition)) + Condition = Ex->IgnoreParens(); + + Condition = ResolveCondition(Condition, BldCtx.getBlock()); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + Condition->getBeginLoc(), + "Error evaluating branch"); + + ExplodedNodeSet CheckersOutSet; + getCheckerManager().runCheckersForBranchCondition(Condition, CheckersOutSet, + Pred, *this); + // We generated only sinks. + if (CheckersOutSet.empty()) + return; + + BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF); + for (const auto PredI : CheckersOutSet) { + if (PredI->isSink()) + continue; + + ProgramStateRef PrevState = PredI->getState(); + SVal X = PrevState->getSVal(Condition, PredI->getLocationContext()); + + if (X.isUnknownOrUndef()) { + // Give it a chance to recover from unknown. + if (const auto *Ex = dyn_cast<Expr>(Condition)) { + if (Ex->getType()->isIntegralOrEnumerationType()) { + // Try to recover some path-sensitivity. Right now casts of symbolic + // integers that promote their values are currently not tracked well. + // If 'Condition' is such an expression, try and recover the + // underlying value and use that instead. + SVal recovered = RecoverCastedSymbol(PrevState, Condition, + PredI->getLocationContext(), + getContext()); + + if (!recovered.isUnknown()) { + X = recovered; + } + } + } + } + + // If the condition is still unknown, give up. + if (X.isUnknownOrUndef()) { + builder.generateNode(PrevState, true, PredI); + builder.generateNode(PrevState, false, PredI); + continue; + } + + DefinedSVal V = X.castAs<DefinedSVal>(); + + ProgramStateRef StTrue, StFalse; + std::tie(StTrue, StFalse) = PrevState->assume(V); + + // Process the true branch. + if (builder.isFeasible(true)) { + if (StTrue) + builder.generateNode(StTrue, true, PredI); + else + builder.markInfeasible(true); + } + + // Process the false branch. + if (builder.isFeasible(false)) { + if (StFalse) + builder.generateNode(StFalse, false, PredI); + else + builder.markInfeasible(false); + } + } + currBldrCtx = nullptr; +} + +/// The GDM component containing the set of global variables which have been +/// previously initialized with explicit initializers. +REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet, + llvm::ImmutableSet<const VarDecl *>) + +void ExprEngine::processStaticInitializer(const DeclStmt *DS, + NodeBuilderContext &BuilderCtx, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const CFGBlock *DstT, + const CFGBlock *DstF) { + PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); + currBldrCtx = &BuilderCtx; + + const auto *VD = cast<VarDecl>(DS->getSingleDecl()); + ProgramStateRef state = Pred->getState(); + bool initHasRun = state->contains<InitializedGlobalsSet>(VD); + BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF); + + if (!initHasRun) { + state = state->add<InitializedGlobalsSet>(VD); + } + + builder.generateNode(state, initHasRun, Pred); + builder.markInfeasible(!initHasRun); + + currBldrCtx = nullptr; +} + +/// processIndirectGoto - Called by CoreEngine. Used to generate successor +/// nodes by processing the 'effects' of a computed goto jump. +void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) { + ProgramStateRef state = builder.getState(); + SVal V = state->getSVal(builder.getTarget(), builder.getLocationContext()); + + // Three possibilities: + // + // (1) We know the computed label. + // (2) The label is NULL (or some other constant), or Undefined. + // (3) We have no clue about the label. Dispatch to all targets. + // + + using iterator = IndirectGotoNodeBuilder::iterator; + + if (Optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) { + const LabelDecl *L = LV->getLabel(); + + for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) { + if (I.getLabel() == L) { + builder.generateNode(I, state); + return; + } + } + + llvm_unreachable("No block with label."); + } + + if (V.getAs<loc::ConcreteInt>() || V.getAs<UndefinedVal>()) { + // Dispatch to the first target and mark it as a sink. + //ExplodedNode* N = builder.generateNode(builder.begin(), state, true); + // FIXME: add checker visit. + // UndefBranches.insert(N); + return; + } + + // This is really a catch-all. We don't support symbolics yet. + // FIXME: Implement dispatch for symbolic pointers. + + for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) + builder.generateNode(I, state); +} + +void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const BlockEdge &L) { + SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC); + getCheckerManager().runCheckersForBeginFunction(Dst, L, Pred, *this); +} + +/// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path +/// nodes when the control reaches the end of a function. +void ExprEngine::processEndOfFunction(NodeBuilderContext& BC, + ExplodedNode *Pred, + const ReturnStmt *RS) { + ProgramStateRef State = Pred->getState(); + + if (!Pred->getStackFrame()->inTopFrame()) + State = finishArgumentConstruction( + State, *getStateManager().getCallEventManager().getCaller( + Pred->getStackFrame(), Pred->getState())); + + // FIXME: We currently cannot assert that temporaries are clear, because + // lifetime extended temporaries are not always modelled correctly. In some + // cases when we materialize the temporary, we do + // createTemporaryRegionIfNeeded(), and the region changes, and also the + // respective destructor becomes automatic from temporary. So for now clean up + // the state manually before asserting. Ideally, this braced block of code + // should go away. + { + const LocationContext *FromLC = Pred->getLocationContext(); + const LocationContext *ToLC = FromLC->getStackFrame()->getParent(); + const LocationContext *LC = FromLC; + while (LC != ToLC) { + assert(LC && "ToLC must be a parent of FromLC!"); + for (auto I : State->get<ObjectsUnderConstruction>()) + if (I.first.getLocationContext() == LC) { + // The comment above only pardons us for not cleaning up a + // temporary destructor. If any other statements are found here, + // it must be a separate problem. + assert(I.first.getItem().getKind() == + ConstructionContextItem::TemporaryDestructorKind || + I.first.getItem().getKind() == + ConstructionContextItem::ElidedDestructorKind); + State = State->remove<ObjectsUnderConstruction>(I.first); + } + LC = LC->getParent(); + } + } + + // Perform the transition with cleanups. + if (State != Pred->getState()) { + ExplodedNodeSet PostCleanup; + NodeBuilder Bldr(Pred, PostCleanup, BC); + Pred = Bldr.generateNode(Pred->getLocation(), State, Pred); + if (!Pred) { + // The node with clean temporaries already exists. We might have reached + // it on a path on which we initialize different temporaries. + return; + } + } + + assert(areAllObjectsFullyConstructed(Pred->getState(), + Pred->getLocationContext(), + Pred->getStackFrame()->getParent())); + + PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext()); + StateMgr.EndPath(Pred->getState()); + + ExplodedNodeSet Dst; + if (Pred->getLocationContext()->inTopFrame()) { + // Remove dead symbols. + ExplodedNodeSet AfterRemovedDead; + removeDeadOnEndOfFunction(BC, Pred, AfterRemovedDead); + + // Notify checkers. + for (const auto I : AfterRemovedDead) + getCheckerManager().runCheckersForEndFunction(BC, Dst, I, *this, RS); + } else { + getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this, RS); + } + + Engine.enqueueEndOfFunction(Dst, RS); +} + +/// ProcessSwitch - Called by CoreEngine. Used to generate successor +/// nodes by processing the 'effects' of a switch statement. +void ExprEngine::processSwitch(SwitchNodeBuilder& builder) { + using iterator = SwitchNodeBuilder::iterator; + + ProgramStateRef state = builder.getState(); + const Expr *CondE = builder.getCondition(); + SVal CondV_untested = state->getSVal(CondE, builder.getLocationContext()); + + if (CondV_untested.isUndef()) { + //ExplodedNode* N = builder.generateDefaultCaseNode(state, true); + // FIXME: add checker + //UndefBranches.insert(N); + + return; + } + DefinedOrUnknownSVal CondV = CondV_untested.castAs<DefinedOrUnknownSVal>(); + + ProgramStateRef DefaultSt = state; + + iterator I = builder.begin(), EI = builder.end(); + bool defaultIsFeasible = I == EI; + + for ( ; I != EI; ++I) { + // Successor may be pruned out during CFG construction. + if (!I.getBlock()) + continue; + + const CaseStmt *Case = I.getCase(); + + // Evaluate the LHS of the case value. + llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext()); + assert(V1.getBitWidth() == getContext().getIntWidth(CondE->getType())); + + // Get the RHS of the case, if it exists. + llvm::APSInt V2; + if (const Expr *E = Case->getRHS()) + V2 = E->EvaluateKnownConstInt(getContext()); + else + V2 = V1; + + ProgramStateRef StateCase; + if (Optional<NonLoc> NL = CondV.getAs<NonLoc>()) + std::tie(StateCase, DefaultSt) = + DefaultSt->assumeInclusiveRange(*NL, V1, V2); + else // UnknownVal + StateCase = DefaultSt; + + if (StateCase) + builder.generateCaseStmtNode(I, StateCase); + + // Now "assume" that the case doesn't match. Add this state + // to the default state (if it is feasible). + if (DefaultSt) + defaultIsFeasible = true; + else { + defaultIsFeasible = false; + break; + } + } + + if (!defaultIsFeasible) + return; + + // If we have switch(enum value), the default branch is not + // feasible if all of the enum constants not covered by 'case:' statements + // are not feasible values for the switch condition. + // + // Note that this isn't as accurate as it could be. Even if there isn't + // a case for a particular enum value as long as that enum value isn't + // feasible then it shouldn't be considered for making 'default:' reachable. + const SwitchStmt *SS = builder.getSwitch(); + const Expr *CondExpr = SS->getCond()->IgnoreParenImpCasts(); + if (CondExpr->getType()->getAs<EnumType>()) { + if (SS->isAllEnumCasesCovered()) + return; + } + + builder.generateDefaultCaseNode(DefaultSt); +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Loads and stores. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + + if (const auto *VD = dyn_cast<VarDecl>(D)) { + // C permits "extern void v", and if you cast the address to a valid type, + // you can even do things with it. We simply pretend + assert(Ex->isGLValue() || VD->getType()->isVoidType()); + const LocationContext *LocCtxt = Pred->getLocationContext(); + const Decl *D = LocCtxt->getDecl(); + const auto *MD = dyn_cast_or_null<CXXMethodDecl>(D); + const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex); + Optional<std::pair<SVal, QualType>> VInfo; + + if (AMgr.options.ShouldInlineLambdas && DeclRefEx && + DeclRefEx->refersToEnclosingVariableOrCapture() && MD && + MD->getParent()->isLambda()) { + // Lookup the field of the lambda. + const CXXRecordDecl *CXXRec = MD->getParent(); + llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields; + FieldDecl *LambdaThisCaptureField; + CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField); + + // Sema follows a sequence of complex rules to determine whether the + // variable should be captured. + if (const FieldDecl *FD = LambdaCaptureFields[VD]) { + Loc CXXThis = + svalBuilder.getCXXThis(MD, LocCtxt->getStackFrame()); + SVal CXXThisVal = state->getSVal(CXXThis); + VInfo = std::make_pair(state->getLValue(FD, CXXThisVal), FD->getType()); + } + } + + if (!VInfo) + VInfo = std::make_pair(state->getLValue(VD, LocCtxt), VD->getType()); + + SVal V = VInfo->first; + bool IsReference = VInfo->second->isReferenceType(); + + // For references, the 'lvalue' is the pointer address stored in the + // reference region. + if (IsReference) { + if (const MemRegion *R = V.getAsRegion()) + V = state->getSVal(R); + else + V = UnknownVal(); + } + + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, + ProgramPoint::PostLValueKind); + return; + } + if (const auto *ED = dyn_cast<EnumConstantDecl>(D)) { + assert(!Ex->isGLValue()); + SVal V = svalBuilder.makeIntVal(ED->getInitVal()); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V)); + return; + } + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { + SVal V = svalBuilder.getFunctionPointer(FD); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, + ProgramPoint::PostLValueKind); + return; + } + if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) { + // FIXME: Compute lvalue of field pointers-to-member. + // Right now we just use a non-null void pointer, so that it gives proper + // results in boolean contexts. + // FIXME: Maybe delegate this to the surrounding operator&. + // Note how this expression is lvalue, however pointer-to-member is NonLoc. + SVal V = svalBuilder.conjureSymbolVal(Ex, LCtx, getContext().VoidPtrTy, + currBldrCtx->blockCount()); + state = state->assume(V.castAs<DefinedOrUnknownSVal>(), true); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr, + ProgramPoint::PostLValueKind); + return; + } + if (isa<BindingDecl>(D)) { + // FIXME: proper support for bound declarations. + // For now, let's just prevent crashing. + return; + } + + llvm_unreachable("Support for this Decl not implemented."); +} + +/// VisitArraySubscriptExpr - Transfer function for array accesses +void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A, + ExplodedNode *Pred, + ExplodedNodeSet &Dst){ + const Expr *Base = A->getBase()->IgnoreParens(); + const Expr *Idx = A->getIdx()->IgnoreParens(); + + ExplodedNodeSet CheckerPreStmt; + getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this); + + ExplodedNodeSet EvalSet; + StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx); + + bool IsVectorType = A->getBase()->getType()->isVectorType(); + + // The "like" case is for situations where C standard prohibits the type to + // be an lvalue, e.g. taking the address of a subscript of an expression of + // type "void *". + bool IsGLValueLike = A->isGLValue() || + (A->getType().isCForbiddenLValueType() && !AMgr.getLangOpts().CPlusPlus); + + for (auto *Node : CheckerPreStmt) { + const LocationContext *LCtx = Node->getLocationContext(); + ProgramStateRef state = Node->getState(); + + if (IsGLValueLike) { + QualType T = A->getType(); + + // One of the forbidden LValue types! We still need to have sensible + // symbolic locations to represent this stuff. Note that arithmetic on + // void pointers is a GCC extension. + if (T->isVoidType()) + T = getContext().CharTy; + + SVal V = state->getLValue(T, + state->getSVal(Idx, LCtx), + state->getSVal(Base, LCtx)); + Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr, + ProgramPoint::PostLValueKind); + } else if (IsVectorType) { + // FIXME: non-glvalue vector reads are not modelled. + Bldr.generateNode(A, Node, state, nullptr); + } else { + llvm_unreachable("Array subscript should be an lValue when not \ +a vector and not a forbidden lvalue type"); + } + } + + getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this); +} + +/// VisitMemberExpr - Transfer function for member expressions. +void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + // FIXME: Prechecks eventually go in ::Visit(). + ExplodedNodeSet CheckedSet; + getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, M, *this); + + ExplodedNodeSet EvalSet; + ValueDecl *Member = M->getMemberDecl(); + + // Handle static member variables and enum constants accessed via + // member syntax. + if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) { + for (const auto I : CheckedSet) + VisitCommonDeclRefExpr(M, Member, I, EvalSet); + } else { + StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx); + ExplodedNodeSet Tmp; + + for (const auto I : CheckedSet) { + ProgramStateRef state = I->getState(); + const LocationContext *LCtx = I->getLocationContext(); + Expr *BaseExpr = M->getBase(); + + // Handle C++ method calls. + if (const auto *MD = dyn_cast<CXXMethodDecl>(Member)) { + if (MD->isInstance()) + state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr); + + SVal MDVal = svalBuilder.getFunctionPointer(MD); + state = state->BindExpr(M, LCtx, MDVal); + + Bldr.generateNode(M, I, state); + continue; + } + + // Handle regular struct fields / member variables. + const SubRegion *MR = nullptr; + state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr, + /*Result=*/nullptr, + /*OutRegionWithAdjustments=*/&MR); + SVal baseExprVal = + MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, LCtx); + + const auto *field = cast<FieldDecl>(Member); + SVal L = state->getLValue(field, baseExprVal); + + if (M->isGLValue() || M->getType()->isArrayType()) { + // We special-case rvalues of array type because the analyzer cannot + // reason about them, since we expect all regions to be wrapped in Locs. + // We instead treat these as lvalues and assume that they will decay to + // pointers as soon as they are used. + if (!M->isGLValue()) { + assert(M->getType()->isArrayType()); + const auto *PE = + dyn_cast<ImplicitCastExpr>(I->getParentMap().getParentIgnoreParens(M)); + if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) { + llvm_unreachable("should always be wrapped in ArrayToPointerDecay"); + } + } + + if (field->getType()->isReferenceType()) { + if (const MemRegion *R = L.getAsRegion()) + L = state->getSVal(R); + else + L = UnknownVal(); + } + + Bldr.generateNode(M, I, state->BindExpr(M, LCtx, L), nullptr, + ProgramPoint::PostLValueKind); + } else { + Bldr.takeNodes(I); + evalLoad(Tmp, M, M, I, state, L); + Bldr.addNodes(Tmp); + } + } + } + + getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, M, *this); +} + +void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ExplodedNodeSet AfterPreSet; + getCheckerManager().runCheckersForPreStmt(AfterPreSet, Pred, AE, *this); + + // For now, treat all the arguments to C11 atomics as escaping. + // FIXME: Ideally we should model the behavior of the atomics precisely here. + + ExplodedNodeSet AfterInvalidateSet; + StmtNodeBuilder Bldr(AfterPreSet, AfterInvalidateSet, *currBldrCtx); + + for (const auto I : AfterPreSet) { + ProgramStateRef State = I->getState(); + const LocationContext *LCtx = I->getLocationContext(); + + SmallVector<SVal, 8> ValuesToInvalidate; + for (unsigned SI = 0, Count = AE->getNumSubExprs(); SI != Count; SI++) { + const Expr *SubExpr = AE->getSubExprs()[SI]; + SVal SubExprVal = State->getSVal(SubExpr, LCtx); + ValuesToInvalidate.push_back(SubExprVal); + } + + State = State->invalidateRegions(ValuesToInvalidate, AE, + currBldrCtx->blockCount(), + LCtx, + /*CausedByPointerEscape*/true, + /*Symbols=*/nullptr); + + SVal ResultVal = UnknownVal(); + State = State->BindExpr(AE, LCtx, ResultVal); + Bldr.generateNode(AE, I, State, nullptr, + ProgramPoint::PostStmtKind); + } + + getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this); +} + +// A value escapes in three possible cases: +// (1) We are binding to something that is not a memory region. +// (2) We are binding to a MemrRegion that does not have stack storage. +// (3) 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; + + // Otherwise, find all symbols referenced by 'val' that we are tracking + // and stop tracking them. + State = escapeValue(State, Val, PSK_EscapeOnBind); + return State; +} + +ProgramStateRef +ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State, + const InvalidatedSymbols *Invalidated, + ArrayRef<const MemRegion *> ExplicitRegions, + const CallEvent *Call, + RegionAndSymbolInvalidationTraits &ITraits) { + if (!Invalidated || Invalidated->empty()) + return State; + + if (!Call) + return getCheckerManager().runCheckersForPointerEscape(State, + *Invalidated, + nullptr, + PSK_EscapeOther, + &ITraits); + + // If the symbols were invalidated by a call, we want to find out which ones + // were invalidated directly due to being arguments to the call. + InvalidatedSymbols SymbolsDirectlyInvalidated; + for (const auto I : ExplicitRegions) { + if (const SymbolicRegion *R = I->StripCasts()->getAs<SymbolicRegion>()) + SymbolsDirectlyInvalidated.insert(R->getSymbol()); + } + + InvalidatedSymbols SymbolsIndirectlyInvalidated; + for (const auto &sym : *Invalidated) { + if (SymbolsDirectlyInvalidated.count(sym)) + continue; + SymbolsIndirectlyInvalidated.insert(sym); + } + + if (!SymbolsDirectlyInvalidated.empty()) + State = getCheckerManager().runCheckersForPointerEscape(State, + SymbolsDirectlyInvalidated, Call, PSK_DirectEscapeOnCall, &ITraits); + + // Notify about the symbols that get indirectly invalidated by the call. + if (!SymbolsIndirectlyInvalidated.empty()) + State = getCheckerManager().runCheckersForPointerEscape(State, + SymbolsIndirectlyInvalidated, Call, PSK_IndirectEscapeOnCall, &ITraits); + + return State; +} + +/// evalBind - Handle the semantics of binding a value to a specific location. +/// This method is used by evalStore and (soon) VisitDeclStmt, and others. +void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE, + ExplodedNode *Pred, + SVal location, SVal Val, + bool atDeclInit, const ProgramPoint *PP) { + const LocationContext *LC = Pred->getLocationContext(); + PostStmt PS(StoreE, LC); + if (!PP) + PP = &PS; + + // Do a previsit of the bind. + ExplodedNodeSet CheckedSet; + getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val, + StoreE, *this, *PP); + + StmtNodeBuilder Bldr(CheckedSet, Dst, *currBldrCtx); + + // If the location is not a 'Loc', it will already be handled by + // the checkers. There is nothing left to do. + if (!location.getAs<Loc>()) { + const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/nullptr, + /*tag*/nullptr); + ProgramStateRef state = Pred->getState(); + state = processPointerEscapedOnBind(state, location, Val, LC); + Bldr.generateNode(L, state, Pred); + return; + } + + for (const auto PredI : CheckedSet) { + ProgramStateRef state = PredI->getState(); + + state = processPointerEscapedOnBind(state, location, Val, LC); + + // When binding the value, pass on the hint that this is a initialization. + // For initializations, we do not need to inform clients of region + // changes. + state = state->bindLoc(location.castAs<Loc>(), + Val, LC, /* notifyChanges = */ !atDeclInit); + + const MemRegion *LocReg = nullptr; + if (Optional<loc::MemRegionVal> LocRegVal = + location.getAs<loc::MemRegionVal>()) { + LocReg = LocRegVal->getRegion(); + } + + const ProgramPoint L = PostStore(StoreE, LC, LocReg, nullptr); + Bldr.generateNode(L, state, PredI); + } +} + +/// evalStore - Handle the semantics of a store via an assignment. +/// @param Dst The node set to store generated state nodes +/// @param AssignE The assignment expression if the store happens in an +/// assignment. +/// @param LocationE The location expression that is stored to. +/// @param state The current simulation state +/// @param location The location to store the value +/// @param Val The value to be stored +void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE, + const Expr *LocationE, + ExplodedNode *Pred, + ProgramStateRef state, SVal location, SVal Val, + const ProgramPointTag *tag) { + // Proceed with the store. We use AssignE as the anchor for the PostStore + // ProgramPoint if it is non-NULL, and LocationE otherwise. + const Expr *StoreE = AssignE ? AssignE : LocationE; + + // Evaluate the location (checks for bad dereferences). + ExplodedNodeSet Tmp; + evalLocation(Tmp, AssignE, LocationE, Pred, state, location, false); + + if (Tmp.empty()) + return; + + if (location.isUndef()) + return; + + for (const auto I : Tmp) + evalBind(Dst, StoreE, I, location, Val, false); +} + +void ExprEngine::evalLoad(ExplodedNodeSet &Dst, + const Expr *NodeEx, + const Expr *BoundEx, + ExplodedNode *Pred, + ProgramStateRef state, + SVal location, + const ProgramPointTag *tag, + QualType LoadTy) { + assert(!location.getAs<NonLoc>() && "location cannot be a NonLoc."); + assert(NodeEx); + assert(BoundEx); + // Evaluate the location (checks for bad dereferences). + ExplodedNodeSet Tmp; + evalLocation(Tmp, NodeEx, BoundEx, Pred, state, location, true); + if (Tmp.empty()) + return; + + StmtNodeBuilder Bldr(Tmp, Dst, *currBldrCtx); + if (location.isUndef()) + return; + + // Proceed with the load. + for (const auto I : Tmp) { + state = I->getState(); + const LocationContext *LCtx = I->getLocationContext(); + + SVal V = UnknownVal(); + if (location.isValid()) { + if (LoadTy.isNull()) + LoadTy = BoundEx->getType(); + V = state->getSVal(location.castAs<Loc>(), LoadTy); + } + + Bldr.generateNode(NodeEx, I, state->BindExpr(BoundEx, LCtx, V), tag, + ProgramPoint::PostLoadKind); + } +} + +void ExprEngine::evalLocation(ExplodedNodeSet &Dst, + const Stmt *NodeEx, + const Stmt *BoundEx, + ExplodedNode *Pred, + ProgramStateRef state, + SVal location, + bool isLoad) { + StmtNodeBuilder BldrTop(Pred, Dst, *currBldrCtx); + // Early checks for performance reason. + if (location.isUnknown()) { + return; + } + + ExplodedNodeSet Src; + BldrTop.takeNodes(Pred); + StmtNodeBuilder Bldr(Pred, Src, *currBldrCtx); + if (Pred->getState() != state) { + // Associate this new state with an ExplodedNode. + // FIXME: If I pass null tag, the graph is incorrect, e.g for + // int *p; + // p = 0; + // *p = 0xDEADBEEF; + // "p = 0" is not noted as "Null pointer value stored to 'p'" but + // instead "int *p" is noted as + // "Variable 'p' initialized to a null pointer value" + + static SimpleProgramPointTag tag(TagProviderName, "Location"); + Bldr.generateNode(NodeEx, Pred, state, &tag); + } + ExplodedNodeSet Tmp; + getCheckerManager().runCheckersForLocation(Tmp, Src, location, isLoad, + NodeEx, BoundEx, *this); + BldrTop.addNodes(Tmp); +} + +std::pair<const ProgramPointTag *, const ProgramPointTag*> +ExprEngine::geteagerlyAssumeBinOpBifurcationTags() { + static SimpleProgramPointTag + eagerlyAssumeBinOpBifurcationTrue(TagProviderName, + "Eagerly Assume True"), + eagerlyAssumeBinOpBifurcationFalse(TagProviderName, + "Eagerly Assume False"); + return std::make_pair(&eagerlyAssumeBinOpBifurcationTrue, + &eagerlyAssumeBinOpBifurcationFalse); +} + +void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, + const Expr *Ex) { + StmtNodeBuilder Bldr(Src, Dst, *currBldrCtx); + + for (const auto Pred : Src) { + // Test if the previous node was as the same expression. This can happen + // when the expression fails to evaluate to anything meaningful and + // (as an optimization) we don't generate a node. + ProgramPoint P = Pred->getLocation(); + if (!P.getAs<PostStmt>() || P.castAs<PostStmt>().getStmt() != Ex) { + continue; + } + + ProgramStateRef state = Pred->getState(); + SVal V = state->getSVal(Ex, Pred->getLocationContext()); + Optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>(); + if (SEV && SEV->isExpression()) { + const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags = + geteagerlyAssumeBinOpBifurcationTags(); + + ProgramStateRef StateTrue, StateFalse; + std::tie(StateTrue, StateFalse) = state->assume(*SEV); + + // First assume that the condition is true. + if (StateTrue) { + SVal Val = svalBuilder.makeIntVal(1U, Ex->getType()); + StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val); + Bldr.generateNode(Ex, Pred, StateTrue, tags.first); + } + + // Next, assume that the condition is false. + if (StateFalse) { + SVal Val = svalBuilder.makeIntVal(0U, Ex->getType()); + StateFalse = StateFalse->BindExpr(Ex, Pred->getLocationContext(), Val); + Bldr.generateNode(Ex, Pred, StateFalse, tags.second); + } + } + } +} + +void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + // We have processed both the inputs and the outputs. All of the outputs + // should evaluate to Locs. Nuke all of their values. + + // FIXME: Some day in the future it would be nice to allow a "plug-in" + // which interprets the inline asm and stores proper results in the + // outputs. + + ProgramStateRef state = Pred->getState(); + + for (const Expr *O : A->outputs()) { + SVal X = state->getSVal(O, Pred->getLocationContext()); + assert(!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef. + + if (Optional<Loc> LV = X.getAs<Loc>()) + state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext()); + } + + Bldr.generateNode(A, Pred, state); +} + +void ExprEngine::VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateNode(A, Pred, Pred->getState()); +} + +//===----------------------------------------------------------------------===// +// Visualization. +//===----------------------------------------------------------------------===// + +#ifndef NDEBUG +namespace llvm { + +template<> +struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits { + DOTGraphTraits (bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {} + + static bool nodeHasBugReport(const ExplodedNode *N) { + BugReporter &BR = static_cast<ExprEngine &>( + N->getState()->getStateManager().getOwningEngine()).getBugReporter(); + + const auto EQClasses = + llvm::make_range(BR.EQClasses_begin(), BR.EQClasses_end()); + + for (const auto &EQ : EQClasses) { + for (const BugReport &Report : EQ) { + if (Report.getErrorNode() == N) + return true; + } + } + return false; + } + + /// \p PreCallback: callback before break. + /// \p PostCallback: callback after break. + /// \p Stop: stop iteration if returns {@code true} + /// \return Whether {@code Stop} ever returned {@code true}. + static bool traverseHiddenNodes( + const ExplodedNode *N, + llvm::function_ref<void(const ExplodedNode *)> PreCallback, + llvm::function_ref<void(const ExplodedNode *)> PostCallback, + llvm::function_ref<bool(const ExplodedNode *)> Stop) { + const ExplodedNode *FirstHiddenNode = N; + while (FirstHiddenNode->pred_size() == 1 && + isNodeHidden(*FirstHiddenNode->pred_begin())) { + FirstHiddenNode = *FirstHiddenNode->pred_begin(); + } + const ExplodedNode *OtherNode = FirstHiddenNode; + while (true) { + PreCallback(OtherNode); + if (Stop(OtherNode)) + return true; + + if (OtherNode == N) + break; + PostCallback(OtherNode); + + OtherNode = *OtherNode->succ_begin(); + } + return false; + } + + static std::string getNodeAttributes(const ExplodedNode *N, + ExplodedGraph *) { + SmallVector<StringRef, 10> Out; + auto Noop = [](const ExplodedNode*){}; + if (traverseHiddenNodes(N, Noop, Noop, &nodeHasBugReport)) { + Out.push_back("style=filled"); + Out.push_back("fillcolor=red"); + } + + if (traverseHiddenNodes(N, Noop, Noop, + [](const ExplodedNode *C) { return C->isSink(); })) + Out.push_back("color=blue"); + return llvm::join(Out, ","); + } + + static bool isNodeHidden(const ExplodedNode *N) { + return N->isTrivial(); + } + + static std::string getNodeLabel(const ExplodedNode *N, ExplodedGraph *G){ + std::string sbuf; + llvm::raw_string_ostream Out(sbuf); + + ProgramStateRef State = N->getState(); + + // Dump program point for all the previously skipped nodes. + traverseHiddenNodes( + N, + [&](const ExplodedNode *OtherNode) { + OtherNode->getLocation().print(/*CR=*/"\\l", Out); + if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag()) + Out << "\\lTag:" << Tag->getTagDescription(); + if (N->isSink()) + Out << "\\lNode is sink\\l"; + if (nodeHasBugReport(N)) + Out << "\\lBug report attached\\l"; + }, + [&](const ExplodedNode *) { Out << "\\l--------\\l"; }, + [&](const ExplodedNode *) { return false; }); + + Out << "\\l\\|"; + + Out << "StateID: ST" << State->getID() << ", NodeID: N" << N->getID(G) + << " <" << (const void *)N << ">\\|"; + + bool SameAsAllPredecessors = + std::all_of(N->pred_begin(), N->pred_end(), [&](const ExplodedNode *P) { + return P->getState() == State; + }); + if (!SameAsAllPredecessors) + State->printDOT(Out, N->getLocationContext()); + return Out.str(); + } +}; + +} // namespace llvm +#endif + +void ExprEngine::ViewGraph(bool trim) { +#ifndef NDEBUG + std::string Filename = DumpGraph(trim); + llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT); +#endif + llvm::errs() << "Warning: viewing graph requires assertions" << "\n"; +} + + +void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode*> Nodes) { +#ifndef NDEBUG + std::string Filename = DumpGraph(Nodes); + llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT); +#endif + llvm::errs() << "Warning: viewing graph requires assertions" << "\n"; +} + +std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) { +#ifndef NDEBUG + if (trim) { + std::vector<const ExplodedNode *> Src; + + // Iterate through the reports and get their nodes. + for (BugReporter::EQClasses_iterator + EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) { + const auto *N = const_cast<ExplodedNode *>(EI->begin()->getErrorNode()); + if (N) Src.push_back(N); + } + return DumpGraph(Src, Filename); + } else { + return llvm::WriteGraph(&G, "ExprEngine", /*ShortNames=*/false, + /*Title=*/"Exploded Graph", /*Filename=*/Filename); + } +#endif + llvm::errs() << "Warning: dumping graph requires assertions" << "\n"; + return ""; +} + +std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes, + StringRef Filename) { +#ifndef NDEBUG + std::unique_ptr<ExplodedGraph> TrimmedG(G.trim(Nodes)); + + if (!TrimmedG.get()) { + llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; + } else { + return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine", + /*ShortNames=*/false, + /*Title=*/"Trimmed Exploded Graph", + /*Filename=*/Filename); + } +#endif + llvm::errs() << "Warning: dumping graph requires assertions" << "\n"; + return ""; +} + +void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() { + static int index = 0; + return &index; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp new file mode 100644 index 000000000000..b980628878e9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -0,0 +1,1127 @@ +//=-- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines ExprEngine's support for C expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/ExprCXX.h" +#include "clang/AST/DeclCXX.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; +using llvm::APSInt; + +/// Optionally conjure and return a symbol for offset when processing +/// an expression \p Expression. +/// If \p Other is a location, conjure a symbol for \p Symbol +/// (offset) if it is unknown so that memory arithmetic always +/// results in an ElementRegion. +/// \p Count The number of times the current basic block was visited. +static SVal conjureOffsetSymbolOnLocation( + SVal Symbol, SVal Other, Expr* Expression, SValBuilder &svalBuilder, + unsigned Count, const LocationContext *LCtx) { + QualType Ty = Expression->getType(); + if (Other.getAs<Loc>() && + Ty->isIntegralOrEnumerationType() && + Symbol.isUnknown()) { + return svalBuilder.conjureSymbolVal(Expression, LCtx, Ty, Count); + } + return Symbol; +} + +void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + Expr *LHS = B->getLHS()->IgnoreParens(); + Expr *RHS = B->getRHS()->IgnoreParens(); + + // FIXME: Prechecks eventually go in ::Visit(). + ExplodedNodeSet CheckedSet; + ExplodedNodeSet Tmp2; + getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, B, *this); + + // With both the LHS and RHS evaluated, process the operation itself. + for (ExplodedNodeSet::iterator it=CheckedSet.begin(), ei=CheckedSet.end(); + it != ei; ++it) { + + ProgramStateRef state = (*it)->getState(); + const LocationContext *LCtx = (*it)->getLocationContext(); + SVal LeftV = state->getSVal(LHS, LCtx); + SVal RightV = state->getSVal(RHS, LCtx); + + BinaryOperator::Opcode Op = B->getOpcode(); + + if (Op == BO_Assign) { + // EXPERIMENTAL: "Conjured" symbols. + // FIXME: Handle structs. + if (RightV.isUnknown()) { + unsigned Count = currBldrCtx->blockCount(); + RightV = svalBuilder.conjureSymbolVal(nullptr, B->getRHS(), LCtx, + Count); + } + // Simulate the effects of a "store": bind the value of the RHS + // to the L-Value represented by the LHS. + SVal ExprVal = B->isGLValue() ? LeftV : RightV; + evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, LCtx, ExprVal), + LeftV, RightV); + continue; + } + + if (!B->isAssignmentOp()) { + StmtNodeBuilder Bldr(*it, Tmp2, *currBldrCtx); + + if (B->isAdditiveOp()) { + // TODO: This can be removed after we enable history tracking with + // SymSymExpr. + unsigned Count = currBldrCtx->blockCount(); + RightV = conjureOffsetSymbolOnLocation( + RightV, LeftV, RHS, svalBuilder, Count, LCtx); + LeftV = conjureOffsetSymbolOnLocation( + LeftV, RightV, LHS, svalBuilder, Count, LCtx); + } + + // Although we don't yet model pointers-to-members, we do need to make + // sure that the members of temporaries have a valid 'this' pointer for + // other checks. + if (B->getOpcode() == BO_PtrMemD) + state = createTemporaryRegionIfNeeded(state, LCtx, LHS); + + // Process non-assignments except commas or short-circuited + // logical expressions (LAnd and LOr). + SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType()); + if (!Result.isUnknown()) { + state = state->BindExpr(B, LCtx, Result); + } + + Bldr.generateNode(B, *it, state); + continue; + } + + assert (B->isCompoundAssignmentOp()); + + switch (Op) { + default: + llvm_unreachable("Invalid opcode for compound assignment."); + case BO_MulAssign: Op = BO_Mul; break; + case BO_DivAssign: Op = BO_Div; break; + case BO_RemAssign: Op = BO_Rem; break; + case BO_AddAssign: Op = BO_Add; break; + case BO_SubAssign: Op = BO_Sub; break; + case BO_ShlAssign: Op = BO_Shl; break; + case BO_ShrAssign: Op = BO_Shr; break; + case BO_AndAssign: Op = BO_And; break; + case BO_XorAssign: Op = BO_Xor; break; + case BO_OrAssign: Op = BO_Or; break; + } + + // Perform a load (the LHS). This performs the checks for + // null dereferences, and so on. + ExplodedNodeSet Tmp; + SVal location = LeftV; + evalLoad(Tmp, B, LHS, *it, state, location); + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; + ++I) { + + state = (*I)->getState(); + const LocationContext *LCtx = (*I)->getLocationContext(); + SVal V = state->getSVal(LHS, LCtx); + + // Get the computation type. + QualType CTy = + cast<CompoundAssignOperator>(B)->getComputationResultType(); + CTy = getContext().getCanonicalType(CTy); + + QualType CLHSTy = + cast<CompoundAssignOperator>(B)->getComputationLHSType(); + CLHSTy = getContext().getCanonicalType(CLHSTy); + + QualType LTy = getContext().getCanonicalType(LHS->getType()); + + // Promote LHS. + V = svalBuilder.evalCast(V, CLHSTy, LTy); + + // Compute the result of the operation. + SVal Result = svalBuilder.evalCast(evalBinOp(state, Op, V, RightV, CTy), + B->getType(), CTy); + + // EXPERIMENTAL: "Conjured" symbols. + // FIXME: Handle structs. + + SVal LHSVal; + + if (Result.isUnknown()) { + // The symbolic value is actually for the type of the left-hand side + // expression, not the computation type, as this is the value the + // LValue on the LHS will bind to. + LHSVal = svalBuilder.conjureSymbolVal(nullptr, B->getRHS(), LCtx, LTy, + currBldrCtx->blockCount()); + // However, we need to convert the symbol to the computation type. + Result = svalBuilder.evalCast(LHSVal, CTy, LTy); + } + else { + // The left-hand side may bind to a different value then the + // computation type. + LHSVal = svalBuilder.evalCast(Result, LTy, CTy); + } + + // In C++, assignment and compound assignment operators return an + // lvalue. + if (B->isGLValue()) + state = state->BindExpr(B, LCtx, location); + else + state = state->BindExpr(B, LCtx, Result); + + evalStore(Tmp2, B, LHS, *I, state, location, LHSVal); + } + } + + // FIXME: postvisits eventually go in ::Visit() + getCheckerManager().runCheckersForPostStmt(Dst, Tmp2, B, *this); +} + +void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + CanQualType T = getContext().getCanonicalType(BE->getType()); + + const BlockDecl *BD = BE->getBlockDecl(); + // Get the value of the block itself. + SVal V = svalBuilder.getBlockPointer(BD, T, + Pred->getLocationContext(), + currBldrCtx->blockCount()); + + ProgramStateRef State = Pred->getState(); + + // If we created a new MemRegion for the block, we should explicitly bind + // the captured variables. + if (const BlockDataRegion *BDR = + dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) { + + BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(), + E = BDR->referenced_vars_end(); + + auto CI = BD->capture_begin(); + auto CE = BD->capture_end(); + for (; I != E; ++I) { + const VarRegion *capturedR = I.getCapturedRegion(); + const VarRegion *originalR = I.getOriginalRegion(); + + // If the capture had a copy expression, use the result of evaluating + // that expression, otherwise use the original value. + // We rely on the invariant that the block declaration's capture variables + // are a prefix of the BlockDataRegion's referenced vars (which may include + // referenced globals, etc.) to enable fast lookup of the capture for a + // given referenced var. + const Expr *copyExpr = nullptr; + if (CI != CE) { + assert(CI->getVariable() == capturedR->getDecl()); + copyExpr = CI->getCopyExpr(); + CI++; + } + + if (capturedR != originalR) { + SVal originalV; + const LocationContext *LCtx = Pred->getLocationContext(); + if (copyExpr) { + originalV = State->getSVal(copyExpr, LCtx); + } else { + originalV = State->getSVal(loc::MemRegionVal(originalR)); + } + State = State->bindLoc(loc::MemRegionVal(capturedR), originalV, LCtx); + } + } + } + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx); + Bldr.generateNode(BE, Pred, + State->BindExpr(BE, Pred->getLocationContext(), V), + nullptr, ProgramPoint::PostLValueKind); + + // FIXME: Move all post/pre visits to ::Visit(). + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this); +} + +ProgramStateRef ExprEngine::handleLValueBitCast( + ProgramStateRef state, const Expr* Ex, const LocationContext* LCtx, + QualType T, QualType ExTy, const CastExpr* CastE, StmtNodeBuilder& Bldr, + ExplodedNode* Pred) { + if (T->isLValueReferenceType()) { + assert(!CastE->getType()->isLValueReferenceType()); + ExTy = getContext().getLValueReferenceType(ExTy); + } else if (T->isRValueReferenceType()) { + assert(!CastE->getType()->isRValueReferenceType()); + ExTy = getContext().getRValueReferenceType(ExTy); + } + // Delegate to SValBuilder to process. + SVal OrigV = state->getSVal(Ex, LCtx); + SVal V = svalBuilder.evalCast(OrigV, T, ExTy); + // Negate the result if we're treating the boolean as a signed i1 + if (CastE->getCastKind() == CK_BooleanToSignedIntegral) + V = evalMinus(V); + state = state->BindExpr(CastE, LCtx, V); + if (V.isUnknown() && !OrigV.isUnknown()) { + state = escapeValue(state, OrigV, PSK_EscapeOther); + } + Bldr.generateNode(CastE, Pred, state); + + return state; +} + +ProgramStateRef ExprEngine::handleLVectorSplat( + ProgramStateRef state, const LocationContext* LCtx, const CastExpr* CastE, + StmtNodeBuilder &Bldr, ExplodedNode* Pred) { + // Recover some path sensitivity by conjuring a new value. + QualType resultType = CastE->getType(); + if (CastE->isGLValue()) + resultType = getContext().getPointerType(resultType); + SVal result = svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, + resultType, + currBldrCtx->blockCount()); + state = state->BindExpr(CastE, LCtx, result); + Bldr.generateNode(CastE, Pred, state); + + return state; +} + +void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + + ExplodedNodeSet dstPreStmt; + getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this); + + if (CastE->getCastKind() == CK_LValueToRValue) { + for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end(); + I!=E; ++I) { + ExplodedNode *subExprNode = *I; + ProgramStateRef state = subExprNode->getState(); + const LocationContext *LCtx = subExprNode->getLocationContext(); + evalLoad(Dst, CastE, CastE, subExprNode, state, state->getSVal(Ex, LCtx)); + } + return; + } + + // All other casts. + QualType T = CastE->getType(); + QualType ExTy = Ex->getType(); + + if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE)) + T = ExCast->getTypeAsWritten(); + + StmtNodeBuilder Bldr(dstPreStmt, Dst, *currBldrCtx); + for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end(); + I != E; ++I) { + + Pred = *I; + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + + switch (CastE->getCastKind()) { + case CK_LValueToRValue: + llvm_unreachable("LValueToRValue casts handled earlier."); + case CK_ToVoid: + continue; + // The analyzer doesn't do anything special with these casts, + // since it understands retain/release semantics already. + case CK_ARCProduceObject: + case CK_ARCConsumeObject: + case CK_ARCReclaimReturnedObject: + case CK_ARCExtendBlockObject: // Fall-through. + case CK_CopyAndAutoreleaseBlockObject: + // The analyser can ignore atomic casts for now, although some future + // checkers may want to make certain that you're not modifying the same + // value through atomic and nonatomic pointers. + case CK_AtomicToNonAtomic: + case CK_NonAtomicToAtomic: + // True no-ops. + case CK_NoOp: + case CK_ConstructorConversion: + case CK_UserDefinedConversion: + case CK_FunctionToPointerDecay: + case CK_BuiltinFnToFnPtr: { + // Copy the SVal of Ex to CastE. + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal V = state->getSVal(Ex, LCtx); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + continue; + } + case CK_MemberPointerToBoolean: + case CK_PointerToBoolean: { + SVal V = state->getSVal(Ex, LCtx); + auto PTMSV = V.getAs<nonloc::PointerToMember>(); + if (PTMSV) + V = svalBuilder.makeTruthVal(!PTMSV->isNullMemberPointer(), ExTy); + if (V.isUndef() || PTMSV) { + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + continue; + } + // Explicitly proceed with default handler for this case cascade. + state = + handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); + continue; + } + case CK_Dependent: + case CK_ArrayToPointerDecay: + case CK_BitCast: + case CK_AddressSpaceConversion: + case CK_BooleanToSignedIntegral: + case CK_NullToPointer: + case CK_IntegralToPointer: + case CK_PointerToIntegral: { + SVal V = state->getSVal(Ex, LCtx); + if (V.getAs<nonloc::PointerToMember>()) { + state = state->BindExpr(CastE, LCtx, UnknownVal()); + Bldr.generateNode(CastE, Pred, state); + continue; + } + // Explicitly proceed with default handler for this case cascade. + state = + handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); + continue; + } + case CK_IntegralToBoolean: + case CK_IntegralToFloating: + case CK_FloatingToIntegral: + case CK_FloatingToBoolean: + case CK_FloatingCast: + case CK_FloatingRealToComplex: + case CK_FloatingComplexToReal: + case CK_FloatingComplexToBoolean: + case CK_FloatingComplexCast: + case CK_FloatingComplexToIntegralComplex: + case CK_IntegralRealToComplex: + case CK_IntegralComplexToReal: + case CK_IntegralComplexToBoolean: + case CK_IntegralComplexCast: + case CK_IntegralComplexToFloatingComplex: + case CK_CPointerToObjCPointerCast: + case CK_BlockPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + case CK_ObjCObjectLValueCast: + case CK_ZeroToOCLOpaqueType: + case CK_IntToOCLSampler: + case CK_LValueBitCast: + case CK_FixedPointCast: + case CK_FixedPointToBoolean: { + state = + handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred); + continue; + } + case CK_IntegralCast: { + // Delegate to SValBuilder to process. + SVal V = state->getSVal(Ex, LCtx); + V = svalBuilder.evalIntegralCast(state, V, T, ExTy); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + continue; + } + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: { + // For DerivedToBase cast, delegate to the store manager. + SVal val = state->getSVal(Ex, LCtx); + val = getStoreManager().evalDerivedToBase(val, CastE); + state = state->BindExpr(CastE, LCtx, val); + Bldr.generateNode(CastE, Pred, state); + continue; + } + // Handle C++ dyn_cast. + case CK_Dynamic: { + SVal val = state->getSVal(Ex, LCtx); + + // Compute the type of the result. + QualType resultType = CastE->getType(); + if (CastE->isGLValue()) + resultType = getContext().getPointerType(resultType); + + bool Failed = false; + + // Check if the value being cast evaluates to 0. + if (val.isZeroConstant()) + Failed = true; + // Else, evaluate the cast. + else + val = getStoreManager().attemptDownCast(val, T, Failed); + + if (Failed) { + if (T->isReferenceType()) { + // A bad_cast exception is thrown if input value is a reference. + // Currently, we model this, by generating a sink. + Bldr.generateSink(CastE, Pred, state); + continue; + } else { + // If the cast fails on a pointer, bind to 0. + state = state->BindExpr(CastE, LCtx, svalBuilder.makeNull()); + } + } else { + // If we don't know if the cast succeeded, conjure a new symbol. + if (val.isUnknown()) { + DefinedOrUnknownSVal NewSym = + svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, resultType, + currBldrCtx->blockCount()); + state = state->BindExpr(CastE, LCtx, NewSym); + } else + // Else, bind to the derived region value. + state = state->BindExpr(CastE, LCtx, val); + } + Bldr.generateNode(CastE, Pred, state); + continue; + } + case CK_BaseToDerived: { + SVal val = state->getSVal(Ex, LCtx); + QualType resultType = CastE->getType(); + if (CastE->isGLValue()) + resultType = getContext().getPointerType(resultType); + + bool Failed = false; + + if (!val.isConstant()) { + val = getStoreManager().attemptDownCast(val, T, Failed); + } + + // Failed to cast or the result is unknown, fall back to conservative. + if (Failed || val.isUnknown()) { + val = + svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, resultType, + currBldrCtx->blockCount()); + } + state = state->BindExpr(CastE, LCtx, val); + Bldr.generateNode(CastE, Pred, state); + continue; + } + case CK_NullToMemberPointer: { + SVal V = svalBuilder.getMemberPointer(nullptr); + state = state->BindExpr(CastE, LCtx, V); + Bldr.generateNode(CastE, Pred, state); + continue; + } + case CK_DerivedToBaseMemberPointer: + case CK_BaseToDerivedMemberPointer: + case CK_ReinterpretMemberPointer: { + SVal V = state->getSVal(Ex, LCtx); + if (auto PTMSV = V.getAs<nonloc::PointerToMember>()) { + SVal CastedPTMSV = svalBuilder.makePointerToMember( + getBasicVals().accumCXXBase( + llvm::make_range<CastExpr::path_const_iterator>( + CastE->path_begin(), CastE->path_end()), *PTMSV)); + state = state->BindExpr(CastE, LCtx, CastedPTMSV); + Bldr.generateNode(CastE, Pred, state); + continue; + } + // Explicitly proceed with default handler for this case cascade. + state = handleLVectorSplat(state, LCtx, CastE, Bldr, Pred); + continue; + } + // Various C++ casts that are not handled yet. + case CK_ToUnion: + case CK_VectorSplat: { + state = handleLVectorSplat(state, LCtx, CastE, Bldr, Pred); + continue; + } + } + } +} + +void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + StmtNodeBuilder B(Pred, Dst, *currBldrCtx); + + ProgramStateRef State = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + + const Expr *Init = CL->getInitializer(); + SVal V = State->getSVal(CL->getInitializer(), LCtx); + + if (isa<CXXConstructExpr>(Init) || isa<CXXStdInitializerListExpr>(Init)) { + // No work needed. Just pass the value up to this expression. + } else { + assert(isa<InitListExpr>(Init)); + Loc CLLoc = State->getLValue(CL, LCtx); + State = State->bindLoc(CLLoc, V, LCtx); + + if (CL->isGLValue()) + V = CLLoc; + } + + B.generateNode(CL, Pred, State->BindExpr(CL, LCtx, V)); +} + +void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + // Assumption: The CFG has one DeclStmt per Decl. + const VarDecl *VD = dyn_cast_or_null<VarDecl>(*DS->decl_begin()); + + if (!VD) { + //TODO:AZ: remove explicit insertion after refactoring is done. + Dst.insert(Pred); + return; + } + + // FIXME: all pre/post visits should eventually be handled by ::Visit(). + ExplodedNodeSet dstPreVisit; + getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this); + + ExplodedNodeSet dstEvaluated; + StmtNodeBuilder B(dstPreVisit, dstEvaluated, *currBldrCtx); + for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); + I!=E; ++I) { + ExplodedNode *N = *I; + ProgramStateRef state = N->getState(); + const LocationContext *LC = N->getLocationContext(); + + // Decls without InitExpr are not initialized explicitly. + if (const Expr *InitEx = VD->getInit()) { + + // Note in the state that the initialization has occurred. + ExplodedNode *UpdatedN = N; + SVal InitVal = state->getSVal(InitEx, LC); + + assert(DS->isSingleDecl()); + if (getObjectUnderConstruction(state, DS, LC)) { + state = finishObjectConstruction(state, DS, LC); + // We constructed the object directly in the variable. + // No need to bind anything. + B.generateNode(DS, UpdatedN, state); + } else { + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if (InitVal.isUnknown()) { + QualType Ty = InitEx->getType(); + if (InitEx->isGLValue()) { + Ty = getContext().getPointerType(Ty); + } + + InitVal = svalBuilder.conjureSymbolVal(nullptr, InitEx, LC, Ty, + currBldrCtx->blockCount()); + } + + + B.takeNodes(UpdatedN); + ExplodedNodeSet Dst2; + evalBind(Dst2, DS, UpdatedN, state->getLValue(VD, LC), InitVal, true); + B.addNodes(Dst2); + } + } + else { + B.generateNode(DS, N, state); + } + } + + getCheckerManager().runCheckersForPostStmt(Dst, B.getResults(), DS, *this); +} + +void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + assert(B->getOpcode() == BO_LAnd || + B->getOpcode() == BO_LOr); + + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + ProgramStateRef state = Pred->getState(); + + if (B->getType()->isVectorType()) { + // FIXME: We do not model vector arithmetic yet. When adding support for + // that, note that the CFG-based reasoning below does not apply, because + // logical operators on vectors are not short-circuit. Currently they are + // modeled as short-circuit in Clang CFG but this is incorrect. + // Do not set the value for the expression. It'd be UnknownVal by default. + Bldr.generateNode(B, Pred, state); + return; + } + + ExplodedNode *N = Pred; + while (!N->getLocation().getAs<BlockEntrance>()) { + ProgramPoint P = N->getLocation(); + assert(P.getAs<PreStmt>()|| P.getAs<PreStmtPurgeDeadSymbols>()); + (void) P; + assert(N->pred_size() == 1); + N = *N->pred_begin(); + } + assert(N->pred_size() == 1); + N = *N->pred_begin(); + BlockEdge BE = N->getLocation().castAs<BlockEdge>(); + SVal X; + + // Determine the value of the expression by introspecting how we + // got this location in the CFG. This requires looking at the previous + // block we were in and what kind of control-flow transfer was involved. + const CFGBlock *SrcBlock = BE.getSrc(); + // The only terminator (if there is one) that makes sense is a logical op. + CFGTerminator T = SrcBlock->getTerminator(); + if (const BinaryOperator *Term = cast_or_null<BinaryOperator>(T.getStmt())) { + (void) Term; + assert(Term->isLogicalOp()); + assert(SrcBlock->succ_size() == 2); + // Did we take the true or false branch? + unsigned constant = (*SrcBlock->succ_begin() == BE.getDst()) ? 1 : 0; + X = svalBuilder.makeIntVal(constant, B->getType()); + } + else { + // If there is no terminator, by construction the last statement + // in SrcBlock is the value of the enclosing expression. + // However, we still need to constrain that value to be 0 or 1. + assert(!SrcBlock->empty()); + CFGStmt Elem = SrcBlock->rbegin()->castAs<CFGStmt>(); + const Expr *RHS = cast<Expr>(Elem.getStmt()); + SVal RHSVal = N->getState()->getSVal(RHS, Pred->getLocationContext()); + + if (RHSVal.isUndef()) { + X = RHSVal; + } else { + // We evaluate "RHSVal != 0" expression which result in 0 if the value is + // known to be false, 1 if the value is known to be true and a new symbol + // when the assumption is unknown. + nonloc::ConcreteInt Zero(getBasicVals().getValue(0, B->getType())); + X = evalBinOp(N->getState(), BO_NE, + svalBuilder.evalCast(RHSVal, B->getType(), RHS->getType()), + Zero, B->getType()); + } + } + Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X)); +} + +void ExprEngine::VisitInitListExpr(const InitListExpr *IE, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + StmtNodeBuilder B(Pred, Dst, *currBldrCtx); + + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + QualType T = getContext().getCanonicalType(IE->getType()); + unsigned NumInitElements = IE->getNumInits(); + + if (!IE->isGLValue() && + (T->isArrayType() || T->isRecordType() || T->isVectorType() || + T->isAnyComplexType())) { + llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList(); + + // Handle base case where the initializer has no elements. + // e.g: static int* myArray[] = {}; + if (NumInitElements == 0) { + SVal V = svalBuilder.makeCompoundVal(T, vals); + B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V)); + return; + } + + for (InitListExpr::const_reverse_iterator it = IE->rbegin(), + ei = IE->rend(); it != ei; ++it) { + SVal V = state->getSVal(cast<Expr>(*it), LCtx); + vals = getBasicVals().prependSVal(V, vals); + } + + B.generateNode(IE, Pred, + state->BindExpr(IE, LCtx, + svalBuilder.makeCompoundVal(T, vals))); + return; + } + + // Handle scalars: int{5} and int{} and GLvalues. + // Note, if the InitListExpr is a GLvalue, it means that there is an address + // representing it, so it must have a single init element. + assert(NumInitElements <= 1); + + SVal V; + if (NumInitElements == 0) + V = getSValBuilder().makeZeroVal(T); + else + V = state->getSVal(IE->getInit(0), LCtx); + + B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V)); +} + +void ExprEngine::VisitGuardedExpr(const Expr *Ex, + const Expr *L, + const Expr *R, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + assert(L && R); + + StmtNodeBuilder B(Pred, Dst, *currBldrCtx); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + const CFGBlock *SrcBlock = nullptr; + + // Find the predecessor block. + ProgramStateRef SrcState = state; + for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) { + ProgramPoint PP = N->getLocation(); + if (PP.getAs<PreStmtPurgeDeadSymbols>() || PP.getAs<BlockEntrance>()) { + // If the state N has multiple predecessors P, it means that successors + // of P are all equivalent. + // In turn, that means that all nodes at P are equivalent in terms + // of observable behavior at N, and we can follow any of them. + // FIXME: a more robust solution which does not walk up the tree. + continue; + } + SrcBlock = PP.castAs<BlockEdge>().getSrc(); + SrcState = N->getState(); + break; + } + + assert(SrcBlock && "missing function entry"); + + // Find the last expression in the predecessor block. That is the + // expression that is used for the value of the ternary expression. + bool hasValue = false; + SVal V; + + for (CFGElement CE : llvm::reverse(*SrcBlock)) { + if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) { + const Expr *ValEx = cast<Expr>(CS->getStmt()); + ValEx = ValEx->IgnoreParens(); + + // For GNU extension '?:' operator, the left hand side will be an + // OpaqueValueExpr, so get the underlying expression. + if (const OpaqueValueExpr *OpaqueEx = dyn_cast<OpaqueValueExpr>(L)) + L = OpaqueEx->getSourceExpr(); + + // If the last expression in the predecessor block matches true or false + // subexpression, get its the value. + if (ValEx == L->IgnoreParens() || ValEx == R->IgnoreParens()) { + hasValue = true; + V = SrcState->getSVal(ValEx, LCtx); + } + break; + } + } + + if (!hasValue) + V = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx, + currBldrCtx->blockCount()); + + // Generate a new node with the binding from the appropriate path. + B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V, true)); +} + +void ExprEngine:: +VisitOffsetOfExpr(const OffsetOfExpr *OOE, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder B(Pred, Dst, *currBldrCtx); + Expr::EvalResult Result; + if (OOE->EvaluateAsInt(Result, getContext())) { + APSInt IV = Result.Val.getInt(); + assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); + assert(OOE->getType()->isBuiltinType()); + assert(OOE->getType()->getAs<BuiltinType>()->isInteger()); + assert(IV.isSigned() == OOE->getType()->isSignedIntegerType()); + SVal X = svalBuilder.makeIntVal(IV); + B.generateNode(OOE, Pred, + Pred->getState()->BindExpr(OOE, Pred->getLocationContext(), + X)); + } + // FIXME: Handle the case where __builtin_offsetof is not a constant. +} + + +void ExprEngine:: +VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + // FIXME: Prechecks eventually go in ::Visit(). + ExplodedNodeSet CheckedSet; + getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, Ex, *this); + + ExplodedNodeSet EvalSet; + StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx); + + QualType T = Ex->getTypeOfArgument(); + + for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); + I != E; ++I) { + if (Ex->getKind() == UETT_SizeOf) { + if (!T->isIncompleteType() && !T->isConstantSizeType()) { + assert(T->isVariableArrayType() && "Unknown non-constant-sized type."); + + // FIXME: Add support for VLA type arguments and VLA expressions. + // When that happens, we should probably refactor VLASizeChecker's code. + continue; + } else if (T->getAs<ObjCObjectType>()) { + // Some code tries to take the sizeof an ObjCObjectType, relying that + // the compiler has laid out its representation. Just report Unknown + // for these. + continue; + } + } + + APSInt Value = Ex->EvaluateKnownConstInt(getContext()); + CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue()); + + ProgramStateRef state = (*I)->getState(); + state = state->BindExpr(Ex, (*I)->getLocationContext(), + svalBuilder.makeIntVal(amt.getQuantity(), + Ex->getType())); + Bldr.generateNode(Ex, *I, state); + } + + getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this); +} + +void ExprEngine::handleUOExtension(ExplodedNodeSet::iterator I, + const UnaryOperator *U, + StmtNodeBuilder &Bldr) { + // FIXME: We can probably just have some magic in Environment::getSVal() + // that propagates values, instead of creating a new node here. + // + // Unary "+" is a no-op, similar to a parentheses. We still have places + // where it may be a block-level expression, so we need to + // generate an extra node that just propagates the value of the + // subexpression. + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + ProgramStateRef state = (*I)->getState(); + const LocationContext *LCtx = (*I)->getLocationContext(); + Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, + state->getSVal(Ex, LCtx))); +} + +void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + // FIXME: Prechecks eventually go in ::Visit(). + ExplodedNodeSet CheckedSet; + getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, U, *this); + + ExplodedNodeSet EvalSet; + StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx); + + for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); + I != E; ++I) { + switch (U->getOpcode()) { + default: { + Bldr.takeNodes(*I); + ExplodedNodeSet Tmp; + VisitIncrementDecrementOperator(U, *I, Tmp); + Bldr.addNodes(Tmp); + break; + } + case UO_Real: { + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + break; + } + + // For all other types, UO_Real is an identity operation. + assert (U->getType() == Ex->getType()); + ProgramStateRef state = (*I)->getState(); + const LocationContext *LCtx = (*I)->getLocationContext(); + Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, + state->getSVal(Ex, LCtx))); + break; + } + + case UO_Imag: { + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + break; + } + // For all other types, UO_Imag returns 0. + ProgramStateRef state = (*I)->getState(); + const LocationContext *LCtx = (*I)->getLocationContext(); + SVal X = svalBuilder.makeZeroVal(Ex->getType()); + Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, X)); + break; + } + + case UO_AddrOf: { + // Process pointer-to-member address operation. + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Ex)) { + const ValueDecl *VD = DRE->getDecl(); + + if (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD)) { + ProgramStateRef State = (*I)->getState(); + const LocationContext *LCtx = (*I)->getLocationContext(); + SVal SV = svalBuilder.getMemberPointer(cast<DeclaratorDecl>(VD)); + Bldr.generateNode(U, *I, State->BindExpr(U, LCtx, SV)); + break; + } + } + // Explicitly proceed with default handler for this case cascade. + handleUOExtension(I, U, Bldr); + break; + } + case UO_Plus: + assert(!U->isGLValue()); + LLVM_FALLTHROUGH; + case UO_Deref: + case UO_Extension: { + handleUOExtension(I, U, Bldr); + break; + } + + case UO_LNot: + case UO_Minus: + case UO_Not: { + assert (!U->isGLValue()); + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + ProgramStateRef state = (*I)->getState(); + const LocationContext *LCtx = (*I)->getLocationContext(); + + // Get the value of the subexpression. + SVal V = state->getSVal(Ex, LCtx); + + if (V.isUnknownOrUndef()) { + Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V)); + break; + } + + switch (U->getOpcode()) { + default: + llvm_unreachable("Invalid Opcode."); + case UO_Not: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, LCtx, evalComplement(V.castAs<NonLoc>())); + break; + case UO_Minus: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, LCtx, evalMinus(V.castAs<NonLoc>())); + break; + case UO_LNot: + // C99 6.5.3.3: "The expression !E is equivalent to (0==E)." + // + // Note: technically we do "E == 0", but this is the same in the + // transfer functions as "0 == E". + SVal Result; + if (Optional<Loc> LV = V.getAs<Loc>()) { + Loc X = svalBuilder.makeNullWithType(Ex->getType()); + Result = evalBinOp(state, BO_EQ, *LV, X, U->getType()); + } else if (Ex->getType()->isFloatingType()) { + // FIXME: handle floating point types. + Result = UnknownVal(); + } else { + nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); + Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X, + U->getType()); + } + + state = state->BindExpr(U, LCtx, Result); + break; + } + Bldr.generateNode(U, *I, state); + break; + } + } + } + + getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, U, *this); +} + +void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + // Handle ++ and -- (both pre- and post-increment). + assert (U->isIncrementDecrementOp()); + const Expr *Ex = U->getSubExpr()->IgnoreParens(); + + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef state = Pred->getState(); + SVal loc = state->getSVal(Ex, LCtx); + + // Perform a load. + ExplodedNodeSet Tmp; + evalLoad(Tmp, U, Ex, Pred, state, loc); + + ExplodedNodeSet Dst2; + StmtNodeBuilder Bldr(Tmp, Dst2, *currBldrCtx); + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end();I!=E;++I) { + + state = (*I)->getState(); + assert(LCtx == (*I)->getLocationContext()); + SVal V2_untested = state->getSVal(Ex, LCtx); + + // Propagate unknown and undefined values. + if (V2_untested.isUnknownOrUndef()) { + state = state->BindExpr(U, LCtx, V2_untested); + + // Perform the store, so that the uninitialized value detection happens. + Bldr.takeNodes(*I); + ExplodedNodeSet Dst3; + evalStore(Dst3, U, Ex, *I, state, loc, V2_untested); + Bldr.addNodes(Dst3); + + continue; + } + DefinedSVal V2 = V2_untested.castAs<DefinedSVal>(); + + // Handle all other values. + BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add : BO_Sub; + + // If the UnaryOperator has non-location type, use its type to create the + // constant value. If the UnaryOperator has location type, create the + // constant with int type and pointer width. + SVal RHS; + SVal Result; + + if (U->getType()->isAnyPointerType()) + RHS = svalBuilder.makeArrayIndex(1); + else if (U->getType()->isIntegralOrEnumerationType()) + RHS = svalBuilder.makeIntVal(1, U->getType()); + else + RHS = UnknownVal(); + + // The use of an operand of type bool with the ++ operators is deprecated + // but valid until C++17. And if the operand of the ++ operator is of type + // bool, it is set to true until C++17. Note that for '_Bool', it is also + // set to true when it encounters ++ operator. + if (U->getType()->isBooleanType() && U->isIncrementOp()) + Result = svalBuilder.makeTruthVal(true, U->getType()); + else + Result = evalBinOp(state, Op, V2, RHS, U->getType()); + + // Conjure a new symbol if necessary to recover precision. + if (Result.isUnknown()){ + DefinedOrUnknownSVal SymVal = + svalBuilder.conjureSymbolVal(nullptr, U, LCtx, + currBldrCtx->blockCount()); + Result = SymVal; + + // If the value is a location, ++/-- should always preserve + // non-nullness. Check if the original value was non-null, and if so + // propagate that constraint. + if (Loc::isLocType(U->getType())) { + DefinedOrUnknownSVal Constraint = + svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType())); + + if (!state->assume(Constraint, true)) { + // It isn't feasible for the original value to be null. + // Propagate this constraint. + Constraint = svalBuilder.evalEQ(state, SymVal, + svalBuilder.makeZeroVal(U->getType())); + + state = state->assume(Constraint, false); + assert(state); + } + } + } + + // Since the lvalue-to-rvalue conversion is explicit in the AST, + // we bind an l-value if the operator is prefix and an lvalue (in C++). + if (U->isGLValue()) + state = state->BindExpr(U, LCtx, loc); + else + state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result); + + // Perform the store. + Bldr.takeNodes(*I); + ExplodedNodeSet Dst3; + evalStore(Dst3, U, Ex, *I, state, loc, Result); + Bldr.addNodes(Dst3); + } + Dst.insert(Dst2); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp new file mode 100644 index 000000000000..6445b9df5a58 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -0,0 +1,908 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines the C++ expression evaluation engine. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/Analysis/ConstructionContext.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/StmtCXX.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/PrettyStackTrace.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" + +using namespace clang; +using namespace ento; + +void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + const Expr *tempExpr = ME->GetTemporaryExpr()->IgnoreParens(); + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + + state = createTemporaryRegionIfNeeded(state, LCtx, tempExpr, ME); + Bldr.generateNode(ME, Pred, state); +} + +// FIXME: This is the sort of code that should eventually live in a Core +// checker rather than as a special case in ExprEngine. +void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred, + const CallEvent &Call) { + SVal ThisVal; + bool AlwaysReturnsLValue; + const CXXRecordDecl *ThisRD = nullptr; + if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) { + assert(Ctor->getDecl()->isTrivial()); + assert(Ctor->getDecl()->isCopyOrMoveConstructor()); + ThisVal = Ctor->getCXXThisVal(); + ThisRD = Ctor->getDecl()->getParent(); + AlwaysReturnsLValue = false; + } else { + assert(cast<CXXMethodDecl>(Call.getDecl())->isTrivial()); + assert(cast<CXXMethodDecl>(Call.getDecl())->getOverloadedOperator() == + OO_Equal); + ThisVal = cast<CXXInstanceCall>(Call).getCXXThisVal(); + ThisRD = cast<CXXMethodDecl>(Call.getDecl())->getParent(); + AlwaysReturnsLValue = true; + } + + assert(ThisRD); + if (ThisRD->isEmpty()) { + // Do nothing for empty classes. Otherwise it'd retrieve an UnknownVal + // and bind it and RegionStore would think that the actual value + // in this region at this offset is unknown. + return; + } + + const LocationContext *LCtx = Pred->getLocationContext(); + + ExplodedNodeSet Dst; + Bldr.takeNodes(Pred); + + SVal V = Call.getArgSVal(0); + + // If the value being copied is not unknown, load from its location to get + // an aggregate rvalue. + if (Optional<Loc> L = V.getAs<Loc>()) + V = Pred->getState()->getSVal(*L); + else + assert(V.isUnknownOrUndef()); + + const Expr *CallExpr = Call.getOriginExpr(); + evalBind(Dst, CallExpr, Pred, ThisVal, V, true); + + PostStmt PS(CallExpr, LCtx); + for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); + I != E; ++I) { + ProgramStateRef State = (*I)->getState(); + if (AlwaysReturnsLValue) + State = State->BindExpr(CallExpr, LCtx, ThisVal); + else + State = bindReturnValue(Call, LCtx, State); + Bldr.generateNode(PS, State, *I); + } +} + + +SVal ExprEngine::makeZeroElementRegion(ProgramStateRef State, SVal LValue, + QualType &Ty, bool &IsArray) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + ASTContext &Ctx = SVB.getContext(); + + while (const ArrayType *AT = Ctx.getAsArrayType(Ty)) { + Ty = AT->getElementType(); + LValue = State->getLValue(Ty, SVB.makeZeroArrayIndex(), LValue); + IsArray = true; + } + + return LValue; +} + +std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction( + const Expr *E, ProgramStateRef State, const LocationContext *LCtx, + const ConstructionContext *CC, EvalCallOptions &CallOpts) { + SValBuilder &SVB = getSValBuilder(); + MemRegionManager &MRMgr = SVB.getRegionManager(); + ASTContext &ACtx = SVB.getContext(); + + // See if we're constructing an existing region by looking at the + // current construction context. + if (CC) { + switch (CC->getKind()) { + case ConstructionContext::CXX17ElidedCopyVariableKind: + case ConstructionContext::SimpleVariableKind: { + const auto *DSCC = cast<VariableConstructionContext>(CC); + const auto *DS = DSCC->getDeclStmt(); + const auto *Var = cast<VarDecl>(DS->getSingleDecl()); + SVal LValue = State->getLValue(Var, LCtx); + QualType Ty = Var->getType(); + LValue = + makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor); + State = + addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, LValue); + return std::make_pair(State, LValue); + } + case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind: + case ConstructionContext::SimpleConstructorInitializerKind: { + const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC); + const auto *Init = ICC->getCXXCtorInitializer(); + assert(Init->isAnyMemberInitializer()); + const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); + Loc ThisPtr = + SVB.getCXXThis(CurCtor, LCtx->getStackFrame()); + SVal ThisVal = State->getSVal(ThisPtr); + + const ValueDecl *Field; + SVal FieldVal; + if (Init->isIndirectMemberInitializer()) { + Field = Init->getIndirectMember(); + FieldVal = State->getLValue(Init->getIndirectMember(), ThisVal); + } else { + Field = Init->getMember(); + FieldVal = State->getLValue(Init->getMember(), ThisVal); + } + + QualType Ty = Field->getType(); + FieldVal = makeZeroElementRegion(State, FieldVal, Ty, + CallOpts.IsArrayCtorOrDtor); + State = addObjectUnderConstruction(State, Init, LCtx, FieldVal); + return std::make_pair(State, FieldVal); + } + case ConstructionContext::NewAllocatedObjectKind: { + if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { + const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC); + const auto *NE = NECC->getCXXNewExpr(); + SVal V = *getObjectUnderConstruction(State, NE, LCtx); + if (const SubRegion *MR = + dyn_cast_or_null<SubRegion>(V.getAsRegion())) { + if (NE->isArray()) { + // TODO: In fact, we need to call the constructor for every + // allocated element, not just the first one! + CallOpts.IsArrayCtorOrDtor = true; + return std::make_pair( + State, loc::MemRegionVal(getStoreManager().GetElementZeroRegion( + MR, NE->getType()->getPointeeType()))); + } + return std::make_pair(State, V); + } + // TODO: Detect when the allocator returns a null pointer. + // Constructor shall not be called in this case. + } + break; + } + case ConstructionContext::SimpleReturnedValueKind: + case ConstructionContext::CXX17ElidedCopyReturnedValueKind: { + // The temporary is to be managed by the parent stack frame. + // So build it in the parent stack frame if we're not in the + // top frame of the analysis. + const StackFrameContext *SFC = LCtx->getStackFrame(); + if (const LocationContext *CallerLCtx = SFC->getParent()) { + auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()] + .getAs<CFGCXXRecordTypedCall>(); + if (!RTC) { + // We were unable to find the correct construction context for the + // call in the parent stack frame. This is equivalent to not being + // able to find construction context at all. + break; + } + return prepareForObjectConstruction( + cast<Expr>(SFC->getCallSite()), State, CallerLCtx, + RTC->getConstructionContext(), CallOpts); + } else { + // We are on the top frame of the analysis. We do not know where is the + // object returned to. Conjure a symbolic region for the return value. + // TODO: We probably need a new MemRegion kind to represent the storage + // of that SymbolicRegion, so that we cound produce a fancy symbol + // instead of an anonymous conjured symbol. + // TODO: Do we need to track the region to avoid having it dead + // too early? It does die too early, at least in C++17, but because + // putting anything into a SymbolicRegion causes an immediate escape, + // it doesn't cause any leak false positives. + const auto *RCC = cast<ReturnedValueConstructionContext>(CC); + // Make sure that this doesn't coincide with any other symbol + // conjured for the returned expression. + static const int TopLevelSymRegionTag = 0; + const Expr *RetE = RCC->getReturnStmt()->getRetValue(); + assert(RetE && "Void returns should not have a construction context"); + QualType ReturnTy = RetE->getType(); + QualType RegionTy = ACtx.getPointerType(ReturnTy); + SVal V = SVB.conjureSymbolVal(&TopLevelSymRegionTag, RetE, SFC, + RegionTy, currBldrCtx->blockCount()); + return std::make_pair(State, V); + } + llvm_unreachable("Unhandled return value construction context!"); + } + case ConstructionContext::ElidedTemporaryObjectKind: { + assert(AMgr.getAnalyzerOptions().ShouldElideConstructors); + const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC); + const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr(); + const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); + const CXXConstructExpr *CE = TCC->getConstructorAfterElision(); + + // Support pre-C++17 copy elision. We'll have the elidable copy + // constructor in the AST and in the CFG, but we'll skip it + // and construct directly into the final object. This call + // also sets the CallOpts flags for us. + SVal V; + // If the elided copy/move constructor is not supported, there's still + // benefit in trying to model the non-elided constructor. + // Stash our state before trying to elide, as it'll get overwritten. + ProgramStateRef PreElideState = State; + EvalCallOptions PreElideCallOpts = CallOpts; + + std::tie(State, V) = prepareForObjectConstruction( + CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts); + + // FIXME: This definition of "copy elision has not failed" is unreliable. + // It doesn't indicate that the constructor will actually be inlined + // later; it is still up to evalCall() to decide. + if (!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) { + // Remember that we've elided the constructor. + State = addObjectUnderConstruction(State, CE, LCtx, V); + + // Remember that we've elided the destructor. + if (BTE) + State = elideDestructor(State, BTE, LCtx); + + // Instead of materialization, shamelessly return + // the final object destination. + if (MTE) + State = addObjectUnderConstruction(State, MTE, LCtx, V); + + return std::make_pair(State, V); + } else { + // Copy elision failed. Revert the changes and proceed as if we have + // a simple temporary. + State = PreElideState; + CallOpts = PreElideCallOpts; + } + LLVM_FALLTHROUGH; + } + case ConstructionContext::SimpleTemporaryObjectKind: { + const auto *TCC = cast<TemporaryObjectConstructionContext>(CC); + const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr(); + const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr(); + SVal V = UnknownVal(); + + if (MTE) { + if (const ValueDecl *VD = MTE->getExtendingDecl()) { + assert(MTE->getStorageDuration() != SD_FullExpression); + if (!VD->getType()->isReferenceType()) { + // We're lifetime-extended by a surrounding aggregate. + // Automatic destructors aren't quite working in this case + // on the CFG side. We should warn the caller about that. + // FIXME: Is there a better way to retrieve this information from + // the MaterializeTemporaryExpr? + CallOpts.IsTemporaryLifetimeExtendedViaAggregate = true; + } + } + + if (MTE->getStorageDuration() == SD_Static || + MTE->getStorageDuration() == SD_Thread) + V = loc::MemRegionVal(MRMgr.getCXXStaticTempObjectRegion(E)); + } + + if (V.isUnknown()) + V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)); + + if (BTE) + State = addObjectUnderConstruction(State, BTE, LCtx, V); + + if (MTE) + State = addObjectUnderConstruction(State, MTE, LCtx, V); + + CallOpts.IsTemporaryCtorOrDtor = true; + return std::make_pair(State, V); + } + case ConstructionContext::ArgumentKind: { + // Arguments are technically temporaries. + CallOpts.IsTemporaryCtorOrDtor = true; + + const auto *ACC = cast<ArgumentConstructionContext>(CC); + const Expr *E = ACC->getCallLikeExpr(); + unsigned Idx = ACC->getIndex(); + const CXXBindTemporaryExpr *BTE = ACC->getCXXBindTemporaryExpr(); + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + SVal V = UnknownVal(); + auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> { + const LocationContext *FutureSFC = Caller->getCalleeStackFrame(); + // Return early if we are unable to reliably foresee + // the future stack frame. + if (!FutureSFC) + return None; + + // This should be equivalent to Caller->getDecl() for now, but + // FutureSFC->getDecl() is likely to support better stuff (like + // virtual functions) earlier. + const Decl *CalleeD = FutureSFC->getDecl(); + + // FIXME: Support for variadic arguments is not implemented here yet. + if (CallEvent::isVariadic(CalleeD)) + return None; + + // Operator arguments do not correspond to operator parameters + // because this-argument is implemented as a normal argument in + // operator call expressions but not in operator declarations. + const VarRegion *VR = Caller->getParameterLocation( + *Caller->getAdjustedParameterIndex(Idx)); + if (!VR) + return None; + + return loc::MemRegionVal(VR); + }; + + if (const auto *CE = dyn_cast<CallExpr>(E)) { + CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx); + if (auto OptV = getArgLoc(Caller)) + V = *OptV; + else + break; + State = addObjectUnderConstruction(State, {CE, Idx}, LCtx, V); + } else if (const auto *CCE = dyn_cast<CXXConstructExpr>(E)) { + // Don't bother figuring out the target region for the future + // constructor because we won't need it. + CallEventRef<> Caller = + CEMgr.getCXXConstructorCall(CCE, /*Target=*/nullptr, State, LCtx); + if (auto OptV = getArgLoc(Caller)) + V = *OptV; + else + break; + State = addObjectUnderConstruction(State, {CCE, Idx}, LCtx, V); + } else if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) { + CallEventRef<> Caller = CEMgr.getObjCMethodCall(ME, State, LCtx); + if (auto OptV = getArgLoc(Caller)) + V = *OptV; + else + break; + State = addObjectUnderConstruction(State, {ME, Idx}, LCtx, V); + } + + assert(!V.isUnknown()); + + if (BTE) + State = addObjectUnderConstruction(State, BTE, LCtx, V); + + return std::make_pair(State, V); + } + } + } + // If we couldn't find an existing region to construct into, assume we're + // constructing a temporary. Notify the caller of our failure. + CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; + return std::make_pair( + State, loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx))); +} + +void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE, + ExplodedNode *Pred, + ExplodedNodeSet &destNodes) { + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef State = Pred->getState(); + + SVal Target = UnknownVal(); + + if (Optional<SVal> ElidedTarget = + getObjectUnderConstruction(State, CE, LCtx)) { + // We've previously modeled an elidable constructor by pretending that it in + // fact constructs into the correct target. This constructor can therefore + // be skipped. + Target = *ElidedTarget; + StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx); + State = finishObjectConstruction(State, CE, LCtx); + if (auto L = Target.getAs<Loc>()) + State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType())); + Bldr.generateNode(CE, Pred, State); + return; + } + + // FIXME: Handle arrays, which run the same constructor for every element. + // For now, we just run the first constructor (which should still invalidate + // the entire array). + + EvalCallOptions CallOpts; + auto C = getCurrentCFGElement().getAs<CFGConstructor>(); + assert(C || getCurrentCFGElement().getAs<CFGStmt>()); + const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr; + + switch (CE->getConstructionKind()) { + case CXXConstructExpr::CK_Complete: { + std::tie(State, Target) = + prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts); + break; + } + case CXXConstructExpr::CK_VirtualBase: + // Make sure we are not calling virtual base class initializers twice. + // Only the most-derived object should initialize virtual base classes. + if (const Stmt *Outer = LCtx->getStackFrame()->getCallSite()) { + const CXXConstructExpr *OuterCtor = dyn_cast<CXXConstructExpr>(Outer); + if (OuterCtor) { + switch (OuterCtor->getConstructionKind()) { + case CXXConstructExpr::CK_NonVirtualBase: + case CXXConstructExpr::CK_VirtualBase: + // Bail out! + destNodes.Add(Pred); + return; + case CXXConstructExpr::CK_Complete: + case CXXConstructExpr::CK_Delegating: + break; + } + } + } + LLVM_FALLTHROUGH; + case CXXConstructExpr::CK_NonVirtualBase: + // In C++17, classes with non-virtual bases may be aggregates, so they would + // be initialized as aggregates without a constructor call, so we may have + // a base class constructed directly into an initializer list without + // having the derived-class constructor call on the previous stack frame. + // Initializer lists may be nested into more initializer lists that + // correspond to surrounding aggregate initializations. + // FIXME: For now this code essentially bails out. We need to find the + // correct target region and set it. + // FIXME: Instead of relying on the ParentMap, we should have the + // trigger-statement (InitListExpr in this case) passed down from CFG or + // otherwise always available during construction. + if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(CE))) { + MemRegionManager &MRMgr = getSValBuilder().getRegionManager(); + Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx)); + CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true; + break; + } + LLVM_FALLTHROUGH; + case CXXConstructExpr::CK_Delegating: { + const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl()); + Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor, + LCtx->getStackFrame()); + SVal ThisVal = State->getSVal(ThisPtr); + + if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) { + Target = ThisVal; + } else { + // Cast to the base type. + bool IsVirtual = + (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase); + SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(), + IsVirtual); + Target = BaseVal; + } + break; + } + } + + if (State != Pred->getState()) { + static SimpleProgramPointTag T("ExprEngine", + "Prepare for object construction"); + ExplodedNodeSet DstPrepare; + StmtNodeBuilder BldrPrepare(Pred, DstPrepare, *currBldrCtx); + BldrPrepare.generateNode(CE, Pred, State, &T, ProgramPoint::PreStmtKind); + assert(DstPrepare.size() <= 1); + if (DstPrepare.size() == 0) + return; + Pred = *BldrPrepare.begin(); + } + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<CXXConstructorCall> Call = + CEMgr.getCXXConstructorCall(CE, Target.getAsRegion(), State, LCtx); + + ExplodedNodeSet DstPreVisit; + getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this); + + // FIXME: Is it possible and/or useful to do this before PreStmt? + ExplodedNodeSet PreInitialized; + { + StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx); + for (ExplodedNodeSet::iterator I = DstPreVisit.begin(), + E = DstPreVisit.end(); + I != E; ++I) { + ProgramStateRef State = (*I)->getState(); + if (CE->requiresZeroInitialization()) { + // FIXME: Once we properly handle constructors in new-expressions, we'll + // need to invalidate the region before setting a default value, to make + // sure there aren't any lingering bindings around. This probably needs + // to happen regardless of whether or not the object is zero-initialized + // to handle random fields of a placement-initialized object picking up + // old bindings. We might only want to do it when we need to, though. + // FIXME: This isn't actually correct for arrays -- we need to zero- + // initialize the entire array, not just the first element -- but our + // handling of arrays everywhere else is weak as well, so this shouldn't + // actually make things worse. Placement new makes this tricky as well, + // since it's then possible to be initializing one part of a multi- + // dimensional array. + State = State->bindDefaultZero(Target, LCtx); + } + + Bldr.generateNode(CE, *I, State, /*tag=*/nullptr, + ProgramPoint::PreStmtKind); + } + } + + ExplodedNodeSet DstPreCall; + getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized, + *Call, *this); + + ExplodedNodeSet DstEvaluated; + StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx); + + if (CE->getConstructor()->isTrivial() && + CE->getConstructor()->isCopyOrMoveConstructor() && + !CallOpts.IsArrayCtorOrDtor) { + // FIXME: Handle other kinds of trivial constructors as well. + for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); + I != E; ++I) + performTrivialCopy(Bldr, *I, *Call); + + } else { + for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); + I != E; ++I) + defaultEvalCall(Bldr, *I, *Call, CallOpts); + } + + // If the CFG was constructed without elements for temporary destructors + // and the just-called constructor created a temporary object then + // stop exploration if the temporary object has a noreturn constructor. + // This can lose coverage because the destructor, if it were present + // in the CFG, would be called at the end of the full expression or + // later (for life-time extended temporaries) -- but avoids infeasible + // paths when no-return temporary destructors are used for assertions. + const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext(); + if (!ADC->getCFGBuildOptions().AddTemporaryDtors) { + const MemRegion *Target = Call->getCXXThisVal().getAsRegion(); + if (Target && isa<CXXTempObjectRegion>(Target) && + Call->getDecl()->getParent()->isAnyDestructorNoReturn()) { + + // If we've inlined the constructor, then DstEvaluated would be empty. + // In this case we still want a sink, which could be implemented + // in processCallExit. But we don't have that implemented at the moment, + // so if you hit this assertion, see if you can avoid inlining + // the respective constructor when analyzer-config cfg-temporary-dtors + // is set to false. + // Otherwise there's nothing wrong with inlining such constructor. + assert(!DstEvaluated.empty() && + "We should not have inlined this constructor!"); + + for (ExplodedNode *N : DstEvaluated) { + Bldr.generateSink(CE, N, N->getState()); + } + + // There is no need to run the PostCall and PostStmt checker + // callbacks because we just generated sinks on all nodes in th + // frontier. + return; + } + } + + ExplodedNodeSet DstPostArgumentCleanup; + for (auto I : DstEvaluated) + finishArgumentConstruction(DstPostArgumentCleanup, I, *Call); + + // If there were other constructors called for object-type arguments + // of this constructor, clean them up. + ExplodedNodeSet DstPostCall; + getCheckerManager().runCheckersForPostCall(DstPostCall, + DstPostArgumentCleanup, + *Call, *this); + getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this); +} + +void ExprEngine::VisitCXXDestructor(QualType ObjectType, + const MemRegion *Dest, + const Stmt *S, + bool IsBaseDtor, + ExplodedNode *Pred, + ExplodedNodeSet &Dst, + const EvalCallOptions &CallOpts) { + const LocationContext *LCtx = Pred->getLocationContext(); + ProgramStateRef State = Pred->getState(); + + const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl(); + assert(RecordDecl && "Only CXXRecordDecls should have destructors"); + const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor(); + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<CXXDestructorCall> Call = + CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx); + + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + Call->getSourceRange().getBegin(), + "Error evaluating destructor"); + + ExplodedNodeSet DstPreCall; + getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, + *Call, *this); + + ExplodedNodeSet DstInvalidated; + StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx); + for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end(); + I != E; ++I) + defaultEvalCall(Bldr, *I, *Call, CallOpts); + + ExplodedNodeSet DstPostCall; + getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated, + *Call, *this); +} + +void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ProgramStateRef State = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + CNE->getBeginLoc(), + "Error evaluating New Allocator Call"); + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<CXXAllocatorCall> Call = + CEMgr.getCXXAllocatorCall(CNE, State, LCtx); + + ExplodedNodeSet DstPreCall; + getCheckerManager().runCheckersForPreCall(DstPreCall, Pred, + *Call, *this); + + ExplodedNodeSet DstPostCall; + StmtNodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx); + for (auto I : DstPreCall) { + // FIXME: Provide evalCall for checkers? + defaultEvalCall(CallBldr, I, *Call); + } + // If the call is inlined, DstPostCall will be empty and we bail out now. + + // Store return value of operator new() for future use, until the actual + // CXXNewExpr gets processed. + ExplodedNodeSet DstPostValue; + StmtNodeBuilder ValueBldr(DstPostCall, DstPostValue, *currBldrCtx); + for (auto I : DstPostCall) { + // FIXME: Because CNE serves as the "call site" for the allocator (due to + // lack of a better expression in the AST), the conjured return value symbol + // is going to be of the same type (C++ object pointer type). Technically + // this is not correct because the operator new's prototype always says that + // it returns a 'void *'. So we should change the type of the symbol, + // and then evaluate the cast over the symbolic pointer from 'void *' to + // the object pointer type. But without changing the symbol's type it + // is breaking too much to evaluate the no-op symbolic cast over it, so we + // skip it for now. + ProgramStateRef State = I->getState(); + SVal RetVal = State->getSVal(CNE, LCtx); + + // If this allocation function is not declared as non-throwing, failures + // /must/ be signalled by exceptions, and thus the return value will never + // be NULL. -fno-exceptions does not influence this semantics. + // FIXME: GCC has a -fcheck-new option, which forces it to consider the case + // where new can return NULL. If we end up supporting that option, we can + // consider adding a check for it here. + // C++11 [basic.stc.dynamic.allocation]p3. + if (const FunctionDecl *FD = CNE->getOperatorNew()) { + QualType Ty = FD->getType(); + if (const auto *ProtoType = Ty->getAs<FunctionProtoType>()) + if (!ProtoType->isNothrow()) + State = State->assume(RetVal.castAs<DefinedOrUnknownSVal>(), true); + } + + ValueBldr.generateNode( + CNE, I, addObjectUnderConstruction(State, CNE, LCtx, RetVal)); + } + + ExplodedNodeSet DstPostPostCallCallback; + getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback, + DstPostValue, *Call, *this); + for (auto I : DstPostPostCallCallback) { + getCheckerManager().runCheckersForNewAllocator( + CNE, *getObjectUnderConstruction(I->getState(), CNE, LCtx), Dst, I, + *this); + } +} + +void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + // FIXME: Much of this should eventually migrate to CXXAllocatorCall. + // Also, we need to decide how allocators actually work -- they're not + // really part of the CXXNewExpr because they happen BEFORE the + // CXXConstructExpr subexpression. See PR12014 for some discussion. + + unsigned blockCount = currBldrCtx->blockCount(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal symVal = UnknownVal(); + FunctionDecl *FD = CNE->getOperatorNew(); + + bool IsStandardGlobalOpNewFunction = + FD->isReplaceableGlobalAllocationFunction(); + + ProgramStateRef State = Pred->getState(); + + // Retrieve the stored operator new() return value. + if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { + symVal = *getObjectUnderConstruction(State, CNE, LCtx); + State = finishObjectConstruction(State, CNE, LCtx); + } + + // We assume all standard global 'operator new' functions allocate memory in + // heap. We realize this is an approximation that might not correctly model + // a custom global allocator. + if (symVal.isUnknown()) { + if (IsStandardGlobalOpNewFunction) + symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount); + else + symVal = svalBuilder.conjureSymbolVal(nullptr, CNE, LCtx, CNE->getType(), + blockCount); + } + + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<CXXAllocatorCall> Call = + CEMgr.getCXXAllocatorCall(CNE, State, LCtx); + + if (!AMgr.getAnalyzerOptions().MayInlineCXXAllocator) { + // Invalidate placement args. + // FIXME: Once we figure out how we want allocators to work, + // we should be using the usual pre-/(default-)eval-/post-call checks here. + State = Call->invalidateRegions(blockCount); + if (!State) + return; + + // If this allocation function is not declared as non-throwing, failures + // /must/ be signalled by exceptions, and thus the return value will never + // be NULL. -fno-exceptions does not influence this semantics. + // FIXME: GCC has a -fcheck-new option, which forces it to consider the case + // where new can return NULL. If we end up supporting that option, we can + // consider adding a check for it here. + // C++11 [basic.stc.dynamic.allocation]p3. + if (FD) { + QualType Ty = FD->getType(); + if (const auto *ProtoType = Ty->getAs<FunctionProtoType>()) + if (!ProtoType->isNothrow()) + if (auto dSymVal = symVal.getAs<DefinedOrUnknownSVal>()) + State = State->assume(*dSymVal, true); + } + } + + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + + SVal Result = symVal; + + if (CNE->isArray()) { + // FIXME: allocating an array requires simulating the constructors. + // For now, just return a symbolicated region. + if (const SubRegion *NewReg = + dyn_cast_or_null<SubRegion>(symVal.getAsRegion())) { + QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType(); + const ElementRegion *EleReg = + getStoreManager().GetElementZeroRegion(NewReg, ObjTy); + Result = loc::MemRegionVal(EleReg); + } + State = State->BindExpr(CNE, Pred->getLocationContext(), Result); + Bldr.generateNode(CNE, Pred, State); + return; + } + + // FIXME: Once we have proper support for CXXConstructExprs inside + // CXXNewExpr, we need to make sure that the constructed object is not + // immediately invalidated here. (The placement call should happen before + // the constructor call anyway.) + if (FD && FD->isReservedGlobalPlacementOperator()) { + // Non-array placement new should always return the placement location. + SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx); + Result = svalBuilder.evalCast(PlacementLoc, CNE->getType(), + CNE->getPlacementArg(0)->getType()); + } + + // Bind the address of the object, then check to see if we cached out. + State = State->BindExpr(CNE, LCtx, Result); + ExplodedNode *NewN = Bldr.generateNode(CNE, Pred, State); + if (!NewN) + return; + + // If the type is not a record, we won't have a CXXConstructExpr as an + // initializer. Copy the value over. + if (const Expr *Init = CNE->getInitializer()) { + if (!isa<CXXConstructExpr>(Init)) { + assert(Bldr.getResults().size() == 1); + Bldr.takeNodes(NewN); + evalBind(Dst, CNE, NewN, Result, State->getSVal(Init, LCtx), + /*FirstInit=*/IsStandardGlobalOpNewFunction); + } + } +} + +void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + ProgramStateRef state = Pred->getState(); + Bldr.generateNode(CDE, Pred, state); +} + +void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const VarDecl *VD = CS->getExceptionDecl(); + if (!VD) { + Dst.Add(Pred); + return; + } + + const LocationContext *LCtx = Pred->getLocationContext(); + SVal V = svalBuilder.conjureSymbolVal(CS, LCtx, VD->getType(), + currBldrCtx->blockCount()); + ProgramStateRef state = Pred->getState(); + state = state->bindLoc(state->getLValue(VD, LCtx), V, LCtx); + + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + Bldr.generateNode(CS, Pred, state); +} + +void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx); + + // Get the this object region from StoreManager. + const LocationContext *LCtx = Pred->getLocationContext(); + const MemRegion *R = + svalBuilder.getRegionManager().getCXXThisRegion( + getContext().getCanonicalType(TE->getType()), + LCtx); + + ProgramStateRef state = Pred->getState(); + SVal V = state->getSVal(loc::MemRegionVal(R)); + Bldr.generateNode(TE, Pred, state->BindExpr(TE, LCtx, V)); +} + +void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const LocationContext *LocCtxt = Pred->getLocationContext(); + + // Get the region of the lambda itself. + const MemRegion *R = svalBuilder.getRegionManager().getCXXTempObjectRegion( + LE, LocCtxt); + SVal V = loc::MemRegionVal(R); + + ProgramStateRef State = Pred->getState(); + + // If we created a new MemRegion for the lambda, we should explicitly bind + // the captures. + CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin(); + for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(), + e = LE->capture_init_end(); + i != e; ++i, ++CurField) { + FieldDecl *FieldForCapture = *CurField; + SVal FieldLoc = State->getLValue(FieldForCapture, V); + + SVal InitVal; + if (!FieldForCapture->hasCapturedVLAType()) { + Expr *InitExpr = *i; + assert(InitExpr && "Capture missing initialization expression"); + InitVal = State->getSVal(InitExpr, LocCtxt); + } else { + // The field stores the length of a captured variable-length array. + // These captures don't have initialization expressions; instead we + // get the length from the VLAType size expression. + Expr *SizeExpr = FieldForCapture->getCapturedVLAType()->getSizeExpr(); + InitVal = State->getSVal(SizeExpr, LocCtxt); + } + + State = State->bindLoc(FieldLoc, InitVal, LocCtxt); + } + + // Decay the Loc into an RValue, because there might be a + // MaterializeTemporaryExpr node above this one which expects the bound value + // to be an RValue. + SVal LambdaRVal = State->getSVal(R); + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx); + // FIXME: is this the right program point kind? + Bldr.generateNode(LE, Pred, + State->BindExpr(LE, LocCtxt, LambdaRVal), + nullptr, ProgramPoint::PostLValueKind); + + // FIXME: Move all post/pre visits to ::Visit(). + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp new file mode 100644 index 000000000000..758195d8d911 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp @@ -0,0 +1,1088 @@ +//=-- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines ExprEngine's support for calls and returns. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "PrettyStackTraceLocationContext.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/DeclCXX.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/ConstructionContext.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/SaveAndRestore.h" + +using namespace clang; +using namespace ento; + +#define DEBUG_TYPE "ExprEngine" + +STATISTIC(NumOfDynamicDispatchPathSplits, + "The # of times we split the path due to imprecise dynamic dispatch info"); + +STATISTIC(NumInlinedCalls, + "The # of times we inlined a call"); + +STATISTIC(NumReachedInlineCountMax, + "The # of times we reached inline count maximum"); + +void ExprEngine::processCallEnter(NodeBuilderContext& BC, CallEnter CE, + ExplodedNode *Pred) { + // Get the entry block in the CFG of the callee. + const StackFrameContext *calleeCtx = CE.getCalleeContext(); + PrettyStackTraceLocationContext CrashInfo(calleeCtx); + const CFGBlock *Entry = CE.getEntry(); + + // Validate the CFG. + assert(Entry->empty()); + assert(Entry->succ_size() == 1); + + // Get the solitary successor. + const CFGBlock *Succ = *(Entry->succ_begin()); + + // Construct an edge representing the starting location in the callee. + BlockEdge Loc(Entry, Succ, calleeCtx); + + ProgramStateRef state = Pred->getState(); + + // Construct a new node, notify checkers that analysis of the function has + // begun, and add the resultant nodes to the worklist. + bool isNew; + ExplodedNode *Node = G.getNode(Loc, state, false, &isNew); + Node->addPredecessor(Pred, G); + if (isNew) { + ExplodedNodeSet DstBegin; + processBeginOfFunction(BC, Node, DstBegin, Loc); + Engine.enqueue(DstBegin); + } +} + +// Find the last statement on the path to the exploded node and the +// corresponding Block. +static std::pair<const Stmt*, + const CFGBlock*> getLastStmt(const ExplodedNode *Node) { + const Stmt *S = nullptr; + const CFGBlock *Blk = nullptr; + const StackFrameContext *SF = Node->getStackFrame(); + + // Back up through the ExplodedGraph until we reach a statement node in this + // stack frame. + while (Node) { + const ProgramPoint &PP = Node->getLocation(); + + if (PP.getStackFrame() == SF) { + if (Optional<StmtPoint> SP = PP.getAs<StmtPoint>()) { + S = SP->getStmt(); + break; + } else if (Optional<CallExitEnd> CEE = PP.getAs<CallExitEnd>()) { + S = CEE->getCalleeContext()->getCallSite(); + if (S) + break; + + // If there is no statement, this is an implicitly-generated call. + // We'll walk backwards over it and then continue the loop to find + // an actual statement. + Optional<CallEnter> CE; + do { + Node = Node->getFirstPred(); + CE = Node->getLocationAs<CallEnter>(); + } while (!CE || CE->getCalleeContext() != CEE->getCalleeContext()); + + // Continue searching the graph. + } else if (Optional<BlockEdge> BE = PP.getAs<BlockEdge>()) { + Blk = BE->getSrc(); + } + } else if (Optional<CallEnter> CE = PP.getAs<CallEnter>()) { + // If we reached the CallEnter for this function, it has no statements. + if (CE->getCalleeContext() == SF) + break; + } + + if (Node->pred_empty()) + return std::make_pair(nullptr, nullptr); + + Node = *Node->pred_begin(); + } + + return std::make_pair(S, Blk); +} + +/// Adjusts a return value when the called function's return type does not +/// match the caller's expression type. This can happen when a dynamic call +/// is devirtualized, and the overriding method has a covariant (more specific) +/// return type than the parent's method. For C++ objects, this means we need +/// to add base casts. +static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy, + StoreManager &StoreMgr) { + // For now, the only adjustments we handle apply only to locations. + if (!V.getAs<Loc>()) + return V; + + // If the types already match, don't do any unnecessary work. + ExpectedTy = ExpectedTy.getCanonicalType(); + ActualTy = ActualTy.getCanonicalType(); + if (ExpectedTy == ActualTy) + return V; + + // No adjustment is needed between Objective-C pointer types. + if (ExpectedTy->isObjCObjectPointerType() && + ActualTy->isObjCObjectPointerType()) + return V; + + // C++ object pointers may need "derived-to-base" casts. + const CXXRecordDecl *ExpectedClass = ExpectedTy->getPointeeCXXRecordDecl(); + const CXXRecordDecl *ActualClass = ActualTy->getPointeeCXXRecordDecl(); + if (ExpectedClass && ActualClass) { + CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + if (ActualClass->isDerivedFrom(ExpectedClass, Paths) && + !Paths.isAmbiguous(ActualTy->getCanonicalTypeUnqualified())) { + return StoreMgr.evalDerivedToBase(V, Paths.front()); + } + } + + // Unfortunately, Objective-C does not enforce that overridden methods have + // covariant return types, so we can't assert that that never happens. + // Be safe and return UnknownVal(). + return UnknownVal(); +} + +void ExprEngine::removeDeadOnEndOfFunction(NodeBuilderContext& BC, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + // Find the last statement in the function and the corresponding basic block. + const Stmt *LastSt = nullptr; + const CFGBlock *Blk = nullptr; + std::tie(LastSt, Blk) = getLastStmt(Pred); + if (!Blk || !LastSt) { + Dst.Add(Pred); + return; + } + + // Here, we destroy the current location context. We use the current + // function's entire body as a diagnostic statement, with which the program + // point will be associated. However, we only want to use LastStmt as a + // reference for what to clean up if it's a ReturnStmt; otherwise, everything + // is dead. + SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC); + const LocationContext *LCtx = Pred->getLocationContext(); + removeDead(Pred, Dst, dyn_cast<ReturnStmt>(LastSt), LCtx, + LCtx->getAnalysisDeclContext()->getBody(), + ProgramPoint::PostStmtPurgeDeadSymbolsKind); +} + +static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call, + const StackFrameContext *calleeCtx) { + const Decl *RuntimeCallee = calleeCtx->getDecl(); + const Decl *StaticDecl = Call->getDecl(); + assert(RuntimeCallee); + if (!StaticDecl) + return true; + return RuntimeCallee->getCanonicalDecl() != StaticDecl->getCanonicalDecl(); +} + +/// The call exit is simulated with a sequence of nodes, which occur between +/// CallExitBegin and CallExitEnd. The following operations occur between the +/// two program points: +/// 1. CallExitBegin (triggers the start of call exit sequence) +/// 2. Bind the return value +/// 3. Run Remove dead bindings to clean up the dead symbols from the callee. +/// 4. CallExitEnd (switch to the caller context) +/// 5. PostStmt<CallExpr> +void ExprEngine::processCallExit(ExplodedNode *CEBNode) { + // Step 1 CEBNode was generated before the call. + PrettyStackTraceLocationContext CrashInfo(CEBNode->getLocationContext()); + const StackFrameContext *calleeCtx = CEBNode->getStackFrame(); + + // The parent context might not be a stack frame, so make sure we + // look up the first enclosing stack frame. + const StackFrameContext *callerCtx = + calleeCtx->getParent()->getStackFrame(); + + const Stmt *CE = calleeCtx->getCallSite(); + ProgramStateRef state = CEBNode->getState(); + // Find the last statement in the function and the corresponding basic block. + const Stmt *LastSt = nullptr; + const CFGBlock *Blk = nullptr; + std::tie(LastSt, Blk) = getLastStmt(CEBNode); + + // Generate a CallEvent /before/ cleaning the state, so that we can get the + // correct value for 'this' (if necessary). + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<> Call = CEMgr.getCaller(calleeCtx, state); + + // Step 2: generate node with bound return value: CEBNode -> BindedRetNode. + + // If the callee returns an expression, bind its value to CallExpr. + if (CE) { + if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) { + const LocationContext *LCtx = CEBNode->getLocationContext(); + SVal V = state->getSVal(RS, LCtx); + + // Ensure that the return type matches the type of the returned Expr. + if (wasDifferentDeclUsedForInlining(Call, calleeCtx)) { + QualType ReturnedTy = + CallEvent::getDeclaredResultType(calleeCtx->getDecl()); + if (!ReturnedTy.isNull()) { + if (const Expr *Ex = dyn_cast<Expr>(CE)) { + V = adjustReturnValue(V, Ex->getType(), ReturnedTy, + getStoreManager()); + } + } + } + + state = state->BindExpr(CE, callerCtx, V); + } + + // Bind the constructed object value to CXXConstructExpr. + if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) { + loc::MemRegionVal This = + svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx); + SVal ThisV = state->getSVal(This); + ThisV = state->getSVal(ThisV.castAs<Loc>()); + state = state->BindExpr(CCE, callerCtx, ThisV); + } + + if (const auto *CNE = dyn_cast<CXXNewExpr>(CE)) { + // We are currently evaluating a CXXNewAllocator CFGElement. It takes a + // while to reach the actual CXXNewExpr element from here, so keep the + // region for later use. + // Additionally cast the return value of the inlined operator new + // (which is of type 'void *') to the correct object type. + SVal AllocV = state->getSVal(CNE, callerCtx); + AllocV = svalBuilder.evalCast( + AllocV, CNE->getType(), + getContext().getPointerType(getContext().VoidTy)); + + state = addObjectUnderConstruction(state, CNE, calleeCtx->getParent(), + AllocV); + } + } + + // Step 3: BindedRetNode -> CleanedNodes + // If we can find a statement and a block in the inlined function, run remove + // dead bindings before returning from the call. This is important to ensure + // that we report the issues such as leaks in the stack contexts in which + // they occurred. + ExplodedNodeSet CleanedNodes; + if (LastSt && Blk && AMgr.options.AnalysisPurgeOpt != PurgeNone) { + static SimpleProgramPointTag retValBind("ExprEngine", "Bind Return Value"); + PostStmt Loc(LastSt, calleeCtx, &retValBind); + bool isNew; + ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew); + BindedRetNode->addPredecessor(CEBNode, G); + if (!isNew) + return; + + NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode); + currBldrCtx = &Ctx; + // Here, we call the Symbol Reaper with 0 statement and callee location + // context, telling it to clean up everything in the callee's context + // (and its children). We use the callee's function body as a diagnostic + // statement, with which the program point will be associated. + removeDead(BindedRetNode, CleanedNodes, nullptr, calleeCtx, + calleeCtx->getAnalysisDeclContext()->getBody(), + ProgramPoint::PostStmtPurgeDeadSymbolsKind); + currBldrCtx = nullptr; + } else { + CleanedNodes.Add(CEBNode); + } + + for (ExplodedNodeSet::iterator I = CleanedNodes.begin(), + E = CleanedNodes.end(); I != E; ++I) { + + // Step 4: Generate the CallExit and leave the callee's context. + // CleanedNodes -> CEENode + CallExitEnd Loc(calleeCtx, callerCtx); + bool isNew; + ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState(); + + ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew); + CEENode->addPredecessor(*I, G); + if (!isNew) + return; + + // Step 5: Perform the post-condition check of the CallExpr and enqueue the + // result onto the work list. + // CEENode -> Dst -> WorkList + NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode); + SaveAndRestore<const NodeBuilderContext*> NBCSave(currBldrCtx, + &Ctx); + SaveAndRestore<unsigned> CBISave(currStmtIdx, calleeCtx->getIndex()); + + CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState); + + ExplodedNodeSet DstPostCall; + if (const CXXNewExpr *CNE = dyn_cast_or_null<CXXNewExpr>(CE)) { + ExplodedNodeSet DstPostPostCallCallback; + getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback, + CEENode, *UpdatedCall, *this, + /*WasInlined=*/true); + for (auto I : DstPostPostCallCallback) { + getCheckerManager().runCheckersForNewAllocator( + CNE, + *getObjectUnderConstruction(I->getState(), CNE, + calleeCtx->getParent()), + DstPostCall, I, *this, + /*WasInlined=*/true); + } + } else { + getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode, + *UpdatedCall, *this, + /*WasInlined=*/true); + } + ExplodedNodeSet Dst; + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) { + getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg, + *this, + /*WasInlined=*/true); + } else if (CE && + !(isa<CXXNewExpr>(CE) && // Called when visiting CXXNewExpr. + AMgr.getAnalyzerOptions().MayInlineCXXAllocator)) { + getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE, + *this, /*WasInlined=*/true); + } else { + Dst.insert(DstPostCall); + } + + // Enqueue the next element in the block. + for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end(); + PSI != PSE; ++PSI) { + Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(), + calleeCtx->getIndex()+1); + } + } +} + +void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx, + bool &IsRecursive, unsigned &StackDepth) { + IsRecursive = false; + StackDepth = 0; + + while (LCtx) { + if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LCtx)) { + const Decl *DI = SFC->getDecl(); + + // Mark recursive (and mutually recursive) functions and always count + // them when measuring the stack depth. + if (DI == D) { + IsRecursive = true; + ++StackDepth; + LCtx = LCtx->getParent(); + continue; + } + + // 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) + ++StackDepth; + } + LCtx = LCtx->getParent(); + } +} + +// The GDM component containing the dynamic dispatch bifurcation info. When +// the exact type of the receiver is not known, we want to explore both paths - +// one on which we do inline it and the other one on which we don't. This is +// done to ensure we do not drop coverage. +// This is the map from the receiver region to a bool, specifying either we +// consider this region's information precise or not along the given path. +namespace { + enum DynamicDispatchMode { + DynamicDispatchModeInlined = 1, + DynamicDispatchModeConservative + }; +} // end anonymous namespace + +REGISTER_MAP_WITH_PROGRAMSTATE(DynamicDispatchBifurcationMap, + const MemRegion *, unsigned) + +bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D, + NodeBuilder &Bldr, ExplodedNode *Pred, + ProgramStateRef State) { + assert(D); + + const LocationContext *CurLC = Pred->getLocationContext(); + const StackFrameContext *CallerSFC = CurLC->getStackFrame(); + const LocationContext *ParentOfCallee = CallerSFC; + if (Call.getKind() == CE_Block && + !cast<BlockCall>(Call).isConversionFromLambda()) { + const BlockDataRegion *BR = cast<BlockCall>(Call).getBlockRegion(); + assert(BR && "If we have the block definition we should have its region"); + AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D); + ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC, + cast<BlockDecl>(D), + BR); + } + + // This may be NULL, but that's fine. + const Expr *CallE = Call.getOriginExpr(); + + // Construct a new stack frame for the callee. + AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D); + const StackFrameContext *CalleeSFC = + CalleeADC->getStackFrame(ParentOfCallee, CallE, + currBldrCtx->getBlock(), + currStmtIdx); + + CallEnter Loc(CallE, CalleeSFC, CurLC); + + // Construct a new state which contains the mapping from actual to + // formal arguments. + State = State->enterStackFrame(Call, CalleeSFC); + + bool isNew; + if (ExplodedNode *N = G.getNode(Loc, State, false, &isNew)) { + N->addPredecessor(Pred, G); + if (isNew) + Engine.getWorkList()->enqueue(N); + } + + // If we decided to inline the call, the successor has been manually + // added onto the work list so remove it from the node builder. + Bldr.takeNodes(Pred); + + NumInlinedCalls++; + Engine.FunctionSummaries->bumpNumTimesInlined(D); + + // Mark the decl as visited. + if (VisitedCallees) + VisitedCallees->insert(D); + + return true; +} + +static ProgramStateRef getInlineFailedState(ProgramStateRef State, + const Stmt *CallE) { + const void *ReplayState = State->get<ReplayWithoutInlining>(); + if (!ReplayState) + return nullptr; + + assert(ReplayState == CallE && "Backtracked to the wrong call."); + (void)CallE; + + return State->remove<ReplayWithoutInlining>(); +} + +void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred, + ExplodedNodeSet &dst) { + // Perform the previsit of the CallExpr. + ExplodedNodeSet dstPreVisit; + getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this); + + // Get the call in its initial state. We use this as a template to perform + // all the checks. + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<> CallTemplate + = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext()); + + // Evaluate the function call. We try each of the checkers + // to see if the can evaluate the function call. + ExplodedNodeSet dstCallEvaluated; + for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end(); + I != E; ++I) { + evalCall(dstCallEvaluated, *I, *CallTemplate); + } + + // Finally, perform the post-condition check of the CallExpr and store + // the created nodes in 'Dst'. + // Note that if the call was inlined, dstCallEvaluated will be empty. + // The post-CallExpr check will occur in processCallExit. + getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE, + *this); +} + +ProgramStateRef ExprEngine::finishArgumentConstruction(ProgramStateRef State, + const CallEvent &Call) { + const Expr *E = Call.getOriginExpr(); + // FIXME: Constructors to placement arguments of operator new + // are not supported yet. + if (!E || isa<CXXNewExpr>(E)) + return State; + + const LocationContext *LC = Call.getLocationContext(); + for (unsigned CallI = 0, CallN = Call.getNumArgs(); CallI != CallN; ++CallI) { + unsigned I = Call.getASTArgumentIndex(CallI); + if (Optional<SVal> V = + getObjectUnderConstruction(State, {E, I}, LC)) { + SVal VV = *V; + (void)VV; + assert(cast<VarRegion>(VV.castAs<loc::MemRegionVal>().getRegion()) + ->getStackFrame()->getParent() + ->getStackFrame() == LC->getStackFrame()); + State = finishObjectConstruction(State, {E, I}, LC); + } + } + + return State; +} + +void ExprEngine::finishArgumentConstruction(ExplodedNodeSet &Dst, + ExplodedNode *Pred, + const CallEvent &Call) { + ProgramStateRef State = Pred->getState(); + ProgramStateRef CleanedState = finishArgumentConstruction(State, Call); + if (CleanedState == State) { + Dst.insert(Pred); + return; + } + + const Expr *E = Call.getOriginExpr(); + const LocationContext *LC = Call.getLocationContext(); + NodeBuilder B(Pred, Dst, *currBldrCtx); + static SimpleProgramPointTag Tag("ExprEngine", + "Finish argument construction"); + PreStmt PP(E, LC, &Tag); + B.generateNode(PP, CleanedState, Pred); +} + +void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred, + const CallEvent &Call) { + // WARNING: At this time, the state attached to 'Call' may be older than the + // state in 'Pred'. This is a minor optimization since CheckerManager will + // use an updated CallEvent instance when calling checkers, but if 'Call' is + // ever used directly in this function all callers should be updated to pass + // the most recent state. (It is probably not worth doing the work here since + // for some callers this will not be necessary.) + + // Run any pre-call checks using the generic call interface. + ExplodedNodeSet dstPreVisit; + getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred, + Call, *this); + + // Actually evaluate the function call. We try each of the checkers + // to see if the can evaluate the function call, and get a callback at + // defaultEvalCall if all of them fail. + ExplodedNodeSet dstCallEvaluated; + getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit, + Call, *this); + + // If there were other constructors called for object-type arguments + // of this call, clean them up. + ExplodedNodeSet dstArgumentCleanup; + for (auto I : dstCallEvaluated) + finishArgumentConstruction(dstArgumentCleanup, I, Call); + + // Finally, run any post-call checks. + getCheckerManager().runCheckersForPostCall(Dst, dstArgumentCleanup, + Call, *this); +} + +ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call, + const LocationContext *LCtx, + ProgramStateRef State) { + const Expr *E = Call.getOriginExpr(); + if (!E) + return State; + + // Some method families have known return values. + if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) { + switch (Msg->getMethodFamily()) { + default: + break; + case OMF_autorelease: + case OMF_retain: + case OMF_self: { + // These methods return their receivers. + return State->BindExpr(E, LCtx, Msg->getReceiverSVal()); + } + } + } else if (const CXXConstructorCall *C = dyn_cast<CXXConstructorCall>(&Call)){ + SVal ThisV = C->getCXXThisVal(); + ThisV = State->getSVal(ThisV.castAs<Loc>()); + return State->BindExpr(E, LCtx, ThisV); + } + + SVal R; + QualType ResultTy = Call.getResultType(); + unsigned Count = currBldrCtx->blockCount(); + if (auto RTC = getCurrentCFGElement().getAs<CFGCXXRecordTypedCall>()) { + // Conjure a temporary if the function returns an object by value. + SVal Target; + assert(RTC->getStmt() == Call.getOriginExpr()); + EvalCallOptions CallOpts; // FIXME: We won't really need those. + std::tie(State, Target) = + prepareForObjectConstruction(Call.getOriginExpr(), State, LCtx, + RTC->getConstructionContext(), CallOpts); + assert(Target.getAsRegion()); + // Invalidate the region so that it didn't look uninitialized. Don't notify + // the checkers. + State = State->invalidateRegions(Target.getAsRegion(), E, Count, LCtx, + /* CausedByPointerEscape=*/false, nullptr, + &Call, nullptr); + + R = State->getSVal(Target.castAs<Loc>(), E->getType()); + } else { + // Conjure a symbol if the return value is unknown. + + // See if we need to conjure a heap pointer instead of + // a regular unknown pointer. + bool IsHeapPointer = false; + if (const auto *CNE = dyn_cast<CXXNewExpr>(E)) + if (CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) { + // FIXME: Delegate this to evalCall in MallocChecker? + IsHeapPointer = true; + } + + R = IsHeapPointer ? svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count) + : svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy, + Count); + } + return State->BindExpr(E, LCtx, R); +} + +// Conservatively evaluate call by invalidating regions and binding +// a conjured return value. +void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr, + ExplodedNode *Pred, + ProgramStateRef State) { + State = Call.invalidateRegions(currBldrCtx->blockCount(), State); + State = bindReturnValue(Call, Pred->getLocationContext(), State); + + // And make the result node. + Bldr.generateNode(Call.getProgramPoint(), State, Pred); +} + +ExprEngine::CallInlinePolicy +ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred, + AnalyzerOptions &Opts, + const ExprEngine::EvalCallOptions &CallOpts) { + const LocationContext *CurLC = Pred->getLocationContext(); + const StackFrameContext *CallerSFC = CurLC->getStackFrame(); + switch (Call.getKind()) { + case CE_Function: + case CE_Block: + break; + case CE_CXXMember: + case CE_CXXMemberOperator: + if (!Opts.mayInlineCXXMemberFunction(CIMK_MemberFunctions)) + return CIP_DisallowedAlways; + break; + case CE_CXXConstructor: { + if (!Opts.mayInlineCXXMemberFunction(CIMK_Constructors)) + return CIP_DisallowedAlways; + + const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call); + + const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr(); + + auto CCE = getCurrentCFGElement().getAs<CFGConstructor>(); + const ConstructionContext *CC = CCE ? CCE->getConstructionContext() + : nullptr; + + if (CC && isa<NewAllocatedObjectConstructionContext>(CC) && + !Opts.MayInlineCXXAllocator) + return CIP_DisallowedOnce; + + // FIXME: We don't handle constructors or destructors for arrays properly. + // Even once we do, we still need to be careful about implicitly-generated + // initializers for array fields in default move/copy constructors. + // We still allow construction into ElementRegion targets when they don't + // represent array elements. + if (CallOpts.IsArrayCtorOrDtor) + return CIP_DisallowedOnce; + + // Inlining constructors requires including initializers in the CFG. + const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); + assert(ADC->getCFGBuildOptions().AddInitializers && "No CFG initializers"); + (void)ADC; + + // If the destructor is trivial, it's always safe to inline the constructor. + if (Ctor.getDecl()->getParent()->hasTrivialDestructor()) + break; + + // For other types, only inline constructors if destructor inlining is + // also enabled. + if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) + return CIP_DisallowedAlways; + + if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) { + // If we don't handle temporary destructors, we shouldn't inline + // their constructors. + if (CallOpts.IsTemporaryCtorOrDtor && + !Opts.ShouldIncludeTemporaryDtorsInCFG) + return CIP_DisallowedOnce; + + // If we did not find the correct this-region, it would be pointless + // to inline the constructor. Instead we will simply invalidate + // the fake temporary target. + if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) + return CIP_DisallowedOnce; + + // If the temporary is lifetime-extended by binding it to a reference-type + // field within an aggregate, automatic destructors don't work properly. + if (CallOpts.IsTemporaryLifetimeExtendedViaAggregate) + return CIP_DisallowedOnce; + } + + break; + } + case CE_CXXDestructor: { + if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors)) + return CIP_DisallowedAlways; + + // Inlining destructors requires building the CFG correctly. + const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext(); + assert(ADC->getCFGBuildOptions().AddImplicitDtors && "No CFG destructors"); + (void)ADC; + + // FIXME: We don't handle constructors or destructors for arrays properly. + if (CallOpts.IsArrayCtorOrDtor) + return CIP_DisallowedOnce; + + // Allow disabling temporary destructor inlining with a separate option. + if (CallOpts.IsTemporaryCtorOrDtor && + !Opts.MayInlineCXXTemporaryDtors) + return CIP_DisallowedOnce; + + // If we did not find the correct this-region, it would be pointless + // to inline the destructor. Instead we will simply invalidate + // the fake temporary target. + if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) + return CIP_DisallowedOnce; + break; + } + case CE_CXXAllocator: + if (Opts.MayInlineCXXAllocator) + break; + // Do not inline allocators until we model deallocators. + // This is unfortunate, but basically necessary for smart pointers and such. + return CIP_DisallowedAlways; + case CE_ObjCMessage: + if (!Opts.MayInlineObjCMethod) + return CIP_DisallowedAlways; + if (!(Opts.getIPAMode() == IPAK_DynamicDispatch || + Opts.getIPAMode() == IPAK_DynamicDispatchBifurcate)) + return CIP_DisallowedAlways; + break; + } + + return CIP_Allowed; +} + +/// Returns true if the given C++ class contains a member with the given name. +static bool hasMember(const ASTContext &Ctx, const CXXRecordDecl *RD, + StringRef Name) { + const IdentifierInfo &II = Ctx.Idents.get(Name); + DeclarationName DeclName = Ctx.DeclarationNames.getIdentifier(&II); + if (!RD->lookup(DeclName).empty()) + return true; + + CXXBasePaths Paths(false, false, false); + if (RD->lookupInBases( + [DeclName](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) { + return CXXRecordDecl::FindOrdinaryMember(Specifier, Path, DeclName); + }, + Paths)) + return true; + + return false; +} + +/// Returns true if the given C++ class is a container or iterator. +/// +/// Our heuristic for this is whether it contains a method named 'begin()' or a +/// nested type named 'iterator' or 'iterator_category'. +static bool isContainerClass(const ASTContext &Ctx, const CXXRecordDecl *RD) { + return hasMember(Ctx, RD, "begin") || + hasMember(Ctx, RD, "iterator") || + hasMember(Ctx, RD, "iterator_category"); +} + +/// Returns true if the given function refers to a method of a C++ container +/// or iterator. +/// +/// We generally do a poor job modeling most containers right now, and might +/// prefer not to inline their methods. +static bool isContainerMethod(const ASTContext &Ctx, + const FunctionDecl *FD) { + if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD)) + return isContainerClass(Ctx, MD->getParent()); + return false; +} + +/// Returns true if the given function is the destructor of a class named +/// "shared_ptr". +static bool isCXXSharedPtrDtor(const FunctionDecl *FD) { + const CXXDestructorDecl *Dtor = dyn_cast<CXXDestructorDecl>(FD); + if (!Dtor) + return false; + + const CXXRecordDecl *RD = Dtor->getParent(); + if (const IdentifierInfo *II = RD->getDeclName().getAsIdentifierInfo()) + if (II->isStr("shared_ptr")) + return true; + + return false; +} + +/// Returns true if the function in \p CalleeADC may be inlined in general. +/// +/// 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) { + AnalyzerOptions &Opts = AMgr.getAnalyzerOptions(); + // FIXME: Do not inline variadic calls. + if (CallEvent::isVariadic(CalleeADC->getDecl())) + return false; + + // Check certain C++-related inlining policies. + ASTContext &Ctx = CalleeADC->getASTContext(); + if (Ctx.getLangOpts().CPlusPlus) { + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeADC->getDecl())) { + // Conditionally control the inlining of template functions. + if (!Opts.MayInlineTemplateFunctions) + if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate) + return false; + + // Conditionally control the inlining of C++ standard library functions. + if (!Opts.MayInlineCXXStandardLibrary) + if (Ctx.getSourceManager().isInSystemHeader(FD->getLocation())) + if (AnalysisDeclContext::isInStdNamespace(FD)) + return false; + + // Conditionally control the inlining of methods on objects that look + // like C++ containers. + if (!Opts.MayInlineCXXContainerMethods) + if (!AMgr.isInCodeFile(FD->getLocation())) + if (isContainerMethod(Ctx, FD)) + return false; + + // Conditionally control the inlining of the destructor of C++ shared_ptr. + // We don't currently do a good job modeling shared_ptr because we can't + // see the reference count, so treating as opaque is probably the best + // idea. + if (!Opts.MayInlineCXXSharedPtrDtor) + if (isCXXSharedPtrDtor(FD)) + return false; + } + } + + // It is possible that the CFG cannot be constructed. + // Be safe, and check if the CalleeCFG is valid. + const CFG *CalleeCFG = CalleeADC->getCFG(); + if (!CalleeCFG) + return false; + + // Do not inline large functions. + if (CalleeCFG->getNumBlockIDs() > Opts.MaxInlinableSize) + return false; + + // It is possible that the live variables analysis cannot be + // run. If so, bail out. + if (!CalleeADC->getAnalysis<RelaxedLiveVariables>()) + return false; + + return true; +} + +bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D, + const ExplodedNode *Pred, + const EvalCallOptions &CallOpts) { + if (!D) + return false; + + AnalysisManager &AMgr = getAnalysisManager(); + AnalyzerOptions &Opts = AMgr.options; + AnalysisDeclContextManager &ADCMgr = AMgr.getAnalysisDeclContextManager(); + AnalysisDeclContext *CalleeADC = ADCMgr.getContext(D); + + // The auto-synthesized bodies are essential to inline as they are + // usually small and commonly used. Note: we should do this check early on to + // ensure we always inline these calls. + if (CalleeADC->isBodyAutosynthesized()) + return true; + + if (!AMgr.shouldInlineCall()) + return false; + + // Check if this function has been marked as non-inlinable. + Optional<bool> MayInline = Engine.FunctionSummaries->mayInline(D); + if (MayInline.hasValue()) { + if (!MayInline.getValue()) + return false; + + } 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)) { + Engine.FunctionSummaries->markMayInline(D); + } else { + Engine.FunctionSummaries->markShouldNotInline(D); + return false; + } + } + + // Check if we should inline a call based on its kind. + // FIXME: this checks both static and dynamic properties of the call, which + // means we're redoing a bit of work that could be cached in the function + // summary. + CallInlinePolicy CIP = mayInlineCallKind(Call, Pred, Opts, CallOpts); + if (CIP != CIP_Allowed) { + if (CIP == CIP_DisallowedAlways) { + assert(!MayInline.hasValue() || MayInline.getValue()); + Engine.FunctionSummaries->markShouldNotInline(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)) + return false; + + // Do not inline large functions too many times. + if ((Engine.FunctionSummaries->getNumTimesInlined(D) > + Opts.MaxTimesInlineLarge) && + CalleeCFG->getNumBlockIDs() >= + Opts.MinCFGSizeTreatFunctionsAsLarge) { + NumReachedInlineCountMax++; + return false; + } + + if (HowToInline == Inline_Minimal && + (CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize + || IsRecursive)) + return false; + + return true; +} + +static bool isTrivialObjectAssignment(const CallEvent &Call) { + const CXXInstanceCall *ICall = dyn_cast<CXXInstanceCall>(&Call); + if (!ICall) + return false; + + const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(ICall->getDecl()); + if (!MD) + return false; + if (!(MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator())) + return false; + + return MD->isTrivial(); +} + +void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred, + const CallEvent &CallTemplate, + const EvalCallOptions &CallOpts) { + // Make sure we have the most recent state attached to the call. + ProgramStateRef State = Pred->getState(); + CallEventRef<> Call = CallTemplate.cloneWithState(State); + + // Special-case trivial assignment operators. + if (isTrivialObjectAssignment(*Call)) { + performTrivialCopy(Bldr, Pred, *Call); + return; + } + + // Try to inline the call. + // The origin expression here is just used as a kind of checksum; + // this should still be safe even for CallEvents that don't come from exprs. + const Expr *E = Call->getOriginExpr(); + + ProgramStateRef InlinedFailedState = getInlineFailedState(State, E); + if (InlinedFailedState) { + // If we already tried once and failed, make sure we don't retry later. + State = InlinedFailedState; + } else { + RuntimeDefinition RD = Call->getRuntimeDefinition(); + const Decl *D = RD.getDecl(); + if (shouldInlineCall(*Call, D, Pred, CallOpts)) { + if (RD.mayHaveOtherDefinitions()) { + AnalyzerOptions &Options = getAnalysisManager().options; + + // Explore with and without inlining the call. + if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) { + BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred); + return; + } + + // Don't inline if we're not in any dynamic dispatch mode. + if (Options.getIPAMode() != IPAK_DynamicDispatch) { + conservativeEvalCall(*Call, Bldr, Pred, State); + return; + } + } + + // We are not bifurcating and we do have a Decl, so just inline. + if (inlineCall(*Call, D, Bldr, Pred, State)) + return; + } + } + + // If we can't inline it, handle the return value and invalidate the regions. + conservativeEvalCall(*Call, Bldr, Pred, State); +} + +void ExprEngine::BifurcateCall(const MemRegion *BifurReg, + const CallEvent &Call, const Decl *D, + NodeBuilder &Bldr, ExplodedNode *Pred) { + assert(BifurReg); + BifurReg = BifurReg->StripCasts(); + + // Check if we've performed the split already - note, we only want + // to split the path once per memory region. + ProgramStateRef State = Pred->getState(); + const unsigned *BState = + State->get<DynamicDispatchBifurcationMap>(BifurReg); + if (BState) { + // If we are on "inline path", keep inlining if possible. + if (*BState == DynamicDispatchModeInlined) + if (inlineCall(Call, D, Bldr, Pred, State)) + return; + // If inline failed, or we are on the path where we assume we + // don't have enough info about the receiver to inline, conjure the + // return value and invalidate the regions. + conservativeEvalCall(Call, Bldr, Pred, State); + return; + } + + // If we got here, this is the first time we process a message to this + // region, so split the path. + ProgramStateRef IState = + State->set<DynamicDispatchBifurcationMap>(BifurReg, + DynamicDispatchModeInlined); + inlineCall(Call, D, Bldr, Pred, IState); + + ProgramStateRef NoIState = + State->set<DynamicDispatchBifurcationMap>(BifurReg, + DynamicDispatchModeConservative); + conservativeEvalCall(Call, Bldr, Pred, NoIState); + + NumOfDynamicDispatchPathSplits++; +} + +void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ExplodedNodeSet dstPreVisit; + getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, RS, *this); + + StmtNodeBuilder B(dstPreVisit, Dst, *currBldrCtx); + + if (RS->getRetValue()) { + for (ExplodedNodeSet::iterator it = dstPreVisit.begin(), + ei = dstPreVisit.end(); it != ei; ++it) { + B.generateNode(RS, *it, (*it)->getState()); + } + } +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp new file mode 100644 index 000000000000..6b8402f621e0 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp @@ -0,0 +1,288 @@ +//=-- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines ExprEngine's support for Objective-C expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/StmtObjC.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + SVal baseVal = state->getSVal(Ex->getBase(), LCtx); + SVal location = state->getLValue(Ex->getDecl(), baseVal); + + ExplodedNodeSet dstIvar; + StmtNodeBuilder Bldr(Pred, dstIvar, *currBldrCtx); + Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, location)); + + // Perform the post-condition check of the ObjCIvarRefExpr and store + // the created nodes in 'Dst'. + getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this); +} + +void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this); +} + +/// Generate a node in \p Bldr for an iteration statement using ObjC +/// for-loop iterator. +static void populateObjCForDestinationSet( + ExplodedNodeSet &dstLocation, SValBuilder &svalBuilder, + const ObjCForCollectionStmt *S, const Stmt *elem, SVal elementV, + SymbolManager &SymMgr, const NodeBuilderContext *currBldrCtx, + StmtNodeBuilder &Bldr, bool hasElements) { + + for (ExplodedNode *Pred : dstLocation) { + ProgramStateRef state = Pred->getState(); + const LocationContext *LCtx = Pred->getLocationContext(); + + SVal hasElementsV = svalBuilder.makeTruthVal(hasElements); + + // FIXME: S is not an expression. We should not be binding values to it. + ProgramStateRef nextState = state->BindExpr(S, LCtx, hasElementsV); + + if (auto MV = elementV.getAs<loc::MemRegionVal>()) + if (const auto *R = dyn_cast<TypedValueRegion>(MV->getRegion())) { + // FIXME: The proper thing to do is to really iterate over the + // container. We will do this with dispatch logic to the store. + // For now, just 'conjure' up a symbolic value. + QualType T = R->getValueType(); + assert(Loc::isLocType(T)); + + SVal V; + if (hasElements) { + SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T, + currBldrCtx->blockCount()); + V = svalBuilder.makeLoc(Sym); + } else { + V = svalBuilder.makeIntVal(0, T); + } + + nextState = nextState->bindLoc(elementV, V, LCtx); + } + + Bldr.generateNode(S, Pred, nextState); + } +} + +void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + // ObjCForCollectionStmts are processed in two places. This method + // handles the case where an ObjCForCollectionStmt* occurs as one of the + // statements within a basic block. This transfer function does two things: + // + // (1) binds the next container value to 'element'. This creates a new + // node in the ExplodedGraph. + // + // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating + // whether or not the container has any more elements. This value + // will be tested in ProcessBranch. We need to explicitly bind + // this value because a container can contain nil elements. + // + // FIXME: Eventually this logic should actually do dispatches to + // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration). + // This will require simulating a temporary NSFastEnumerationState, either + // through an SVal or through the use of MemRegions. This value can + // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop + // terminates we reclaim the temporary (it goes out of scope) and we + // we can test if the SVal is 0 or if the MemRegion is null (depending + // on what approach we take). + // + // For now: simulate (1) by assigning either a symbol or nil if the + // container is empty. Thus this transfer function will by default + // result in state splitting. + + const Stmt *elem = S->getElement(); + const Stmt *collection = S->getCollection(); + ProgramStateRef state = Pred->getState(); + SVal collectionV = state->getSVal(collection, Pred->getLocationContext()); + + SVal elementV; + if (const auto *DS = dyn_cast<DeclStmt>(elem)) { + const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl()); + assert(elemD->getInit() == nullptr); + elementV = state->getLValue(elemD, Pred->getLocationContext()); + } else { + elementV = state->getSVal(elem, Pred->getLocationContext()); + } + + bool isContainerNull = state->isNull(collectionV).isConstrainedTrue(); + + ExplodedNodeSet dstLocation; + evalLocation(dstLocation, S, elem, Pred, state, elementV, false); + + ExplodedNodeSet Tmp; + StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx); + + if (!isContainerNull) + populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV, + SymMgr, currBldrCtx, Bldr, + /*hasElements=*/true); + + populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV, + SymMgr, currBldrCtx, Bldr, + /*hasElements=*/false); + + // Finally, run any custom checkers. + // FIXME: Eventually all pre- and post-checks should live in VisitStmt. + getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this); +} + +void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + CallEventManager &CEMgr = getStateManager().getCallEventManager(); + CallEventRef<ObjCMethodCall> Msg = + CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext()); + + // There are three cases for the receiver: + // (1) it is definitely nil, + // (2) it is definitely non-nil, and + // (3) we don't know. + // + // If the receiver is definitely nil, we skip the pre/post callbacks and + // instead call the ObjCMessageNil callbacks and return. + // + // If the receiver is definitely non-nil, we call the pre- callbacks, + // evaluate the call, and call the post- callbacks. + // + // If we don't know, we drop the potential nil flow and instead + // continue from the assumed non-nil state as in (2). This approach + // intentionally drops coverage in order to prevent false alarms + // in the following scenario: + // + // id result = [o someMethod] + // if (result) { + // if (!o) { + // // <-- This program point should be unreachable because if o is nil + // // it must the case that result is nil as well. + // } + // } + // + // We could avoid dropping coverage by performing an explicit case split + // on each method call -- but this would get very expensive. An alternative + // would be to introduce lazy constraints. + // FIXME: This ignores many potential bugs (<rdar://problem/11733396>). + // Revisit once we have lazier constraints. + if (Msg->isInstanceMessage()) { + SVal recVal = Msg->getReceiverSVal(); + if (!recVal.isUndef()) { + // Bifurcate the state into nil and non-nil ones. + DefinedOrUnknownSVal receiverVal = + recVal.castAs<DefinedOrUnknownSVal>(); + ProgramStateRef State = Pred->getState(); + + ProgramStateRef notNilState, nilState; + std::tie(notNilState, nilState) = State->assume(receiverVal); + + // Receiver is definitely nil, so run ObjCMessageNil callbacks and return. + if (nilState && !notNilState) { + ExplodedNodeSet dstNil; + StmtNodeBuilder Bldr(Pred, dstNil, *currBldrCtx); + bool HasTag = Pred->getLocation().getTag(); + Pred = Bldr.generateNode(ME, Pred, nilState, nullptr, + ProgramPoint::PreStmtKind); + assert((Pred || HasTag) && "Should have cached out already!"); + (void)HasTag; + if (!Pred) + return; + + ExplodedNodeSet dstPostCheckers; + getCheckerManager().runCheckersForObjCMessageNil(dstPostCheckers, Pred, + *Msg, *this); + for (auto I : dstPostCheckers) + finishArgumentConstruction(Dst, I, *Msg); + return; + } + + ExplodedNodeSet dstNonNil; + StmtNodeBuilder Bldr(Pred, dstNonNil, *currBldrCtx); + // Generate a transition to the non-nil state, dropping any potential + // nil flow. + if (notNilState != State) { + bool HasTag = Pred->getLocation().getTag(); + Pred = Bldr.generateNode(ME, Pred, notNilState); + assert((Pred || HasTag) && "Should have cached out already!"); + (void)HasTag; + if (!Pred) + return; + } + } + } + + // Handle the previsits checks. + ExplodedNodeSet dstPrevisit; + getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred, + *Msg, *this); + ExplodedNodeSet dstGenericPrevisit; + getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit, + *Msg, *this); + + // Proceed with evaluate the message expression. + ExplodedNodeSet dstEval; + StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *currBldrCtx); + + for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(), + DE = dstGenericPrevisit.end(); DI != DE; ++DI) { + ExplodedNode *Pred = *DI; + ProgramStateRef State = Pred->getState(); + CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State); + + if (UpdatedMsg->isInstanceMessage()) { + SVal recVal = UpdatedMsg->getReceiverSVal(); + if (!recVal.isUndef()) { + if (ObjCNoRet.isImplicitNoReturn(ME)) { + // If we raise an exception, for now treat it as a sink. + // Eventually we will want to handle exceptions properly. + Bldr.generateSink(ME, Pred, State); + continue; + } + } + } else { + // Check for special class methods that are known to not return + // and that we should treat as a sink. + if (ObjCNoRet.isImplicitNoReturn(ME)) { + // If we raise an exception, for now treat it as a sink. + // Eventually we will want to handle exceptions properly. + Bldr.generateSink(ME, Pred, Pred->getState()); + continue; + } + } + + defaultEvalCall(Bldr, Pred, *UpdatedMsg); + } + + // If there were constructors called for object-type arguments, clean them up. + ExplodedNodeSet dstArgCleanup; + for (auto I : dstEval) + finishArgumentConstruction(dstArgCleanup, I, *Msg); + + ExplodedNodeSet dstPostvisit; + getCheckerManager().runCheckersForPostCall(dstPostvisit, dstArgCleanup, + *Msg, *this); + + // Finally, perform the post-condition check of the ObjCMessageExpr and store + // the created nodes in 'Dst'. + getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit, + *Msg, *this); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp new file mode 100644 index 000000000000..94edd84d15d2 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp @@ -0,0 +1,31 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a summary of a function gathered/used by static analysis. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h" + +using namespace clang; +using namespace ento; + +unsigned FunctionSummariesTy::getTotalNumBasicBlocks() { + unsigned Total = 0; + for (const auto &I : Map) + Total += I.second.TotalBasicBlocks; + return Total; +} + +unsigned FunctionSummariesTy::getTotalNumVisitedBasicBlocks() { + unsigned Total = 0; + for (const auto &I : Map) + Total += I.second.VisitedBasicBlocks.count(); + return Total; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp new file mode 100644 index 000000000000..fc82f1176942 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp @@ -0,0 +1,1017 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines the HTMLDiagnostics object. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/Stmt.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/Token.h" +#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/Rewrite/Core/Rewriter.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/IssueHash.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> +#include <cassert> +#include <map> +#include <memory> +#include <set> +#include <sstream> +#include <string> +#include <system_error> +#include <utility> +#include <vector> + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Boilerplate. +//===----------------------------------------------------------------------===// + +namespace { + +class HTMLDiagnostics : public PathDiagnosticConsumer { + std::string Directory; + bool createdDir = false; + bool noDir = false; + const Preprocessor &PP; + AnalyzerOptions &AnalyzerOpts; + const bool SupportsCrossFileDiagnostics; + +public: + HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts, + const std::string& prefix, + const Preprocessor &pp, + bool supportsMultipleFiles) + : Directory(prefix), PP(pp), AnalyzerOpts(AnalyzerOpts), + SupportsCrossFileDiagnostics(supportsMultipleFiles) {} + + ~HTMLDiagnostics() override { FlushDiagnostics(nullptr); } + + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + FilesMade *filesMade) override; + + StringRef getName() const override { + return "HTMLDiagnostics"; + } + + bool supportsCrossFileDiagnostics() const override { + return SupportsCrossFileDiagnostics; + } + + unsigned ProcessMacroPiece(raw_ostream &os, + const PathDiagnosticMacroPiece& P, + unsigned num); + + void HandlePiece(Rewriter& R, FileID BugFileID, + const PathDiagnosticPiece& P, unsigned num, unsigned max); + + void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range, + const char *HighlightStart = "<span class=\"mrange\">", + const char *HighlightEnd = "</span>"); + + void ReportDiag(const PathDiagnostic& D, + FilesMade *filesMade); + + // Generate the full HTML report + std::string GenerateHTML(const PathDiagnostic& D, Rewriter &R, + const SourceManager& SMgr, const PathPieces& path, + const char *declName); + + // Add HTML header/footers to file specified by FID + void FinalizeHTML(const PathDiagnostic& D, Rewriter &R, + const SourceManager& SMgr, const PathPieces& path, + FileID FID, const FileEntry *Entry, const char *declName); + + // Rewrite the file specified by FID with HTML formatting. + void RewriteFile(Rewriter &R, const PathPieces& path, FileID FID); + + +private: + /// \return Javascript for displaying shortcuts help; + StringRef showHelpJavascript(); + + /// \return Javascript for navigating the HTML report using j/k keys. + StringRef generateKeyboardNavigationJavascript(); + + /// \return JavaScript for an option to only show relevant lines. + std::string showRelevantLinesJavascript( + const PathDiagnostic &D, const PathPieces &path); + + /// Write executed lines from \p D in JSON format into \p os. + void dumpCoverageData(const PathDiagnostic &D, + const PathPieces &path, + llvm::raw_string_ostream &os); +}; + +} // namespace + +void ento::createHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, + const std::string& prefix, + const Preprocessor &PP) { + C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, true)); +} + +void ento::createHTMLSingleFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, + const std::string& prefix, + const Preprocessor &PP) { + C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, false)); +} + +//===----------------------------------------------------------------------===// +// Report processing. +//===----------------------------------------------------------------------===// + +void HTMLDiagnostics::FlushDiagnosticsImpl( + std::vector<const PathDiagnostic *> &Diags, + FilesMade *filesMade) { + for (const auto Diag : Diags) + ReportDiag(*Diag, filesMade); +} + +void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, + FilesMade *filesMade) { + // Create the HTML directory if it is missing. + if (!createdDir) { + createdDir = true; + if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) { + llvm::errs() << "warning: could not create directory '" + << Directory << "': " << ec.message() << '\n'; + noDir = true; + return; + } + } + + if (noDir) + return; + + // First flatten out the entire path to make it easier to use. + PathPieces path = D.path.flatten(/*ShouldFlattenMacros=*/false); + + // The path as already been prechecked that the path is non-empty. + assert(!path.empty()); + const SourceManager &SMgr = path.front()->getLocation().getManager(); + + // Create a new rewriter to generate HTML. + Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts()); + + // The file for the first path element is considered the main report file, it + // will usually be equivalent to SMgr.getMainFileID(); however, it might be a + // header when -analyzer-opt-analyze-headers is used. + FileID ReportFile = path.front()->getLocation().asLocation().getExpansionLoc().getFileID(); + + // Get the function/method name + SmallString<128> declName("unknown"); + int offsetDecl = 0; + if (const Decl *DeclWithIssue = D.getDeclWithIssue()) { + if (const auto *ND = dyn_cast<NamedDecl>(DeclWithIssue)) + declName = ND->getDeclName().getAsString(); + + if (const Stmt *Body = DeclWithIssue->getBody()) { + // Retrieve the relative position of the declaration which will be used + // for the file name + FullSourceLoc L( + SMgr.getExpansionLoc(path.back()->getLocation().asLocation()), + SMgr); + FullSourceLoc FunL(SMgr.getExpansionLoc(Body->getBeginLoc()), SMgr); + offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber(); + } + } + + std::string report = GenerateHTML(D, R, SMgr, path, declName.c_str()); + if (report.empty()) { + llvm::errs() << "warning: no diagnostics generated for main file.\n"; + return; + } + + // Create a path for the target HTML file. + int FD; + SmallString<128> Model, ResultPath; + + if (!AnalyzerOpts.ShouldWriteStableReportFilename) { + llvm::sys::path::append(Model, Directory, "report-%%%%%%.html"); + if (std::error_code EC = + llvm::sys::fs::make_absolute(Model)) { + llvm::errs() << "warning: could not make '" << Model + << "' absolute: " << EC.message() << '\n'; + return; + } + if (std::error_code EC = + llvm::sys::fs::createUniqueFile(Model, FD, ResultPath)) { + llvm::errs() << "warning: could not create file in '" << Directory + << "': " << EC.message() << '\n'; + return; + } + } else { + int i = 1; + std::error_code EC; + do { + // Find a filename which is not already used + const FileEntry* Entry = SMgr.getFileEntryForID(ReportFile); + std::stringstream filename; + Model = ""; + filename << "report-" + << llvm::sys::path::filename(Entry->getName()).str() + << "-" << declName.c_str() + << "-" << offsetDecl + << "-" << i << ".html"; + llvm::sys::path::append(Model, Directory, + filename.str()); + EC = llvm::sys::fs::openFileForReadWrite( + Model, FD, llvm::sys::fs::CD_CreateNew, llvm::sys::fs::OF_None); + if (EC && EC != llvm::errc::file_exists) { + llvm::errs() << "warning: could not create file '" << Model + << "': " << EC.message() << '\n'; + return; + } + i++; + } while (EC); + } + + llvm::raw_fd_ostream os(FD, true); + + if (filesMade) + filesMade->addDiagnostic(D, getName(), + llvm::sys::path::filename(ResultPath)); + + // Emit the HTML to disk. + os << report; +} + +std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R, + const SourceManager& SMgr, const PathPieces& path, const char *declName) { + // Rewrite source files as HTML for every new file the path crosses + 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()) + continue; + + FileIDs.push_back(FID); + RewriteFile(R, path, FID); + } + + if (SupportsCrossFileDiagnostics && FileIDs.size() > 1) { + // Prefix file names, anchor tags, and nav cursors to every file + for (auto I = FileIDs.begin(), E = FileIDs.end(); I != E; I++) { + std::string s; + llvm::raw_string_ostream os(s); + + if (I != FileIDs.begin()) + os << "<hr class=divider>\n"; + + os << "<div id=File" << I->getHashValue() << ">\n"; + + // Left nav arrow + if (I != FileIDs.begin()) + os << "<div class=FileNav><a href=\"#File" << (I - 1)->getHashValue() + << "\">←</a></div>"; + + os << "<h4 class=FileName>" << SMgr.getFileEntryForID(*I)->getName() + << "</h4>\n"; + + // Right nav arrow + if (I + 1 != E) + os << "<div class=FileNav><a href=\"#File" << (I + 1)->getHashValue() + << "\">→</a></div>"; + + os << "</div>\n"; + + R.InsertTextBefore(SMgr.getLocForStartOfFile(*I), os.str()); + } + + // Append files to the main report file in the order they appear in the path + for (auto I : llvm::make_range(FileIDs.begin() + 1, FileIDs.end())) { + std::string s; + llvm::raw_string_ostream os(s); + + const RewriteBuffer *Buf = R.getRewriteBufferFor(I); + for (auto BI : *Buf) + os << BI; + + R.InsertTextAfter(SMgr.getLocForEndOfFile(FileIDs[0]), os.str()); + } + } + + const RewriteBuffer *Buf = R.getRewriteBufferFor(FileIDs[0]); + if (!Buf) + return {}; + + // Add CSS, header, and footer. + FileID FID = + path.back()->getLocation().asLocation().getExpansionLoc().getFileID(); + const FileEntry* Entry = SMgr.getFileEntryForID(FID); + FinalizeHTML(D, R, SMgr, path, FileIDs[0], Entry, declName); + + std::string file; + llvm::raw_string_ostream os(file); + for (auto BI : *Buf) + os << BI; + + return os.str(); +} + +void HTMLDiagnostics::dumpCoverageData( + const PathDiagnostic &D, + const PathPieces &path, + llvm::raw_string_ostream &os) { + + const FilesToLineNumsMap &ExecutedLines = D.getExecutedLines(); + + os << "var relevant_lines = {"; + for (auto I = ExecutedLines.begin(), + E = ExecutedLines.end(); I != E; ++I) { + if (I != ExecutedLines.begin()) + os << ", "; + + os << "\"" << I->first.getHashValue() << "\": {"; + for (unsigned LineNo : I->second) { + if (LineNo != *(I->second.begin())) + os << ", "; + + os << "\"" << LineNo << "\": 1"; + } + os << "}"; + } + + os << "};"; +} + +std::string HTMLDiagnostics::showRelevantLinesJavascript( + const PathDiagnostic &D, const PathPieces &path) { + std::string s; + llvm::raw_string_ostream os(s); + os << "<script type='text/javascript'>\n"; + dumpCoverageData(D, path, os); + os << R"<<<( + +var filterCounterexample = function (hide) { + var tables = document.getElementsByClassName("code"); + for (var t=0; t<tables.length; t++) { + var table = tables[t]; + var file_id = table.getAttribute("data-fileid"); + var lines_in_fid = relevant_lines[file_id]; + if (!lines_in_fid) { + lines_in_fid = {}; + } + var lines = table.getElementsByClassName("codeline"); + for (var i=0; i<lines.length; i++) { + var el = lines[i]; + var lineNo = el.getAttribute("data-linenumber"); + if (!lines_in_fid[lineNo]) { + if (hide) { + el.setAttribute("hidden", ""); + } else { + el.removeAttribute("hidden"); + } + } + } + } +} + +window.addEventListener("keydown", function (event) { + if (event.defaultPrevented) { + return; + } + if (event.key == "S") { + var checked = document.getElementsByName("showCounterexample")[0].checked; + filterCounterexample(!checked); + document.getElementsByName("showCounterexample")[0].checked = !checked; + } else { + return; + } + event.preventDefault(); +}, true); + +document.addEventListener("DOMContentLoaded", function() { + document.querySelector('input[name="showCounterexample"]').onchange= + function (event) { + filterCounterexample(this.checked); + }; +}); +</script> + +<form> + <input type="checkbox" name="showCounterexample" id="showCounterexample" /> + <label for="showCounterexample"> + Show only relevant lines + </label> +</form> +)<<<"; + + return os.str(); +} + +void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R, + const SourceManager& SMgr, const PathPieces& path, FileID FID, + const FileEntry *Entry, const char *declName) { + // This is a cludge; basically we want to append either the full + // working directory if we have no directory information. This is + // a work in progress. + + llvm::SmallString<0> DirName; + + if (llvm::sys::path::is_relative(Entry->getName())) { + llvm::sys::fs::current_path(DirName); + DirName += '/'; + } + + int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber(); + int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber(); + + R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), showHelpJavascript()); + + R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), + generateKeyboardNavigationJavascript()); + + // Checkbox and javascript for filtering the output to the counterexample. + R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), + showRelevantLinesJavascript(D, path)); + + // Add the name of the file as an <h1> tag. + { + std::string s; + llvm::raw_string_ostream os(s); + + os << "<!-- REPORTHEADER -->\n" + << "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n" + "<tr><td class=\"rowname\">File:</td><td>" + << html::EscapeText(DirName) + << html::EscapeText(Entry->getName()) + << "</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>" + "<a href=\"#EndPath\">line " + << LineNumber + << ", column " + << ColumnNumber + << "</a><br />" + << D.getVerboseDescription() << "</td></tr>\n"; + + // The navigation across the extra notes pieces. + unsigned NumExtraPieces = 0; + for (const auto &Piece : path) { + if (const auto *P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) { + int LineNumber = + P->getLocation().asLocation().getExpansionLineNumber(); + int ColumnNumber = + P->getLocation().asLocation().getExpansionColumnNumber(); + os << "<tr><td class=\"rowname\">Note:</td><td>" + << "<a href=\"#Note" << NumExtraPieces << "\">line " + << LineNumber << ", column " << ColumnNumber << "</a><br />" + << P->getString() << "</td></tr>"; + ++NumExtraPieces; + } + } + + // Output any other meta data. + + for (PathDiagnostic::meta_iterator I = D.meta_begin(), E = D.meta_end(); + I != E; ++I) { + os << "<tr><td></td><td>" << html::EscapeText(*I) << "</td></tr>\n"; + } + + os << R"<<<( +</table> +<!-- REPORTSUMMARYEXTRA --> +<h3>Annotated Source Code</h3> +<p>Press <a href="#" onclick="toggleHelp(); return false;">'?'</a> + to see keyboard shortcuts</p> +<input type="checkbox" class="spoilerhider" id="showinvocation" /> +<label for="showinvocation" >Show analyzer invocation</label> +<div class="spoiler">clang -cc1 )<<<"; + os << html::EscapeText(AnalyzerOpts.FullCompilerInvocation); + os << R"<<<( +</div> +<div id='tooltiphint' hidden="true"> + <p>Keyboard shortcuts: </p> + <ul> + <li>Use 'j/k' keys for keyboard navigation</li> + <li>Use 'Shift+S' to show/hide relevant lines</li> + <li>Use '?' to toggle this window</li> + </ul> + <a href="#" onclick="toggleHelp(); return false;">Close</a> +</div> +)<<<"; + R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str()); + } + + // Embed meta-data tags. + { + std::string s; + llvm::raw_string_ostream os(s); + + StringRef BugDesc = D.getVerboseDescription(); + if (!BugDesc.empty()) + os << "\n<!-- BUGDESC " << BugDesc << " -->\n"; + + StringRef BugType = D.getBugType(); + if (!BugType.empty()) + os << "\n<!-- BUGTYPE " << BugType << " -->\n"; + + PathDiagnosticLocation UPDLoc = D.getUniqueingLoc(); + FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid() + ? UPDLoc.asLocation() + : D.getLocation().asLocation()), + SMgr); + const Decl *DeclWithIssue = D.getDeclWithIssue(); + + StringRef BugCategory = D.getCategory(); + if (!BugCategory.empty()) + os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n"; + + os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n"; + + os << "\n<!-- FILENAME " << llvm::sys::path::filename(Entry->getName()) << " -->\n"; + + os << "\n<!-- FUNCTIONNAME " << declName << " -->\n"; + + os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " + << GetIssueHash(SMgr, L, D.getCheckName(), D.getBugType(), DeclWithIssue, + PP.getLangOpts()) << " -->\n"; + + os << "\n<!-- BUGLINE " + << LineNumber + << " -->\n"; + + os << "\n<!-- BUGCOLUMN " + << ColumnNumber + << " -->\n"; + + os << "\n<!-- BUGPATHLENGTH " << path.size() << " -->\n"; + + // Mark the end of the tags. + os << "\n<!-- BUGMETAEND -->\n"; + + // Insert the text. + R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str()); + } + + html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName()); +} + +StringRef HTMLDiagnostics::showHelpJavascript() { + return R"<<<( +<script type='text/javascript'> + +var toggleHelp = function() { + var hint = document.querySelector("#tooltiphint"); + var attributeName = "hidden"; + if (hint.hasAttribute(attributeName)) { + hint.removeAttribute(attributeName); + } else { + hint.setAttribute("hidden", "true"); + } +}; +window.addEventListener("keydown", function (event) { + if (event.defaultPrevented) { + return; + } + if (event.key == "?") { + toggleHelp(); + } else { + return; + } + event.preventDefault(); +}); +</script> +)<<<"; +} + +void HTMLDiagnostics::RewriteFile(Rewriter &R, + const PathPieces& path, FileID FID) { + // Process the path. + // Maintain the counts of extra note pieces separately. + unsigned TotalPieces = path.size(); + unsigned TotalNotePieces = + std::count_if(path.begin(), path.end(), + [](const std::shared_ptr<PathDiagnosticPiece> &p) { + return isa<PathDiagnosticNotePiece>(*p); + }); + + unsigned TotalRegularPieces = TotalPieces - TotalNotePieces; + unsigned NumRegularPieces = TotalRegularPieces; + unsigned NumNotePieces = TotalNotePieces; + + for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) { + if (isa<PathDiagnosticNotePiece>(I->get())) { + // This adds diagnostic bubbles, but not navigation. + // Navigation through note pieces would be added later, + // as a separate pass through the piece list. + HandlePiece(R, FID, **I, NumNotePieces, TotalNotePieces); + --NumNotePieces; + } else { + HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces); + --NumRegularPieces; + } + } + + // Add line numbers, header, footer, etc. + + html::EscapeText(R, FID); + html::AddLineNumbers(R, FID); + + // If we have a preprocessor, relex the file and syntax highlight. + // We might not have a preprocessor if we come from a deserialized AST file, + // for example. + + html::SyntaxHighlight(R, FID, PP); + html::HighlightMacros(R, FID, PP); +} + +void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID, + const PathDiagnosticPiece& P, + unsigned num, unsigned max) { + // For now, just draw a box above the line in question, and emit the + // warning. + FullSourceLoc Pos = P.getLocation().asLocation(); + + if (!Pos.isValid()) + return; + + SourceManager &SM = R.getSourceMgr(); + assert(&Pos.getManager() == &SM && "SourceManagers are different!"); + std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedExpansionLoc(Pos); + + if (LPosInfo.first != BugFileID) + return; + + const llvm::MemoryBuffer *Buf = SM.getBuffer(LPosInfo.first); + const char* FileStart = Buf->getBufferStart(); + + // Compute the column number. Rewind from the current position to the start + // of the line. + unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second); + const char *TokInstantiationPtr =Pos.getExpansionLoc().getCharacterData(); + const char *LineStart = TokInstantiationPtr-ColNo; + + // Compute LineEnd. + const char *LineEnd = TokInstantiationPtr; + const char* FileEnd = Buf->getBufferEnd(); + while (*LineEnd != '\n' && LineEnd != FileEnd) + ++LineEnd; + + // Compute the margin offset by counting tabs and non-tabs. + unsigned PosNo = 0; + for (const char* c = LineStart; c != TokInstantiationPtr; ++c) + PosNo += *c == '\t' ? 8 : 1; + + // Create the html for the message. + + const char *Kind = nullptr; + bool IsNote = false; + bool SuppressIndex = (max == 1); + switch (P.getKind()) { + case PathDiagnosticPiece::Call: + llvm_unreachable("Calls and extra notes should already be handled"); + case PathDiagnosticPiece::Event: Kind = "Event"; break; + case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break; + // Setting Kind to "Control" is intentional. + case PathDiagnosticPiece::Macro: Kind = "Control"; break; + case PathDiagnosticPiece::Note: + Kind = "Note"; + IsNote = true; + SuppressIndex = true; + break; + } + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\""; + + if (IsNote) + os << "Note" << num; + else if (num == max) + os << "EndPath"; + else + os << "Path" << num; + + os << "\" class=\"msg"; + if (Kind) + os << " msg" << Kind; + os << "\" style=\"margin-left:" << PosNo << "ex"; + + // Output a maximum size. + if (!isa<PathDiagnosticMacroPiece>(P)) { + // Get the string and determining its maximum substring. + const auto &Msg = P.getString(); + unsigned max_token = 0; + unsigned cnt = 0; + unsigned len = Msg.size(); + + for (char C : Msg) + switch (C) { + default: + ++cnt; + continue; + case ' ': + case '\t': + case '\n': + if (cnt > max_token) max_token = cnt; + cnt = 0; + } + + if (cnt > max_token) + max_token = cnt; + + // Determine the approximate size of the message bubble in em. + unsigned em; + const unsigned max_line = 120; + + if (max_token >= max_line) + em = max_token / 2; + else { + unsigned characters = max_line; + unsigned lines = len / max_line; + + if (lines > 0) { + for (; characters > max_token; --characters) + if (len / characters > lines) { + ++characters; + break; + } + } + + em = characters / 2; + } + + if (em < max_line/2) + os << "; max-width:" << em << "em"; + } + else + os << "; max-width:100em"; + + os << "\">"; + + if (!SuppressIndex) { + os << "<table class=\"msgT\"><tr><td valign=\"top\">"; + os << "<div class=\"PathIndex"; + if (Kind) os << " PathIndex" << Kind; + os << "\">" << num << "</div>"; + + if (num > 1) { + os << "</td><td><div class=\"PathNav\"><a href=\"#Path" + << (num - 1) + << "\" title=\"Previous event (" + << (num - 1) + << ")\">←</a></div></td>"; + } + + os << "</td><td>"; + } + + if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(&P)) { + os << "Within the expansion of the macro '"; + + // Get the name of the macro by relexing it. + { + FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc(); + assert(L.isFileID()); + StringRef BufferInfo = L.getBufferData(); + std::pair<FileID, unsigned> LocInfo = L.getDecomposedLoc(); + const char* MacroName = LocInfo.second + BufferInfo.data(); + Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(), + BufferInfo.begin(), MacroName, BufferInfo.end()); + + Token TheTok; + rawLexer.LexFromRawLexer(TheTok); + for (unsigned i = 0, n = TheTok.getLength(); i < n; ++i) + os << MacroName[i]; + } + + os << "':\n"; + + if (!SuppressIndex) { + os << "</td>"; + if (num < max) { + os << "<td><div class=\"PathNav\"><a href=\"#"; + if (num == max - 1) + os << "EndPath"; + else + os << "Path" << (num + 1); + os << "\" title=\"Next event (" + << (num + 1) + << ")\">→</a></div></td>"; + } + + os << "</tr></table>"; + } + + // Within a macro piece. Write out each event. + ProcessMacroPiece(os, *MP, 0); + } + else { + os << html::EscapeText(P.getString()); + + if (!SuppressIndex) { + os << "</td>"; + if (num < max) { + os << "<td><div class=\"PathNav\"><a href=\"#"; + if (num == max - 1) + os << "EndPath"; + else + os << "Path" << (num + 1); + os << "\" title=\"Next event (" + << (num + 1) + << ")\">→</a></div></td>"; + } + + os << "</tr></table>"; + } + } + + os << "</div></td></tr>"; + + // Insert the new html. + unsigned DisplayPos = LineEnd - FileStart; + SourceLocation Loc = + SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos); + + R.InsertTextBefore(Loc, os.str()); + + // Now highlight the ranges. + ArrayRef<SourceRange> Ranges = P.getRanges(); + for (const auto &Range : Ranges) + HighlightRange(R, LPosInfo.first, Range); +} + +static void EmitAlphaCounter(raw_ostream &os, unsigned n) { + unsigned x = n % ('z' - 'a'); + n /= 'z' - 'a'; + + if (n > 0) + EmitAlphaCounter(os, n); + + os << char('a' + x); +} + +unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os, + const PathDiagnosticMacroPiece& P, + unsigned num) { + for (const auto &subPiece : P.subPieces) { + if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(subPiece.get())) { + num = ProcessMacroPiece(os, *MP, num); + continue; + } + + if (const auto *EP = dyn_cast<PathDiagnosticEventPiece>(subPiece.get())) { + os << "<div class=\"msg msgEvent\" style=\"width:94%; " + "margin-left:5px\">" + "<table class=\"msgT\"><tr>" + "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">"; + EmitAlphaCounter(os, num++); + os << "</div></td><td valign=\"top\">" + << html::EscapeText(EP->getString()) + << "</td></tr></table></div>\n"; + } + } + + return num; +} + +void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID, + SourceRange Range, + const char *HighlightStart, + const char *HighlightEnd) { + SourceManager &SM = R.getSourceMgr(); + const LangOptions &LangOpts = R.getLangOpts(); + + SourceLocation InstantiationStart = SM.getExpansionLoc(Range.getBegin()); + unsigned StartLineNo = SM.getExpansionLineNumber(InstantiationStart); + + SourceLocation InstantiationEnd = SM.getExpansionLoc(Range.getEnd()); + unsigned EndLineNo = SM.getExpansionLineNumber(InstantiationEnd); + + if (EndLineNo < StartLineNo) + return; + + if (SM.getFileID(InstantiationStart) != BugFileID || + SM.getFileID(InstantiationEnd) != BugFileID) + return; + + // Compute the column number of the end. + unsigned EndColNo = SM.getExpansionColumnNumber(InstantiationEnd); + unsigned OldEndColNo = EndColNo; + + if (EndColNo) { + // Add in the length of the token, so that we cover multi-char tokens. + EndColNo += Lexer::MeasureTokenLength(Range.getEnd(), SM, LangOpts)-1; + } + + // Highlight the range. Make the span tag the outermost tag for the + // selected range. + + SourceLocation E = + InstantiationEnd.getLocWithOffset(EndColNo - OldEndColNo); + + html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd); +} + +StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() { + return R"<<<( +<script type='text/javascript'> +var digitMatcher = new RegExp("[0-9]+"); + +document.addEventListener("DOMContentLoaded", function() { + document.querySelectorAll(".PathNav > a").forEach( + function(currentValue, currentIndex) { + var hrefValue = currentValue.getAttribute("href"); + currentValue.onclick = function() { + scrollTo(document.querySelector(hrefValue)); + return false; + }; + }); +}); + +var findNum = function() { + var s = document.querySelector(".selected"); + if (!s || s.id == "EndPath") { + return 0; + } + var out = parseInt(digitMatcher.exec(s.id)[0]); + return out; +}; + +var scrollTo = function(el) { + document.querySelectorAll(".selected").forEach(function(s) { + s.classList.remove("selected"); + }); + el.classList.add("selected"); + window.scrollBy(0, el.getBoundingClientRect().top - + (window.innerHeight / 2)); +} + +var move = function(num, up, numItems) { + if (num == 1 && up || num == numItems - 1 && !up) { + return 0; + } else if (num == 0 && up) { + return numItems - 1; + } else if (num == 0 && !up) { + return 1 % numItems; + } + return up ? num - 1 : num + 1; +} + +var numToId = function(num) { + if (num == 0) { + return document.getElementById("EndPath") + } + return document.getElementById("Path" + num); +}; + +var navigateTo = function(up) { + var numItems = document.querySelectorAll( + ".line > .msgEvent, .line > .msgControl").length; + var currentSelected = findNum(); + var newSelected = move(currentSelected, up, numItems); + var newEl = numToId(newSelected, numItems); + + // Scroll element into center. + scrollTo(newEl); +}; + +window.addEventListener("keydown", function (event) { + if (event.defaultPrevented) { + return; + } + if (event.key == "j") { + navigateTo(/*up=*/false); + } else if (event.key == "k") { + navigateTo(/*up=*/true); + } else { + return; + } + event.preventDefault(); +}, true); +</script> + )<<<"; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/IssueHash.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/IssueHash.cpp new file mode 100644 index 000000000000..6c55c61dd399 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/IssueHash.cpp @@ -0,0 +1,205 @@ +//===---------- 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. +// +//===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/IssueHash.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Specifiers.h" +#include "clang/Lex/Lexer.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/LineIterator.h" +#include "llvm/Support/MD5.h" +#include "llvm/Support/Path.h" + +#include <functional> +#include <sstream> +#include <string> + +using namespace clang; + +// Get a string representation of the parts of the signature that can be +// overloaded on. +static std::string GetSignature(const FunctionDecl *Target) { + if (!Target) + return ""; + std::string Signature; + + // When a flow sensitive bug happens in templated code we should not generate + // distinct hash value for every instantiation. Use the signature from the + // primary template. + if (const FunctionDecl *InstantiatedFrom = + Target->getTemplateInstantiationPattern()) + Target = InstantiatedFrom; + + if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) && + !isa<CXXConversionDecl>(Target)) + Signature.append(Target->getReturnType().getAsString()).append(" "); + Signature.append(Target->getQualifiedNameAsString()).append("("); + + for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) { + if (i) + Signature.append(", "); + Signature.append(Target->getParamDecl(i)->getType().getAsString()); + } + + if (Target->isVariadic()) + Signature.append(", ..."); + Signature.append(")"); + + const auto *TargetT = + llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr()); + + if (!TargetT || !isa<CXXMethodDecl>(Target)) + return Signature; + + if (TargetT->isConst()) + Signature.append(" const"); + if (TargetT->isVolatile()) + Signature.append(" volatile"); + if (TargetT->isRestrict()) + Signature.append(" restrict"); + + if (const auto *TargetPT = + dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) { + switch (TargetPT->getRefQualifier()) { + case RQ_LValue: + Signature.append(" &"); + break; + case RQ_RValue: + Signature.append(" &&"); + break; + default: + break; + } + } + + return Signature; +} + +static std::string GetEnclosingDeclContextSignature(const Decl *D) { + if (!D) + return ""; + + if (const auto *ND = dyn_cast<NamedDecl>(D)) { + std::string DeclName; + + switch (ND->getKind()) { + case Decl::Namespace: + case Decl::Record: + case Decl::CXXRecord: + case Decl::Enum: + DeclName = ND->getQualifiedNameAsString(); + break; + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: + case Decl::CXXMethod: + case Decl::Function: + DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND)); + break; + case Decl::ObjCMethod: + // ObjC Methods can not be overloaded, qualified name uniquely identifies + // the method. + DeclName = ND->getQualifiedNameAsString(); + break; + default: + break; + } + + return DeclName; + } + + return ""; +} + +static StringRef GetNthLineOfFile(llvm::MemoryBuffer *Buffer, int Line) { + if (!Buffer) + return ""; + + llvm::line_iterator LI(*Buffer, false); + for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI) + ; + + return *LI; +} + +static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L, + const LangOptions &LangOpts) { + static StringRef Whitespaces = " \t\n"; + + StringRef Str = GetNthLineOfFile(SM.getBuffer(L.getFileID(), L), + L.getExpansionLineNumber()); + StringRef::size_type col = Str.find_first_not_of(Whitespaces); + if (col == StringRef::npos) + col = 1; // The line only contains whitespace. + else + col++; + SourceLocation StartOfLine = + SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col); + llvm::MemoryBuffer *Buffer = + SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine); + if (!Buffer) + return {}; + + const char *BufferPos = SM.getCharacterData(StartOfLine); + + Token Token; + Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts, + Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd()); + + size_t NextStart = 0; + std::ostringstream LineBuff; + while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) { + if (Token.isAtStartOfLine() && NextStart++ > 0) + continue; + LineBuff << std::string(SM.getCharacterData(Token.getLocation()), + Token.getLength()); + } + + return LineBuff.str(); +} + +static llvm::SmallString<32> GetHashOfContent(StringRef Content) { + llvm::MD5 Hash; + llvm::MD5::MD5Result MD5Res; + SmallString<32> Res; + + Hash.update(Content); + Hash.final(MD5Res); + llvm::MD5::stringifyResult(MD5Res, Res); + + return Res; +} + +std::string clang::GetIssueString(const SourceManager &SM, + FullSourceLoc &IssueLoc, + StringRef CheckerName, StringRef BugType, + const Decl *D, + const LangOptions &LangOpts) { + static StringRef Delimiter = "$"; + + return (llvm::Twine(CheckerName) + Delimiter + + GetEnclosingDeclContextSignature(D) + Delimiter + + Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter + + NormalizeLine(SM, IssueLoc, LangOpts) + Delimiter + BugType) + .str(); +} + +SmallString<32> clang::GetIssueHash(const SourceManager &SM, + FullSourceLoc &IssueLoc, + StringRef CheckerName, StringRef BugType, + const Decl *D, + const LangOptions &LangOpts) { + + return GetHashOfContent( + GetIssueString(SM, IssueLoc, CheckerName, BugType, D, LangOpts)); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp new file mode 100644 index 000000000000..da4574c61515 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp @@ -0,0 +1,294 @@ +//===--- 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. +// +//===----------------------------------------------------------------------===// +/// +/// This file contains functions which are used to decide if a loop worth to be +/// unrolled. Moreover, these functions manages the stack of loop which is +/// tracked by the ProgramState. +/// +//===----------------------------------------------------------------------===// + +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h" + +using namespace clang; +using namespace ento; +using namespace clang::ast_matchers; + +static const int MAXIMUM_STEP_UNROLLED = 128; + +struct LoopState { +private: + enum Kind { Normal, Unrolled } K; + const Stmt *LoopStmt; + const LocationContext *LCtx; + unsigned maxStep; + LoopState(Kind InK, const Stmt *S, const LocationContext *L, unsigned N) + : K(InK), LoopStmt(S), LCtx(L), maxStep(N) {} + +public: + static LoopState getNormal(const Stmt *S, const LocationContext *L, + unsigned N) { + return LoopState(Normal, S, L, N); + } + static LoopState getUnrolled(const Stmt *S, const LocationContext *L, + unsigned N) { + return LoopState(Unrolled, S, L, N); + } + bool isUnrolled() const { return K == Unrolled; } + unsigned getMaxStep() const { return maxStep; } + const Stmt *getLoopStmt() const { return LoopStmt; } + const LocationContext *getLocationContext() const { return LCtx; } + bool operator==(const LoopState &X) const { + return K == X.K && LoopStmt == X.LoopStmt; + } + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + ID.AddPointer(LoopStmt); + ID.AddPointer(LCtx); + ID.AddInteger(maxStep); + } +}; + +// The tracked stack of loops. The stack indicates that which loops the +// simulated element contained by. The loops are marked depending if we decided +// to unroll them. +// TODO: The loop stack should not need to be in the program state since it is +// lexical in nature. Instead, the stack of loops should be tracked in the +// LocationContext. +REGISTER_LIST_WITH_PROGRAMSTATE(LoopStack, LoopState) + +namespace clang { +namespace ento { + +static bool isLoopStmt(const Stmt *S) { + return S && (isa<ForStmt>(S) || isa<WhileStmt>(S) || isa<DoStmt>(S)); +} + +ProgramStateRef processLoopEnd(const Stmt *LoopStmt, ProgramStateRef State) { + auto LS = State->get<LoopStack>(); + if (!LS.isEmpty() && LS.getHead().getLoopStmt() == LoopStmt) + State = State->set<LoopStack>(LS.getTail()); + return State; +} + +static internal::Matcher<Stmt> simpleCondition(StringRef BindName) { + return binaryOperator(anyOf(hasOperatorName("<"), hasOperatorName(">"), + hasOperatorName("<="), hasOperatorName(">="), + hasOperatorName("!=")), + hasEitherOperand(ignoringParenImpCasts(declRefExpr( + to(varDecl(hasType(isInteger())).bind(BindName))))), + hasEitherOperand(ignoringParenImpCasts( + integerLiteral().bind("boundNum")))) + .bind("conditionOperator"); +} + +static internal::Matcher<Stmt> +changeIntBoundNode(internal::Matcher<Decl> VarNodeMatcher) { + return anyOf( + unaryOperator(anyOf(hasOperatorName("--"), hasOperatorName("++")), + hasUnaryOperand(ignoringParenImpCasts( + declRefExpr(to(varDecl(VarNodeMatcher)))))), + binaryOperator(isAssignmentOperator(), + hasLHS(ignoringParenImpCasts( + declRefExpr(to(varDecl(VarNodeMatcher))))))); +} + +static internal::Matcher<Stmt> +callByRef(internal::Matcher<Decl> VarNodeMatcher) { + return callExpr(forEachArgumentWithParam( + declRefExpr(to(varDecl(VarNodeMatcher))), + parmVarDecl(hasType(references(qualType(unless(isConstQualified()))))))); +} + +static internal::Matcher<Stmt> +assignedToRef(internal::Matcher<Decl> VarNodeMatcher) { + return declStmt(hasDescendant(varDecl( + allOf(hasType(referenceType()), + hasInitializer(anyOf( + initListExpr(has(declRefExpr(to(varDecl(VarNodeMatcher))))), + declRefExpr(to(varDecl(VarNodeMatcher))))))))); +} + +static internal::Matcher<Stmt> +getAddrTo(internal::Matcher<Decl> VarNodeMatcher) { + return unaryOperator( + hasOperatorName("&"), + hasUnaryOperand(declRefExpr(hasDeclaration(VarNodeMatcher)))); +} + +static internal::Matcher<Stmt> hasSuspiciousStmt(StringRef NodeName) { + return hasDescendant(stmt( + anyOf(gotoStmt(), switchStmt(), returnStmt(), + // Escaping and not known mutation of the loop counter is handled + // by exclusion of assigning and address-of operators and + // pass-by-ref function calls on the loop counter from the body. + changeIntBoundNode(equalsBoundNode(NodeName)), + callByRef(equalsBoundNode(NodeName)), + getAddrTo(equalsBoundNode(NodeName)), + assignedToRef(equalsBoundNode(NodeName))))); +} + +static internal::Matcher<Stmt> forLoopMatcher() { + return forStmt( + hasCondition(simpleCondition("initVarName")), + // Initialization should match the form: 'int i = 6' or 'i = 42'. + hasLoopInit( + anyOf(declStmt(hasSingleDecl( + varDecl(allOf(hasInitializer(ignoringParenImpCasts( + integerLiteral().bind("initNum"))), + equalsBoundNode("initVarName"))))), + binaryOperator(hasLHS(declRefExpr(to(varDecl( + equalsBoundNode("initVarName"))))), + hasRHS(ignoringParenImpCasts( + integerLiteral().bind("initNum")))))), + // Incrementation should be a simple increment or decrement + // operator call. + hasIncrement(unaryOperator( + anyOf(hasOperatorName("++"), hasOperatorName("--")), + hasUnaryOperand(declRefExpr( + to(varDecl(allOf(equalsBoundNode("initVarName"), + hasType(isInteger())))))))), + unless(hasBody(hasSuspiciousStmt("initVarName")))).bind("forLoop"); +} + +static bool isPossiblyEscaped(const VarDecl *VD, ExplodedNode *N) { + // Global variables assumed as escaped variables. + if (VD->hasGlobalStorage()) + return true; + + while (!N->pred_empty()) { + const Stmt *S = PathDiagnosticLocation::getStmt(N); + if (!S) { + N = N->getFirstPred(); + continue; + } + + if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) { + for (const Decl *D : DS->decls()) { + // Once we reach the declaration of the VD we can return. + if (D->getCanonicalDecl() == VD) + return false; + } + } + // Check the usage of the pass-by-ref function calls and adress-of operator + // on VD and reference initialized by VD. + ASTContext &ASTCtx = + N->getLocationContext()->getAnalysisDeclContext()->getASTContext(); + auto Match = + match(stmt(anyOf(callByRef(equalsNode(VD)), getAddrTo(equalsNode(VD)), + assignedToRef(equalsNode(VD)))), + *S, ASTCtx); + if (!Match.empty()) + return true; + + N = N->getFirstPred(); + } + llvm_unreachable("Reached root without finding the declaration of VD"); +} + +bool shouldCompletelyUnroll(const Stmt *LoopStmt, ASTContext &ASTCtx, + ExplodedNode *Pred, unsigned &maxStep) { + + if (!isLoopStmt(LoopStmt)) + return false; + + // TODO: Match the cases where the bound is not a concrete literal but an + // integer with known value + auto Matches = match(forLoopMatcher(), *LoopStmt, ASTCtx); + if (Matches.empty()) + return false; + + auto CounterVar = Matches[0].getNodeAs<VarDecl>("initVarName"); + llvm::APInt BoundNum = + Matches[0].getNodeAs<IntegerLiteral>("boundNum")->getValue(); + llvm::APInt InitNum = + Matches[0].getNodeAs<IntegerLiteral>("initNum")->getValue(); + auto CondOp = Matches[0].getNodeAs<BinaryOperator>("conditionOperator"); + if (InitNum.getBitWidth() != BoundNum.getBitWidth()) { + InitNum = InitNum.zextOrSelf(BoundNum.getBitWidth()); + BoundNum = BoundNum.zextOrSelf(InitNum.getBitWidth()); + } + + if (CondOp->getOpcode() == BO_GE || CondOp->getOpcode() == BO_LE) + maxStep = (BoundNum - InitNum + 1).abs().getZExtValue(); + else + maxStep = (BoundNum - InitNum).abs().getZExtValue(); + + // Check if the counter of the loop is not escaped before. + return !isPossiblyEscaped(CounterVar->getCanonicalDecl(), Pred); +} + +bool madeNewBranch(ExplodedNode *N, const Stmt *LoopStmt) { + const Stmt *S = nullptr; + while (!N->pred_empty()) { + if (N->succ_size() > 1) + return true; + + ProgramPoint P = N->getLocation(); + if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) + S = BE->getBlock()->getTerminator(); + + if (S == LoopStmt) + return false; + + N = N->getFirstPred(); + } + + llvm_unreachable("Reached root without encountering the previous step"); +} + +// updateLoopStack is called on every basic block, therefore it needs to be fast +ProgramStateRef updateLoopStack(const Stmt *LoopStmt, ASTContext &ASTCtx, + ExplodedNode *Pred, unsigned maxVisitOnPath) { + auto State = Pred->getState(); + auto LCtx = Pred->getLocationContext(); + + if (!isLoopStmt(LoopStmt)) + return State; + + auto LS = State->get<LoopStack>(); + if (!LS.isEmpty() && LoopStmt == LS.getHead().getLoopStmt() && + LCtx == LS.getHead().getLocationContext()) { + if (LS.getHead().isUnrolled() && madeNewBranch(Pred, LoopStmt)) { + State = State->set<LoopStack>(LS.getTail()); + State = State->add<LoopStack>( + LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath)); + } + return State; + } + unsigned maxStep; + if (!shouldCompletelyUnroll(LoopStmt, ASTCtx, Pred, maxStep)) { + State = State->add<LoopStack>( + LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath)); + return State; + } + + unsigned outerStep = (LS.isEmpty() ? 1 : LS.getHead().getMaxStep()); + + unsigned innerMaxStep = maxStep * outerStep; + if (innerMaxStep > MAXIMUM_STEP_UNROLLED) + State = State->add<LoopStack>( + LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath)); + else + State = State->add<LoopStack>( + LoopState::getUnrolled(LoopStmt, LCtx, innerMaxStep)); + return State; +} + +bool isUnrolledState(ProgramStateRef State) { + auto LS = State->get<LoopStack>(); + if (LS.isEmpty() || !LS.getHead().isUnrolled()) + return false; + return true; +} +} +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp new file mode 100644 index 000000000000..8f6cb9a6b09e --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp @@ -0,0 +1,100 @@ +//===--- 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. +// +//===----------------------------------------------------------------------===// +/// +/// This file contains functions which are used to widen loops. A loop may be +/// widened to approximate the exit state(s), without analyzing every +/// iteration. The widening is done by invalidating anything which might be +/// modified by the body of the loop. +/// +//===----------------------------------------------------------------------===// + +#include "clang/AST/AST.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h" + +using namespace clang; +using namespace ento; +using namespace clang::ast_matchers; + +const auto MatchRef = "matchref"; + +/// Return the loops condition Stmt or NULL if LoopStmt is not a loop +static const Expr *getLoopCondition(const Stmt *LoopStmt) { + switch (LoopStmt->getStmtClass()) { + default: + return nullptr; + case Stmt::ForStmtClass: + return cast<ForStmt>(LoopStmt)->getCond(); + case Stmt::WhileStmtClass: + return cast<WhileStmt>(LoopStmt)->getCond(); + case Stmt::DoStmtClass: + return cast<DoStmt>(LoopStmt)->getCond(); + } +} + +namespace clang { +namespace ento { + +ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState, + const LocationContext *LCtx, + unsigned BlockCount, const Stmt *LoopStmt) { + + assert(isa<ForStmt>(LoopStmt) || isa<WhileStmt>(LoopStmt) || + isa<DoStmt>(LoopStmt)); + + // Invalidate values in the current state. + // TODO Make this more conservative by only invalidating values that might + // be modified by the body of the loop. + // TODO Nested loops are currently widened as a result of the invalidation + // being so inprecise. When the invalidation is improved, the handling + // of nested loops will also need to be improved. + ASTContext &ASTCtx = LCtx->getAnalysisDeclContext()->getASTContext(); + const StackFrameContext *STC = LCtx->getStackFrame(); + MemRegionManager &MRMgr = PrevState->getStateManager().getRegionManager(); + const MemRegion *Regions[] = {MRMgr.getStackLocalsRegion(STC), + MRMgr.getStackArgumentsRegion(STC), + MRMgr.getGlobalsRegion()}; + RegionAndSymbolInvalidationTraits ITraits; + for (auto *Region : Regions) { + ITraits.setTrait(Region, + RegionAndSymbolInvalidationTraits::TK_EntireMemSpace); + } + + // References should not be invalidated. + auto Matches = match(findAll(stmt(hasDescendant(varDecl(hasType(referenceType())).bind(MatchRef)))), + *LCtx->getDecl()->getBody(), ASTCtx); + for (BoundNodes Match : Matches) { + const VarDecl *VD = Match.getNodeAs<VarDecl>(MatchRef); + assert(VD); + const VarRegion *VarMem = MRMgr.getVarRegion(VD, LCtx); + ITraits.setTrait(VarMem, + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + } + + + // 'this' pointer is not an lvalue, we should not invalidate it. If the loop + // is located in a method, constructor or destructor, the value of 'this' + // pointer should remain unchanged. Ignore static methods, since they do not + // have 'this' pointers. + const CXXMethodDecl *CXXMD = dyn_cast<CXXMethodDecl>(STC->getDecl()); + if (CXXMD && !CXXMD->isStatic()) { + const CXXThisRegion *ThisR = + MRMgr.getCXXThisRegion(CXXMD->getThisType(), STC); + ITraits.setTrait(ThisR, + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + } + + return PrevState->invalidateRegions(Regions, getLoopCondition(LoopStmt), + BlockCount, LCtx, true, nullptr, nullptr, + &ITraits); +} + +} // end namespace ento +} // end namespace clang diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp new file mode 100644 index 000000000000..9a1d4d73c20b --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -0,0 +1,1618 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines MemRegion and its subclasses. MemRegion defines a +// partially-typed abstraction of memory useful for path-sensitive dataflow +// analyses. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Attr.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/PrettyPrinter.h" +#include "clang/AST/RecordLayout.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/Support/BumpVector.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/APInt.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/PointerUnion.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Allocator.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/CheckedArithmetic.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cstdint> +#include <functional> +#include <iterator> +#include <string> +#include <tuple> +#include <utility> + +using namespace clang; +using namespace ento; + +#define DEBUG_TYPE "MemRegion" + +//===----------------------------------------------------------------------===// +// MemRegion Construction. +//===----------------------------------------------------------------------===// + +template <typename RegionTy, typename SuperTy, typename Arg1Ty> +RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, + const SuperTy *superRegion) { + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, arg1, superRegion); + void *InsertPos; + auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); + + if (!R) { + R = A.Allocate<RegionTy>(); + new (R) RegionTy(arg1, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template <typename RegionTy, typename SuperTy, typename Arg1Ty, typename Arg2Ty> +RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2, + const SuperTy *superRegion) { + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, arg1, arg2, superRegion); + void *InsertPos; + auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); + + if (!R) { + R = A.Allocate<RegionTy>(); + new (R) RegionTy(arg1, arg2, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template <typename RegionTy, typename SuperTy, + typename Arg1Ty, typename Arg2Ty, typename Arg3Ty> +RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2, + const Arg3Ty arg3, + const SuperTy *superRegion) { + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, arg1, arg2, arg3, superRegion); + void *InsertPos; + auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos)); + + if (!R) { + R = A.Allocate<RegionTy>(); + new (R) RegionTy(arg1, arg2, arg3, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +//===----------------------------------------------------------------------===// +// Object destruction. +//===----------------------------------------------------------------------===// + +MemRegion::~MemRegion() = default; + +// All regions and their data are BumpPtrAllocated. No need to call their +// destructors. +MemRegionManager::~MemRegionManager() = default; + +//===----------------------------------------------------------------------===// +// Basic methods. +//===----------------------------------------------------------------------===// + +bool SubRegion::isSubRegionOf(const MemRegion* R) const { + const MemRegion* r = this; + do { + if (r == R) + return true; + if (const auto *sr = dyn_cast<SubRegion>(r)) + r = sr->getSuperRegion(); + else + break; + } while (r != nullptr); + return false; +} + +MemRegionManager* SubRegion::getMemRegionManager() const { + const SubRegion* r = this; + do { + const MemRegion *superRegion = r->getSuperRegion(); + if (const auto *sr = dyn_cast<SubRegion>(superRegion)) { + r = sr; + continue; + } + return superRegion->getMemRegionManager(); + } while (true); +} + +const StackFrameContext *VarRegion::getStackFrame() const { + const auto *SSR = dyn_cast<StackSpaceRegion>(getMemorySpace()); + return SSR ? SSR->getStackFrame() : nullptr; +} + +//===----------------------------------------------------------------------===// +// Region extents. +//===----------------------------------------------------------------------===// + +DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const { + ASTContext &Ctx = svalBuilder.getContext(); + QualType T = getDesugaredValueType(Ctx); + + if (isa<VariableArrayType>(T)) + return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this)); + if (T->isIncompleteType()) + return UnknownVal(); + + CharUnits size = Ctx.getTypeSizeInChars(T); + QualType sizeTy = svalBuilder.getArrayIndexType(); + return svalBuilder.makeIntVal(size.getQuantity(), sizeTy); +} + +DefinedOrUnknownSVal FieldRegion::getExtent(SValBuilder &svalBuilder) const { + // Force callers to deal with bitfields explicitly. + if (getDecl()->isBitField()) + return UnknownVal(); + + DefinedOrUnknownSVal Extent = DeclRegion::getExtent(svalBuilder); + + // A zero-length array at the end of a struct often stands for dynamically- + // allocated extra memory. + if (Extent.isZeroConstant()) { + QualType T = getDesugaredValueType(svalBuilder.getContext()); + + if (isa<ConstantArrayType>(T)) + return UnknownVal(); + } + + return Extent; +} + +DefinedOrUnknownSVal AllocaRegion::getExtent(SValBuilder &svalBuilder) const { + return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this)); +} + +DefinedOrUnknownSVal SymbolicRegion::getExtent(SValBuilder &svalBuilder) const { + return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this)); +} + +DefinedOrUnknownSVal StringRegion::getExtent(SValBuilder &svalBuilder) const { + return svalBuilder.makeIntVal(getStringLiteral()->getByteLength()+1, + svalBuilder.getArrayIndexType()); +} + +ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg) + : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {} + +const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { + return cast<ObjCIvarDecl>(D); +} + +QualType ObjCIvarRegion::getValueType() const { + return getDecl()->getType(); +} + +QualType CXXBaseObjectRegion::getValueType() const { + return QualType(getDecl()->getTypeForDecl(), 0); +} + +QualType CXXDerivedObjectRegion::getValueType() const { + return QualType(getDecl()->getTypeForDecl(), 0); +} + +//===----------------------------------------------------------------------===// +// FoldingSet profiling. +//===----------------------------------------------------------------------===// + +void MemSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(static_cast<unsigned>(getKind())); +} + +void StackSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(static_cast<unsigned>(getKind())); + ID.AddPointer(getStackFrame()); +} + +void StaticGlobalSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(static_cast<unsigned>(getKind())); + ID.AddPointer(getCodeRegion()); +} + +void StringRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + const StringLiteral *Str, + const MemRegion *superRegion) { + ID.AddInteger(static_cast<unsigned>(StringRegionKind)); + ID.AddPointer(Str); + ID.AddPointer(superRegion); +} + +void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + const ObjCStringLiteral *Str, + const MemRegion *superRegion) { + ID.AddInteger(static_cast<unsigned>(ObjCStringRegionKind)); + ID.AddPointer(Str); + ID.AddPointer(superRegion); +} + +void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const Expr *Ex, unsigned cnt, + const MemRegion *superRegion) { + ID.AddInteger(static_cast<unsigned>(AllocaRegionKind)); + ID.AddPointer(Ex); + ID.AddInteger(cnt); + ID.AddPointer(superRegion); +} + +void AllocaRegion::Profile(llvm::FoldingSetNodeID& ID) const { + ProfileRegion(ID, Ex, Cnt, superRegion); +} + +void CompoundLiteralRegion::Profile(llvm::FoldingSetNodeID& ID) const { + CompoundLiteralRegion::ProfileRegion(ID, CL, superRegion); +} + +void CompoundLiteralRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const CompoundLiteralExpr *CL, + const MemRegion* superRegion) { + ID.AddInteger(static_cast<unsigned>(CompoundLiteralRegionKind)); + ID.AddPointer(CL); + ID.AddPointer(superRegion); +} + +void CXXThisRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + const PointerType *PT, + const MemRegion *sRegion) { + ID.AddInteger(static_cast<unsigned>(CXXThisRegionKind)); + ID.AddPointer(PT); + ID.AddPointer(sRegion); +} + +void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const { + CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion); +} + +void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const ObjCIvarDecl *ivd, + const MemRegion* superRegion) { + DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind); +} + +void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D, + const MemRegion* superRegion, Kind k) { + ID.AddInteger(static_cast<unsigned>(k)); + ID.AddPointer(D); + ID.AddPointer(superRegion); +} + +void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const { + DeclRegion::ProfileRegion(ID, D, superRegion, getKind()); +} + +void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const { + VarRegion::ProfileRegion(ID, getDecl(), superRegion); +} + +void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym, + const MemRegion *sreg) { + ID.AddInteger(static_cast<unsigned>(MemRegion::SymbolicRegionKind)); + ID.Add(sym); + ID.AddPointer(sreg); +} + +void SymbolicRegion::Profile(llvm::FoldingSetNodeID& ID) const { + SymbolicRegion::ProfileRegion(ID, sym, getSuperRegion()); +} + +void ElementRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + QualType ElementType, SVal Idx, + const MemRegion* superRegion) { + ID.AddInteger(MemRegion::ElementRegionKind); + ID.Add(ElementType); + ID.AddPointer(superRegion); + Idx.Profile(ID); +} + +void ElementRegion::Profile(llvm::FoldingSetNodeID& ID) const { + ElementRegion::ProfileRegion(ID, ElementType, Index, superRegion); +} + +void FunctionCodeRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const NamedDecl *FD, + const MemRegion*) { + ID.AddInteger(MemRegion::FunctionCodeRegionKind); + ID.AddPointer(FD); +} + +void FunctionCodeRegion::Profile(llvm::FoldingSetNodeID& ID) const { + FunctionCodeRegion::ProfileRegion(ID, FD, superRegion); +} + +void BlockCodeRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const BlockDecl *BD, CanQualType, + const AnalysisDeclContext *AC, + const MemRegion*) { + ID.AddInteger(MemRegion::BlockCodeRegionKind); + ID.AddPointer(BD); +} + +void BlockCodeRegion::Profile(llvm::FoldingSetNodeID& ID) const { + BlockCodeRegion::ProfileRegion(ID, BD, locTy, AC, superRegion); +} + +void BlockDataRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const BlockCodeRegion *BC, + const LocationContext *LC, + unsigned BlkCount, + const MemRegion *sReg) { + ID.AddInteger(MemRegion::BlockDataRegionKind); + ID.AddPointer(BC); + ID.AddPointer(LC); + ID.AddInteger(BlkCount); + ID.AddPointer(sReg); +} + +void BlockDataRegion::Profile(llvm::FoldingSetNodeID& ID) const { + BlockDataRegion::ProfileRegion(ID, BC, LC, BlockCount, getSuperRegion()); +} + +void CXXTempObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + Expr const *Ex, + const MemRegion *sReg) { + ID.AddPointer(Ex); + ID.AddPointer(sReg); +} + +void CXXTempObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ProfileRegion(ID, Ex, getSuperRegion()); +} + +void CXXBaseObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + const CXXRecordDecl *RD, + bool IsVirtual, + const MemRegion *SReg) { + ID.AddPointer(RD); + ID.AddBoolean(IsVirtual); + ID.AddPointer(SReg); +} + +void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ProfileRegion(ID, getDecl(), isVirtual(), superRegion); +} + +void CXXDerivedObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + const CXXRecordDecl *RD, + const MemRegion *SReg) { + ID.AddPointer(RD); + ID.AddPointer(SReg); +} + +void CXXDerivedObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ProfileRegion(ID, getDecl(), superRegion); +} + +//===----------------------------------------------------------------------===// +// Region anchors. +//===----------------------------------------------------------------------===// + +void GlobalsSpaceRegion::anchor() {} + +void NonStaticGlobalSpaceRegion::anchor() {} + +void StackSpaceRegion::anchor() {} + +void TypedRegion::anchor() {} + +void TypedValueRegion::anchor() {} + +void CodeTextRegion::anchor() {} + +void SubRegion::anchor() {} + +//===----------------------------------------------------------------------===// +// Region pretty-printing. +//===----------------------------------------------------------------------===// + +LLVM_DUMP_METHOD void MemRegion::dump() const { + dumpToStream(llvm::errs()); +} + +std::string MemRegion::getString() const { + std::string s; + llvm::raw_string_ostream os(s); + dumpToStream(os); + return os.str(); +} + +void MemRegion::dumpToStream(raw_ostream &os) const { + os << "<Unknown Region>"; +} + +void AllocaRegion::dumpToStream(raw_ostream &os) const { + os << "alloca{S" << Ex->getID(getContext()) << ',' << Cnt << '}'; +} + +void FunctionCodeRegion::dumpToStream(raw_ostream &os) const { + os << "code{" << getDecl()->getDeclName().getAsString() << '}'; +} + +void BlockCodeRegion::dumpToStream(raw_ostream &os) const { + os << "block_code{" << static_cast<const void *>(this) << '}'; +} + +void BlockDataRegion::dumpToStream(raw_ostream &os) const { + os << "block_data{" << BC; + os << "; "; + for (BlockDataRegion::referenced_vars_iterator + I = referenced_vars_begin(), + E = referenced_vars_end(); I != E; ++I) + os << "(" << I.getCapturedRegion() << "<-" << + I.getOriginalRegion() << ") "; + os << '}'; +} + +void CompoundLiteralRegion::dumpToStream(raw_ostream &os) const { + // FIXME: More elaborate pretty-printing. + os << "{ S" << CL->getID(getContext()) << " }"; +} + +void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const { + os << "temp_object{" << getValueType().getAsString() << ", " + << "S" << Ex->getID(getContext()) << '}'; +} + +void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const { + os << "Base{" << superRegion << ',' << getDecl()->getName() << '}'; +} + +void CXXDerivedObjectRegion::dumpToStream(raw_ostream &os) const { + os << "Derived{" << superRegion << ',' << getDecl()->getName() << '}'; +} + +void CXXThisRegion::dumpToStream(raw_ostream &os) const { + os << "this"; +} + +void ElementRegion::dumpToStream(raw_ostream &os) const { + os << "Element{" << superRegion << ',' + << Index << ',' << getElementType().getAsString() << '}'; +} + +void FieldRegion::dumpToStream(raw_ostream &os) const { + os << superRegion << "->" << *getDecl(); +} + +void ObjCIvarRegion::dumpToStream(raw_ostream &os) const { + os << "Ivar{" << superRegion << ',' << *getDecl() << '}'; +} + +void StringRegion::dumpToStream(raw_ostream &os) const { + assert(Str != nullptr && "Expecting non-null StringLiteral"); + Str->printPretty(os, nullptr, PrintingPolicy(getContext().getLangOpts())); +} + +void ObjCStringRegion::dumpToStream(raw_ostream &os) const { + assert(Str != nullptr && "Expecting non-null ObjCStringLiteral"); + Str->printPretty(os, nullptr, PrintingPolicy(getContext().getLangOpts())); +} + +void SymbolicRegion::dumpToStream(raw_ostream &os) const { + if (isa<HeapSpaceRegion>(getSuperRegion())) + os << "Heap"; + os << "SymRegion{" << sym << '}'; +} + +void VarRegion::dumpToStream(raw_ostream &os) const { + const auto *VD = cast<VarDecl>(D); + if (const IdentifierInfo *ID = VD->getIdentifier()) + os << ID->getName(); + else + os << "VarRegion{D" << VD->getID() << '}'; +} + +LLVM_DUMP_METHOD void RegionRawOffset::dump() const { + dumpToStream(llvm::errs()); +} + +void RegionRawOffset::dumpToStream(raw_ostream &os) const { + os << "raw_offset{" << getRegion() << ',' << getOffset().getQuantity() << '}'; +} + +void CodeSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "CodeSpaceRegion"; +} + +void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "StaticGlobalsMemSpace{" << CR << '}'; +} + +void GlobalInternalSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "GlobalInternalSpaceRegion"; +} + +void GlobalSystemSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "GlobalSystemSpaceRegion"; +} + +void GlobalImmutableSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "GlobalImmutableSpaceRegion"; +} + +void HeapSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "HeapSpaceRegion"; +} + +void UnknownSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "UnknownSpaceRegion"; +} + +void StackArgumentsSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "StackArgumentsSpaceRegion"; +} + +void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const { + os << "StackLocalsSpaceRegion"; +} + +bool MemRegion::canPrintPretty() const { + return canPrintPrettyAsExpr(); +} + +bool MemRegion::canPrintPrettyAsExpr() const { + return false; +} + +void MemRegion::printPretty(raw_ostream &os) const { + assert(canPrintPretty() && "This region cannot be printed pretty."); + os << "'"; + printPrettyAsExpr(os); + os << "'"; +} + +void MemRegion::printPrettyAsExpr(raw_ostream &) const { + llvm_unreachable("This region cannot be printed pretty."); +} + +bool VarRegion::canPrintPrettyAsExpr() const { + return true; +} + +void VarRegion::printPrettyAsExpr(raw_ostream &os) const { + os << getDecl()->getName(); +} + +bool ObjCIvarRegion::canPrintPrettyAsExpr() const { + return true; +} + +void ObjCIvarRegion::printPrettyAsExpr(raw_ostream &os) const { + os << getDecl()->getName(); +} + +bool FieldRegion::canPrintPretty() const { + return true; +} + +bool FieldRegion::canPrintPrettyAsExpr() const { + return superRegion->canPrintPrettyAsExpr(); +} + +void FieldRegion::printPrettyAsExpr(raw_ostream &os) const { + assert(canPrintPrettyAsExpr()); + superRegion->printPrettyAsExpr(os); + os << "." << getDecl()->getName(); +} + +void FieldRegion::printPretty(raw_ostream &os) const { + if (canPrintPrettyAsExpr()) { + os << "\'"; + printPrettyAsExpr(os); + os << "'"; + } else { + os << "field " << "\'" << getDecl()->getName() << "'"; + } +} + +bool CXXBaseObjectRegion::canPrintPrettyAsExpr() const { + return superRegion->canPrintPrettyAsExpr(); +} + +void CXXBaseObjectRegion::printPrettyAsExpr(raw_ostream &os) const { + superRegion->printPrettyAsExpr(os); +} + +bool CXXDerivedObjectRegion::canPrintPrettyAsExpr() const { + return superRegion->canPrintPrettyAsExpr(); +} + +void CXXDerivedObjectRegion::printPrettyAsExpr(raw_ostream &os) const { + superRegion->printPrettyAsExpr(os); +} + +std::string MemRegion::getDescriptiveName(bool UseQuotes) const { + std::string VariableName; + std::string ArrayIndices; + const MemRegion *R = this; + SmallString<50> buf; + llvm::raw_svector_ostream os(buf); + + // Obtain array indices to add them to the variable name. + const ElementRegion *ER = nullptr; + while ((ER = R->getAs<ElementRegion>())) { + // Index is a ConcreteInt. + if (auto CI = ER->getIndex().getAs<nonloc::ConcreteInt>()) { + llvm::SmallString<2> Idx; + CI->getValue().toString(Idx); + ArrayIndices = (llvm::Twine("[") + Idx.str() + "]" + ArrayIndices).str(); + } + // If not a ConcreteInt, try to obtain the variable + // name by calling 'getDescriptiveName' recursively. + else { + std::string Idx = ER->getDescriptiveName(false); + if (!Idx.empty()) { + ArrayIndices = (llvm::Twine("[") + Idx + "]" + ArrayIndices).str(); + } + } + R = ER->getSuperRegion(); + } + + // Get variable name. + if (R && R->canPrintPrettyAsExpr()) { + R->printPrettyAsExpr(os); + if (UseQuotes) + return (llvm::Twine("'") + os.str() + ArrayIndices + "'").str(); + else + return (llvm::Twine(os.str()) + ArrayIndices).str(); + } + + return VariableName; +} + +SourceRange MemRegion::sourceRange() const { + const auto *const VR = dyn_cast<VarRegion>(this->getBaseRegion()); + const auto *const FR = dyn_cast<FieldRegion>(this); + + // Check for more specific regions first. + // FieldRegion + if (FR) { + return FR->getDecl()->getSourceRange(); + } + // VarRegion + else if (VR) { + return VR->getDecl()->getSourceRange(); + } + // Return invalid source range (can be checked by client). + else + return {}; +} + +//===----------------------------------------------------------------------===// +// MemRegionManager methods. +//===----------------------------------------------------------------------===// + +template <typename REG> +const REG *MemRegionManager::LazyAllocate(REG*& region) { + if (!region) { + region = A.Allocate<REG>(); + new (region) REG(this); + } + + return region; +} + +template <typename REG, typename ARG> +const REG *MemRegionManager::LazyAllocate(REG*& region, ARG a) { + if (!region) { + region = A.Allocate<REG>(); + new (region) REG(this, a); + } + + return region; +} + +const StackLocalsSpaceRegion* +MemRegionManager::getStackLocalsRegion(const StackFrameContext *STC) { + assert(STC); + StackLocalsSpaceRegion *&R = StackLocalsSpaceRegions[STC]; + + if (R) + return R; + + R = A.Allocate<StackLocalsSpaceRegion>(); + new (R) StackLocalsSpaceRegion(this, STC); + return R; +} + +const StackArgumentsSpaceRegion * +MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) { + assert(STC); + StackArgumentsSpaceRegion *&R = StackArgumentsSpaceRegions[STC]; + + if (R) + return R; + + R = A.Allocate<StackArgumentsSpaceRegion>(); + new (R) StackArgumentsSpaceRegion(this, STC); + return R; +} + +const GlobalsSpaceRegion +*MemRegionManager::getGlobalsRegion(MemRegion::Kind K, + const CodeTextRegion *CR) { + if (!CR) { + if (K == MemRegion::GlobalSystemSpaceRegionKind) + return LazyAllocate(SystemGlobals); + if (K == MemRegion::GlobalImmutableSpaceRegionKind) + return LazyAllocate(ImmutableGlobals); + assert(K == MemRegion::GlobalInternalSpaceRegionKind); + return LazyAllocate(InternalGlobals); + } + + assert(K == MemRegion::StaticGlobalSpaceRegionKind); + StaticGlobalSpaceRegion *&R = StaticsGlobalSpaceRegions[CR]; + if (R) + return R; + + R = A.Allocate<StaticGlobalSpaceRegion>(); + new (R) StaticGlobalSpaceRegion(this, CR); + return R; +} + +const HeapSpaceRegion *MemRegionManager::getHeapRegion() { + return LazyAllocate(heap); +} + +const UnknownSpaceRegion *MemRegionManager::getUnknownRegion() { + return LazyAllocate(unknown); +} + +const CodeSpaceRegion *MemRegionManager::getCodeRegion() { + return LazyAllocate(code); +} + +//===----------------------------------------------------------------------===// +// Constructing regions. +//===----------------------------------------------------------------------===// + +const StringRegion *MemRegionManager::getStringRegion(const StringLiteral *Str){ + return getSubRegion<StringRegion>( + Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion())); +} + +const ObjCStringRegion * +MemRegionManager::getObjCStringRegion(const ObjCStringLiteral *Str){ + return getSubRegion<ObjCStringRegion>( + Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion())); +} + +/// Look through a chain of LocationContexts to either find the +/// StackFrameContext that matches a DeclContext, or find a VarRegion +/// for a variable captured by a block. +static llvm::PointerUnion<const StackFrameContext *, const VarRegion *> +getStackOrCaptureRegionForDeclContext(const LocationContext *LC, + const DeclContext *DC, + const VarDecl *VD) { + while (LC) { + if (const auto *SFC = dyn_cast<StackFrameContext>(LC)) { + if (cast<DeclContext>(SFC->getDecl()) == DC) + return SFC; + } + if (const auto *BC = dyn_cast<BlockInvocationContext>(LC)) { + const auto *BR = + static_cast<const BlockDataRegion *>(BC->getContextData()); + // FIXME: This can be made more efficient. + for (BlockDataRegion::referenced_vars_iterator + I = BR->referenced_vars_begin(), + E = BR->referenced_vars_end(); I != E; ++I) { + const VarRegion *VR = I.getOriginalRegion(); + if (VR->getDecl() == VD) + return cast<VarRegion>(I.getCapturedRegion()); + } + } + + LC = LC->getParent(); + } + return (const StackFrameContext *)nullptr; +} + +const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, + const LocationContext *LC) { + const MemRegion *sReg = nullptr; + + if (D->hasGlobalStorage() && !D->isStaticLocal()) { + + // First handle the globals defined in system headers. + if (C.getSourceManager().isInSystemHeader(D->getLocation())) { + // Whitelist the system globals which often DO GET modified, assume the + // rest are immutable. + if (D->getName().find("errno") != StringRef::npos) + sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind); + else + sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); + + // Treat other globals as GlobalInternal unless they are constants. + } else { + QualType GQT = D->getType(); + const Type *GT = GQT.getTypePtrOrNull(); + // TODO: We could walk the complex types here and see if everything is + // constified. + if (GT && GQT.isConstQualified() && GT->isArithmeticType()) + sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); + else + sReg = getGlobalsRegion(); + } + + // Finally handle static locals. + } else { + // FIXME: Once we implement scope handling, we will need to properly lookup + // 'D' to the proper LocationContext. + const DeclContext *DC = D->getDeclContext(); + llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V = + getStackOrCaptureRegionForDeclContext(LC, DC, D); + + if (V.is<const VarRegion*>()) + return V.get<const VarRegion*>(); + + const auto *STC = V.get<const StackFrameContext *>(); + + if (!STC) { + // FIXME: Assign a more sensible memory space to static locals + // we see from within blocks that we analyze as top-level declarations. + sReg = getUnknownRegion(); + } else { + if (D->hasLocalStorage()) { + sReg = isa<ParmVarDecl>(D) || isa<ImplicitParamDecl>(D) + ? static_cast<const MemRegion*>(getStackArgumentsRegion(STC)) + : static_cast<const MemRegion*>(getStackLocalsRegion(STC)); + } + else { + assert(D->isStaticLocal()); + const Decl *STCD = STC->getDecl(); + if (isa<FunctionDecl>(STCD) || isa<ObjCMethodDecl>(STCD)) + sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, + getFunctionCodeRegion(cast<NamedDecl>(STCD))); + else if (const auto *BD = dyn_cast<BlockDecl>(STCD)) { + // FIXME: The fallback type here is totally bogus -- though it should + // never be queried, it will prevent uniquing with the real + // BlockCodeRegion. Ideally we'd fix the AST so that we always had a + // signature. + QualType T; + if (const TypeSourceInfo *TSI = BD->getSignatureAsWritten()) + T = TSI->getType(); + if (T.isNull()) + T = getContext().VoidTy; + if (!T->getAs<FunctionType>()) + T = getContext().getFunctionNoProtoType(T); + T = getContext().getBlockPointerType(T); + + const BlockCodeRegion *BTR = + getBlockCodeRegion(BD, C.getCanonicalType(T), + STC->getAnalysisDeclContext()); + sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind, + BTR); + } + else { + sReg = getGlobalsRegion(); + } + } + } + } + + return getSubRegion<VarRegion>(D, sReg); +} + +const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, + const MemRegion *superR) { + return getSubRegion<VarRegion>(D, superR); +} + +const BlockDataRegion * +MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC, + const LocationContext *LC, + unsigned blockCount) { + const MemSpaceRegion *sReg = nullptr; + const BlockDecl *BD = BC->getDecl(); + if (!BD->hasCaptures()) { + // This handles 'static' blocks. + sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind); + } + else { + if (LC) { + // FIXME: Once we implement scope handling, we want the parent region + // to be the scope. + const StackFrameContext *STC = LC->getStackFrame(); + assert(STC); + sReg = getStackLocalsRegion(STC); + } + else { + // We allow 'LC' to be NULL for cases where want BlockDataRegions + // without context-sensitivity. + sReg = getUnknownRegion(); + } + } + + return getSubRegion<BlockDataRegion>(BC, LC, blockCount, sReg); +} + +const CXXTempObjectRegion * +MemRegionManager::getCXXStaticTempObjectRegion(const Expr *Ex) { + return getSubRegion<CXXTempObjectRegion>( + Ex, getGlobalsRegion(MemRegion::GlobalInternalSpaceRegionKind, nullptr)); +} + +const CompoundLiteralRegion* +MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL, + const LocationContext *LC) { + const MemSpaceRegion *sReg = nullptr; + + if (CL->isFileScope()) + sReg = getGlobalsRegion(); + else { + const StackFrameContext *STC = LC->getStackFrame(); + assert(STC); + sReg = getStackLocalsRegion(STC); + } + + return getSubRegion<CompoundLiteralRegion>(CL, sReg); +} + +const ElementRegion* +MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, + const SubRegion* superRegion, + ASTContext &Ctx){ + QualType T = Ctx.getCanonicalType(elementType).getUnqualifiedType(); + + llvm::FoldingSetNodeID ID; + ElementRegion::ProfileRegion(ID, T, Idx, superRegion); + + void *InsertPos; + MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos); + auto *R = cast_or_null<ElementRegion>(data); + + if (!R) { + R = A.Allocate<ElementRegion>(); + new (R) ElementRegion(T, Idx, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +const FunctionCodeRegion * +MemRegionManager::getFunctionCodeRegion(const NamedDecl *FD) { + return getSubRegion<FunctionCodeRegion>(FD, getCodeRegion()); +} + +const BlockCodeRegion * +MemRegionManager::getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy, + AnalysisDeclContext *AC) { + return getSubRegion<BlockCodeRegion>(BD, locTy, AC, getCodeRegion()); +} + +/// getSymbolicRegion - Retrieve or create a "symbolic" memory region. +const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { + return getSubRegion<SymbolicRegion>(sym, getUnknownRegion()); +} + +const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) { + return getSubRegion<SymbolicRegion>(Sym, getHeapRegion()); +} + +const FieldRegion* +MemRegionManager::getFieldRegion(const FieldDecl *d, + const SubRegion* superRegion){ + return getSubRegion<FieldRegion>(d, superRegion); +} + +const ObjCIvarRegion* +MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl *d, + const SubRegion* superRegion) { + return getSubRegion<ObjCIvarRegion>(d, superRegion); +} + +const CXXTempObjectRegion* +MemRegionManager::getCXXTempObjectRegion(Expr const *E, + LocationContext const *LC) { + const StackFrameContext *SFC = LC->getStackFrame(); + assert(SFC); + return getSubRegion<CXXTempObjectRegion>(E, getStackLocalsRegion(SFC)); +} + +/// Checks whether \p BaseClass is a valid virtual or direct non-virtual base +/// class of the type of \p Super. +static bool isValidBaseClass(const CXXRecordDecl *BaseClass, + const TypedValueRegion *Super, + bool IsVirtual) { + BaseClass = BaseClass->getCanonicalDecl(); + + const CXXRecordDecl *Class = Super->getValueType()->getAsCXXRecordDecl(); + if (!Class) + return true; + + if (IsVirtual) + return Class->isVirtuallyDerivedFrom(BaseClass); + + for (const auto &I : Class->bases()) { + if (I.getType()->getAsCXXRecordDecl()->getCanonicalDecl() == BaseClass) + return true; + } + + return false; +} + +const CXXBaseObjectRegion * +MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD, + const SubRegion *Super, + bool IsVirtual) { + if (isa<TypedValueRegion>(Super)) { + assert(isValidBaseClass(RD, dyn_cast<TypedValueRegion>(Super), IsVirtual)); + (void)&isValidBaseClass; + + if (IsVirtual) { + // Virtual base regions should not be layered, since the layout rules + // are different. + while (const auto *Base = dyn_cast<CXXBaseObjectRegion>(Super)) + Super = cast<SubRegion>(Base->getSuperRegion()); + assert(Super && !isa<MemSpaceRegion>(Super)); + } + } + + return getSubRegion<CXXBaseObjectRegion>(RD, IsVirtual, Super); +} + +const CXXDerivedObjectRegion * +MemRegionManager::getCXXDerivedObjectRegion(const CXXRecordDecl *RD, + const SubRegion *Super) { + return getSubRegion<CXXDerivedObjectRegion>(RD, Super); +} + +const CXXThisRegion* +MemRegionManager::getCXXThisRegion(QualType thisPointerTy, + const LocationContext *LC) { + const auto *PT = thisPointerTy->getAs<PointerType>(); + assert(PT); + // Inside the body of the operator() of a lambda a this expr might refer to an + // object in one of the parent location contexts. + const auto *D = dyn_cast<CXXMethodDecl>(LC->getDecl()); + // FIXME: when operator() of lambda is analyzed as a top level function and + // 'this' refers to a this to the enclosing scope, there is no right region to + // return. + while (!LC->inTopFrame() && (!D || D->isStatic() || + PT != D->getThisType()->getAs<PointerType>())) { + LC = LC->getParent(); + D = dyn_cast<CXXMethodDecl>(LC->getDecl()); + } + const StackFrameContext *STC = LC->getStackFrame(); + assert(STC); + return getSubRegion<CXXThisRegion>(PT, getStackArgumentsRegion(STC)); +} + +const AllocaRegion* +MemRegionManager::getAllocaRegion(const Expr *E, unsigned cnt, + const LocationContext *LC) { + const StackFrameContext *STC = LC->getStackFrame(); + assert(STC); + return getSubRegion<AllocaRegion>(E, cnt, getStackLocalsRegion(STC)); +} + +const MemSpaceRegion *MemRegion::getMemorySpace() const { + const MemRegion *R = this; + const auto *SR = dyn_cast<SubRegion>(this); + + while (SR) { + R = SR->getSuperRegion(); + SR = dyn_cast<SubRegion>(R); + } + + return dyn_cast<MemSpaceRegion>(R); +} + +bool MemRegion::hasStackStorage() const { + return isa<StackSpaceRegion>(getMemorySpace()); +} + +bool MemRegion::hasStackNonParametersStorage() const { + return isa<StackLocalsSpaceRegion>(getMemorySpace()); +} + +bool MemRegion::hasStackParametersStorage() const { + return isa<StackArgumentsSpaceRegion>(getMemorySpace()); +} + +bool MemRegion::hasGlobalsOrParametersStorage() const { + const MemSpaceRegion *MS = getMemorySpace(); + return isa<StackArgumentsSpaceRegion>(MS) || + isa<GlobalsSpaceRegion>(MS); +} + +// getBaseRegion strips away all elements and fields, and get the base region +// of them. +const MemRegion *MemRegion::getBaseRegion() const { + const MemRegion *R = this; + while (true) { + switch (R->getKind()) { + case MemRegion::ElementRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + case MemRegion::CXXBaseObjectRegionKind: + case MemRegion::CXXDerivedObjectRegionKind: + R = cast<SubRegion>(R)->getSuperRegion(); + continue; + default: + break; + } + break; + } + return R; +} + +// getgetMostDerivedObjectRegion gets the region of the root class of a C++ +// class hierarchy. +const MemRegion *MemRegion::getMostDerivedObjectRegion() const { + const MemRegion *R = this; + while (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R)) + R = BR->getSuperRegion(); + return R; +} + +bool MemRegion::isSubRegionOf(const MemRegion *) const { + return false; +} + +//===----------------------------------------------------------------------===// +// View handling. +//===----------------------------------------------------------------------===// + +const MemRegion *MemRegion::StripCasts(bool StripBaseAndDerivedCasts) const { + const MemRegion *R = this; + while (true) { + switch (R->getKind()) { + case ElementRegionKind: { + const auto *ER = cast<ElementRegion>(R); + if (!ER->getIndex().isZeroConstant()) + return R; + R = ER->getSuperRegion(); + break; + } + case CXXBaseObjectRegionKind: + case CXXDerivedObjectRegionKind: + if (!StripBaseAndDerivedCasts) + return R; + R = cast<TypedValueRegion>(R)->getSuperRegion(); + break; + default: + return R; + } + } +} + +const SymbolicRegion *MemRegion::getSymbolicBase() const { + const auto *SubR = dyn_cast<SubRegion>(this); + + while (SubR) { + if (const auto *SymR = dyn_cast<SymbolicRegion>(SubR)) + return SymR; + SubR = dyn_cast<SubRegion>(SubR->getSuperRegion()); + } + return nullptr; +} + +RegionRawOffset ElementRegion::getAsArrayOffset() const { + int64_t offset = 0; + const ElementRegion *ER = this; + const MemRegion *superR = nullptr; + ASTContext &C = getContext(); + + // FIXME: Handle multi-dimensional arrays. + + while (ER) { + superR = ER->getSuperRegion(); + + // FIXME: generalize to symbolic offsets. + SVal index = ER->getIndex(); + if (auto CI = index.getAs<nonloc::ConcreteInt>()) { + // Update the offset. + int64_t i = CI->getValue().getSExtValue(); + + if (i != 0) { + QualType elemType = ER->getElementType(); + + // If we are pointing to an incomplete type, go no further. + if (elemType->isIncompleteType()) { + superR = ER; + break; + } + + int64_t size = C.getTypeSizeInChars(elemType).getQuantity(); + if (auto NewOffset = llvm::checkedMulAdd(i, size, offset)) { + offset = *NewOffset; + } else { + LLVM_DEBUG(llvm::dbgs() << "MemRegion::getAsArrayOffset: " + << "offset overflowing, returning unknown\n"); + + return nullptr; + } + } + + // Go to the next ElementRegion (if any). + ER = dyn_cast<ElementRegion>(superR); + continue; + } + + return nullptr; + } + + assert(superR && "super region cannot be NULL"); + return RegionRawOffset(superR, CharUnits::fromQuantity(offset)); +} + +/// Returns true if \p Base is an immediate base class of \p Child +static bool isImmediateBase(const CXXRecordDecl *Child, + const CXXRecordDecl *Base) { + assert(Child && "Child must not be null"); + // Note that we do NOT canonicalize the base class here, because + // ASTRecordLayout doesn't either. If that leads us down the wrong path, + // so be it; at least we won't crash. + for (const auto &I : Child->bases()) { + if (I.getType()->getAsCXXRecordDecl() == Base) + return true; + } + + return false; +} + +static RegionOffset calculateOffset(const MemRegion *R) { + const MemRegion *SymbolicOffsetBase = nullptr; + int64_t Offset = 0; + + while (true) { + switch (R->getKind()) { + case MemRegion::CodeSpaceRegionKind: + case MemRegion::StackLocalsSpaceRegionKind: + case MemRegion::StackArgumentsSpaceRegionKind: + case MemRegion::HeapSpaceRegionKind: + case MemRegion::UnknownSpaceRegionKind: + case MemRegion::StaticGlobalSpaceRegionKind: + case MemRegion::GlobalInternalSpaceRegionKind: + case MemRegion::GlobalSystemSpaceRegionKind: + case MemRegion::GlobalImmutableSpaceRegionKind: + // Stores can bind directly to a region space to set a default value. + assert(Offset == 0 && !SymbolicOffsetBase); + goto Finish; + + case MemRegion::FunctionCodeRegionKind: + case MemRegion::BlockCodeRegionKind: + case MemRegion::BlockDataRegionKind: + // These will never have bindings, but may end up having values requested + // if the user does some strange casting. + if (Offset != 0) + SymbolicOffsetBase = R; + goto Finish; + + case MemRegion::SymbolicRegionKind: + case MemRegion::AllocaRegionKind: + case MemRegion::CompoundLiteralRegionKind: + case MemRegion::CXXThisRegionKind: + case MemRegion::StringRegionKind: + case MemRegion::ObjCStringRegionKind: + case MemRegion::VarRegionKind: + case MemRegion::CXXTempObjectRegionKind: + // Usual base regions. + goto Finish; + + case MemRegion::ObjCIvarRegionKind: + // This is a little strange, but it's a compromise between + // ObjCIvarRegions having unknown compile-time offsets (when using the + // non-fragile runtime) and yet still being distinct, non-overlapping + // regions. Thus we treat them as "like" base regions for the purposes + // of computing offsets. + goto Finish; + + case MemRegion::CXXBaseObjectRegionKind: { + const auto *BOR = cast<CXXBaseObjectRegion>(R); + R = BOR->getSuperRegion(); + + QualType Ty; + bool RootIsSymbolic = false; + if (const auto *TVR = dyn_cast<TypedValueRegion>(R)) { + Ty = TVR->getDesugaredValueType(R->getContext()); + } else if (const auto *SR = dyn_cast<SymbolicRegion>(R)) { + // If our base region is symbolic, we don't know what type it really is. + // Pretend the type of the symbol is the true dynamic type. + // (This will at least be self-consistent for the life of the symbol.) + Ty = SR->getSymbol()->getType()->getPointeeType(); + RootIsSymbolic = true; + } + + const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl(); + if (!Child) { + // We cannot compute the offset of the base class. + SymbolicOffsetBase = R; + } else { + if (RootIsSymbolic) { + // Base layers on symbolic regions may not be type-correct. + // Double-check the inheritance here, and revert to a symbolic offset + // if it's invalid (e.g. due to a reinterpret_cast). + if (BOR->isVirtual()) { + if (!Child->isVirtuallyDerivedFrom(BOR->getDecl())) + SymbolicOffsetBase = R; + } else { + if (!isImmediateBase(Child, BOR->getDecl())) + SymbolicOffsetBase = R; + } + } + } + + // Don't bother calculating precise offsets if we already have a + // symbolic offset somewhere in the chain. + if (SymbolicOffsetBase) + continue; + + CharUnits BaseOffset; + const ASTRecordLayout &Layout = R->getContext().getASTRecordLayout(Child); + if (BOR->isVirtual()) + BaseOffset = Layout.getVBaseClassOffset(BOR->getDecl()); + else + BaseOffset = Layout.getBaseClassOffset(BOR->getDecl()); + + // The base offset is in chars, not in bits. + Offset += BaseOffset.getQuantity() * R->getContext().getCharWidth(); + break; + } + + case MemRegion::CXXDerivedObjectRegionKind: { + // TODO: Store the base type in the CXXDerivedObjectRegion and use it. + goto Finish; + } + + case MemRegion::ElementRegionKind: { + const auto *ER = cast<ElementRegion>(R); + R = ER->getSuperRegion(); + + QualType EleTy = ER->getValueType(); + if (EleTy->isIncompleteType()) { + // We cannot compute the offset of the base class. + SymbolicOffsetBase = R; + continue; + } + + SVal Index = ER->getIndex(); + if (Optional<nonloc::ConcreteInt> CI = + Index.getAs<nonloc::ConcreteInt>()) { + // Don't bother calculating precise offsets if we already have a + // symbolic offset somewhere in the chain. + if (SymbolicOffsetBase) + continue; + + int64_t i = CI->getValue().getSExtValue(); + // This type size is in bits. + Offset += i * R->getContext().getTypeSize(EleTy); + } else { + // We cannot compute offset for non-concrete index. + SymbolicOffsetBase = R; + } + break; + } + case MemRegion::FieldRegionKind: { + const auto *FR = cast<FieldRegion>(R); + R = FR->getSuperRegion(); + + const RecordDecl *RD = FR->getDecl()->getParent(); + if (RD->isUnion() || !RD->isCompleteDefinition()) { + // We cannot compute offset for incomplete type. + // For unions, we could treat everything as offset 0, but we'd rather + // treat each field as a symbolic offset so they aren't stored on top + // of each other, since we depend on things in typed regions actually + // matching their types. + SymbolicOffsetBase = R; + } + + // Don't bother calculating precise offsets if we already have a + // symbolic offset somewhere in the chain. + if (SymbolicOffsetBase) + continue; + + // Get the field number. + unsigned idx = 0; + for (RecordDecl::field_iterator FI = RD->field_begin(), + FE = RD->field_end(); FI != FE; ++FI, ++idx) { + if (FR->getDecl() == *FI) + break; + } + const ASTRecordLayout &Layout = R->getContext().getASTRecordLayout(RD); + // This is offset in bits. + Offset += Layout.getFieldOffset(idx); + break; + } + } + } + + Finish: + if (SymbolicOffsetBase) + return RegionOffset(SymbolicOffsetBase, RegionOffset::Symbolic); + return RegionOffset(R, Offset); +} + +RegionOffset MemRegion::getAsOffset() const { + if (!cachedOffset) + cachedOffset = calculateOffset(this); + return *cachedOffset; +} + +//===----------------------------------------------------------------------===// +// BlockDataRegion +//===----------------------------------------------------------------------===// + +std::pair<const VarRegion *, const VarRegion *> +BlockDataRegion::getCaptureRegions(const VarDecl *VD) { + MemRegionManager &MemMgr = *getMemRegionManager(); + const VarRegion *VR = nullptr; + const VarRegion *OriginalVR = nullptr; + + if (!VD->hasAttr<BlocksAttr>() && VD->hasLocalStorage()) { + VR = MemMgr.getVarRegion(VD, this); + OriginalVR = MemMgr.getVarRegion(VD, LC); + } + else { + if (LC) { + VR = MemMgr.getVarRegion(VD, LC); + OriginalVR = VR; + } + else { + VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); + OriginalVR = MemMgr.getVarRegion(VD, LC); + } + } + return std::make_pair(VR, OriginalVR); +} + +void BlockDataRegion::LazyInitializeReferencedVars() { + if (ReferencedVars) + return; + + AnalysisDeclContext *AC = getCodeRegion()->getAnalysisDeclContext(); + const auto &ReferencedBlockVars = AC->getReferencedBlockVars(BC->getDecl()); + auto NumBlockVars = + std::distance(ReferencedBlockVars.begin(), ReferencedBlockVars.end()); + + if (NumBlockVars == 0) { + ReferencedVars = (void*) 0x1; + return; + } + + MemRegionManager &MemMgr = *getMemRegionManager(); + llvm::BumpPtrAllocator &A = MemMgr.getAllocator(); + BumpVectorContext BC(A); + + using VarVec = BumpVector<const MemRegion *>; + + auto *BV = A.Allocate<VarVec>(); + new (BV) VarVec(BC, NumBlockVars); + auto *BVOriginal = A.Allocate<VarVec>(); + new (BVOriginal) VarVec(BC, NumBlockVars); + + for (const auto *VD : ReferencedBlockVars) { + const VarRegion *VR = nullptr; + const VarRegion *OriginalVR = nullptr; + std::tie(VR, OriginalVR) = getCaptureRegions(VD); + assert(VR); + assert(OriginalVR); + BV->push_back(VR, BC); + BVOriginal->push_back(OriginalVR, BC); + } + + ReferencedVars = BV; + OriginalVars = BVOriginal; +} + +BlockDataRegion::referenced_vars_iterator +BlockDataRegion::referenced_vars_begin() const { + const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars(); + + auto *Vec = static_cast<BumpVector<const MemRegion *> *>(ReferencedVars); + + if (Vec == (void*) 0x1) + return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr); + + auto *VecOriginal = + static_cast<BumpVector<const MemRegion *> *>(OriginalVars); + + return BlockDataRegion::referenced_vars_iterator(Vec->begin(), + VecOriginal->begin()); +} + +BlockDataRegion::referenced_vars_iterator +BlockDataRegion::referenced_vars_end() const { + const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars(); + + auto *Vec = static_cast<BumpVector<const MemRegion *> *>(ReferencedVars); + + if (Vec == (void*) 0x1) + return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr); + + auto *VecOriginal = + static_cast<BumpVector<const MemRegion *> *>(OriginalVars); + + return BlockDataRegion::referenced_vars_iterator(Vec->end(), + VecOriginal->end()); +} + +const VarRegion *BlockDataRegion::getOriginalRegion(const VarRegion *R) const { + for (referenced_vars_iterator I = referenced_vars_begin(), + E = referenced_vars_end(); + I != E; ++I) { + if (I.getCapturedRegion() == R) + return I.getOriginalRegion(); + } + return nullptr; +} + +//===----------------------------------------------------------------------===// +// RegionAndSymbolInvalidationTraits +//===----------------------------------------------------------------------===// + +void RegionAndSymbolInvalidationTraits::setTrait(SymbolRef Sym, + InvalidationKinds IK) { + SymTraitsMap[Sym] |= IK; +} + +void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR, + InvalidationKinds IK) { + assert(MR); + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) + setTrait(SR->getSymbol(), IK); + else + MRTraitsMap[MR] |= IK; +} + +bool RegionAndSymbolInvalidationTraits::hasTrait(SymbolRef Sym, + InvalidationKinds IK) const { + const_symbol_iterator I = SymTraitsMap.find(Sym); + if (I != SymTraitsMap.end()) + return I->second & IK; + + return false; +} + +bool RegionAndSymbolInvalidationTraits::hasTrait(const MemRegion *MR, + InvalidationKinds IK) const { + if (!MR) + return false; + + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) + return hasTrait(SR->getSymbol(), IK); + + const_region_iterator I = MRTraitsMap.find(MR); + if (I != MRTraitsMap.end()) + return I->second & IK; + + return false; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp new file mode 100644 index 000000000000..3e93bb6a7c4f --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp @@ -0,0 +1,1416 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines the PathDiagnostic-related interfaces. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclBase.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/OperationKinds.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Basic/FileManager.h" +#include "clang/Basic/LLVM.h" +#include "clang/Basic/SourceLocation.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> +#include <cstring> +#include <memory> +#include <utility> +#include <vector> + +using namespace clang; +using namespace ento; + +bool PathDiagnosticMacroPiece::containsEvent() const { + for (const auto &P : subPieces) { + if (isa<PathDiagnosticEventPiece>(*P)) + return true; + if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(P.get())) + if (MP->containsEvent()) + return true; + } + return false; +} + +static StringRef StripTrailingDots(StringRef s) { + for (StringRef::size_type i = s.size(); i != 0; --i) + if (s[i - 1] != '.') + return s.substr(0, i); + return {}; +} + +PathDiagnosticPiece::PathDiagnosticPiece(StringRef s, + Kind k, DisplayHint hint) + : str(StripTrailingDots(s)), kind(k), Hint(hint) {} + +PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint) + : kind(k), Hint(hint) {} + +PathDiagnosticPiece::~PathDiagnosticPiece() = default; + +PathDiagnosticEventPiece::~PathDiagnosticEventPiece() = default; + +PathDiagnosticCallPiece::~PathDiagnosticCallPiece() = default; + +PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() = default; + +PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default; + +PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default; + +void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current, + bool ShouldFlattenMacros) const { + for (auto &Piece : *this) { + switch (Piece->getKind()) { + case PathDiagnosticPiece::Call: { + auto &Call = cast<PathDiagnosticCallPiece>(*Piece); + if (auto CallEnter = Call.getCallEnterEvent()) + Current.push_back(std::move(CallEnter)); + Call.path.flattenTo(Primary, Primary, ShouldFlattenMacros); + if (auto callExit = Call.getCallExitEvent()) + Current.push_back(std::move(callExit)); + break; + } + case PathDiagnosticPiece::Macro: { + auto &Macro = cast<PathDiagnosticMacroPiece>(*Piece); + if (ShouldFlattenMacros) { + Macro.subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros); + } else { + Current.push_back(Piece); + PathPieces NewPath; + Macro.subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros); + // FIXME: This probably shouldn't mutate the original path piece. + Macro.subPieces = NewPath; + } + break; + } + case PathDiagnosticPiece::Event: + case PathDiagnosticPiece::ControlFlow: + case PathDiagnosticPiece::Note: + Current.push_back(Piece); + break; + } + } +} + +PathDiagnostic::~PathDiagnostic() = default; + +PathDiagnostic::PathDiagnostic( + StringRef CheckName, const Decl *declWithIssue, StringRef bugtype, + StringRef verboseDesc, StringRef shortDesc, StringRef category, + PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique, + std::unique_ptr<FilesToLineNumsMap> ExecutedLines) + : CheckName(CheckName), DeclWithIssue(declWithIssue), + BugType(StripTrailingDots(bugtype)), + VerboseDesc(StripTrailingDots(verboseDesc)), + ShortDesc(StripTrailingDots(shortDesc)), + Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique), + UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)), + path(pathImpl) {} + +static PathDiagnosticCallPiece * +getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP, + const SourceManager &SMgr) { + SourceLocation CallLoc = CP->callEnter.asLocation(); + + // If the call is within a macro, don't do anything (for now). + if (CallLoc.isMacroID()) + return nullptr; + + assert(AnalysisManager::isInCodeFile(CallLoc, SMgr) && + "The call piece should not be in a header file."); + + // Check if CP represents a path through a function outside of the main file. + if (!AnalysisManager::isInCodeFile(CP->callEnterWithin.asLocation(), SMgr)) + return CP; + + const PathPieces &Path = CP->path; + if (Path.empty()) + return nullptr; + + // Check if the last piece in the callee path is a call to a function outside + // of the main file. + if (auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get())) + return getFirstStackedCallToHeaderFile(CPInner, SMgr); + + // Otherwise, the last piece is in the main file. + return nullptr; +} + +void PathDiagnostic::resetDiagnosticLocationToMainFile() { + if (path.empty()) + return; + + PathDiagnosticPiece *LastP = path.back().get(); + assert(LastP); + const SourceManager &SMgr = LastP->getLocation().getManager(); + + // We only need to check if the report ends inside headers, if the last piece + // is a call piece. + if (auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) { + CP = getFirstStackedCallToHeaderFile(CP, SMgr); + if (CP) { + // Mark the piece. + CP->setAsLastInMainSourceFile(); + + // Update the path diagnostic message. + const auto *ND = dyn_cast<NamedDecl>(CP->getCallee()); + if (ND) { + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + os << " (within a call to '" << ND->getDeclName() << "')"; + appendToDesc(os.str()); + } + + // Reset the report containing declaration and location. + DeclWithIssue = CP->getCaller(); + Loc = CP->getLocation(); + + return; + } + } +} + +void PathDiagnosticConsumer::anchor() {} + +PathDiagnosticConsumer::~PathDiagnosticConsumer() { + // Delete the contents of the FoldingSet if it isn't empty already. + for (auto &Diag : Diags) + delete &Diag; +} + +void PathDiagnosticConsumer::HandlePathDiagnostic( + std::unique_ptr<PathDiagnostic> D) { + if (!D || D->path.empty()) + return; + + // We need to flatten the locations (convert Stmt* to locations) because + // the referenced statements may be freed by the time the diagnostics + // are emitted. + D->flattenLocations(); + + // If the PathDiagnosticConsumer does not support diagnostics that + // cross file boundaries, prune out such diagnostics now. + if (!supportsCrossFileDiagnostics()) { + // Verify that the entire path is from the same FileID. + FileID FID; + const SourceManager &SMgr = D->path.front()->getLocation().getManager(); + SmallVector<const PathPieces *, 5> WorkList; + WorkList.push_back(&D->path); + SmallString<128> buf; + llvm::raw_svector_ostream warning(buf); + warning << "warning: Path diagnostic report is not generated. Current " + << "output format does not support diagnostics that cross file " + << "boundaries. Refer to --analyzer-output for valid output " + << "formats\n"; + + while (!WorkList.empty()) { + const PathPieces &path = *WorkList.pop_back_val(); + + for (const auto &I : path) { + const PathDiagnosticPiece *piece = I.get(); + FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc(); + + if (FID.isInvalid()) { + FID = SMgr.getFileID(L); + } else if (SMgr.getFileID(L) != FID) { + llvm::errs() << warning.str(); + return; + } + + // Check the source ranges. + ArrayRef<SourceRange> Ranges = piece->getRanges(); + for (const auto &I : Ranges) { + SourceLocation L = SMgr.getExpansionLoc(I.getBegin()); + if (!L.isFileID() || SMgr.getFileID(L) != FID) { + llvm::errs() << warning.str(); + return; + } + L = SMgr.getExpansionLoc(I.getEnd()); + if (!L.isFileID() || SMgr.getFileID(L) != FID) { + llvm::errs() << warning.str(); + return; + } + } + + if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece)) + WorkList.push_back(&call->path); + else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece)) + WorkList.push_back(¯o->subPieces); + } + } + + if (FID.isInvalid()) + return; // FIXME: Emit a warning? + } + + // Profile the node to see if we already have something matching it + llvm::FoldingSetNodeID profile; + D->Profile(profile); + void *InsertPos = nullptr; + + if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) { + // Keep the PathDiagnostic with the shorter path. + // Note, the enclosing routine is called in deterministic order, so the + // results will be consistent between runs (no reason to break ties if the + // size is the same). + const unsigned orig_size = orig->full_size(); + const unsigned new_size = D->full_size(); + if (orig_size <= new_size) + return; + + assert(orig != D.get()); + Diags.RemoveNode(orig); + delete orig; + } + + Diags.InsertNode(D.release()); +} + +static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y); + +static Optional<bool> +compareControlFlow(const PathDiagnosticControlFlowPiece &X, + const PathDiagnosticControlFlowPiece &Y) { + FullSourceLoc XSL = X.getStartLocation().asLocation(); + FullSourceLoc YSL = Y.getStartLocation().asLocation(); + if (XSL != YSL) + return XSL.isBeforeInTranslationUnitThan(YSL); + FullSourceLoc XEL = X.getEndLocation().asLocation(); + FullSourceLoc YEL = Y.getEndLocation().asLocation(); + if (XEL != YEL) + return XEL.isBeforeInTranslationUnitThan(YEL); + return None; +} + +static Optional<bool> compareMacro(const PathDiagnosticMacroPiece &X, + const PathDiagnosticMacroPiece &Y) { + return comparePath(X.subPieces, Y.subPieces); +} + +static Optional<bool> compareCall(const PathDiagnosticCallPiece &X, + const PathDiagnosticCallPiece &Y) { + FullSourceLoc X_CEL = X.callEnter.asLocation(); + FullSourceLoc Y_CEL = Y.callEnter.asLocation(); + if (X_CEL != Y_CEL) + return X_CEL.isBeforeInTranslationUnitThan(Y_CEL); + FullSourceLoc X_CEWL = X.callEnterWithin.asLocation(); + FullSourceLoc Y_CEWL = Y.callEnterWithin.asLocation(); + if (X_CEWL != Y_CEWL) + return X_CEWL.isBeforeInTranslationUnitThan(Y_CEWL); + FullSourceLoc X_CRL = X.callReturn.asLocation(); + FullSourceLoc Y_CRL = Y.callReturn.asLocation(); + if (X_CRL != Y_CRL) + return X_CRL.isBeforeInTranslationUnitThan(Y_CRL); + return comparePath(X.path, Y.path); +} + +static Optional<bool> comparePiece(const PathDiagnosticPiece &X, + const PathDiagnosticPiece &Y) { + if (X.getKind() != Y.getKind()) + return X.getKind() < Y.getKind(); + + FullSourceLoc XL = X.getLocation().asLocation(); + FullSourceLoc YL = Y.getLocation().asLocation(); + if (XL != YL) + return XL.isBeforeInTranslationUnitThan(YL); + + if (X.getString() != Y.getString()) + return X.getString() < Y.getString(); + + if (X.getRanges().size() != Y.getRanges().size()) + return X.getRanges().size() < Y.getRanges().size(); + + const SourceManager &SM = XL.getManager(); + + for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) { + SourceRange XR = X.getRanges()[i]; + SourceRange YR = Y.getRanges()[i]; + if (XR != YR) { + if (XR.getBegin() != YR.getBegin()) + return SM.isBeforeInTranslationUnit(XR.getBegin(), YR.getBegin()); + return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd()); + } + } + + switch (X.getKind()) { + case PathDiagnosticPiece::ControlFlow: + return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X), + cast<PathDiagnosticControlFlowPiece>(Y)); + case PathDiagnosticPiece::Event: + case PathDiagnosticPiece::Note: + return None; + case PathDiagnosticPiece::Macro: + return compareMacro(cast<PathDiagnosticMacroPiece>(X), + cast<PathDiagnosticMacroPiece>(Y)); + case PathDiagnosticPiece::Call: + return compareCall(cast<PathDiagnosticCallPiece>(X), + cast<PathDiagnosticCallPiece>(Y)); + } + llvm_unreachable("all cases handled"); +} + +static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) { + if (X.size() != Y.size()) + return X.size() < Y.size(); + + PathPieces::const_iterator X_I = X.begin(), X_end = X.end(); + PathPieces::const_iterator Y_I = Y.begin(), Y_end = Y.end(); + + for ( ; X_I != X_end && Y_I != Y_end; ++X_I, ++Y_I) { + Optional<bool> b = comparePiece(**X_I, **Y_I); + if (b.hasValue()) + return b.getValue(); + } + + return None; +} + +static bool compareCrossTUSourceLocs(FullSourceLoc XL, FullSourceLoc YL) { + std::pair<FileID, unsigned> XOffs = XL.getDecomposedLoc(); + std::pair<FileID, unsigned> YOffs = YL.getDecomposedLoc(); + const SourceManager &SM = XL.getManager(); + std::pair<bool, bool> InSameTU = SM.isInTheSameTranslationUnit(XOffs, YOffs); + if (InSameTU.first) + return XL.isBeforeInTranslationUnitThan(YL); + const FileEntry *XFE = SM.getFileEntryForID(XL.getSpellingLoc().getFileID()); + const FileEntry *YFE = SM.getFileEntryForID(YL.getSpellingLoc().getFileID()); + if (!XFE || !YFE) + return XFE && !YFE; + int NameCmp = XFE->getName().compare(YFE->getName()); + if (NameCmp != 0) + return NameCmp == -1; + // Last resort: Compare raw file IDs that are possibly expansions. + return XL.getFileID() < YL.getFileID(); +} + +static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) { + FullSourceLoc XL = X.getLocation().asLocation(); + FullSourceLoc YL = Y.getLocation().asLocation(); + if (XL != YL) + return compareCrossTUSourceLocs(XL, YL); + if (X.getBugType() != Y.getBugType()) + return X.getBugType() < Y.getBugType(); + if (X.getCategory() != Y.getCategory()) + return X.getCategory() < Y.getCategory(); + if (X.getVerboseDescription() != Y.getVerboseDescription()) + return X.getVerboseDescription() < Y.getVerboseDescription(); + if (X.getShortDescription() != Y.getShortDescription()) + return X.getShortDescription() < Y.getShortDescription(); + if (X.getDeclWithIssue() != Y.getDeclWithIssue()) { + const Decl *XD = X.getDeclWithIssue(); + if (!XD) + return true; + const Decl *YD = Y.getDeclWithIssue(); + if (!YD) + return false; + SourceLocation XDL = XD->getLocation(); + SourceLocation YDL = YD->getLocation(); + if (XDL != YDL) { + const SourceManager &SM = XL.getManager(); + return compareCrossTUSourceLocs(FullSourceLoc(XDL, SM), + FullSourceLoc(YDL, SM)); + } + } + PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end(); + PathDiagnostic::meta_iterator YI = Y.meta_begin(), YE = Y.meta_end(); + if (XE - XI != YE - YI) + return (XE - XI) < (YE - YI); + for ( ; XI != XE ; ++XI, ++YI) { + if (*XI != *YI) + return (*XI) < (*YI); + } + Optional<bool> b = comparePath(X.path, Y.path); + assert(b.hasValue()); + return b.getValue(); +} + +void PathDiagnosticConsumer::FlushDiagnostics( + PathDiagnosticConsumer::FilesMade *Files) { + if (flushed) + return; + + flushed = true; + + std::vector<const PathDiagnostic *> BatchDiags; + for (const auto &D : Diags) + BatchDiags.push_back(&D); + + // Sort the diagnostics so that they are always emitted in a deterministic + // order. + int (*Comp)(const PathDiagnostic *const *, const PathDiagnostic *const *) = + [](const PathDiagnostic *const *X, const PathDiagnostic *const *Y) { + assert(*X != *Y && "PathDiagnostics not uniqued!"); + if (compare(**X, **Y)) + return -1; + assert(compare(**Y, **X) && "Not a total order!"); + return 1; + }; + array_pod_sort(BatchDiags.begin(), BatchDiags.end(), Comp); + + FlushDiagnosticsImpl(BatchDiags, Files); + + // Delete the flushed diagnostics. + for (const auto D : BatchDiags) + delete D; + + // Clear out the FoldingSet. + Diags.clear(); +} + +PathDiagnosticConsumer::FilesMade::~FilesMade() { + for (PDFileEntry &Entry : Set) + Entry.~PDFileEntry(); +} + +void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD, + StringRef ConsumerName, + StringRef FileName) { + llvm::FoldingSetNodeID NodeID; + NodeID.Add(PD); + void *InsertPos; + PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos); + if (!Entry) { + Entry = Alloc.Allocate<PDFileEntry>(); + Entry = new (Entry) PDFileEntry(NodeID); + Set.InsertNode(Entry, InsertPos); + } + + // Allocate persistent storage for the file name. + char *FileName_cstr = (char*) Alloc.Allocate(FileName.size(), 1); + memcpy(FileName_cstr, FileName.data(), FileName.size()); + + Entry->files.push_back(std::make_pair(ConsumerName, + StringRef(FileName_cstr, + FileName.size()))); +} + +PathDiagnosticConsumer::PDFileEntry::ConsumerFiles * +PathDiagnosticConsumer::FilesMade::getFiles(const PathDiagnostic &PD) { + llvm::FoldingSetNodeID NodeID; + NodeID.Add(PD); + void *InsertPos; + PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos); + if (!Entry) + return nullptr; + return &Entry->files; +} + +//===----------------------------------------------------------------------===// +// PathDiagnosticLocation methods. +//===----------------------------------------------------------------------===// + +static SourceLocation getValidSourceLocation(const Stmt* S, + LocationOrAnalysisDeclContext LAC, + bool UseEnd = false) { + SourceLocation L = UseEnd ? S->getEndLoc() : S->getBeginLoc(); + assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should " + "be passed to PathDiagnosticLocation upon creation."); + + // S might be a temporary statement that does not have a location in the + // source code, so find an enclosing statement and use its location. + if (!L.isValid()) { + AnalysisDeclContext *ADC; + if (LAC.is<const LocationContext*>()) + ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext(); + else + ADC = LAC.get<AnalysisDeclContext*>(); + + ParentMap &PM = ADC->getParentMap(); + + const Stmt *Parent = S; + do { + Parent = PM.getParent(Parent); + + // In rare cases, we have implicit top-level expressions, + // such as arguments for implicit member initializers. + // In this case, fall back to the start of the body (even if we were + // asked for the statement end location). + if (!Parent) { + const Stmt *Body = ADC->getBody(); + if (Body) + L = Body->getBeginLoc(); + else + L = ADC->getDecl()->getEndLoc(); + break; + } + + L = UseEnd ? Parent->getEndLoc() : Parent->getBeginLoc(); + } while (!L.isValid()); + } + + return L; +} + +static PathDiagnosticLocation +getLocationForCaller(const StackFrameContext *SFC, + const LocationContext *CallerCtx, + const SourceManager &SM) { + const CFGBlock &Block = *SFC->getCallSiteBlock(); + CFGElement Source = Block[SFC->getIndex()]; + + switch (Source.getKind()) { + case CFGElement::Statement: + case CFGElement::Constructor: + case CFGElement::CXXRecordTypedCall: + return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(), + SM, CallerCtx); + case CFGElement::Initializer: { + const CFGInitializer &Init = Source.castAs<CFGInitializer>(); + return PathDiagnosticLocation(Init.getInitializer()->getInit(), + SM, CallerCtx); + } + case CFGElement::AutomaticObjectDtor: { + const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>(); + return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(), + SM, CallerCtx); + } + case CFGElement::DeleteDtor: { + const CFGDeleteDtor &Dtor = Source.castAs<CFGDeleteDtor>(); + return PathDiagnosticLocation(Dtor.getDeleteExpr(), SM, CallerCtx); + } + case CFGElement::BaseDtor: + case CFGElement::MemberDtor: { + const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext(); + if (const Stmt *CallerBody = CallerInfo->getBody()) + return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx); + return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM); + } + case CFGElement::NewAllocator: { + const CFGNewAllocator &Alloc = Source.castAs<CFGNewAllocator>(); + return PathDiagnosticLocation(Alloc.getAllocatorExpr(), SM, CallerCtx); + } + case CFGElement::TemporaryDtor: { + // Temporary destructors are for temporaries. They die immediately at around + // the location of CXXBindTemporaryExpr. If they are lifetime-extended, + // they'd be dealt with via an AutomaticObjectDtor instead. + const auto &Dtor = Source.castAs<CFGTemporaryDtor>(); + return PathDiagnosticLocation::createEnd(Dtor.getBindTemporaryExpr(), SM, + CallerCtx); + } + case CFGElement::ScopeBegin: + case CFGElement::ScopeEnd: + llvm_unreachable("not yet implemented!"); + case CFGElement::LifetimeEnds: + case CFGElement::LoopExit: + llvm_unreachable("CFGElement kind should not be on callsite!"); + } + + llvm_unreachable("Unknown CFGElement kind"); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createBegin(const Decl *D, + const SourceManager &SM) { + return PathDiagnosticLocation(D->getBeginLoc(), SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createBegin(const Stmt *S, + const SourceManager &SM, + LocationOrAnalysisDeclContext LAC) { + return PathDiagnosticLocation(getValidSourceLocation(S, LAC), + SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createEnd(const Stmt *S, + const SourceManager &SM, + LocationOrAnalysisDeclContext LAC) { + if (const auto *CS = dyn_cast<CompoundStmt>(S)) + return createEndBrace(CS, SM); + return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true), + SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO, + const SourceManager &SM) { + return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createConditionalColonLoc( + const ConditionalOperator *CO, + const SourceManager &SM) { + return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME, + const SourceManager &SM) { + return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS, + const SourceManager &SM) { + SourceLocation L = CS->getLBracLoc(); + return PathDiagnosticLocation(L, SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS, + const SourceManager &SM) { + SourceLocation L = CS->getRBracLoc(); + return PathDiagnosticLocation(L, SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createDeclBegin(const LocationContext *LC, + const SourceManager &SM) { + // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. + if (const auto *CS = dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody())) + if (!CS->body_empty()) { + SourceLocation Loc = (*CS->body_begin())->getBeginLoc(); + return PathDiagnosticLocation(Loc, SM, SingleLocK); + } + + return PathDiagnosticLocation(); +} + +PathDiagnosticLocation +PathDiagnosticLocation::createDeclEnd(const LocationContext *LC, + const SourceManager &SM) { + SourceLocation L = LC->getDecl()->getBodyRBrace(); + return PathDiagnosticLocation(L, SM, SingleLocK); +} + +PathDiagnosticLocation +PathDiagnosticLocation::create(const ProgramPoint& P, + const SourceManager &SMng) { + const Stmt* S = nullptr; + if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) { + const CFGBlock *BSrc = BE->getSrc(); + S = BSrc->getTerminatorCondition(); + } else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) { + S = SP->getStmt(); + if (P.getAs<PostStmtPurgeDeadSymbols>()) + return PathDiagnosticLocation::createEnd(S, SMng, P.getLocationContext()); + } else if (Optional<PostInitializer> PIP = P.getAs<PostInitializer>()) { + return PathDiagnosticLocation(PIP->getInitializer()->getSourceLocation(), + SMng); + } else if (Optional<PreImplicitCall> PIC = P.getAs<PreImplicitCall>()) { + return PathDiagnosticLocation(PIC->getLocation(), SMng); + } else if (Optional<PostImplicitCall> PIE = P.getAs<PostImplicitCall>()) { + return PathDiagnosticLocation(PIE->getLocation(), SMng); + } else if (Optional<CallEnter> CE = P.getAs<CallEnter>()) { + return getLocationForCaller(CE->getCalleeContext(), + CE->getLocationContext(), + SMng); + } else if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) { + return getLocationForCaller(CEE->getCalleeContext(), + CEE->getLocationContext(), + SMng); + } else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) { + CFGElement BlockFront = BE->getBlock()->front(); + if (auto StmtElt = BlockFront.getAs<CFGStmt>()) { + return PathDiagnosticLocation(StmtElt->getStmt()->getBeginLoc(), SMng); + } else if (auto NewAllocElt = BlockFront.getAs<CFGNewAllocator>()) { + return PathDiagnosticLocation( + NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng); + } + llvm_unreachable("Unexpected CFG element at front of block"); + } else { + llvm_unreachable("Unexpected ProgramPoint"); + } + + return PathDiagnosticLocation(S, SMng, P.getLocationContext()); +} + +static const LocationContext * +findTopAutosynthesizedParentContext(const LocationContext *LC) { + assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized()); + const LocationContext *ParentLC = LC->getParent(); + assert(ParentLC && "We don't start analysis from autosynthesized code"); + while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) { + LC = ParentLC; + ParentLC = LC->getParent(); + assert(ParentLC && "We don't start analysis from autosynthesized code"); + } + return LC; +} + +const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) { + // We cannot place diagnostics on autosynthesized code. + // Put them onto the call site through which we jumped into autosynthesized + // code for the first time. + const LocationContext *LC = N->getLocationContext(); + if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) { + // It must be a stack frame because we only autosynthesize functions. + return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC)) + ->getCallSite(); + } + // Otherwise, see if the node's program point directly points to a statement. + ProgramPoint P = N->getLocation(); + if (auto SP = P.getAs<StmtPoint>()) + return SP->getStmt(); + if (auto BE = P.getAs<BlockEdge>()) + return BE->getSrc()->getTerminator(); + if (auto CE = P.getAs<CallEnter>()) + return CE->getCallExpr(); + if (auto CEE = P.getAs<CallExitEnd>()) + return CEE->getCalleeContext()->getCallSite(); + if (auto PIPP = P.getAs<PostInitializer>()) + return PIPP->getInitializer()->getInit(); + if (auto CEB = P.getAs<CallExitBegin>()) + return CEB->getReturnStmt(); + if (auto FEP = P.getAs<FunctionExitPoint>()) + return FEP->getStmt(); + + return nullptr; +} + +const Stmt *PathDiagnosticLocation::getNextStmt(const ExplodedNode *N) { + for (N = N->getFirstSucc(); N; N = N->getFirstSucc()) { + if (const Stmt *S = getStmt(N)) { + // Check if the statement is '?' or '&&'/'||'. These are "merges", + // not actual statement points. + switch (S->getStmtClass()) { + case Stmt::ChooseExprClass: + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: + continue; + case Stmt::BinaryOperatorClass: { + BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode(); + if (Op == BO_LAnd || Op == BO_LOr) + continue; + break; + } + default: + break; + } + // We found the statement, so return it. + return S; + } + } + + return nullptr; +} + +PathDiagnosticLocation + PathDiagnosticLocation::createEndOfPath(const ExplodedNode *N, + const SourceManager &SM) { + assert(N && "Cannot create a location with a null node."); + const Stmt *S = getStmt(N); + const LocationContext *LC = N->getLocationContext(); + + if (!S) { + // If this is an implicit call, return the implicit call point location. + if (Optional<PreImplicitCall> PIE = N->getLocationAs<PreImplicitCall>()) + return PathDiagnosticLocation(PIE->getLocation(), SM); + if (auto FE = N->getLocationAs<FunctionExitPoint>()) { + if (const ReturnStmt *RS = FE->getStmt()) + return PathDiagnosticLocation::createBegin(RS, SM, LC); + } + S = getNextStmt(N); + } + + if (S) { + ProgramPoint P = N->getLocation(); + + // For member expressions, return the location of the '.' or '->'. + if (const auto *ME = dyn_cast<MemberExpr>(S)) + return PathDiagnosticLocation::createMemberLoc(ME, SM); + + // For binary operators, return the location of the operator. + if (const auto *B = dyn_cast<BinaryOperator>(S)) + return PathDiagnosticLocation::createOperatorLoc(B, SM); + + if (P.getAs<PostStmtPurgeDeadSymbols>()) + return PathDiagnosticLocation::createEnd(S, SM, LC); + + if (S->getBeginLoc().isValid()) + return PathDiagnosticLocation(S, SM, LC); + return PathDiagnosticLocation(getValidSourceLocation(S, LC), SM); + } + + return createDeclEnd(N->getLocationContext(), SM); +} + +PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation( + const PathDiagnosticLocation &PDL) { + FullSourceLoc L = PDL.asLocation(); + return PathDiagnosticLocation(L, L.getManager(), SingleLocK); +} + +FullSourceLoc + PathDiagnosticLocation::genLocation(SourceLocation L, + LocationOrAnalysisDeclContext LAC) const { + assert(isValid()); + // Note that we want a 'switch' here so that the compiler can warn us in + // case we add more cases. + switch (K) { + case SingleLocK: + case RangeK: + break; + case StmtK: + // Defensive checking. + if (!S) + break; + return FullSourceLoc(getValidSourceLocation(S, LAC), + const_cast<SourceManager&>(*SM)); + case DeclK: + // Defensive checking. + if (!D) + break; + return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM)); + } + + return FullSourceLoc(L, const_cast<SourceManager&>(*SM)); +} + +PathDiagnosticRange + PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const { + assert(isValid()); + // Note that we want a 'switch' here so that the compiler can warn us in + // case we add more cases. + switch (K) { + case SingleLocK: + return PathDiagnosticRange(SourceRange(Loc,Loc), true); + case RangeK: + break; + case StmtK: { + const Stmt *S = asStmt(); + switch (S->getStmtClass()) { + default: + break; + case Stmt::DeclStmtClass: { + const auto *DS = cast<DeclStmt>(S); + if (DS->isSingleDecl()) { + // Should always be the case, but we'll be defensive. + return SourceRange(DS->getBeginLoc(), + DS->getSingleDecl()->getLocation()); + } + break; + } + // FIXME: Provide better range information for different + // terminators. + case Stmt::IfStmtClass: + case Stmt::WhileStmtClass: + case Stmt::DoStmtClass: + case Stmt::ForStmtClass: + case Stmt::ChooseExprClass: + case Stmt::IndirectGotoStmtClass: + case Stmt::SwitchStmtClass: + case Stmt::BinaryConditionalOperatorClass: + case Stmt::ConditionalOperatorClass: + case Stmt::ObjCForCollectionStmtClass: { + SourceLocation L = getValidSourceLocation(S, LAC); + return SourceRange(L, L); + } + } + SourceRange R = S->getSourceRange(); + if (R.isValid()) + return R; + break; + } + case DeclK: + if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) + return MD->getSourceRange(); + if (const auto *FD = dyn_cast<FunctionDecl>(D)) { + if (Stmt *Body = FD->getBody()) + return Body->getSourceRange(); + } + else { + SourceLocation L = D->getLocation(); + return PathDiagnosticRange(SourceRange(L, L), true); + } + } + + return SourceRange(Loc, Loc); +} + +void PathDiagnosticLocation::flatten() { + if (K == StmtK) { + K = RangeK; + S = nullptr; + D = nullptr; + } + else if (K == DeclK) { + K = SingleLocK; + S = nullptr; + D = nullptr; + } +} + +//===----------------------------------------------------------------------===// +// Manipulation of PathDiagnosticCallPieces. +//===----------------------------------------------------------------------===// + +std::shared_ptr<PathDiagnosticCallPiece> +PathDiagnosticCallPiece::construct(const CallExitEnd &CE, + const SourceManager &SM) { + const Decl *caller = CE.getLocationContext()->getDecl(); + PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(), + CE.getLocationContext(), + SM); + return std::shared_ptr<PathDiagnosticCallPiece>( + new PathDiagnosticCallPiece(caller, pos)); +} + +PathDiagnosticCallPiece * +PathDiagnosticCallPiece::construct(PathPieces &path, + const Decl *caller) { + std::shared_ptr<PathDiagnosticCallPiece> C( + new PathDiagnosticCallPiece(path, caller)); + path.clear(); + auto *R = C.get(); + path.push_front(std::move(C)); + return R; +} + +void PathDiagnosticCallPiece::setCallee(const CallEnter &CE, + const SourceManager &SM) { + const StackFrameContext *CalleeCtx = CE.getCalleeContext(); + Callee = CalleeCtx->getDecl(); + + callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM); + callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM); + + // Autosynthesized property accessors are special because we'd never + // pop back up to non-autosynthesized code until we leave them. + // This is not generally true for autosynthesized callees, which may call + // non-autosynthesized callbacks. + // Unless set here, the IsCalleeAnAutosynthesizedPropertyAccessor flag + // defaults to false. + if (const auto *MD = dyn_cast<ObjCMethodDecl>(Callee)) + IsCalleeAnAutosynthesizedPropertyAccessor = ( + MD->isPropertyAccessor() && + CalleeCtx->getAnalysisDeclContext()->isBodyAutosynthesized()); +} + +static void describeTemplateParameters(raw_ostream &Out, + const ArrayRef<TemplateArgument> TAList, + const LangOptions &LO, + StringRef Prefix = StringRef(), + StringRef Postfix = StringRef()); + +static void describeTemplateParameter(raw_ostream &Out, + const TemplateArgument &TArg, + const LangOptions &LO) { + + if (TArg.getKind() == TemplateArgument::ArgKind::Pack) { + describeTemplateParameters(Out, TArg.getPackAsArray(), LO); + } else { + TArg.print(PrintingPolicy(LO), Out); + } +} + +static void describeTemplateParameters(raw_ostream &Out, + const ArrayRef<TemplateArgument> TAList, + const LangOptions &LO, + StringRef Prefix, StringRef Postfix) { + if (TAList.empty()) + return; + + Out << Prefix; + for (int I = 0, Last = TAList.size() - 1; I != Last; ++I) { + describeTemplateParameter(Out, TAList[I], LO); + Out << ", "; + } + describeTemplateParameter(Out, TAList[TAList.size() - 1], LO); + Out << Postfix; +} + +static void describeClass(raw_ostream &Out, const CXXRecordDecl *D, + StringRef Prefix = StringRef()) { + if (!D->getIdentifier()) + return; + Out << Prefix << '\'' << *D; + if (const auto T = dyn_cast<ClassTemplateSpecializationDecl>(D)) + describeTemplateParameters(Out, T->getTemplateArgs().asArray(), + D->getASTContext().getLangOpts(), "<", ">"); + + Out << '\''; +} + +static bool describeCodeDecl(raw_ostream &Out, const Decl *D, + bool ExtendedDescription, + StringRef Prefix = StringRef()) { + if (!D) + return false; + + if (isa<BlockDecl>(D)) { + if (ExtendedDescription) + Out << Prefix << "anonymous block"; + return ExtendedDescription; + } + + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { + Out << Prefix; + if (ExtendedDescription && !MD->isUserProvided()) { + if (MD->isExplicitlyDefaulted()) + Out << "defaulted "; + else + Out << "implicit "; + } + + if (const auto *CD = dyn_cast<CXXConstructorDecl>(MD)) { + if (CD->isDefaultConstructor()) + Out << "default "; + else if (CD->isCopyConstructor()) + Out << "copy "; + else if (CD->isMoveConstructor()) + Out << "move "; + + Out << "constructor"; + describeClass(Out, MD->getParent(), " for "); + } else if (isa<CXXDestructorDecl>(MD)) { + if (!MD->isUserProvided()) { + Out << "destructor"; + describeClass(Out, MD->getParent(), " for "); + } else { + // Use ~Foo for explicitly-written destructors. + Out << "'" << *MD << "'"; + } + } else if (MD->isCopyAssignmentOperator()) { + Out << "copy assignment operator"; + describeClass(Out, MD->getParent(), " for "); + } else if (MD->isMoveAssignmentOperator()) { + Out << "move assignment operator"; + describeClass(Out, MD->getParent(), " for "); + } else { + if (MD->getParent()->getIdentifier()) + Out << "'" << *MD->getParent() << "::" << *MD << "'"; + else + Out << "'" << *MD << "'"; + } + + return true; + } + + Out << Prefix << '\'' << cast<NamedDecl>(*D); + + // Adding template parameters. + if (const auto FD = dyn_cast<FunctionDecl>(D)) + if (const TemplateArgumentList *TAList = + FD->getTemplateSpecializationArgs()) + describeTemplateParameters(Out, TAList->asArray(), + FD->getASTContext().getLangOpts(), "<", ">"); + + Out << '\''; + return true; +} + +std::shared_ptr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallEnterEvent() const { + // We do not produce call enters and call exits for autosynthesized property + // accessors. We do generally produce them for other functions coming from + // the body farm because they may call callbacks that bring us back into + // visible code. + if (!Callee || IsCalleeAnAutosynthesizedPropertyAccessor) + return nullptr; + + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + + Out << "Calling "; + describeCodeDecl(Out, Callee, /*ExtendedDescription=*/true); + + assert(callEnter.asLocation().isValid()); + return std::make_shared<PathDiagnosticEventPiece>(callEnter, Out.str()); +} + +std::shared_ptr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const { + if (!callEnterWithin.asLocation().isValid()) + return nullptr; + if (Callee->isImplicit() || !Callee->hasBody()) + return nullptr; + if (const auto *MD = dyn_cast<CXXMethodDecl>(Callee)) + if (MD->isDefaulted()) + return nullptr; + + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + + Out << "Entered call"; + describeCodeDecl(Out, Caller, /*ExtendedDescription=*/false, " from "); + + return std::make_shared<PathDiagnosticEventPiece>(callEnterWithin, Out.str()); +} + +std::shared_ptr<PathDiagnosticEventPiece> +PathDiagnosticCallPiece::getCallExitEvent() const { + // We do not produce call enters and call exits for autosynthesized property + // accessors. We do generally produce them for other functions coming from + // the body farm because they may call callbacks that bring us back into + // visible code. + if (NoExit || IsCalleeAnAutosynthesizedPropertyAccessor) + return nullptr; + + SmallString<256> buf; + llvm::raw_svector_ostream Out(buf); + + if (!CallStackMessage.empty()) { + Out << CallStackMessage; + } else { + bool DidDescribe = describeCodeDecl(Out, Callee, + /*ExtendedDescription=*/false, + "Returning from "); + if (!DidDescribe) + Out << "Returning to caller"; + } + + assert(callReturn.asLocation().isValid()); + return std::make_shared<PathDiagnosticEventPiece>(callReturn, Out.str()); +} + +static void compute_path_size(const PathPieces &pieces, unsigned &size) { + for (const auto &I : pieces) { + const PathDiagnosticPiece *piece = I.get(); + if (const auto *cp = dyn_cast<PathDiagnosticCallPiece>(piece)) + compute_path_size(cp->path, size); + else + ++size; + } +} + +unsigned PathDiagnostic::full_size() { + unsigned size = 0; + compute_path_size(path, size); + return size; +} + +//===----------------------------------------------------------------------===// +// FoldingSet profiling methods. +//===----------------------------------------------------------------------===// + +void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(Range.getBegin().getRawEncoding()); + ID.AddInteger(Range.getEnd().getRawEncoding()); + ID.AddInteger(Loc.getRawEncoding()); +} + +void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger((unsigned) getKind()); + ID.AddString(str); + // FIXME: Add profiling support for code hints. + ID.AddInteger((unsigned) getDisplayHint()); + ArrayRef<SourceRange> Ranges = getRanges(); + for (const auto &I : Ranges) { + ID.AddInteger(I.getBegin().getRawEncoding()); + ID.AddInteger(I.getEnd().getRawEncoding()); + } +} + +void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticPiece::Profile(ID); + for (const auto &I : path) + ID.Add(*I); +} + +void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticPiece::Profile(ID); + ID.Add(Pos); +} + +void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticPiece::Profile(ID); + for (const auto &I : *this) + ID.Add(I); +} + +void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticSpotPiece::Profile(ID); + for (const auto &I : subPieces) + ID.Add(*I); +} + +void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticSpotPiece::Profile(ID); +} + +void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { + ID.Add(getLocation()); + ID.AddString(BugType); + ID.AddString(VerboseDesc); + ID.AddString(Category); +} + +void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const { + Profile(ID); + for (const auto &I : path) + ID.Add(*I); + for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) + ID.AddString(*I); +} + +StackHintGenerator::~StackHintGenerator() = default; + +std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){ + if (!N) + return getMessageForSymbolNotFound(); + + ProgramPoint P = N->getLocation(); + CallExitEnd CExit = P.castAs<CallExitEnd>(); + + // FIXME: Use CallEvent to abstract this over all calls. + const Stmt *CallSite = CExit.getCalleeContext()->getCallSite(); + const auto *CE = dyn_cast_or_null<CallExpr>(CallSite); + if (!CE) + return {}; + + // Check if one of the parameters are set to the interesting symbol. + unsigned ArgIndex = 0; + for (CallExpr::const_arg_iterator I = CE->arg_begin(), + E = CE->arg_end(); I != E; ++I, ++ArgIndex){ + SVal SV = N->getSVal(*I); + + // Check if the variable corresponding to the symbol is passed by value. + SymbolRef AS = SV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + + // Check if the parameter is a pointer to the symbol. + if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) { + // Do not attempt to dereference void*. + if ((*I)->getType()->isVoidPointerType()) + continue; + SVal PSV = N->getState()->getSVal(Reg->getRegion()); + SymbolRef AS = PSV.getAsLocSymbol(); + if (AS == Sym) { + return getMessageForArg(*I, ArgIndex); + } + } + } + + // Check if we are returning the interesting symbol. + SVal SV = N->getSVal(CE); + SymbolRef RetSym = SV.getAsLocSymbol(); + if (RetSym == Sym) { + return getMessageForReturn(CE); + } + + return getMessageForSymbolNotFound(); +} + +std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE, + unsigned ArgIndex) { + // Printed parameters start at 1, not 0. + ++ArgIndex; + + SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << Msg << " via " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex) + << " parameter"; + + return os.str(); +} + +LLVM_DUMP_METHOD void PathPieces::dump() const { + unsigned index = 0; + for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) { + llvm::errs() << "[" << index++ << "] "; + (*I)->dump(); + llvm::errs() << "\n"; + } +} + +LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const { + llvm::errs() << "CALL\n--------------\n"; + + if (const Stmt *SLoc = getLocation().getStmtOrNull()) + SLoc->dump(); + else if (const auto *ND = dyn_cast_or_null<NamedDecl>(getCallee())) + llvm::errs() << *ND << "\n"; + else + getLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const { + llvm::errs() << "EVENT\n--------------\n"; + llvm::errs() << getString() << "\n"; + llvm::errs() << " ---- at ----\n"; + getLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const { + llvm::errs() << "CONTROL\n--------------\n"; + getStartLocation().dump(); + llvm::errs() << " ---- to ----\n"; + getEndLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const { + llvm::errs() << "MACRO\n--------------\n"; + // FIXME: Print which macro is being invoked. +} + +LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const { + llvm::errs() << "NOTE\n--------------\n"; + llvm::errs() << getString() << "\n"; + llvm::errs() << " ---- at ----\n"; + getLocation().dump(); +} + +LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const { + if (!isValid()) { + llvm::errs() << "<INVALID>\n"; + return; + } + + switch (K) { + case RangeK: + // FIXME: actually print the range. + llvm::errs() << "<range>\n"; + break; + case SingleLocK: + asLocation().dump(); + llvm::errs() << "\n"; + break; + case StmtK: + if (S) + S->dump(); + else + llvm::errs() << "<NULL STMT>\n"; + break; + case DeclK: + if (const auto *ND = dyn_cast_or_null<NamedDecl>(D)) + llvm::errs() << *ND << "\n"; + else if (isa<BlockDecl>(D)) + // FIXME: Make this nicer. + llvm::errs() << "<block>\n"; + else if (D) + llvm::errs() << "<unknown decl>\n"; + else + llvm::errs() << "<NULL DECL>\n"; + break; + } +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp new file mode 100644 index 000000000000..db4cf76578d8 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp @@ -0,0 +1,1117 @@ +//===--- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines the PlistDiagnostics object. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/FileManager.h" +#include "clang/Basic/PlistSupport.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/Version.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/TokenConcatenation.h" +#include "clang/Rewrite/Core/HTMLRewrite.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/IssueHash.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" + +using namespace clang; +using namespace ento; +using namespace markup; + +//===----------------------------------------------------------------------===// +// Declarations of helper classes and functions for emitting bug reports in +// plist format. +//===----------------------------------------------------------------------===// + +namespace { + class PlistDiagnostics : public PathDiagnosticConsumer { + const std::string OutputFile; + const Preprocessor &PP; + AnalyzerOptions &AnOpts; + const bool SupportsCrossFileDiagnostics; + public: + PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, + const std::string& prefix, + const Preprocessor &PP, + bool supportsMultipleFiles); + + ~PlistDiagnostics() override {} + + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + FilesMade *filesMade) override; + + StringRef getName() const override { + return "PlistDiagnostics"; + } + + PathGenerationScheme getGenerationScheme() const override { + return Extensive; + } + bool supportsLogicalOpControlFlow() const override { return true; } + bool supportsCrossFileDiagnostics() const override { + return SupportsCrossFileDiagnostics; + } + }; +} // end anonymous namespace + +namespace { + +/// A helper class for emitting a single report. +class PlistPrinter { + const FIDMap& FM; + AnalyzerOptions &AnOpts; + const Preprocessor &PP; + llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces; + +public: + PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts, + const Preprocessor &PP) + : FM(FM), AnOpts(AnOpts), PP(PP) { + } + + void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) { + ReportPiece(o, P, /*indent*/ 4, /*depth*/ 0, /*includeControlFlow*/ true); + + // Don't emit a warning about an unused private field. + (void)AnOpts; + } + + /// Print the expansions of the collected macro pieces. + /// + /// Each time ReportDiag is called on a PathDiagnosticMacroPiece (or, if one + /// is found through a call piece, etc), it's subpieces are reported, and the + /// piece itself is collected. Call this function after the entire bugpath + /// was reported. + void ReportMacroExpansions(raw_ostream &o, unsigned indent); + +private: + void ReportPiece(raw_ostream &o, const PathDiagnosticPiece &P, + unsigned indent, unsigned depth, bool includeControlFlow, + bool isKeyEvent = false) { + switch (P.getKind()) { + case PathDiagnosticPiece::ControlFlow: + if (includeControlFlow) + ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), indent); + break; + case PathDiagnosticPiece::Call: + ReportCall(o, cast<PathDiagnosticCallPiece>(P), indent, + depth); + break; + case PathDiagnosticPiece::Event: + ReportEvent(o, cast<PathDiagnosticEventPiece>(P), indent, depth, + isKeyEvent); + break; + case PathDiagnosticPiece::Macro: + ReportMacroSubPieces(o, cast<PathDiagnosticMacroPiece>(P), indent, + depth); + break; + case PathDiagnosticPiece::Note: + ReportNote(o, cast<PathDiagnosticNotePiece>(P), indent); + break; + } + } + + void EmitRanges(raw_ostream &o, const ArrayRef<SourceRange> Ranges, + unsigned indent); + void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent); + + void ReportControlFlow(raw_ostream &o, + const PathDiagnosticControlFlowPiece& P, + unsigned indent); + void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, + unsigned indent, unsigned depth, bool isKeyEvent = false); + void ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, + unsigned indent, unsigned depth); + void ReportMacroSubPieces(raw_ostream &o, const PathDiagnosticMacroPiece& P, + unsigned indent, unsigned depth); + void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, + unsigned indent); +}; + +} // end of anonymous namespace + +namespace { + +struct ExpansionInfo { + std::string MacroName; + std::string Expansion; + ExpansionInfo(std::string N, std::string E) + : MacroName(std::move(N)), Expansion(std::move(E)) {} +}; + +} // end of anonymous namespace + +static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, + AnalyzerOptions &AnOpts, + const Preprocessor &PP, + const PathPieces &Path); + +/// Print coverage information to output stream {@code o}. +/// May modify the used list of files {@code Fids} by inserting new ones. +static void printCoverage(const PathDiagnostic *D, + unsigned InputIndentLevel, + SmallVectorImpl<FileID> &Fids, + FIDMap &FM, + llvm::raw_fd_ostream &o); + +static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, + const Preprocessor &PP); + +//===----------------------------------------------------------------------===// +// Methods of PlistPrinter. +//===----------------------------------------------------------------------===// + +void PlistPrinter::EmitRanges(raw_ostream &o, + const ArrayRef<SourceRange> Ranges, + unsigned indent) { + + if (Ranges.empty()) + return; + + Indent(o, indent) << "<key>ranges</key>\n"; + Indent(o, indent) << "<array>\n"; + ++indent; + + const SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); + + for (auto &R : Ranges) + EmitRange(o, SM, + Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts), + FM, indent + 1); + --indent; + Indent(o, indent) << "</array>\n"; +} + +void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message, + unsigned indent) { + // Output the text. + assert(!Message.empty()); + Indent(o, indent) << "<key>extended_message</key>\n"; + Indent(o, indent); + EmitString(o, Message) << '\n'; + + // Output the short text. + // FIXME: Really use a short string. + Indent(o, indent) << "<key>message</key>\n"; + Indent(o, indent); + EmitString(o, Message) << '\n'; +} + +void PlistPrinter::ReportControlFlow(raw_ostream &o, + const PathDiagnosticControlFlowPiece& P, + unsigned indent) { + + const SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); + + Indent(o, indent) << "<dict>\n"; + ++indent; + + Indent(o, indent) << "<key>kind</key><string>control</string>\n"; + + // Emit edges. + Indent(o, indent) << "<key>edges</key>\n"; + ++indent; + Indent(o, indent) << "<array>\n"; + ++indent; + for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); + I!=E; ++I) { + Indent(o, indent) << "<dict>\n"; + ++indent; + + // Make the ranges of the start and end point self-consistent with adjacent edges + // by forcing to use only the beginning of the range. This simplifies the layout + // logic for clients. + Indent(o, indent) << "<key>start</key>\n"; + SourceRange StartEdge( + SM.getExpansionLoc(I->getStart().asRange().getBegin())); + EmitRange(o, SM, Lexer::getAsCharRange(StartEdge, SM, LangOpts), FM, + indent + 1); + + Indent(o, indent) << "<key>end</key>\n"; + SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin())); + EmitRange(o, SM, Lexer::getAsCharRange(EndEdge, SM, LangOpts), FM, + indent + 1); + + --indent; + Indent(o, indent) << "</dict>\n"; + } + --indent; + Indent(o, indent) << "</array>\n"; + --indent; + + // Output any helper text. + const auto &s = P.getString(); + if (!s.empty()) { + Indent(o, indent) << "<key>alternate</key>"; + EmitString(o, s) << '\n'; + } + + --indent; + Indent(o, indent) << "</dict>\n"; +} + +void PlistPrinter::ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P, + unsigned indent, unsigned depth, + bool isKeyEvent) { + + const SourceManager &SM = PP.getSourceManager(); + + Indent(o, indent) << "<dict>\n"; + ++indent; + + Indent(o, indent) << "<key>kind</key><string>event</string>\n"; + + if (isKeyEvent) { + Indent(o, indent) << "<key>key_event</key><true/>\n"; + } + + // Output the location. + FullSourceLoc L = P.getLocation().asLocation(); + + Indent(o, indent) << "<key>location</key>\n"; + EmitLocation(o, SM, L, FM, indent); + + // Output the ranges (if any). + ArrayRef<SourceRange> Ranges = P.getRanges(); + EmitRanges(o, Ranges, indent); + + // Output the call depth. + Indent(o, indent) << "<key>depth</key>"; + EmitInteger(o, depth) << '\n'; + + // Output the text. + EmitMessage(o, P.getString(), indent); + + // Finish up. + --indent; + Indent(o, indent); o << "</dict>\n"; +} + +void PlistPrinter::ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P, + unsigned indent, + unsigned depth) { + + if (auto callEnter = P.getCallEnterEvent()) + ReportPiece(o, *callEnter, indent, depth, /*includeControlFlow*/ true, + P.isLastInMainSourceFile()); + + + ++depth; + + if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent()) + ReportPiece(o, *callEnterWithinCaller, indent, depth, + /*includeControlFlow*/ true); + + for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I) + ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ true); + + --depth; + + if (auto callExit = P.getCallExitEvent()) + ReportPiece(o, *callExit, indent, depth, /*includeControlFlow*/ true); +} + +void PlistPrinter::ReportMacroSubPieces(raw_ostream &o, + const PathDiagnosticMacroPiece& P, + unsigned indent, unsigned depth) { + MacroPieces.push_back(&P); + + for (PathPieces::const_iterator I = P.subPieces.begin(), + E = P.subPieces.end(); + I != E; ++I) { + ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ false); + } +} + +void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) { + + for (const PathDiagnosticMacroPiece *P : MacroPieces) { + const SourceManager &SM = PP.getSourceManager(); + ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP); + + Indent(o, indent) << "<dict>\n"; + ++indent; + + // Output the location. + FullSourceLoc L = P->getLocation().asLocation(); + + Indent(o, indent) << "<key>location</key>\n"; + EmitLocation(o, SM, L, FM, indent); + + // Output the ranges (if any). + ArrayRef<SourceRange> Ranges = P->getRanges(); + EmitRanges(o, Ranges, indent); + + // Output the macro name. + Indent(o, indent) << "<key>name</key>"; + EmitString(o, EI.MacroName) << '\n'; + + // Output what it expands into. + Indent(o, indent) << "<key>expansion</key>"; + EmitString(o, EI.Expansion) << '\n'; + + // Finish up. + --indent; + Indent(o, indent); + o << "</dict>\n"; + } +} + +void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P, + unsigned indent) { + + const SourceManager &SM = PP.getSourceManager(); + + Indent(o, indent) << "<dict>\n"; + ++indent; + + // Output the location. + FullSourceLoc L = P.getLocation().asLocation(); + + Indent(o, indent) << "<key>location</key>\n"; + EmitLocation(o, SM, L, FM, indent); + + // Output the ranges (if any). + ArrayRef<SourceRange> Ranges = P.getRanges(); + EmitRanges(o, Ranges, indent); + + // Output the text. + EmitMessage(o, P.getString(), indent); + + // Finish up. + --indent; + Indent(o, indent); o << "</dict>\n"; +} + +//===----------------------------------------------------------------------===// +// Static function definitions. +//===----------------------------------------------------------------------===// + +/// Print coverage information to output stream {@code o}. +/// May modify the used list of files {@code Fids} by inserting new ones. +static void printCoverage(const PathDiagnostic *D, + unsigned InputIndentLevel, + SmallVectorImpl<FileID> &Fids, + FIDMap &FM, + llvm::raw_fd_ostream &o) { + unsigned IndentLevel = InputIndentLevel; + + Indent(o, IndentLevel) << "<key>ExecutedLines</key>\n"; + Indent(o, IndentLevel) << "<dict>\n"; + IndentLevel++; + + // Mapping from file IDs to executed lines. + const FilesToLineNumsMap &ExecutedLines = D->getExecutedLines(); + for (auto I = ExecutedLines.begin(), E = ExecutedLines.end(); I != E; ++I) { + unsigned FileKey = AddFID(FM, Fids, I->first); + Indent(o, IndentLevel) << "<key>" << FileKey << "</key>\n"; + Indent(o, IndentLevel) << "<array>\n"; + IndentLevel++; + for (unsigned LineNo : I->second) { + Indent(o, IndentLevel); + EmitInteger(o, LineNo) << "\n"; + } + IndentLevel--; + Indent(o, IndentLevel) << "</array>\n"; + } + IndentLevel--; + Indent(o, IndentLevel) << "</dict>\n"; + + assert(IndentLevel == InputIndentLevel); +} + +static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM, + AnalyzerOptions &AnOpts, + const Preprocessor &PP, + const PathPieces &Path) { + PlistPrinter Printer(FM, AnOpts, PP); + assert(std::is_partitioned( + Path.begin(), Path.end(), + [](const std::shared_ptr<PathDiagnosticPiece> &E) + { return E->getKind() == PathDiagnosticPiece::Note; }) && + "PathDiagnostic is not partitioned so that notes precede the rest"); + + PathPieces::const_iterator FirstNonNote = std::partition_point( + Path.begin(), Path.end(), + [](const std::shared_ptr<PathDiagnosticPiece> &E) + { return E->getKind() == PathDiagnosticPiece::Note; }); + + PathPieces::const_iterator I = Path.begin(); + + if (FirstNonNote != Path.begin()) { + o << " <key>notes</key>\n" + " <array>\n"; + + for (; I != FirstNonNote; ++I) + Printer.ReportDiag(o, **I); + + o << " </array>\n"; + } + + o << " <key>path</key>\n"; + + o << " <array>\n"; + + for (PathPieces::const_iterator E = Path.end(); I != E; ++I) + Printer.ReportDiag(o, **I); + + o << " </array>\n"; + + if (!AnOpts.ShouldDisplayMacroExpansions) + return; + + o << " <key>macro_expansions</key>\n" + " <array>\n"; + Printer.ReportMacroExpansions(o, /* indent */ 4); + o << " </array>\n"; +} + +//===----------------------------------------------------------------------===// +// Methods of PlistDiagnostics. +//===----------------------------------------------------------------------===// + +PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts, + const std::string& output, + const Preprocessor &PP, + bool supportsMultipleFiles) + : OutputFile(output), PP(PP), AnOpts(AnalyzerOpts), + SupportsCrossFileDiagnostics(supportsMultipleFiles) {} + +void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, + const std::string& s, + const Preprocessor &PP) { + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, + /*supportsMultipleFiles*/ false)); +} + +void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, + const std::string &s, + const Preprocessor &PP) { + C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP, + /*supportsMultipleFiles*/ true)); +} +void PlistDiagnostics::FlushDiagnosticsImpl( + std::vector<const PathDiagnostic *> &Diags, + FilesMade *filesMade) { + // Build up a set of FIDs that we use by scanning the locations and + // ranges of the diagnostics. + FIDMap FM; + SmallVector<FileID, 10> Fids; + const SourceManager& SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); + + auto AddPieceFID = [&FM, &Fids, &SM](const PathDiagnosticPiece &Piece) { + AddFID(FM, Fids, SM, Piece.getLocation().asLocation()); + ArrayRef<SourceRange> Ranges = Piece.getRanges(); + for (const SourceRange &Range : Ranges) { + AddFID(FM, Fids, SM, Range.getBegin()); + AddFID(FM, Fids, SM, Range.getEnd()); + } + }; + + for (const PathDiagnostic *D : Diags) { + + SmallVector<const PathPieces *, 5> WorkList; + WorkList.push_back(&D->path); + + while (!WorkList.empty()) { + const PathPieces &Path = *WorkList.pop_back_val(); + + for (const auto &Iter : Path) { + const PathDiagnosticPiece &Piece = *Iter; + AddPieceFID(Piece); + + if (const PathDiagnosticCallPiece *Call = + dyn_cast<PathDiagnosticCallPiece>(&Piece)) { + if (auto CallEnterWithin = Call->getCallEnterWithinCallerEvent()) + AddPieceFID(*CallEnterWithin); + + if (auto CallEnterEvent = Call->getCallEnterEvent()) + AddPieceFID(*CallEnterEvent); + + WorkList.push_back(&Call->path); + } else if (const PathDiagnosticMacroPiece *Macro = + dyn_cast<PathDiagnosticMacroPiece>(&Piece)) { + WorkList.push_back(&Macro->subPieces); + } + } + } + } + + // Open the file. + std::error_code EC; + llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text); + if (EC) { + llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; + return; + } + + EmitPlistHeader(o); + + // Write the root object: a <dict> containing... + // - "clang_version", the string representation of clang version + // - "files", an <array> mapping from FIDs to file names + // - "diagnostics", an <array> containing the path diagnostics + o << "<dict>\n" << + " <key>clang_version</key>\n"; + EmitString(o, getClangFullVersion()) << '\n'; + o << " <key>diagnostics</key>\n" + " <array>\n"; + + for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(), + DE = Diags.end(); DI!=DE; ++DI) { + + o << " <dict>\n"; + + const PathDiagnostic *D = *DI; + printBugPath(o, FM, AnOpts, PP, D->path); + + // Output the bug type and bug category. + o << " <key>description</key>"; + EmitString(o, D->getShortDescription()) << '\n'; + o << " <key>category</key>"; + EmitString(o, D->getCategory()) << '\n'; + o << " <key>type</key>"; + EmitString(o, D->getBugType()) << '\n'; + o << " <key>check_name</key>"; + EmitString(o, D->getCheckName()) << '\n'; + + o << " <!-- This hash is experimental and going to change! -->\n"; + o << " <key>issue_hash_content_of_line_in_context</key>"; + PathDiagnosticLocation UPDLoc = D->getUniqueingLoc(); + FullSourceLoc L(SM.getExpansionLoc(UPDLoc.isValid() + ? UPDLoc.asLocation() + : D->getLocation().asLocation()), + SM); + const Decl *DeclWithIssue = D->getDeclWithIssue(); + EmitString(o, GetIssueHash(SM, L, D->getCheckName(), D->getBugType(), + DeclWithIssue, LangOpts)) + << '\n'; + + // Output information about the semantic context where + // the issue occurred. + if (const Decl *DeclWithIssue = D->getDeclWithIssue()) { + // FIXME: handle blocks, which have no name. + if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) { + StringRef declKind; + switch (ND->getKind()) { + case Decl::CXXRecord: + declKind = "C++ class"; + break; + case Decl::CXXMethod: + declKind = "C++ method"; + break; + case Decl::ObjCMethod: + declKind = "Objective-C method"; + break; + case Decl::Function: + declKind = "function"; + break; + default: + break; + } + if (!declKind.empty()) { + const std::string &declName = ND->getDeclName().getAsString(); + o << " <key>issue_context_kind</key>"; + EmitString(o, declKind) << '\n'; + o << " <key>issue_context</key>"; + EmitString(o, declName) << '\n'; + } + + // Output the bug hash for issue unique-ing. Currently, it's just an + // offset from the beginning of the function. + if (const Stmt *Body = DeclWithIssue->getBody()) { + + // If the bug uniqueing location exists, use it for the hash. + // For example, this ensures that two leaks reported on the same line + // will have different issue_hashes and that the hash will identify + // the leak location even after code is added between the allocation + // site and the end of scope (leak report location). + if (UPDLoc.isValid()) { + FullSourceLoc UFunL( + SM.getExpansionLoc( + D->getUniqueingDecl()->getBody()->getBeginLoc()), + SM); + o << " <key>issue_hash_function_offset</key><string>" + << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber() + << "</string>\n"; + + // Otherwise, use the location on which the bug is reported. + } else { + FullSourceLoc FunL(SM.getExpansionLoc(Body->getBeginLoc()), SM); + o << " <key>issue_hash_function_offset</key><string>" + << L.getExpansionLineNumber() - FunL.getExpansionLineNumber() + << "</string>\n"; + } + + } + } + } + + // Output the location of the bug. + o << " <key>location</key>\n"; + EmitLocation(o, SM, D->getLocation().asLocation(), FM, 2); + + // Output the diagnostic to the sub-diagnostic client, if any. + if (!filesMade->empty()) { + StringRef lastName; + PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D); + if (files) { + for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(), + CE = files->end(); CI != CE; ++CI) { + StringRef newName = CI->first; + if (newName != lastName) { + if (!lastName.empty()) { + o << " </array>\n"; + } + lastName = newName; + o << " <key>" << lastName << "_files</key>\n"; + o << " <array>\n"; + } + o << " <string>" << CI->second << "</string>\n"; + } + o << " </array>\n"; + } + } + + printCoverage(D, /*IndentLevel=*/2, Fids, FM, o); + + // Close up the entry. + o << " </dict>\n"; + } + + o << " </array>\n"; + + o << " <key>files</key>\n" + " <array>\n"; + for (FileID FID : Fids) + EmitString(o << " ", SM.getFileEntryForID(FID)->getName()) << '\n'; + o << " </array>\n"; + + if (llvm::AreStatisticsEnabled() && AnOpts.ShouldSerializeStats) { + o << " <key>statistics</key>\n"; + std::string stats; + llvm::raw_string_ostream os(stats); + llvm::PrintStatisticsJSON(os); + os.flush(); + EmitString(o, html::EscapeText(stats)) << '\n'; + } + + // Finish. + o << "</dict>\n</plist>"; +} + +//===----------------------------------------------------------------------===// +// Declarations of helper functions and data structures for expanding macros. +//===----------------------------------------------------------------------===// + +namespace { + +using ExpArgTokens = llvm::SmallVector<Token, 2>; + +/// Maps unexpanded macro arguments to expanded arguments. A macro argument may +/// need to expanded further when it is nested inside another macro. +class MacroArgMap : public std::map<const IdentifierInfo *, ExpArgTokens> { +public: + void expandFromPrevMacro(const MacroArgMap &Super); +}; + +struct MacroNameAndArgs { + std::string Name; + const MacroInfo *MI = nullptr; + MacroArgMap Args; + + MacroNameAndArgs(std::string N, const MacroInfo *MI, MacroArgMap M) + : Name(std::move(N)), MI(MI), Args(std::move(M)) {} +}; + +class TokenPrinter { + llvm::raw_ostream &OS; + const Preprocessor &PP; + + Token PrevTok, PrevPrevTok; + TokenConcatenation ConcatInfo; + +public: + TokenPrinter(llvm::raw_ostream &OS, const Preprocessor &PP) + : OS(OS), PP(PP), ConcatInfo(PP) { + PrevTok.setKind(tok::unknown); + PrevPrevTok.setKind(tok::unknown); + } + + void printToken(const Token &Tok); +}; + +} // end of anonymous namespace + +/// The implementation method of getMacroExpansion: It prints the expansion of +/// a macro to \p Printer, and returns with the name of the macro. +/// +/// Since macros can be nested in one another, this function may call itself +/// recursively. +/// +/// Unfortunately, macro arguments have to expanded manually. To understand why, +/// observe the following example: +/// +/// #define PRINT(x) print(x) +/// #define DO_SOMETHING(str) PRINT(str) +/// +/// DO_SOMETHING("Cute panda cubs."); +/// +/// 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); + +/// Retrieves the name of the macro and what it's arguments expand into +/// at \p ExpanLoc. +/// +/// For example, for the following macro expansion: +/// +/// #define SET_TO_NULL(x) x = 0 +/// #define NOT_SUSPICIOUS(a) \ +/// { \ +/// int b = 0; \ +/// } \ +/// SET_TO_NULL(a) +/// +/// int *ptr = new int(4); +/// NOT_SUSPICIOUS(&ptr); +/// *ptr = 5; +/// +/// When \p ExpanLoc references the last line, the macro name "NOT_SUSPICIOUS" +/// and the MacroArgMap map { (a, &ptr) } will be returned. +/// +/// When \p ExpanLoc references "SET_TO_NULL(a)" within the definition of +/// "NOT_SUSPICOUS", the macro name "SET_TO_NULL" and the MacroArgMap map +/// { (x, a) } will be returned. +static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, + const Preprocessor &PP); + +/// Retrieves the ')' token that matches '(' \p It points to. +static MacroInfo::tokens_iterator getMatchingRParen( + MacroInfo::tokens_iterator It, + MacroInfo::tokens_iterator End); + +/// Retrieves the macro info for \p II refers to at \p Loc. This is important +/// because macros can be redefined or undefined. +static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP, + const SourceManager &SM, + const IdentifierInfo *II, + SourceLocation Loc); + +//===----------------------------------------------------------------------===// +// Definitions of helper functions and methods for expanding macros. +//===----------------------------------------------------------------------===// + +static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc, + const Preprocessor &PP) { + + llvm::SmallString<200> ExpansionBuf; + llvm::raw_svector_ostream OS(ExpansionBuf); + TokenPrinter Printer(OS, PP); + std::string MacroName = + getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{}); + return { MacroName, OS.str() }; +} + +static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer, + SourceLocation MacroLoc, + const Preprocessor &PP, + const MacroArgMap &PrevArgs) { + + const SourceManager &SM = PP.getSourceManager(); + + MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP); + + // Manually expand its arguments from the previous macro. + Info.Args.expandFromPrevMacro(PrevArgs); + + // Iterate over the macro's tokens and stringify them. + for (auto It = Info.MI->tokens_begin(), E = Info.MI->tokens_end(); It != E; + ++It) { + Token T = *It; + + // If this token is not an identifier, we only need to print it. + if (T.isNot(tok::identifier)) { + Printer.printToken(T); + continue; + } + + const auto *II = T.getIdentifierInfo(); + assert(II && + "This token is an identifier but has no IdentifierInfo!"); + + // 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 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) + It = getMatchingRParen(++It, E); + continue; + } + + // If this token is the current macro's argument, we should expand it. + auto ArgMapIt = Info.Args.find(II); + if (ArgMapIt != Info.Args.end()) { + for (MacroInfo::tokens_iterator ArgIt = ArgMapIt->second.begin(), + ArgEnd = ArgMapIt->second.end(); + ArgIt != ArgEnd; ++ArgIt) { + + // These tokens may still be macros, if that is the case, handle it the + // same way we did above. + const auto *ArgII = ArgIt->getIdentifierInfo(); + if (!ArgII) { + Printer.printToken(*ArgIt); + continue; + } + + const auto *MI = PP.getMacroInfo(ArgII); + if (!MI) { + Printer.printToken(*ArgIt); + continue; + } + + getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP, + Info.Args); + if (MI->getNumParams() != 0) + ArgIt = getMatchingRParen(++ArgIt, ArgEnd); + } + continue; + } + + // If control reached here, then this token isn't a macro identifier, nor an + // unexpanded macro argument that we need to handle, print it. + Printer.printToken(T); + } + + return Info.Name; +} + +static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc, + const Preprocessor &PP) { + + const SourceManager &SM = PP.getSourceManager(); + const LangOptions &LangOpts = PP.getLangOpts(); + + // First, we create a Lexer to lex *at the expansion location* the tokens + // referring to the macro's name and its arguments. + std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ExpanLoc); + const llvm::MemoryBuffer *MB = SM.getBuffer(LocInfo.first); + const char *MacroNameTokenPos = MB->getBufferStart() + LocInfo.second; + + Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts, + MB->getBufferStart(), MacroNameTokenPos, MB->getBufferEnd()); + + // Acquire the macro's name. + Token TheTok; + RawLexer.LexFromRawLexer(TheTok); + + std::string MacroName = PP.getSpelling(TheTok); + + const auto *II = PP.getIdentifierInfo(MacroName); + 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!"); + + // Acquire the macro's arguments. + // + // The rough idea here is to lex from the first left parentheses to the last + // right parentheses, and map the macro's unexpanded arguments to what they + // will be expanded to. An expanded macro argument may contain several tokens + // (like '3 + 4'), so we'll lex until we find a tok::comma or tok::r_paren, at + // which point we start lexing the next argument or finish. + ArrayRef<const IdentifierInfo *> MacroArgs = MI->params(); + if (MacroArgs.empty()) + return { MacroName, MI, {} }; + + RawLexer.LexFromRawLexer(TheTok); + assert(TheTok.is(tok::l_paren) && + "The token after the macro's identifier token should be '('!"); + + MacroArgMap Args; + + // When the macro's argument is a function call, like + // CALL_FN(someFunctionName(param1, param2)) + // we will find tok::l_paren, tok::r_paren, and tok::comma that do not divide + // actual macro arguments, or do not represent the macro argument's closing + // parentheses, so we'll count how many parentheses aren't closed yet. + // If ParanthesesDepth + // * = 0, then there are no more arguments to lex. + // * = 1, then if we find a tok::comma, we can start lexing the next arg. + // * > 1, then tok::comma is a part of the current arg. + int ParenthesesDepth = 1; + + // If we encounter __VA_ARGS__, we will lex until the closing tok::r_paren, + // even if we lex a tok::comma and ParanthesesDepth == 1. + const IdentifierInfo *__VA_ARGS__II = PP.getIdentifierInfo("__VA_ARGS__"); + + for (const IdentifierInfo *UnexpArgII : MacroArgs) { + MacroArgMap::mapped_type ExpandedArgTokens; + + // One could also simply not supply a single argument to __VA_ARGS__ -- this + // results in a preprocessor warning, but is not an error: + // #define VARIADIC(ptr, ...) \ + // someVariadicTemplateFunction(__VA_ARGS__) + // + // int *ptr; + // VARIADIC(ptr); // Note that there are no commas, this isn't just an + // // empty parameter -- there are no parameters for '...'. + // In any other case, ParenthesesDepth mustn't be 0 here. + if (ParenthesesDepth != 0) { + + // Lex the first token of the next macro parameter. + RawLexer.LexFromRawLexer(TheTok); + + while (!(ParenthesesDepth == 1 && + (UnexpArgII == __VA_ARGS__II ? false : TheTok.is(tok::comma)))) { + assert(TheTok.isNot(tok::eof) && + "EOF encountered while looking for expanded macro args!"); + + if (TheTok.is(tok::l_paren)) + ++ParenthesesDepth; + + if (TheTok.is(tok::r_paren)) + --ParenthesesDepth; + + if (ParenthesesDepth == 0) + break; + + if (TheTok.is(tok::raw_identifier)) + PP.LookUpIdentifierInfo(TheTok); + + ExpandedArgTokens.push_back(TheTok); + RawLexer.LexFromRawLexer(TheTok); + } + } else { + assert(UnexpArgII == __VA_ARGS__II); + } + + Args.emplace(UnexpArgII, std::move(ExpandedArgTokens)); + } + + assert(TheTok.is(tok::r_paren) && + "Expanded macro argument acquisition failed! After the end of the loop" + " this token should be ')'!"); + + return { MacroName, MI, Args }; +} + +static MacroInfo::tokens_iterator getMatchingRParen( + MacroInfo::tokens_iterator It, + MacroInfo::tokens_iterator End) { + + assert(It->is(tok::l_paren) && "This token should be '('!"); + + // Skip until we find the closing ')'. + int ParenthesesDepth = 1; + while (ParenthesesDepth != 0) { + ++It; + + assert(It->isNot(tok::eof) && + "Encountered EOF while attempting to skip macro arguments!"); + assert(It != End && + "End of the macro definition reached before finding ')'!"); + + if (It->is(tok::l_paren)) + ++ParenthesesDepth; + + if (It->is(tok::r_paren)) + --ParenthesesDepth; + } + return It; +} + +static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP, + const SourceManager &SM, + const IdentifierInfo *II, + SourceLocation Loc) { + + const MacroDirective *MD = PP.getLocalMacroDirectiveHistory(II); + if (!MD) + return nullptr; + + return MD->findDirectiveAtLoc(Loc, SM).getMacroInfo(); +} + +void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) { + + for (value_type &Pair : *this) { + ExpArgTokens &CurrExpArgTokens = Pair.second; + + // For each token in the expanded macro argument. + auto It = CurrExpArgTokens.begin(); + while (It != CurrExpArgTokens.end()) { + if (It->isNot(tok::identifier)) { + ++It; + continue; + } + + const auto *II = It->getIdentifierInfo(); + assert(II); + + // Is this an argument that "Super" expands further? + if (!Super.count(II)) { + ++It; + continue; + } + + const ExpArgTokens &SuperExpArgTokens = Super.at(II); + + It = CurrExpArgTokens.insert( + It, SuperExpArgTokens.begin(), SuperExpArgTokens.end()); + std::advance(It, SuperExpArgTokens.size()); + It = CurrExpArgTokens.erase(It); + } + } +} + +void TokenPrinter::printToken(const Token &Tok) { + // If this is the first token to be printed, don't print space. + if (PrevTok.isNot(tok::unknown)) { + // If the tokens were already space separated, or if they must be to avoid + // them being implicitly pasted, add a space between them. + if(Tok.hasLeadingSpace() || ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok, + Tok)) { + // AvoidConcat doesn't check for ##, don't print a space around it. + if (PrevTok.isNot(tok::hashhash) && Tok.isNot(tok::hashhash)) { + OS << ' '; + } + } + } + + if (!Tok.isOneOf(tok::hash, tok::hashhash)) { + if (PrevTok.is(tok::hash)) + OS << '\"' << PP.getSpelling(Tok) << '\"'; + else + OS << PP.getSpelling(Tok); + } + + PrevPrevTok = PrevTok; + PrevTok = Tok; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h new file mode 100644 index 000000000000..4bb694819c2a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h @@ -0,0 +1,45 @@ +//==- 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_PRETTYSTACKTRACELOCATIONCONTEXT_H +#define LLVM_CLANG_LIB_STATICANALYZER_CORE_PRETTYSTACKTRACELOCATIONCONTEXT_H + +#include "clang/Analysis/AnalysisDeclContext.h" + +namespace clang { +namespace ento { + +/// While alive, includes the current analysis stack in a crash trace. +/// +/// Example: +/// \code +/// 0. Program arguments: ... +/// 1. <eof> parser at end of file +/// 2. While analyzing stack: +/// #0 void inlined() +/// #1 void test() +/// 3. crash-trace.c:6:3: Error evaluating statement +/// \endcode +class PrettyStackTraceLocationContext : public llvm::PrettyStackTraceEntry { + const LocationContext *LCtx; +public: + PrettyStackTraceLocationContext(const LocationContext *LC) : LCtx(LC) { + assert(LCtx); + } + + void print(raw_ostream &OS) const override { + OS << "While analyzing stack: \n"; + LCtx->dumpStack(OS, "\t"); + } +}; + +} // end ento namespace +} // end clang namespace + +#endif diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp new file mode 100644 index 000000000000..2e2e2ec94f39 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp @@ -0,0 +1,823 @@ +//= 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. +// +//===----------------------------------------------------------------------===// +// +// This file implements ProgramState and ProgramStateManager. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/Analysis/CFG.h" +#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" + +using namespace clang; +using namespace ento; + +namespace clang { namespace ento { +/// Increments the number of times this state is referenced. + +void ProgramStateRetain(const ProgramState *state) { + ++const_cast<ProgramState*>(state)->refCount; +} + +/// Decrement the number of times this state is referenced. +void ProgramStateRelease(const ProgramState *state) { + assert(state->refCount > 0); + ProgramState *s = const_cast<ProgramState*>(state); + if (--s->refCount == 0) { + ProgramStateManager &Mgr = s->getStateManager(); + Mgr.StateSet.RemoveNode(s); + s->~ProgramState(); + Mgr.freeStates.push_back(s); + } +} +}} + +ProgramState::ProgramState(ProgramStateManager *mgr, const Environment& env, + StoreRef st, GenericDataMap gdm) + : stateMgr(mgr), + Env(env), + store(st.getStore()), + GDM(gdm), + refCount(0) { + stateMgr->getStoreManager().incrementReferenceCount(store); +} + +ProgramState::ProgramState(const ProgramState &RHS) + : llvm::FoldingSetNode(), + stateMgr(RHS.stateMgr), + Env(RHS.Env), + store(RHS.store), + GDM(RHS.GDM), + refCount(0) { + stateMgr->getStoreManager().incrementReferenceCount(store); +} + +ProgramState::~ProgramState() { + if (store) + stateMgr->getStoreManager().decrementReferenceCount(store); +} + +int64_t ProgramState::getID() const { + return getStateManager().Alloc.identifyKnownAlignedObject<ProgramState>(this); +} + +ProgramStateManager::ProgramStateManager(ASTContext &Ctx, + StoreManagerCreator CreateSMgr, + ConstraintManagerCreator CreateCMgr, + llvm::BumpPtrAllocator &alloc, + SubEngine *SubEng) + : Eng(SubEng), EnvMgr(alloc), GDMFactory(alloc), + svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)), + CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) { + StoreMgr = (*CreateSMgr)(*this); + ConstraintMgr = (*CreateCMgr)(*this, SubEng); +} + + +ProgramStateManager::~ProgramStateManager() { + for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end(); + I!=E; ++I) + I->second.second(I->second.first); +} + +ProgramStateRef +ProgramStateManager::removeDeadBindings(ProgramStateRef state, + const StackFrameContext *LCtx, + SymbolReaper& SymReaper) { + + // This code essentially performs a "mark-and-sweep" of the VariableBindings. + // The roots are any Block-level exprs and Decls that our liveness algorithm + // tells us are live. We then see what Decls they may reference, and keep + // those around. This code more than likely can be made faster, and the + // frequency of which this method is called should be experimented with + // for optimum performance. + ProgramState NewState = *state; + + NewState.Env = EnvMgr.removeDeadBindings(NewState.Env, SymReaper, state); + + // Clean up the store. + StoreRef newStore = StoreMgr->removeDeadBindings(NewState.getStore(), LCtx, + SymReaper); + NewState.setStore(newStore); + SymReaper.setReapedStore(newStore); + + ProgramStateRef Result = getPersistentState(NewState); + return ConstraintMgr->removeDeadBindings(Result, SymReaper); +} + +ProgramStateRef ProgramState::bindLoc(Loc LV, + SVal V, + const LocationContext *LCtx, + bool notifyChanges) const { + ProgramStateManager &Mgr = getStateManager(); + ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(), + LV, V)); + const MemRegion *MR = LV.getAsRegion(); + if (MR && notifyChanges) + return Mgr.getOwningEngine().processRegionChange(newState, MR, LCtx); + + return newState; +} + +ProgramStateRef +ProgramState::bindDefaultInitial(SVal loc, SVal V, + const LocationContext *LCtx) const { + ProgramStateManager &Mgr = getStateManager(); + const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion(); + const StoreRef &newStore = Mgr.StoreMgr->BindDefaultInitial(getStore(), R, V); + ProgramStateRef new_state = makeWithStore(newStore); + return Mgr.getOwningEngine().processRegionChange(new_state, R, LCtx); +} + +ProgramStateRef +ProgramState::bindDefaultZero(SVal loc, const LocationContext *LCtx) const { + ProgramStateManager &Mgr = getStateManager(); + const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion(); + const StoreRef &newStore = Mgr.StoreMgr->BindDefaultZero(getStore(), R); + ProgramStateRef new_state = makeWithStore(newStore); + return Mgr.getOwningEngine().processRegionChange(new_state, R, LCtx); +} + +typedef ArrayRef<const MemRegion *> RegionList; +typedef ArrayRef<SVal> ValueList; + +ProgramStateRef +ProgramState::invalidateRegions(RegionList Regions, + const Expr *E, unsigned Count, + const LocationContext *LCtx, + bool CausedByPointerEscape, + InvalidatedSymbols *IS, + const CallEvent *Call, + RegionAndSymbolInvalidationTraits *ITraits) const { + SmallVector<SVal, 8> Values; + for (RegionList::const_iterator I = Regions.begin(), + End = Regions.end(); I != End; ++I) + Values.push_back(loc::MemRegionVal(*I)); + + return invalidateRegionsImpl(Values, E, Count, LCtx, CausedByPointerEscape, + IS, ITraits, Call); +} + +ProgramStateRef +ProgramState::invalidateRegions(ValueList Values, + const Expr *E, unsigned Count, + const LocationContext *LCtx, + bool CausedByPointerEscape, + InvalidatedSymbols *IS, + const CallEvent *Call, + RegionAndSymbolInvalidationTraits *ITraits) const { + + return invalidateRegionsImpl(Values, E, Count, LCtx, CausedByPointerEscape, + IS, ITraits, Call); +} + +ProgramStateRef +ProgramState::invalidateRegionsImpl(ValueList Values, + const Expr *E, unsigned Count, + const LocationContext *LCtx, + bool CausedByPointerEscape, + InvalidatedSymbols *IS, + RegionAndSymbolInvalidationTraits *ITraits, + const CallEvent *Call) const { + ProgramStateManager &Mgr = getStateManager(); + SubEngine &Eng = Mgr.getOwningEngine(); + + InvalidatedSymbols InvalidatedSyms; + if (!IS) + IS = &InvalidatedSyms; + + RegionAndSymbolInvalidationTraits ITraitsLocal; + if (!ITraits) + ITraits = &ITraitsLocal; + + StoreManager::InvalidatedRegions TopLevelInvalidated; + StoreManager::InvalidatedRegions Invalidated; + const StoreRef &newStore + = Mgr.StoreMgr->invalidateRegions(getStore(), Values, E, Count, LCtx, Call, + *IS, *ITraits, &TopLevelInvalidated, + &Invalidated); + + ProgramStateRef newState = makeWithStore(newStore); + + if (CausedByPointerEscape) { + newState = Eng.notifyCheckersOfPointerEscape(newState, IS, + TopLevelInvalidated, + Call, + *ITraits); + } + + return Eng.processRegionChanges(newState, IS, TopLevelInvalidated, + Invalidated, LCtx, Call); +} + +ProgramStateRef ProgramState::killBinding(Loc LV) const { + assert(!LV.getAs<loc::MemRegionVal>() && "Use invalidateRegion instead."); + + Store OldStore = getStore(); + const StoreRef &newStore = + getStateManager().StoreMgr->killBinding(OldStore, LV); + + if (newStore.getStore() == OldStore) + return this; + + return makeWithStore(newStore); +} + +ProgramStateRef +ProgramState::enterStackFrame(const CallEvent &Call, + const StackFrameContext *CalleeCtx) const { + const StoreRef &NewStore = + getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx); + return makeWithStore(NewStore); +} + +SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const { + // We only want to do fetches from regions that we can actually bind + // values. For example, SymbolicRegions of type 'id<...>' cannot + // have direct bindings (but their can be bindings on their subregions). + if (!R->isBoundable()) + return UnknownVal(); + + if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) { + QualType T = TR->getValueType(); + if (Loc::isLocType(T) || T->isIntegralOrEnumerationType()) + return getSVal(R); + } + + return UnknownVal(); +} + +SVal ProgramState::getSVal(Loc location, QualType T) const { + SVal V = getRawSVal(location, T); + + // If 'V' is a symbolic value that is *perfectly* constrained to + // be a constant value, use that value instead to lessen the burden + // on later analysis stages (so we have less symbolic values to reason + // about). + // We only go into this branch if we can convert the APSInt value we have + // to the type of T, which is not always the case (e.g. for void). + if (!T.isNull() && (T->isIntegralOrEnumerationType() || Loc::isLocType(T))) { + if (SymbolRef sym = V.getAsSymbol()) { + if (const llvm::APSInt *Int = getStateManager() + .getConstraintManager() + .getSymVal(this, sym)) { + // FIXME: Because we don't correctly model (yet) sign-extension + // and truncation of symbolic values, we need to convert + // the integer value to the correct signedness and bitwidth. + // + // This shows up in the following: + // + // char foo(); + // unsigned x = foo(); + // if (x == 54) + // ... + // + // The symbolic value stored to 'x' is actually the conjured + // symbol for the call to foo(); the type of that symbol is 'char', + // not unsigned. + const llvm::APSInt &NewV = getBasicVals().Convert(T, *Int); + + if (V.getAs<Loc>()) + return loc::ConcreteInt(NewV); + else + return nonloc::ConcreteInt(NewV); + } + } + } + + return V; +} + +ProgramStateRef ProgramState::BindExpr(const Stmt *S, + const LocationContext *LCtx, + SVal V, bool Invalidate) const{ + Environment NewEnv = + getStateManager().EnvMgr.bindExpr(Env, EnvironmentEntry(S, LCtx), V, + Invalidate); + if (NewEnv == Env) + return this; + + ProgramState NewSt = *this; + NewSt.Env = NewEnv; + return getStateManager().getPersistentState(NewSt); +} + +ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx, + DefinedOrUnknownSVal UpperBound, + bool Assumption, + QualType indexTy) const { + if (Idx.isUnknown() || UpperBound.isUnknown()) + return this; + + // Build an expression for 0 <= Idx < UpperBound. + // This is the same as Idx + MIN < UpperBound + MIN, if overflow is allowed. + // FIXME: This should probably be part of SValBuilder. + ProgramStateManager &SM = getStateManager(); + SValBuilder &svalBuilder = SM.getSValBuilder(); + ASTContext &Ctx = svalBuilder.getContext(); + + // Get the offset: the minimum value of the array index type. + BasicValueFactory &BVF = svalBuilder.getBasicValueFactory(); + if (indexTy.isNull()) + indexTy = svalBuilder.getArrayIndexType(); + nonloc::ConcreteInt Min(BVF.getMinValue(indexTy)); + + // Adjust the index. + SVal newIdx = svalBuilder.evalBinOpNN(this, BO_Add, + Idx.castAs<NonLoc>(), Min, indexTy); + if (newIdx.isUnknownOrUndef()) + return this; + + // Adjust the upper bound. + SVal newBound = + svalBuilder.evalBinOpNN(this, BO_Add, UpperBound.castAs<NonLoc>(), + Min, indexTy); + + if (newBound.isUnknownOrUndef()) + return this; + + // Build the actual comparison. + SVal inBound = svalBuilder.evalBinOpNN(this, BO_LT, newIdx.castAs<NonLoc>(), + newBound.castAs<NonLoc>(), Ctx.IntTy); + if (inBound.isUnknownOrUndef()) + return this; + + // Finally, let the constraint manager take care of it. + ConstraintManager &CM = SM.getConstraintManager(); + return CM.assume(this, inBound.castAs<DefinedSVal>(), Assumption); +} + +ConditionTruthVal ProgramState::isNonNull(SVal V) const { + ConditionTruthVal IsNull = isNull(V); + if (IsNull.isUnderconstrained()) + return IsNull; + return ConditionTruthVal(!IsNull.getValue()); +} + +ConditionTruthVal ProgramState::areEqual(SVal Lhs, SVal Rhs) const { + return stateMgr->getSValBuilder().areEqual(this, Lhs, Rhs); +} + +ConditionTruthVal ProgramState::isNull(SVal V) const { + if (V.isZeroConstant()) + return true; + + if (V.isConstant()) + return false; + + SymbolRef Sym = V.getAsSymbol(/* IncludeBaseRegion */ true); + if (!Sym) + return ConditionTruthVal(); + + return getStateManager().ConstraintMgr->isNull(this, Sym); +} + +ProgramStateRef ProgramStateManager::getInitialState(const LocationContext *InitLoc) { + ProgramState State(this, + EnvMgr.getInitialEnvironment(), + StoreMgr->getInitialStore(InitLoc), + GDMFactory.getEmptyMap()); + + return getPersistentState(State); +} + +ProgramStateRef ProgramStateManager::getPersistentStateWithGDM( + ProgramStateRef FromState, + ProgramStateRef GDMState) { + ProgramState NewState(*FromState); + NewState.GDM = GDMState->GDM; + return getPersistentState(NewState); +} + +ProgramStateRef ProgramStateManager::getPersistentState(ProgramState &State) { + + llvm::FoldingSetNodeID ID; + State.Profile(ID); + void *InsertPos; + + if (ProgramState *I = StateSet.FindNodeOrInsertPos(ID, InsertPos)) + return I; + + ProgramState *newState = nullptr; + if (!freeStates.empty()) { + newState = freeStates.back(); + freeStates.pop_back(); + } + else { + newState = (ProgramState*) Alloc.Allocate<ProgramState>(); + } + new (newState) ProgramState(State); + StateSet.InsertNode(newState, InsertPos); + return newState; +} + +ProgramStateRef ProgramState::makeWithStore(const StoreRef &store) const { + ProgramState NewSt(*this); + NewSt.setStore(store); + return getStateManager().getPersistentState(NewSt); +} + +void ProgramState::setStore(const StoreRef &newStore) { + Store newStoreStore = newStore.getStore(); + if (newStoreStore) + stateMgr->getStoreManager().incrementReferenceCount(newStoreStore); + if (store) + stateMgr->getStoreManager().decrementReferenceCount(store); + store = newStoreStore; +} + +//===----------------------------------------------------------------------===// +// State pretty-printing. +//===----------------------------------------------------------------------===// + +void ProgramState::print(raw_ostream &Out, + const char *NL, const char *Sep, + const LocationContext *LC) const { + // Print the store. + ProgramStateManager &Mgr = getStateManager(); + const ASTContext &Context = getStateManager().getContext(); + Mgr.getStoreManager().print(getStore(), Out, NL); + + // Print out the environment. + Env.print(Out, NL, Sep, Context, LC); + + // Print out the constraints. + Mgr.getConstraintManager().print(this, Out, NL, Sep); + + // 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); +} + +void ProgramState::printDOT(raw_ostream &Out, + const LocationContext *LC) const { + print(Out, "\\l", "\\|", LC); +} + +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(); +} + +//===----------------------------------------------------------------------===// +// Generic Data Map. +//===----------------------------------------------------------------------===// + +void *const* ProgramState::FindGDM(void *K) const { + return GDM.lookup(K); +} + +void* +ProgramStateManager::FindGDMContext(void *K, + void *(*CreateContext)(llvm::BumpPtrAllocator&), + void (*DeleteContext)(void*)) { + + std::pair<void*, void (*)(void*)>& p = GDMContexts[K]; + if (!p.first) { + p.first = CreateContext(Alloc); + p.second = DeleteContext; + } + + return p.first; +} + +ProgramStateRef ProgramStateManager::addGDM(ProgramStateRef St, void *Key, void *Data){ + ProgramState::GenericDataMap M1 = St->getGDM(); + ProgramState::GenericDataMap M2 = GDMFactory.add(M1, Key, Data); + + if (M1 == M2) + return St; + + ProgramState NewSt = *St; + NewSt.GDM = M2; + return getPersistentState(NewSt); +} + +ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, void *Key) { + ProgramState::GenericDataMap OldM = state->getGDM(); + ProgramState::GenericDataMap NewM = GDMFactory.remove(OldM, Key); + + if (NewM == OldM) + return state; + + ProgramState NewState = *state; + NewState.GDM = NewM; + return getPersistentState(NewState); +} + +bool ScanReachableSymbols::scan(nonloc::LazyCompoundVal val) { + bool wasVisited = !visited.insert(val.getCVData()).second; + if (wasVisited) + return true; + + StoreManager &StoreMgr = state->getStateManager().getStoreManager(); + // FIXME: We don't really want to use getBaseRegion() here because pointer + // arithmetic doesn't apply, but scanReachableSymbols only accepts base + // regions right now. + const MemRegion *R = val.getRegion()->getBaseRegion(); + return StoreMgr.scanReachableSymbols(val.getStore(), R, *this); +} + +bool ScanReachableSymbols::scan(nonloc::CompoundVal val) { + for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I) + if (!scan(*I)) + return false; + + return true; +} + +bool ScanReachableSymbols::scan(const SymExpr *sym) { + for (SymExpr::symbol_iterator SI = sym->symbol_begin(), + SE = sym->symbol_end(); + SI != SE; ++SI) { + bool wasVisited = !visited.insert(*SI).second; + if (wasVisited) + continue; + + if (!visitor.VisitSymbol(*SI)) + return false; + } + + return true; +} + +bool ScanReachableSymbols::scan(SVal val) { + if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) + return scan(X->getRegion()); + + if (Optional<nonloc::LazyCompoundVal> X = + val.getAs<nonloc::LazyCompoundVal>()) + return scan(*X); + + if (Optional<nonloc::LocAsInteger> X = val.getAs<nonloc::LocAsInteger>()) + return scan(X->getLoc()); + + if (SymbolRef Sym = val.getAsSymbol()) + return scan(Sym); + + if (const SymExpr *Sym = val.getAsSymbolicExpression()) + return scan(Sym); + + if (Optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>()) + return scan(*X); + + return true; +} + +bool ScanReachableSymbols::scan(const MemRegion *R) { + if (isa<MemSpaceRegion>(R)) + return true; + + bool wasVisited = !visited.insert(R).second; + if (wasVisited) + return true; + + if (!visitor.VisitMemRegion(R)) + return false; + + // If this is a symbolic region, visit the symbol for the region. + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) + if (!visitor.VisitSymbol(SR->getSymbol())) + return false; + + // If this is a subregion, also visit the parent regions. + if (const SubRegion *SR = dyn_cast<SubRegion>(R)) { + const MemRegion *Super = SR->getSuperRegion(); + if (!scan(Super)) + return false; + + // When we reach the topmost region, scan all symbols in it. + if (isa<MemSpaceRegion>(Super)) { + StoreManager &StoreMgr = state->getStateManager().getStoreManager(); + if (!StoreMgr.scanReachableSymbols(state->getStore(), SR, *this)) + return false; + } + } + + // Regions captured by a block are also implicitly reachable. + if (const BlockDataRegion *BDR = dyn_cast<BlockDataRegion>(R)) { + BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(), + E = BDR->referenced_vars_end(); + for ( ; I != E; ++I) { + if (!scan(I.getCapturedRegion())) + return false; + } + } + + return true; +} + +bool ProgramState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const { + ScanReachableSymbols S(this, visitor); + return S.scan(val); +} + +bool ProgramState::scanReachableSymbols( + llvm::iterator_range<region_iterator> Reachable, + SymbolVisitor &visitor) const { + ScanReachableSymbols S(this, visitor); + for (const MemRegion *R : Reachable) { + if (!S.scan(R)) + return false; + } + 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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp new file mode 100644 index 000000000000..d9b58d0f5185 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -0,0 +1,751 @@ +//== 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines RangeConstraintManager, a class that tracks simple +// equality and inequality constraints on symbolic values of ProgramState. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +void RangeSet::IntersectInRange(BasicValueFactory &BV, Factory &F, + const llvm::APSInt &Lower, const llvm::APSInt &Upper, + PrimRangeSet &newRanges, PrimRangeSet::iterator &i, + PrimRangeSet::iterator &e) const { + // There are six cases for each range R in the set: + // 1. R is entirely before the intersection range. + // 2. R is entirely after the intersection range. + // 3. R contains the entire intersection range. + // 4. R starts before the intersection range and ends in the middle. + // 5. R starts in the middle of the intersection range and ends after it. + // 6. R is entirely contained in the intersection range. + // These correspond to each of the conditions below. + for (/* i = begin(), e = end() */; i != e; ++i) { + if (i->To() < Lower) { + continue; + } + if (i->From() > Upper) { + break; + } + + if (i->Includes(Lower)) { + if (i->Includes(Upper)) { + newRanges = + F.add(newRanges, Range(BV.getValue(Lower), BV.getValue(Upper))); + break; + } else + newRanges = F.add(newRanges, Range(BV.getValue(Lower), i->To())); + } else { + if (i->Includes(Upper)) { + newRanges = F.add(newRanges, Range(i->From(), BV.getValue(Upper))); + break; + } else + newRanges = F.add(newRanges, *i); + } + } +} + +const llvm::APSInt &RangeSet::getMinValue() const { + assert(!isEmpty()); + return ranges.begin()->From(); +} + +bool RangeSet::pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const { + // This function has nine cases, the cartesian product of range-testing + // both the upper and lower bounds against the symbol's type. + // Each case requires a different pinning operation. + // The function returns false if the described range is entirely outside + // the range of values for the associated symbol. + APSIntType Type(getMinValue()); + APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower, true); + APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper, true); + + switch (LowerTest) { + case APSIntType::RTR_Below: + switch (UpperTest) { + case APSIntType::RTR_Below: + // The entire range is outside the symbol's set of possible values. + // If this is a conventionally-ordered range, the state is infeasible. + if (Lower <= Upper) + return false; + + // However, if the range wraps around, it spans all possible values. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); + break; + case APSIntType::RTR_Within: + // The range starts below what's possible but ends within it. Pin. + Lower = Type.getMinValue(); + Type.apply(Upper); + break; + case APSIntType::RTR_Above: + // The range spans all possible values for the symbol. Pin. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); + break; + } + break; + case APSIntType::RTR_Within: + switch (UpperTest) { + case APSIntType::RTR_Below: + // The range wraps around, but all lower values are not possible. + Type.apply(Lower); + Upper = Type.getMaxValue(); + break; + case APSIntType::RTR_Within: + // The range may or may not wrap around, but both limits are valid. + Type.apply(Lower); + Type.apply(Upper); + break; + case APSIntType::RTR_Above: + // The range starts within what's possible but ends above it. Pin. + Type.apply(Lower); + Upper = Type.getMaxValue(); + break; + } + break; + case APSIntType::RTR_Above: + switch (UpperTest) { + case APSIntType::RTR_Below: + // The range wraps but is outside the symbol's set of possible values. + return false; + case APSIntType::RTR_Within: + // The range starts above what's possible but ends within it (wrap). + Lower = Type.getMinValue(); + Type.apply(Upper); + break; + case APSIntType::RTR_Above: + // The entire range is outside the symbol's set of possible values. + // If this is a conventionally-ordered range, the state is infeasible. + if (Lower <= Upper) + return false; + + // However, if the range wraps around, it spans all possible values. + Lower = Type.getMinValue(); + Upper = Type.getMaxValue(); + break; + } + break; + } + + return true; +} + +// Returns a set containing the values in the receiving set, intersected with +// the closed range [Lower, Upper]. Unlike the Range type, this range uses +// modular arithmetic, corresponding to the common treatment of C integer +// overflow. Thus, if the Lower bound is greater than the Upper bound, the +// range is taken to wrap around. This is equivalent to taking the +// intersection with the two ranges [Min, Upper] and [Lower, Max], +// or, alternatively, /removing/ all integers between Upper and Lower. +RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F, + llvm::APSInt Lower, llvm::APSInt Upper) const { + if (!pin(Lower, Upper)) + return F.getEmptySet(); + + PrimRangeSet newRanges = F.getEmptySet(); + + PrimRangeSet::iterator i = begin(), e = end(); + if (Lower <= Upper) + IntersectInRange(BV, F, Lower, Upper, newRanges, i, e); + else { + // The order of the next two statements is important! + // IntersectInRange() does not reset the iteration state for i and e. + // Therefore, the lower range most be handled first. + IntersectInRange(BV, F, BV.getMinValue(Upper), Upper, newRanges, i, e); + IntersectInRange(BV, F, Lower, BV.getMaxValue(Lower), newRanges, i, e); + } + + 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. +RangeSet RangeSet::Negate(BasicValueFactory &BV, Factory &F) const { + PrimRangeSet newRanges = F.getEmptySet(); + + for (iterator i = begin(), e = end(); i != e; ++i) { + const llvm::APSInt &from = i->From(), &to = i->To(); + const llvm::APSInt &newTo = (from.isMinSignedValue() ? + BV.getMaxValue(from) : + BV.getValue(- from)); + if (to.isMaxSignedValue() && !newRanges.isEmpty() && + newRanges.begin()->From().isMinSignedValue()) { + assert(newRanges.begin()->To().isMinSignedValue() && + "Ranges should not overlap"); + assert(!from.isMinSignedValue() && "Ranges should not overlap"); + const llvm::APSInt &newFrom = newRanges.begin()->From(); + newRanges = + F.add(F.remove(newRanges, *newRanges.begin()), Range(newFrom, newTo)); + } else if (!to.isMinSignedValue()) { + const llvm::APSInt &newFrom = BV.getValue(- to); + newRanges = F.add(newRanges, Range(newFrom, newTo)); + } + if (from.isMinSignedValue()) { + newRanges = F.add(newRanges, Range(BV.getMinValue(from), + BV.getMinValue(from))); + } + } + + return newRanges; +} + +void RangeSet::print(raw_ostream &os) const { + bool isFirst = true; + os << "{ "; + for (iterator i = begin(), e = end(); i != e; ++i) { + if (isFirst) + isFirst = false; + else + os << ", "; + + os << '[' << i->From().toString(10) << ", " << i->To().toString(10) + << ']'; + } + os << " }"; +} + +namespace { +class RangeConstraintManager : public RangedConstraintManager { +public: + RangeConstraintManager(SubEngine *SE, SValBuilder &SVB) + : RangedConstraintManager(SE, SVB) {} + + //===------------------------------------------------------------------===// + // Implementation for interface from ConstraintManager. + //===------------------------------------------------------------------===// + + bool canReasonAbout(SVal X) const override; + + ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override; + + const llvm::APSInt *getSymVal(ProgramStateRef State, + SymbolRef Sym) const override; + + ProgramStateRef removeDeadBindings(ProgramStateRef State, + SymbolReaper &SymReaper) override; + + void print(ProgramStateRef State, raw_ostream &Out, const char *nl, + const char *sep) override; + + //===------------------------------------------------------------------===// + // Implementation for interface from RangedConstraintManager. + //===------------------------------------------------------------------===// + + ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; + + ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; + + ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; + + ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; + + ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; + + ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) override; + + ProgramStateRef assumeSymWithinInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) override; + + ProgramStateRef assumeSymOutsideInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) override; + +private: + RangeSet::Factory F; + + RangeSet getRange(ProgramStateRef State, SymbolRef Sym); + const RangeSet* getRangeForMinusSymbol(ProgramStateRef State, + SymbolRef Sym); + + RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment); + RangeSet getSymGTRange(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment); + RangeSet getSymLERange(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment); + RangeSet getSymLERange(llvm::function_ref<RangeSet()> RS, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment); + RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment); + +}; + +} // end anonymous namespace + +std::unique_ptr<ConstraintManager> +ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) { + return llvm::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder()); +} + +bool RangeConstraintManager::canReasonAbout(SVal X) const { + Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>(); + if (SymVal && SymVal->isExpression()) { + const SymExpr *SE = SymVal->getSymbol(); + + if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) { + switch (SIE->getOpcode()) { + // We don't reason yet about bitwise-constraints on symbolic values. + case BO_And: + case BO_Or: + case BO_Xor: + return false; + // We don't reason yet about these arithmetic constraints on + // symbolic values. + case BO_Mul: + case BO_Div: + case BO_Rem: + case BO_Shl: + case BO_Shr: + return false; + // All other cases. + default: + return true; + } + } + + if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) { + // FIXME: Handle <=> here. + if (BinaryOperator::isEqualityOp(SSE->getOpcode()) || + BinaryOperator::isRelationalOp(SSE->getOpcode())) { + // We handle Loc <> Loc comparisons, but not (yet) NonLoc <> NonLoc. + // We've recently started producing Loc <> NonLoc comparisons (that + // result from casts of one of the operands between eg. intptr_t and + // void *), but we can't reason about them yet. + if (Loc::isLocType(SSE->getLHS()->getType())) { + return Loc::isLocType(SSE->getRHS()->getType()); + } + } + } + + return false; + } + + return true; +} + +ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State, + SymbolRef Sym) { + const RangeSet *Ranges = State->get<ConstraintRange>(Sym); + + // If we don't have any information about this symbol, it's underconstrained. + if (!Ranges) + return ConditionTruthVal(); + + // If we have a concrete value, see if it's zero. + if (const llvm::APSInt *Value = Ranges->getConcreteValue()) + return *Value == 0; + + BasicValueFactory &BV = getBasicVals(); + APSIntType IntType = BV.getAPSIntType(Sym->getType()); + llvm::APSInt Zero = IntType.getZeroValue(); + + // Check if zero is in the set of possible values. + if (Ranges->Intersect(BV, F, Zero, Zero).isEmpty()) + return false; + + // Zero is a possible value, but it is not the /only/ possible value. + return ConditionTruthVal(); +} + +const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St, + SymbolRef Sym) const { + const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(Sym); + return T ? T->getConcreteValue() : nullptr; +} + +/// Scan all symbols referenced by the constraints. If the symbol is not alive +/// as marked in LSymbols, mark it as dead in DSymbols. +ProgramStateRef +RangeConstraintManager::removeDeadBindings(ProgramStateRef State, + SymbolReaper &SymReaper) { + bool Changed = false; + ConstraintRangeTy CR = State->get<ConstraintRange>(); + ConstraintRangeTy::Factory &CRFactory = State->get_context<ConstraintRange>(); + + for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) { + SymbolRef Sym = I.getKey(); + if (SymReaper.isDead(Sym)) { + Changed = true; + CR = CRFactory.remove(CR, Sym); + } + } + + return Changed ? State->set<ConstraintRange>(CR) : State; +} + +/// Return a range set subtracting zero from \p Domain. +static RangeSet assumeNonZero( + BasicValueFactory &BV, + RangeSet::Factory &F, + SymbolRef Sym, + RangeSet Domain) { + APSIntType IntType = BV.getAPSIntType(Sym->getType()); + return Domain.Intersect(BV, F, ++IntType.getZeroValue(), + --IntType.getZeroValue()); +} + +/// Apply implicit constraints for bitwise OR- and AND-. +/// For unsigned types, bitwise OR with a constant always returns +/// a value greater-or-equal than the constant, and bitwise AND +/// returns a value less-or-equal then the constant. +/// +/// Pattern matches the expression \p Sym against those rule, +/// and applies the required constraints. +/// \p Input Previously established expression range set +static RangeSet applyBitwiseConstraints( + BasicValueFactory &BV, + RangeSet::Factory &F, + RangeSet Input, + const SymIntExpr* SIE) { + QualType T = SIE->getType(); + bool IsUnsigned = T->isUnsignedIntegerType(); + const llvm::APSInt &RHS = SIE->getRHS(); + const llvm::APSInt &Zero = BV.getAPSIntType(T).getZeroValue(); + BinaryOperator::Opcode Operator = SIE->getOpcode(); + + // For unsigned types, the output of bitwise-or is bigger-or-equal than RHS. + if (Operator == BO_Or && IsUnsigned) + return Input.Intersect(BV, F, RHS, BV.getMaxValue(T)); + + // Bitwise-or with a non-zero constant is always non-zero. + if (Operator == BO_Or && RHS != Zero) + return assumeNonZero(BV, F, SIE, Input); + + // For unsigned types, or positive RHS, + // bitwise-and output is always smaller-or-equal than RHS (assuming two's + // complement representation of signed types). + if (Operator == BO_And && (IsUnsigned || RHS >= Zero)) + return Input.Intersect(BV, F, BV.getMinValue(T), RHS); + + return Input; +} + +RangeSet RangeConstraintManager::getRange(ProgramStateRef State, + SymbolRef Sym) { + if (ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym)) + return *V; + + BasicValueFactory &BV = getBasicVals(); + + // 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)) + return R->Negate(BV, F); + + // Lazily generate a new RangeSet representing all possible values for the + // given symbol type. + QualType T = Sym->getType(); + + RangeSet Result(F, BV.getMinValue(T), BV.getMaxValue(T)); + + // References are known to be non-zero. + if (T->isReferenceType()) + return assumeNonZero(BV, F, Sym, Result); + + // Known constraints on ranges of bitwise expressions. + if (const SymIntExpr* SIE = dyn_cast<SymIntExpr>(Sym)) + return applyBitwiseConstraints(BV, F, Result, SIE); + + return Result; +} + +// FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to +// obtain the negated symbolic expression instead of constructing the +// symbol manually. This will allow us to support finding ranges of not +// only negated SymSymExpr-type expressions, but also of other, simpler +// expressions which we currently do not know how to negate. +const RangeSet* +RangeConstraintManager::getRangeForMinusSymbol(ProgramStateRef State, + SymbolRef Sym) { + if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) { + if (SSE->getOpcode() == BO_Sub) { + QualType T = Sym->getType(); + SymbolManager &SymMgr = State->getSymbolManager(); + SymbolRef negSym = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, + SSE->getLHS(), T); + if (const RangeSet *negV = State->get<ConstraintRange>(negSym)) { + // Unsigned range set cannot be negated, unless it is [0, 0]. + if ((negV->getConcreteValue() && + (*negV->getConcreteValue() == 0)) || + T->isSignedIntegerOrEnumerationType()) + return negV; + } + } + } + return nullptr; +} + +//===------------------------------------------------------------------------=== +// assumeSymX methods: protected interface for RangeConstraintManager. +//===------------------------------------------------------------------------===/ + +// The syntax for ranges below is mathematical, using [x, y] for closed ranges +// and (x, y) for open ranges. These ranges are modular, corresponding with +// a common treatment of C integer overflow. This means that these methods +// do not have to worry about overflow; RangeSet::Intersect can handle such a +// "wraparound" range. +// As an example, the range [UINT_MAX-1, 3) contains five values: UINT_MAX-1, +// UINT_MAX, 0, 1, and 2. + +ProgramStateRef +RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within) + return St; + + llvm::APSInt Lower = AdjustmentType.convert(Int) - Adjustment; + llvm::APSInt Upper = Lower; + --Lower; + ++Upper; + + // [Int-Adjustment+1, Int-Adjustment-1] + // Notice that the lower bound is greater than the upper bound. + RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower); + return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); +} + +ProgramStateRef +RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within) + return nullptr; + + // [Int-Adjustment, Int-Adjustment] + llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment; + RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt); + return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); +} + +RangeSet RangeConstraintManager::getSymLTRange(ProgramStateRef St, + SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int, true)) { + case APSIntType::RTR_Below: + return F.getEmptySet(); + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return getRange(St, Sym); + } + + // Special case for Int == Min. This is always false. + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (ComparisonVal == Min) + return F.getEmptySet(); + + llvm::APSInt Lower = Min - Adjustment; + llvm::APSInt Upper = ComparisonVal - Adjustment; + --Upper; + + return getRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); +} + +ProgramStateRef +RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + RangeSet New = getSymLTRange(St, Sym, Int, Adjustment); + return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); +} + +RangeSet RangeConstraintManager::getSymGTRange(ProgramStateRef St, + SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int, true)) { + case APSIntType::RTR_Below: + return getRange(St, Sym); + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return F.getEmptySet(); + } + + // Special case for Int == Max. This is always false. + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (ComparisonVal == Max) + return F.getEmptySet(); + + llvm::APSInt Lower = ComparisonVal - Adjustment; + llvm::APSInt Upper = Max - Adjustment; + ++Lower; + + return getRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); +} + +ProgramStateRef +RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + RangeSet New = getSymGTRange(St, Sym, Int, Adjustment); + return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); +} + +RangeSet RangeConstraintManager::getSymGERange(ProgramStateRef St, + SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int, true)) { + case APSIntType::RTR_Below: + return getRange(St, Sym); + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return F.getEmptySet(); + } + + // Special case for Int == Min. This is always feasible. + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Min = AdjustmentType.getMinValue(); + if (ComparisonVal == Min) + return getRange(St, Sym); + + llvm::APSInt Max = AdjustmentType.getMaxValue(); + llvm::APSInt Lower = ComparisonVal - Adjustment; + llvm::APSInt Upper = Max - Adjustment; + + return getRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper); +} + +ProgramStateRef +RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + RangeSet New = getSymGERange(St, Sym, Int, Adjustment); + return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); +} + +RangeSet RangeConstraintManager::getSymLERange( + llvm::function_ref<RangeSet()> RS, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + // Before we do any real work, see if the value can even show up. + APSIntType AdjustmentType(Adjustment); + switch (AdjustmentType.testInRange(Int, true)) { + case APSIntType::RTR_Below: + return F.getEmptySet(); + case APSIntType::RTR_Within: + break; + case APSIntType::RTR_Above: + return RS(); + } + + // Special case for Int == Max. This is always feasible. + llvm::APSInt ComparisonVal = AdjustmentType.convert(Int); + llvm::APSInt Max = AdjustmentType.getMaxValue(); + if (ComparisonVal == Max) + return RS(); + + llvm::APSInt Min = AdjustmentType.getMinValue(); + llvm::APSInt Lower = Min - Adjustment; + llvm::APSInt Upper = ComparisonVal - Adjustment; + + return RS().Intersect(getBasicVals(), F, Lower, Upper); +} + +RangeSet RangeConstraintManager::getSymLERange(ProgramStateRef St, + SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + return getSymLERange([&] { return getRange(St, Sym); }, Int, Adjustment); +} + +ProgramStateRef +RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym, + const llvm::APSInt &Int, + const llvm::APSInt &Adjustment) { + RangeSet New = getSymLERange(St, Sym, Int, Adjustment); + return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New); +} + +ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) { + RangeSet New = getSymGERange(State, Sym, From, Adjustment); + if (New.isEmpty()) + return nullptr; + RangeSet Out = getSymLERange([&] { return New; }, To, Adjustment); + return Out.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, Out); +} + +ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, const llvm::APSInt &Adjustment) { + RangeSet RangeLT = getSymLTRange(State, Sym, From, Adjustment); + RangeSet RangeGT = getSymGTRange(State, Sym, To, Adjustment); + RangeSet New(RangeLT.addRange(F, RangeGT)); + return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New); +} + +//===------------------------------------------------------------------------=== +// Pretty-printing. +//===------------------------------------------------------------------------===/ + +void RangeConstraintManager::print(ProgramStateRef St, raw_ostream &Out, + const char *nl, const char *sep) { + + ConstraintRangeTy Ranges = St->get<ConstraintRange>(); + + if (Ranges.isEmpty()) { + Out << nl << sep << "Ranges are empty." << nl; + return; + } + + Out << nl << sep << "Ranges of symbol values:"; + for (ConstraintRangeTy::iterator I = Ranges.begin(), E = Ranges.end(); I != E; + ++I) { + Out << nl << ' ' << I.getKey() << " : "; + I.getData().print(Out); + } + Out << nl; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp new file mode 100644 index 000000000000..146dc20ad021 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp @@ -0,0 +1,210 @@ +//== RangedConstraintManager.cpp --------------------------------*- 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 RangedConstraintManager, a class that provides a +// range-based constraint manager interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h" + +namespace clang { + +namespace ento { + +RangedConstraintManager::~RangedConstraintManager() {} + +ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State, + SymbolRef Sym, + bool Assumption) { + // Handle SymbolData. + if (isa<SymbolData>(Sym)) { + return assumeSymUnsupported(State, Sym, Assumption); + + // Handle symbolic expression. + } else if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(Sym)) { + // We can only simplify expressions whose RHS is an integer. + + BinaryOperator::Opcode op = SIE->getOpcode(); + if (BinaryOperator::isComparisonOp(op) && op != BO_Cmp) { + if (!Assumption) + op = BinaryOperator::negateComparisonOp(op); + + return assumeSymRel(State, SIE->getLHS(), op, SIE->getRHS()); + } + + } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) { + // Translate "a != b" to "(b - a) != 0". + // We invert the order of the operands as a heuristic for how loop + // conditions are usually written ("begin != end") as compared to length + // calculations ("end - begin"). The more correct thing to do would be to + // canonicalize "a - b" and "b - a", which would allow us to treat + // "a != b" and "b != a" the same. + SymbolManager &SymMgr = getSymbolManager(); + BinaryOperator::Opcode Op = SSE->getOpcode(); + assert(BinaryOperator::isComparisonOp(Op)); + + // For now, we only support comparing pointers. + if (Loc::isLocType(SSE->getLHS()->getType()) && + Loc::isLocType(SSE->getRHS()->getType())) { + QualType DiffTy = SymMgr.getContext().getPointerDiffType(); + SymbolRef Subtraction = + SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy); + + const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy); + Op = BinaryOperator::reverseComparisonOp(Op); + if (!Assumption) + Op = BinaryOperator::negateComparisonOp(Op); + return assumeSymRel(State, Subtraction, Op, Zero); + } + } + + // If we get here, there's nothing else we can do but treat the symbol as + // opaque. + return assumeSymUnsupported(State, Sym, Assumption); +} + +ProgramStateRef RangedConstraintManager::assumeSymInclusiveRange( + ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From, + const llvm::APSInt &To, bool InRange) { + // Get the type used for calculating wraparound. + BasicValueFactory &BVF = getBasicVals(); + APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType()); + + llvm::APSInt Adjustment = WraparoundType.getZeroValue(); + SymbolRef AdjustedSym = Sym; + computeAdjustment(AdjustedSym, Adjustment); + + // Convert the right-hand side integer as necessary. + APSIntType ComparisonType = std::max(WraparoundType, APSIntType(From)); + llvm::APSInt ConvertedFrom = ComparisonType.convert(From); + llvm::APSInt ConvertedTo = ComparisonType.convert(To); + + // Prefer unsigned comparisons. + if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() && + ComparisonType.isUnsigned() && !WraparoundType.isUnsigned()) + Adjustment.setIsSigned(false); + + if (InRange) + return assumeSymWithinInclusiveRange(State, AdjustedSym, ConvertedFrom, + ConvertedTo, Adjustment); + return assumeSymOutsideInclusiveRange(State, AdjustedSym, ConvertedFrom, + ConvertedTo, Adjustment); +} + +ProgramStateRef +RangedConstraintManager::assumeSymUnsupported(ProgramStateRef State, + SymbolRef Sym, bool Assumption) { + BasicValueFactory &BVF = getBasicVals(); + QualType T = Sym->getType(); + + // Non-integer types are not supported. + if (!T->isIntegralOrEnumerationType()) + return State; + + // Reverse the operation and add directly to state. + const llvm::APSInt &Zero = BVF.getValue(0, T); + if (Assumption) + return assumeSymNE(State, Sym, Zero, Zero); + else + return assumeSymEQ(State, Sym, Zero, Zero); +} + +ProgramStateRef RangedConstraintManager::assumeSymRel(ProgramStateRef State, + SymbolRef Sym, + BinaryOperator::Opcode Op, + const llvm::APSInt &Int) { + assert(BinaryOperator::isComparisonOp(Op) && + "Non-comparison ops should be rewritten as comparisons to zero."); + + // Simplification: translate an assume of a constraint of the form + // "(exp comparison_op expr) != 0" to true into an assume of + // "exp comparison_op expr" to true. (And similarly, an assume of the form + // "(exp comparison_op expr) == 0" to true into an assume of + // "exp comparison_op expr" to false.) + if (Int == 0 && (Op == BO_EQ || Op == BO_NE)) { + if (const BinarySymExpr *SE = dyn_cast<BinarySymExpr>(Sym)) + if (BinaryOperator::isComparisonOp(SE->getOpcode())) + return assumeSym(State, Sym, (Op == BO_NE ? true : false)); + } + + // Get the type used for calculating wraparound. + BasicValueFactory &BVF = getBasicVals(); + APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType()); + + // We only handle simple comparisons of the form "$sym == constant" + // or "($sym+constant1) == constant2". + // The adjustment is "constant1" in the above expression. It's used to + // "slide" the solution range around for modular arithmetic. For example, + // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which + // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to + // the subclasses of SimpleConstraintManager to handle the adjustment. + llvm::APSInt Adjustment = WraparoundType.getZeroValue(); + computeAdjustment(Sym, Adjustment); + + // Convert the right-hand side integer as necessary. + APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int)); + llvm::APSInt ConvertedInt = ComparisonType.convert(Int); + + // Prefer unsigned comparisons. + if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() && + ComparisonType.isUnsigned() && !WraparoundType.isUnsigned()) + Adjustment.setIsSigned(false); + + switch (Op) { + default: + llvm_unreachable("invalid operation not caught by assertion above"); + + case BO_EQ: + return assumeSymEQ(State, Sym, ConvertedInt, Adjustment); + + case BO_NE: + return assumeSymNE(State, Sym, ConvertedInt, Adjustment); + + case BO_GT: + return assumeSymGT(State, Sym, ConvertedInt, Adjustment); + + case BO_GE: + return assumeSymGE(State, Sym, ConvertedInt, Adjustment); + + case BO_LT: + return assumeSymLT(State, Sym, ConvertedInt, Adjustment); + + case BO_LE: + return assumeSymLE(State, Sym, ConvertedInt, Adjustment); + } // end switch +} + +void RangedConstraintManager::computeAdjustment(SymbolRef &Sym, + llvm::APSInt &Adjustment) { + // Is it a "($sym+constant1)" expression? + if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) { + BinaryOperator::Opcode Op = SE->getOpcode(); + if (Op == BO_Add || Op == BO_Sub) { + Sym = SE->getLHS(); + Adjustment = APSIntType(Adjustment).convert(SE->getRHS()); + + // Don't forget to negate the adjustment if it's being subtracted. + // This should happen /after/ promotion, in case the value being + // subtracted is, say, CHAR_MIN, and the promoted type is 'int'. + if (Op == BO_Sub) + Adjustment = -Adjustment; + } + } +} + +void *ProgramStateTrait<ConstraintRange>::GDMIndex() { + static int Index; + return &Index; +} + +} // end of namespace ento + +} // end of namespace clang diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp new file mode 100644 index 000000000000..b2339be4f263 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp @@ -0,0 +1,2591 @@ +//== 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines a basic region store model. In this model, we do have field +// sensitivity. But we assume nothing about the heap shape. So recursive data +// structures are largely ignored. Basically we do 1-limiting analysis. +// Parameter pointers are assumed with no aliasing. Pointee objects of +// parameters are created lazily. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Attr.h" +#include "clang/AST/CharUnits.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/raw_ostream.h" +#include <utility> + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Representation of binding keys. +//===----------------------------------------------------------------------===// + +namespace { +class BindingKey { +public: + enum Kind { Default = 0x0, Direct = 0x1 }; +private: + enum { Symbolic = 0x2 }; + + llvm::PointerIntPair<const MemRegion *, 2> P; + uint64_t Data; + + /// Create a key for a binding to region \p r, which has a symbolic offset + /// from region \p Base. + explicit BindingKey(const SubRegion *r, const SubRegion *Base, Kind k) + : P(r, k | Symbolic), Data(reinterpret_cast<uintptr_t>(Base)) { + assert(r && Base && "Must have known regions."); + assert(getConcreteOffsetRegion() == Base && "Failed to store base region"); + } + + /// Create a key for a binding at \p offset from base region \p r. + explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k) + : P(r, k), Data(offset) { + assert(r && "Must have known regions."); + assert(getOffset() == offset && "Failed to store offset"); + assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r) || + isa <CXXDerivedObjectRegion>(r)) && + "Not a base"); + } +public: + + bool isDirect() const { return P.getInt() & Direct; } + bool hasSymbolicOffset() const { return P.getInt() & Symbolic; } + + const MemRegion *getRegion() const { return P.getPointer(); } + uint64_t getOffset() const { + assert(!hasSymbolicOffset()); + return Data; + } + + const SubRegion *getConcreteOffsetRegion() const { + assert(hasSymbolicOffset()); + return reinterpret_cast<const SubRegion *>(static_cast<uintptr_t>(Data)); + } + + const MemRegion *getBaseRegion() const { + if (hasSymbolicOffset()) + return getConcreteOffsetRegion()->getBaseRegion(); + return getRegion()->getBaseRegion(); + } + + void Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddPointer(P.getOpaqueValue()); + ID.AddInteger(Data); + } + + static BindingKey Make(const MemRegion *R, Kind k); + + bool operator<(const BindingKey &X) const { + if (P.getOpaqueValue() < X.P.getOpaqueValue()) + return true; + if (P.getOpaqueValue() > X.P.getOpaqueValue()) + return false; + return Data < X.Data; + } + + bool operator==(const BindingKey &X) const { + return P.getOpaqueValue() == X.P.getOpaqueValue() && + Data == X.Data; + } + + void dump() const; +}; +} // end anonymous namespace + +BindingKey BindingKey::Make(const MemRegion *R, Kind k) { + const RegionOffset &RO = R->getAsOffset(); + if (RO.hasSymbolicOffset()) + return BindingKey(cast<SubRegion>(R), cast<SubRegion>(RO.getRegion()), k); + + return BindingKey(RO.getRegion(), RO.getOffset(), k); +} + +namespace llvm { + static inline + raw_ostream &operator<<(raw_ostream &os, BindingKey K) { + os << '(' << K.getRegion(); + if (!K.hasSymbolicOffset()) + os << ',' << K.getOffset(); + os << ',' << (K.isDirect() ? "direct" : "default") + << ')'; + return os; + } + + template <typename T> struct isPodLike; + template <> struct isPodLike<BindingKey> { + static const bool value = true; + }; +} // end llvm namespace + +#ifndef NDEBUG +LLVM_DUMP_METHOD void BindingKey::dump() const { llvm::errs() << *this; } +#endif + +//===----------------------------------------------------------------------===// +// Actual Store type. +//===----------------------------------------------------------------------===// + +typedef llvm::ImmutableMap<BindingKey, SVal> ClusterBindings; +typedef llvm::ImmutableMapRef<BindingKey, SVal> ClusterBindingsRef; +typedef std::pair<BindingKey, SVal> BindingPair; + +typedef llvm::ImmutableMap<const MemRegion *, ClusterBindings> + RegionBindings; + +namespace { +class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *, + ClusterBindings> { + ClusterBindings::Factory *CBFactory; + +public: + typedef llvm::ImmutableMapRef<const MemRegion *, ClusterBindings> + ParentTy; + + RegionBindingsRef(ClusterBindings::Factory &CBFactory, + const RegionBindings::TreeTy *T, + RegionBindings::TreeTy::Factory *F) + : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(T, F), + CBFactory(&CBFactory) {} + + RegionBindingsRef(const ParentTy &P, ClusterBindings::Factory &CBFactory) + : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(P), + CBFactory(&CBFactory) {} + + RegionBindingsRef add(key_type_ref K, data_type_ref D) const { + return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D), + *CBFactory); + } + + RegionBindingsRef remove(key_type_ref K) const { + return RegionBindingsRef(static_cast<const ParentTy *>(this)->remove(K), + *CBFactory); + } + + RegionBindingsRef addBinding(BindingKey K, SVal V) const; + + RegionBindingsRef addBinding(const MemRegion *R, + BindingKey::Kind k, SVal V) const; + + const SVal *lookup(BindingKey K) const; + const SVal *lookup(const MemRegion *R, BindingKey::Kind k) const; + using llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>::lookup; + + RegionBindingsRef removeBinding(BindingKey K); + + RegionBindingsRef removeBinding(const MemRegion *R, + BindingKey::Kind k); + + RegionBindingsRef removeBinding(const MemRegion *R) { + return removeBinding(R, BindingKey::Direct). + removeBinding(R, BindingKey::Default); + } + + Optional<SVal> getDirectBinding(const MemRegion *R) const; + + /// getDefaultBinding - Returns an SVal* representing an optional default + /// binding associated with a region and its subregions. + Optional<SVal> getDefaultBinding(const MemRegion *R) const; + + /// Return the internal tree as a Store. + Store asStore() const { + return asImmutableMap().getRootWithoutRetain(); + } + + void dump(raw_ostream &OS, const char *nl) const { + for (iterator I = begin(), E = end(); I != E; ++I) { + const ClusterBindings &Cluster = I.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + OS << ' ' << CI.getKey() << " : " << CI.getData() << nl; + } + OS << nl; + } + } + + LLVM_DUMP_METHOD void dump() const { dump(llvm::errs(), "\n"); } +}; +} // end anonymous namespace + +typedef const RegionBindingsRef& RegionBindingsConstRef; + +Optional<SVal> RegionBindingsRef::getDirectBinding(const MemRegion *R) const { + return Optional<SVal>::create(lookup(R, BindingKey::Direct)); +} + +Optional<SVal> RegionBindingsRef::getDefaultBinding(const MemRegion *R) const { + return Optional<SVal>::create(lookup(R, BindingKey::Default)); +} + +RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const { + const MemRegion *Base = K.getBaseRegion(); + + const ClusterBindings *ExistingCluster = lookup(Base); + ClusterBindings Cluster = + (ExistingCluster ? *ExistingCluster : CBFactory->getEmptyMap()); + + ClusterBindings NewCluster = CBFactory->add(Cluster, K, V); + return add(Base, NewCluster); +} + + +RegionBindingsRef RegionBindingsRef::addBinding(const MemRegion *R, + BindingKey::Kind k, + SVal V) const { + return addBinding(BindingKey::Make(R, k), V); +} + +const SVal *RegionBindingsRef::lookup(BindingKey K) const { + const ClusterBindings *Cluster = lookup(K.getBaseRegion()); + if (!Cluster) + return nullptr; + return Cluster->lookup(K); +} + +const SVal *RegionBindingsRef::lookup(const MemRegion *R, + BindingKey::Kind k) const { + return lookup(BindingKey::Make(R, k)); +} + +RegionBindingsRef RegionBindingsRef::removeBinding(BindingKey K) { + const MemRegion *Base = K.getBaseRegion(); + const ClusterBindings *Cluster = lookup(Base); + if (!Cluster) + return *this; + + ClusterBindings NewCluster = CBFactory->remove(*Cluster, K); + if (NewCluster.isEmpty()) + return remove(Base); + return add(Base, NewCluster); +} + +RegionBindingsRef RegionBindingsRef::removeBinding(const MemRegion *R, + BindingKey::Kind k){ + return removeBinding(BindingKey::Make(R, k)); +} + +//===----------------------------------------------------------------------===// +// Fine-grained control of RegionStoreManager. +//===----------------------------------------------------------------------===// + +namespace { +struct minimal_features_tag {}; +struct maximal_features_tag {}; + +class RegionStoreFeatures { + bool SupportsFields; +public: + RegionStoreFeatures(minimal_features_tag) : + SupportsFields(false) {} + + RegionStoreFeatures(maximal_features_tag) : + SupportsFields(true) {} + + void enableFields(bool t) { SupportsFields = t; } + + bool supportsFields() const { return SupportsFields; } +}; +} + +//===----------------------------------------------------------------------===// +// Main RegionStore logic. +//===----------------------------------------------------------------------===// + +namespace { +class InvalidateRegionsWorker; + +class RegionStoreManager : public StoreManager { +public: + const RegionStoreFeatures Features; + + RegionBindings::Factory RBFactory; + mutable ClusterBindings::Factory CBFactory; + + typedef std::vector<SVal> SValListTy; +private: + typedef llvm::DenseMap<const LazyCompoundValData *, + SValListTy> LazyBindingsMapTy; + LazyBindingsMapTy LazyBindingsMap; + + /// The largest number of fields a struct can have and still be + /// considered "small". + /// + /// This is currently used to decide whether or not it is worth "forcing" a + /// LazyCompoundVal on bind. + /// + /// This is controlled by 'region-store-small-struct-limit' option. + /// To disable all small-struct-dependent behavior, set the option to "0". + unsigned SmallStructLimit; + + /// A helper used to populate the work list with the given set of + /// regions. + void populateWorkList(InvalidateRegionsWorker &W, + ArrayRef<SVal> Values, + InvalidatedRegions *TopLevelRegions); + +public: + RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f) + : StoreManager(mgr), Features(f), + RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()), + SmallStructLimit(0) { + SubEngine &Eng = StateMgr.getOwningEngine(); + AnalyzerOptions &Options = Eng.getAnalysisManager().options; + SmallStructLimit = Options.RegionStoreSmallStructLimit; + } + + + /// setImplicitDefaultValue - Set the default binding for the provided + /// MemRegion to the value implicitly defined for compound literals when + /// the value is not specified. + RegionBindingsRef setImplicitDefaultValue(RegionBindingsConstRef B, + const MemRegion *R, QualType T); + + /// ArrayToPointer - Emulates the "decay" of an array to a pointer + /// type. 'Array' represents the lvalue of the array being decayed + /// to a pointer, and the returned SVal represents the decayed + /// version of that lvalue (i.e., a pointer to the first element of + /// the array). This is called by ExprEngine when evaluating + /// casts from arrays to pointers. + SVal ArrayToPointer(Loc Array, QualType ElementTy) override; + + StoreRef getInitialStore(const LocationContext *InitLoc) override { + return StoreRef(RBFactory.getEmptyMap().getRootWithoutRetain(), *this); + } + + //===-------------------------------------------------------------------===// + // Binding values to regions. + //===-------------------------------------------------------------------===// + RegionBindingsRef invalidateGlobalRegion(MemRegion::Kind K, + const Expr *Ex, + unsigned Count, + const LocationContext *LCtx, + RegionBindingsRef B, + InvalidatedRegions *Invalidated); + + StoreRef invalidateRegions(Store store, + ArrayRef<SVal> Values, + const Expr *E, unsigned Count, + const LocationContext *LCtx, + const CallEvent *Call, + InvalidatedSymbols &IS, + RegionAndSymbolInvalidationTraits &ITraits, + InvalidatedRegions *Invalidated, + InvalidatedRegions *InvalidatedTopLevel) override; + + bool scanReachableSymbols(Store S, const MemRegion *R, + ScanReachableSymbols &Callbacks) override; + + RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B, + const SubRegion *R); + +public: // Part of public interface to class. + + StoreRef Bind(Store store, Loc LV, SVal V) override { + return StoreRef(bind(getRegionBindings(store), LV, V).asStore(), *this); + } + + RegionBindingsRef bind(RegionBindingsConstRef B, Loc LV, SVal V); + + // BindDefaultInitial is only used to initialize a region with + // a default value. + StoreRef BindDefaultInitial(Store store, const MemRegion *R, + SVal V) override { + RegionBindingsRef B = getRegionBindings(store); + // Use other APIs when you have to wipe the region that was initialized + // earlier. + assert(!(B.getDefaultBinding(R) || B.getDirectBinding(R)) && + "Double initialization!"); + B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V); + return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this); + } + + // BindDefaultZero is used for zeroing constructors that may accidentally + // overwrite existing bindings. + StoreRef BindDefaultZero(Store store, const MemRegion *R) override { + // FIXME: The offsets of empty bases can be tricky because of + // of the so called "empty base class optimization". + // If a base class has been optimized out + // we should not try to create a binding, otherwise we should. + // Unfortunately, at the moment ASTRecordLayout doesn't expose + // the actual sizes of the empty bases + // and trying to infer them from offsets/alignments + // seems to be error-prone and non-trivial because of the trailing padding. + // As a temporary mitigation we don't create bindings for empty bases. + if (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R)) + if (BR->getDecl()->isEmpty()) + return StoreRef(store, *this); + + RegionBindingsRef B = getRegionBindings(store); + SVal V = svalBuilder.makeZeroVal(Ctx.CharTy); + B = removeSubRegionBindings(B, cast<SubRegion>(R)); + B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V); + return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this); + } + + /// Attempt to extract the fields of \p LCV and bind them to the struct region + /// \p R. + /// + /// This path is used when it seems advantageous to "force" loading the values + /// within a LazyCompoundVal to bind memberwise to the struct region, rather + /// than using a Default binding at the base of the entire region. This is a + /// heuristic attempting to avoid building long chains of LazyCompoundVals. + /// + /// \returns The updated store bindings, or \c None if binding non-lazily + /// would be too expensive. + Optional<RegionBindingsRef> tryBindSmallStruct(RegionBindingsConstRef B, + const TypedValueRegion *R, + const RecordDecl *RD, + nonloc::LazyCompoundVal LCV); + + /// BindStruct - Bind a compound value to a structure. + RegionBindingsRef bindStruct(RegionBindingsConstRef B, + const TypedValueRegion* R, SVal V); + + /// BindVector - Bind a compound value to a vector. + RegionBindingsRef bindVector(RegionBindingsConstRef B, + const TypedValueRegion* R, SVal V); + + RegionBindingsRef bindArray(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal V); + + /// Clears out all bindings in the given region and assigns a new value + /// as a Default binding. + RegionBindingsRef bindAggregate(RegionBindingsConstRef B, + const TypedRegion *R, + SVal DefaultVal); + + /// Create a new store with the specified binding removed. + /// \param ST the original store, that is the basis for the new store. + /// \param L the location whose binding should be removed. + StoreRef killBinding(Store ST, Loc L) override; + + void incrementReferenceCount(Store store) override { + getRegionBindings(store).manualRetain(); + } + + /// If the StoreManager supports it, decrement the reference count of + /// the specified Store object. If the reference count hits 0, the memory + /// associated with the object is recycled. + void decrementReferenceCount(Store store) override { + getRegionBindings(store).manualRelease(); + } + + bool includedInBindings(Store store, const MemRegion *region) const override; + + /// Return the value bound to specified location in a given state. + /// + /// The high level logic for this method is this: + /// getBinding (L) + /// if L has binding + /// return L's binding + /// else if L is in killset + /// return unknown + /// else + /// if L is on stack or heap + /// return undefined + /// else + /// return symbolic + SVal getBinding(Store S, Loc L, QualType T) override { + return getBinding(getRegionBindings(S), L, T); + } + + Optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override { + RegionBindingsRef B = getRegionBindings(S); + // Default bindings are always applied over a base region so look up the + // base region's default binding, otherwise the lookup will fail when R + // is at an offset from R->getBaseRegion(). + return B.getDefaultBinding(R->getBaseRegion()); + } + + SVal getBinding(RegionBindingsConstRef B, Loc L, QualType T = QualType()); + + SVal getBindingForElement(RegionBindingsConstRef B, const ElementRegion *R); + + SVal getBindingForField(RegionBindingsConstRef B, const FieldRegion *R); + + SVal getBindingForObjCIvar(RegionBindingsConstRef B, const ObjCIvarRegion *R); + + SVal getBindingForVar(RegionBindingsConstRef B, const VarRegion *R); + + SVal getBindingForLazySymbol(const TypedValueRegion *R); + + SVal getBindingForFieldOrElementCommon(RegionBindingsConstRef B, + const TypedValueRegion *R, + QualType Ty); + + SVal getLazyBinding(const SubRegion *LazyBindingRegion, + RegionBindingsRef LazyBinding); + + /// Get bindings for the values in a struct and return a CompoundVal, used + /// when doing struct copy: + /// struct s x, y; + /// x = y; + /// y's value is retrieved by this method. + SVal getBindingForStruct(RegionBindingsConstRef B, const TypedValueRegion *R); + SVal getBindingForArray(RegionBindingsConstRef B, const TypedValueRegion *R); + NonLoc createLazyBinding(RegionBindingsConstRef B, const TypedValueRegion *R); + + /// Used to lazily generate derived symbols for bindings that are defined + /// implicitly by default bindings in a super region. + /// + /// Note that callers may need to specially handle LazyCompoundVals, which + /// are returned as is in case the caller needs to treat them differently. + Optional<SVal> getBindingForDerivedDefaultValue(RegionBindingsConstRef B, + const MemRegion *superR, + const TypedValueRegion *R, + QualType Ty); + + /// Get the state and region whose binding this region \p R corresponds to. + /// + /// If there is no lazy binding for \p R, the returned value will have a null + /// \c second. Note that a null pointer can represents a valid Store. + std::pair<Store, const SubRegion *> + findLazyBinding(RegionBindingsConstRef B, const SubRegion *R, + const SubRegion *originalRegion); + + /// Returns the cached set of interesting SVals contained within a lazy + /// binding. + /// + /// The precise value of "interesting" is determined for the purposes of + /// RegionStore's internal analysis. It must always contain all regions and + /// symbols, but may omit constants and other kinds of SVal. + const SValListTy &getInterestingValues(nonloc::LazyCompoundVal LCV); + + //===------------------------------------------------------------------===// + // State pruning. + //===------------------------------------------------------------------===// + + /// removeDeadBindings - Scans the RegionStore of 'state' for dead values. + /// It returns a new Store with these values removed. + StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx, + SymbolReaper& SymReaper) override; + + //===------------------------------------------------------------------===// + // Region "extents". + //===------------------------------------------------------------------===// + + // FIXME: This method will soon be eliminated; see the note in Store.h. + DefinedOrUnknownSVal getSizeInElements(ProgramStateRef state, + const MemRegion* R, + QualType EleTy) override; + + //===------------------------------------------------------------------===// + // Utility methods. + //===------------------------------------------------------------------===// + + RegionBindingsRef getRegionBindings(Store store) const { + return RegionBindingsRef(CBFactory, + static_cast<const RegionBindings::TreeTy*>(store), + RBFactory.getTreeFactory()); + } + + void print(Store store, raw_ostream &Out, const char* nl) override; + + void iterBindings(Store store, BindingsHandler& f) override { + RegionBindingsRef B = getRegionBindings(store); + for (RegionBindingsRef::iterator I = B.begin(), E = B.end(); I != E; ++I) { + const ClusterBindings &Cluster = I.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + const BindingKey &K = CI.getKey(); + if (!K.isDirect()) + continue; + if (const SubRegion *R = dyn_cast<SubRegion>(K.getRegion())) { + // FIXME: Possibly incorporate the offset? + if (!f.HandleBinding(*this, store, R, CI.getData())) + return; + } + } + } + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// RegionStore creation. +//===----------------------------------------------------------------------===// + +std::unique_ptr<StoreManager> +ento::CreateRegionStoreManager(ProgramStateManager &StMgr) { + RegionStoreFeatures F = maximal_features_tag(); + return llvm::make_unique<RegionStoreManager>(StMgr, F); +} + +std::unique_ptr<StoreManager> +ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) { + RegionStoreFeatures F = minimal_features_tag(); + F.enableFields(true); + return llvm::make_unique<RegionStoreManager>(StMgr, F); +} + + +//===----------------------------------------------------------------------===// +// Region Cluster analysis. +//===----------------------------------------------------------------------===// + +namespace { +/// Used to determine which global regions are automatically included in the +/// initial worklist of a ClusterAnalysis. +enum GlobalsFilterKind { + /// Don't include any global regions. + GFK_None, + /// Only include system globals. + GFK_SystemOnly, + /// Include all global regions. + GFK_All +}; + +template <typename DERIVED> +class ClusterAnalysis { +protected: + typedef llvm::DenseMap<const MemRegion *, const ClusterBindings *> ClusterMap; + typedef const MemRegion * WorkListElement; + typedef SmallVector<WorkListElement, 10> WorkList; + + llvm::SmallPtrSet<const ClusterBindings *, 16> Visited; + + WorkList WL; + + RegionStoreManager &RM; + ASTContext &Ctx; + SValBuilder &svalBuilder; + + RegionBindingsRef B; + + +protected: + const ClusterBindings *getCluster(const MemRegion *R) { + return B.lookup(R); + } + + /// Returns true if all clusters in the given memspace should be initially + /// included in the cluster analysis. Subclasses may provide their + /// own implementation. + bool includeEntireMemorySpace(const MemRegion *Base) { + return false; + } + +public: + ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr, + RegionBindingsRef b) + : RM(rm), Ctx(StateMgr.getContext()), + svalBuilder(StateMgr.getSValBuilder()), B(std::move(b)) {} + + RegionBindingsRef getRegionBindings() const { return B; } + + bool isVisited(const MemRegion *R) { + return Visited.count(getCluster(R)); + } + + void GenerateClusters() { + // Scan the entire set of bindings and record the region clusters. + for (RegionBindingsRef::iterator RI = B.begin(), RE = B.end(); + RI != RE; ++RI){ + const MemRegion *Base = RI.getKey(); + + const ClusterBindings &Cluster = RI.getData(); + assert(!Cluster.isEmpty() && "Empty clusters should be removed"); + static_cast<DERIVED*>(this)->VisitAddedToCluster(Base, Cluster); + + // If the base's memspace should be entirely invalidated, add the cluster + // to the workspace up front. + if (static_cast<DERIVED*>(this)->includeEntireMemorySpace(Base)) + AddToWorkList(WorkListElement(Base), &Cluster); + } + } + + bool AddToWorkList(WorkListElement E, const ClusterBindings *C) { + if (C && !Visited.insert(C).second) + return false; + WL.push_back(E); + return true; + } + + bool AddToWorkList(const MemRegion *R) { + return static_cast<DERIVED*>(this)->AddToWorkList(R); + } + + void RunWorkList() { + while (!WL.empty()) { + WorkListElement E = WL.pop_back_val(); + const MemRegion *BaseR = E; + + static_cast<DERIVED*>(this)->VisitCluster(BaseR, getCluster(BaseR)); + } + } + + void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C) {} + void VisitCluster(const MemRegion *baseR, const ClusterBindings *C) {} + + void VisitCluster(const MemRegion *BaseR, const ClusterBindings *C, + bool Flag) { + static_cast<DERIVED*>(this)->VisitCluster(BaseR, C); + } +}; +} + +//===----------------------------------------------------------------------===// +// Binding invalidation. +//===----------------------------------------------------------------------===// + +bool RegionStoreManager::scanReachableSymbols(Store S, const MemRegion *R, + ScanReachableSymbols &Callbacks) { + assert(R == R->getBaseRegion() && "Should only be called for base regions"); + RegionBindingsRef B = getRegionBindings(S); + const ClusterBindings *Cluster = B.lookup(R); + + if (!Cluster) + return true; + + for (ClusterBindings::iterator RI = Cluster->begin(), RE = Cluster->end(); + RI != RE; ++RI) { + if (!Callbacks.scan(RI.getData())) + return false; + } + + return true; +} + +static inline bool isUnionField(const FieldRegion *FR) { + return FR->getDecl()->getParent()->isUnion(); +} + +typedef SmallVector<const FieldDecl *, 8> FieldVector; + +static void getSymbolicOffsetFields(BindingKey K, FieldVector &Fields) { + assert(K.hasSymbolicOffset() && "Not implemented for concrete offset keys"); + + const MemRegion *Base = K.getConcreteOffsetRegion(); + const MemRegion *R = K.getRegion(); + + while (R != Base) { + if (const FieldRegion *FR = dyn_cast<FieldRegion>(R)) + if (!isUnionField(FR)) + Fields.push_back(FR->getDecl()); + + R = cast<SubRegion>(R)->getSuperRegion(); + } +} + +static bool isCompatibleWithFields(BindingKey K, const FieldVector &Fields) { + assert(K.hasSymbolicOffset() && "Not implemented for concrete offset keys"); + + if (Fields.empty()) + return true; + + FieldVector FieldsInBindingKey; + getSymbolicOffsetFields(K, FieldsInBindingKey); + + ptrdiff_t Delta = FieldsInBindingKey.size() - Fields.size(); + if (Delta >= 0) + return std::equal(FieldsInBindingKey.begin() + Delta, + FieldsInBindingKey.end(), + Fields.begin()); + else + return std::equal(FieldsInBindingKey.begin(), FieldsInBindingKey.end(), + Fields.begin() - Delta); +} + +/// Collects all bindings in \p Cluster that may refer to bindings within +/// \p Top. +/// +/// Each binding is a pair whose \c first is the key (a BindingKey) and whose +/// \c second is the value (an SVal). +/// +/// The \p IncludeAllDefaultBindings parameter specifies whether to include +/// default bindings that may extend beyond \p Top itself, e.g. if \p Top is +/// an aggregate within a larger aggregate with a default binding. +static void +collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, + SValBuilder &SVB, const ClusterBindings &Cluster, + const SubRegion *Top, BindingKey TopKey, + bool IncludeAllDefaultBindings) { + FieldVector FieldsInSymbolicSubregions; + if (TopKey.hasSymbolicOffset()) { + getSymbolicOffsetFields(TopKey, FieldsInSymbolicSubregions); + Top = TopKey.getConcreteOffsetRegion(); + TopKey = BindingKey::Make(Top, BindingKey::Default); + } + + // Find the length (in bits) of the region being invalidated. + uint64_t Length = UINT64_MAX; + SVal Extent = Top->getExtent(SVB); + if (Optional<nonloc::ConcreteInt> ExtentCI = + Extent.getAs<nonloc::ConcreteInt>()) { + const llvm::APSInt &ExtentInt = ExtentCI->getValue(); + assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned()); + // Extents are in bytes but region offsets are in bits. Be careful! + Length = ExtentInt.getLimitedValue() * SVB.getContext().getCharWidth(); + } else if (const FieldRegion *FR = dyn_cast<FieldRegion>(Top)) { + if (FR->getDecl()->isBitField()) + Length = FR->getDecl()->getBitWidthValue(SVB.getContext()); + } + + for (ClusterBindings::iterator I = Cluster.begin(), E = Cluster.end(); + I != E; ++I) { + BindingKey NextKey = I.getKey(); + if (NextKey.getRegion() == TopKey.getRegion()) { + // FIXME: This doesn't catch the case where we're really invalidating a + // region with a symbolic offset. Example: + // R: points[i].y + // Next: points[0].x + + if (NextKey.getOffset() > TopKey.getOffset() && + NextKey.getOffset() - TopKey.getOffset() < Length) { + // Case 1: The next binding is inside the region we're invalidating. + // Include it. + Bindings.push_back(*I); + + } else if (NextKey.getOffset() == TopKey.getOffset()) { + // Case 2: The next binding is at the same offset as the region we're + // invalidating. In this case, we need to leave default bindings alone, + // since they may be providing a default value for a regions beyond what + // we're invalidating. + // FIXME: This is probably incorrect; consider invalidating an outer + // struct whose first field is bound to a LazyCompoundVal. + if (IncludeAllDefaultBindings || NextKey.isDirect()) + Bindings.push_back(*I); + } + + } else if (NextKey.hasSymbolicOffset()) { + const MemRegion *Base = NextKey.getConcreteOffsetRegion(); + if (Top->isSubRegionOf(Base) && Top != Base) { + // Case 3: The next key is symbolic and we just changed something within + // its concrete region. We don't know if the binding is still valid, so + // we'll be conservative and include it. + if (IncludeAllDefaultBindings || NextKey.isDirect()) + if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions)) + Bindings.push_back(*I); + } else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base)) { + // Case 4: The next key is symbolic, but we changed a known + // super-region. In this case the binding is certainly included. + if (BaseSR->isSubRegionOf(Top)) + if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions)) + Bindings.push_back(*I); + } + } + } +} + +static void +collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings, + SValBuilder &SVB, const ClusterBindings &Cluster, + const SubRegion *Top, bool IncludeAllDefaultBindings) { + collectSubRegionBindings(Bindings, SVB, Cluster, Top, + BindingKey::Make(Top, BindingKey::Default), + IncludeAllDefaultBindings); +} + +RegionBindingsRef +RegionStoreManager::removeSubRegionBindings(RegionBindingsConstRef B, + const SubRegion *Top) { + BindingKey TopKey = BindingKey::Make(Top, BindingKey::Default); + const MemRegion *ClusterHead = TopKey.getBaseRegion(); + + if (Top == ClusterHead) { + // We can remove an entire cluster's bindings all in one go. + return B.remove(Top); + } + + const ClusterBindings *Cluster = B.lookup(ClusterHead); + if (!Cluster) { + // If we're invalidating a region with a symbolic offset, we need to make + // sure we don't treat the base region as uninitialized anymore. + if (TopKey.hasSymbolicOffset()) { + const SubRegion *Concrete = TopKey.getConcreteOffsetRegion(); + return B.addBinding(Concrete, BindingKey::Default, UnknownVal()); + } + return B; + } + + SmallVector<BindingPair, 32> Bindings; + collectSubRegionBindings(Bindings, svalBuilder, *Cluster, Top, TopKey, + /*IncludeAllDefaultBindings=*/false); + + ClusterBindingsRef Result(*Cluster, CBFactory); + for (SmallVectorImpl<BindingPair>::const_iterator I = Bindings.begin(), + E = Bindings.end(); + I != E; ++I) + Result = Result.remove(I->first); + + // If we're invalidating a region with a symbolic offset, we need to make sure + // we don't treat the base region as uninitialized anymore. + // FIXME: This isn't very precise; see the example in + // collectSubRegionBindings. + if (TopKey.hasSymbolicOffset()) { + const SubRegion *Concrete = TopKey.getConcreteOffsetRegion(); + Result = Result.add(BindingKey::Make(Concrete, BindingKey::Default), + UnknownVal()); + } + + if (Result.isEmpty()) + return B.remove(ClusterHead); + return B.add(ClusterHead, Result.asImmutableMap()); +} + +namespace { +class InvalidateRegionsWorker : public ClusterAnalysis<InvalidateRegionsWorker> +{ + const Expr *Ex; + unsigned Count; + const LocationContext *LCtx; + InvalidatedSymbols &IS; + RegionAndSymbolInvalidationTraits &ITraits; + StoreManager::InvalidatedRegions *Regions; + GlobalsFilterKind GlobalsFilter; +public: + InvalidateRegionsWorker(RegionStoreManager &rm, + ProgramStateManager &stateMgr, + RegionBindingsRef b, + const Expr *ex, unsigned count, + const LocationContext *lctx, + InvalidatedSymbols &is, + RegionAndSymbolInvalidationTraits &ITraitsIn, + StoreManager::InvalidatedRegions *r, + GlobalsFilterKind GFK) + : ClusterAnalysis<InvalidateRegionsWorker>(rm, stateMgr, b), + Ex(ex), Count(count), LCtx(lctx), IS(is), ITraits(ITraitsIn), Regions(r), + GlobalsFilter(GFK) {} + + void VisitCluster(const MemRegion *baseR, const ClusterBindings *C); + void VisitBinding(SVal V); + + using ClusterAnalysis::AddToWorkList; + + bool AddToWorkList(const MemRegion *R); + + /// Returns true if all clusters in the memory space for \p Base should be + /// be invalidated. + bool includeEntireMemorySpace(const MemRegion *Base); + + /// Returns true if the memory space of the given region is one of the global + /// regions specially included at the start of invalidation. + bool isInitiallyIncludedGlobalRegion(const MemRegion *R); +}; +} + +bool InvalidateRegionsWorker::AddToWorkList(const MemRegion *R) { + bool doNotInvalidateSuperRegion = ITraits.hasTrait( + R, RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + const MemRegion *BaseR = doNotInvalidateSuperRegion ? R : R->getBaseRegion(); + return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR)); +} + +void InvalidateRegionsWorker::VisitBinding(SVal V) { + // A symbol? Mark it touched by the invalidation. + if (SymbolRef Sym = V.getAsSymbol()) + IS.insert(Sym); + + if (const MemRegion *R = V.getAsRegion()) { + AddToWorkList(R); + return; + } + + // Is it a LazyCompoundVal? All references get invalidated as well. + if (Optional<nonloc::LazyCompoundVal> LCS = + V.getAs<nonloc::LazyCompoundVal>()) { + + const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS); + + for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(), + E = Vals.end(); + I != E; ++I) + VisitBinding(*I); + + return; + } +} + +void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR, + const ClusterBindings *C) { + + bool PreserveRegionsContents = + ITraits.hasTrait(baseR, + RegionAndSymbolInvalidationTraits::TK_PreserveContents); + + if (C) { + for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) + VisitBinding(I.getData()); + + // Invalidate regions contents. + if (!PreserveRegionsContents) + B = B.remove(baseR); + } + + if (const auto *TO = dyn_cast<TypedValueRegion>(baseR)) { + if (const auto *RD = TO->getValueType()->getAsCXXRecordDecl()) { + + // Lambdas can affect all static local variables without explicitly + // capturing those. + // We invalidate all static locals referenced inside the lambda body. + if (RD->isLambda() && RD->getLambdaCallOperator()->getBody()) { + using namespace ast_matchers; + + const char *DeclBind = "DeclBind"; + StatementMatcher RefToStatic = stmt(hasDescendant(declRefExpr( + to(varDecl(hasStaticStorageDuration()).bind(DeclBind))))); + auto Matches = + match(RefToStatic, *RD->getLambdaCallOperator()->getBody(), + RD->getASTContext()); + + for (BoundNodes &Match : Matches) { + auto *VD = Match.getNodeAs<VarDecl>(DeclBind); + const VarRegion *ToInvalidate = + RM.getRegionManager().getVarRegion(VD, LCtx); + AddToWorkList(ToInvalidate); + } + } + } + } + + // BlockDataRegion? If so, invalidate captured variables that are passed + // by reference. + if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(baseR)) { + for (BlockDataRegion::referenced_vars_iterator + BI = BR->referenced_vars_begin(), BE = BR->referenced_vars_end() ; + BI != BE; ++BI) { + const VarRegion *VR = BI.getCapturedRegion(); + const VarDecl *VD = VR->getDecl(); + if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage()) { + AddToWorkList(VR); + } + else if (Loc::isLocType(VR->getValueType())) { + // Map the current bindings to a Store to retrieve the value + // of the binding. If that binding itself is a region, we should + // invalidate that region. This is because a block may capture + // a pointer value, but the thing pointed by that pointer may + // get invalidated. + SVal V = RM.getBinding(B, loc::MemRegionVal(VR)); + if (Optional<Loc> L = V.getAs<Loc>()) { + if (const MemRegion *LR = L->getAsRegion()) + AddToWorkList(LR); + } + } + } + return; + } + + // Symbolic region? + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) + IS.insert(SR->getSymbol()); + + // Nothing else should be done in the case when we preserve regions context. + if (PreserveRegionsContents) + return; + + // Otherwise, we have a normal data region. Record that we touched the region. + if (Regions) + Regions->push_back(baseR); + + if (isa<AllocaRegion>(baseR) || isa<SymbolicRegion>(baseR)) { + // Invalidate the region by setting its default value to + // conjured symbol. The type of the symbol is irrelevant. + DefinedOrUnknownSVal V = + svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count); + B = B.addBinding(baseR, BindingKey::Default, V); + return; + } + + if (!baseR->isBoundable()) + return; + + const TypedValueRegion *TR = cast<TypedValueRegion>(baseR); + QualType T = TR->getValueType(); + + if (isInitiallyIncludedGlobalRegion(baseR)) { + // If the region is a global and we are invalidating all globals, + // erasing the entry is good enough. This causes all globals to be lazily + // symbolicated from the same base symbol. + return; + } + + if (T->isRecordType()) { + // Invalidate the region by setting its default value to + // conjured symbol. The type of the symbol is irrelevant. + DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, + Ctx.IntTy, Count); + B = B.addBinding(baseR, BindingKey::Default, V); + return; + } + + if (const ArrayType *AT = Ctx.getAsArrayType(T)) { + bool doNotInvalidateSuperRegion = ITraits.hasTrait( + baseR, + RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion); + + if (doNotInvalidateSuperRegion) { + // We are not doing blank invalidation of the whole array region so we + // have to manually invalidate each elements. + Optional<uint64_t> NumElements; + + // Compute lower and upper offsets for region within array. + if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT)) + NumElements = CAT->getSize().getZExtValue(); + if (!NumElements) // We are not dealing with a constant size array + goto conjure_default; + QualType ElementTy = AT->getElementType(); + uint64_t ElemSize = Ctx.getTypeSize(ElementTy); + const RegionOffset &RO = baseR->getAsOffset(); + const MemRegion *SuperR = baseR->getBaseRegion(); + if (RO.hasSymbolicOffset()) { + // If base region has a symbolic offset, + // we revert to invalidating the super region. + if (SuperR) + AddToWorkList(SuperR); + goto conjure_default; + } + + uint64_t LowerOffset = RO.getOffset(); + uint64_t UpperOffset = LowerOffset + *NumElements * ElemSize; + bool UpperOverflow = UpperOffset < LowerOffset; + + // Invalidate regions which are within array boundaries, + // or have a symbolic offset. + if (!SuperR) + goto conjure_default; + + const ClusterBindings *C = B.lookup(SuperR); + if (!C) + goto conjure_default; + + for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; + ++I) { + const BindingKey &BK = I.getKey(); + Optional<uint64_t> ROffset = + BK.hasSymbolicOffset() ? Optional<uint64_t>() : BK.getOffset(); + + // Check offset is not symbolic and within array's boundaries. + // Handles arrays of 0 elements and of 0-sized elements as well. + if (!ROffset || + ((*ROffset >= LowerOffset && *ROffset < UpperOffset) || + (UpperOverflow && + (*ROffset >= LowerOffset || *ROffset < UpperOffset)) || + (LowerOffset == UpperOffset && *ROffset == LowerOffset))) { + B = B.removeBinding(I.getKey()); + // Bound symbolic regions need to be invalidated for dead symbol + // detection. + SVal V = I.getData(); + const MemRegion *R = V.getAsRegion(); + if (R && isa<SymbolicRegion>(R)) + VisitBinding(V); + } + } + } + conjure_default: + // Set the default value of the array to conjured symbol. + DefinedOrUnknownSVal V = + svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, + AT->getElementType(), Count); + B = B.addBinding(baseR, BindingKey::Default, V); + return; + } + + DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, + T,Count); + assert(SymbolManager::canSymbolicate(T) || V.isUnknown()); + B = B.addBinding(baseR, BindingKey::Direct, V); +} + +bool InvalidateRegionsWorker::isInitiallyIncludedGlobalRegion( + const MemRegion *R) { + switch (GlobalsFilter) { + case GFK_None: + return false; + case GFK_SystemOnly: + return isa<GlobalSystemSpaceRegion>(R->getMemorySpace()); + case GFK_All: + return isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace()); + } + + llvm_unreachable("unknown globals filter"); +} + +bool InvalidateRegionsWorker::includeEntireMemorySpace(const MemRegion *Base) { + if (isInitiallyIncludedGlobalRegion(Base)) + return true; + + const MemSpaceRegion *MemSpace = Base->getMemorySpace(); + return ITraits.hasTrait(MemSpace, + RegionAndSymbolInvalidationTraits::TK_EntireMemSpace); +} + +RegionBindingsRef +RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K, + const Expr *Ex, + unsigned Count, + const LocationContext *LCtx, + RegionBindingsRef B, + InvalidatedRegions *Invalidated) { + // Bind the globals memory space to a new symbol that we will use to derive + // the bindings for all globals. + const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(K); + SVal V = svalBuilder.conjureSymbolVal(/* SymbolTag = */ (const void*) GS, Ex, LCtx, + /* type does not matter */ Ctx.IntTy, + Count); + + B = B.removeBinding(GS) + .addBinding(BindingKey::Make(GS, BindingKey::Default), V); + + // Even if there are no bindings in the global scope, we still need to + // record that we touched it. + if (Invalidated) + Invalidated->push_back(GS); + + return B; +} + +void RegionStoreManager::populateWorkList(InvalidateRegionsWorker &W, + ArrayRef<SVal> Values, + InvalidatedRegions *TopLevelRegions) { + for (ArrayRef<SVal>::iterator I = Values.begin(), + E = Values.end(); I != E; ++I) { + SVal V = *I; + if (Optional<nonloc::LazyCompoundVal> LCS = + V.getAs<nonloc::LazyCompoundVal>()) { + + const SValListTy &Vals = getInterestingValues(*LCS); + + for (SValListTy::const_iterator I = Vals.begin(), + E = Vals.end(); I != E; ++I) { + // Note: the last argument is false here because these are + // non-top-level regions. + if (const MemRegion *R = (*I).getAsRegion()) + W.AddToWorkList(R); + } + continue; + } + + if (const MemRegion *R = V.getAsRegion()) { + if (TopLevelRegions) + TopLevelRegions->push_back(R); + W.AddToWorkList(R); + continue; + } + } +} + +StoreRef +RegionStoreManager::invalidateRegions(Store store, + ArrayRef<SVal> Values, + const Expr *Ex, unsigned Count, + const LocationContext *LCtx, + const CallEvent *Call, + InvalidatedSymbols &IS, + RegionAndSymbolInvalidationTraits &ITraits, + InvalidatedRegions *TopLevelRegions, + InvalidatedRegions *Invalidated) { + GlobalsFilterKind GlobalsFilter; + if (Call) { + if (Call->isInSystemHeader()) + GlobalsFilter = GFK_SystemOnly; + else + GlobalsFilter = GFK_All; + } else { + GlobalsFilter = GFK_None; + } + + RegionBindingsRef B = getRegionBindings(store); + InvalidateRegionsWorker W(*this, StateMgr, B, Ex, Count, LCtx, IS, ITraits, + Invalidated, GlobalsFilter); + + // Scan the bindings and generate the clusters. + W.GenerateClusters(); + + // Add the regions to the worklist. + populateWorkList(W, Values, TopLevelRegions); + + W.RunWorkList(); + + // Return the new bindings. + B = W.getRegionBindings(); + + // For calls, determine which global regions should be invalidated and + // invalidate them. (Note that function-static and immutable globals are never + // invalidated by this.) + // TODO: This could possibly be more precise with modules. + switch (GlobalsFilter) { + case GFK_All: + B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind, + Ex, Count, LCtx, B, Invalidated); + LLVM_FALLTHROUGH; + case GFK_SystemOnly: + B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind, + Ex, Count, LCtx, B, Invalidated); + LLVM_FALLTHROUGH; + case GFK_None: + break; + } + + return StoreRef(B.asStore(), *this); +} + +//===----------------------------------------------------------------------===// +// Extents for regions. +//===----------------------------------------------------------------------===// + +DefinedOrUnknownSVal +RegionStoreManager::getSizeInElements(ProgramStateRef state, + const MemRegion *R, + QualType EleTy) { + SVal Size = cast<SubRegion>(R)->getExtent(svalBuilder); + const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size); + if (!SizeInt) + return UnknownVal(); + + CharUnits RegionSize = CharUnits::fromQuantity(SizeInt->getSExtValue()); + + if (Ctx.getAsVariableArrayType(EleTy)) { + // FIXME: We need to track extra state to properly record the size + // of VLAs. Returning UnknownVal here, however, is a stop-gap so that + // we don't have a divide-by-zero below. + return UnknownVal(); + } + + CharUnits EleSize = Ctx.getTypeSizeInChars(EleTy); + + // If a variable is reinterpreted as a type that doesn't fit into a larger + // type evenly, round it down. + // This is a signed value, since it's used in arithmetic with signed indices. + return svalBuilder.makeIntVal(RegionSize / EleSize, + svalBuilder.getArrayIndexType()); +} + +//===----------------------------------------------------------------------===// +// Location and region casting. +//===----------------------------------------------------------------------===// + +/// ArrayToPointer - Emulates the "decay" of an array to a pointer +/// type. 'Array' represents the lvalue of the array being decayed +/// to a pointer, and the returned SVal represents the decayed +/// version of that lvalue (i.e., a pointer to the first element of +/// the array). This is called by ExprEngine when evaluating casts +/// from arrays to pointers. +SVal RegionStoreManager::ArrayToPointer(Loc Array, QualType T) { + if (Array.getAs<loc::ConcreteInt>()) + return Array; + + if (!Array.getAs<loc::MemRegionVal>()) + return UnknownVal(); + + const SubRegion *R = + cast<SubRegion>(Array.castAs<loc::MemRegionVal>().getRegion()); + NonLoc ZeroIdx = svalBuilder.makeZeroArrayIndex(); + return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, R, Ctx)); +} + +//===----------------------------------------------------------------------===// +// Loading values from regions. +//===----------------------------------------------------------------------===// + +SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) { + assert(!L.getAs<UnknownVal>() && "location unknown"); + assert(!L.getAs<UndefinedVal>() && "location undefined"); + + // For access to concrete addresses, return UnknownVal. Checks + // for null dereferences (and similar errors) are done by checkers, not + // the Store. + // FIXME: We can consider lazily symbolicating such memory, but we really + // should defer this when we can reason easily about symbolicating arrays + // of bytes. + if (L.getAs<loc::ConcreteInt>()) { + return UnknownVal(); + } + if (!L.getAs<loc::MemRegionVal>()) { + return UnknownVal(); + } + + const MemRegion *MR = L.castAs<loc::MemRegionVal>().getRegion(); + + if (isa<BlockDataRegion>(MR)) { + return UnknownVal(); + } + + if (!isa<TypedValueRegion>(MR)) { + if (T.isNull()) { + if (const TypedRegion *TR = dyn_cast<TypedRegion>(MR)) + T = TR->getLocationType()->getPointeeType(); + else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR)) + T = SR->getSymbol()->getType()->getPointeeType(); + } + assert(!T.isNull() && "Unable to auto-detect binding type!"); + assert(!T->isVoidType() && "Attempting to dereference a void pointer!"); + MR = GetElementZeroRegion(cast<SubRegion>(MR), T); + } else { + T = cast<TypedValueRegion>(MR)->getValueType(); + } + + // FIXME: Perhaps this method should just take a 'const MemRegion*' argument + // instead of 'Loc', and have the other Loc cases handled at a higher level. + const TypedValueRegion *R = cast<TypedValueRegion>(MR); + QualType RTy = R->getValueType(); + + // FIXME: we do not yet model the parts of a complex type, so treat the + // whole thing as "unknown". + if (RTy->isAnyComplexType()) + return UnknownVal(); + + // FIXME: We should eventually handle funny addressing. e.g.: + // + // int x = ...; + // int *p = &x; + // char *q = (char*) p; + // char c = *q; // returns the first byte of 'x'. + // + // Such funny addressing will occur due to layering of regions. + if (RTy->isStructureOrClassType()) + return getBindingForStruct(B, R); + + // FIXME: Handle unions. + if (RTy->isUnionType()) + return createLazyBinding(B, R); + + if (RTy->isArrayType()) { + if (RTy->isConstantArrayType()) + return getBindingForArray(B, R); + else + return UnknownVal(); + } + + // FIXME: handle Vector types. + if (RTy->isVectorType()) + return UnknownVal(); + + if (const FieldRegion* FR = dyn_cast<FieldRegion>(R)) + return CastRetrievedVal(getBindingForField(B, FR), FR, T); + + if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) { + // FIXME: Here we actually perform an implicit conversion from the loaded + // value to the element type. Eventually we want to compose these values + // more intelligently. For example, an 'element' can encompass multiple + // bound regions (e.g., several bound bytes), or could be a subset of + // a larger value. + return CastRetrievedVal(getBindingForElement(B, ER), ER, T); + } + + if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) { + // FIXME: Here we actually perform an implicit conversion from the loaded + // value to the ivar type. What we should model is stores to ivars + // that blow past the extent of the ivar. If the address of the ivar is + // reinterpretted, it is possible we stored a different value that could + // fit within the ivar. Either we need to cast these when storing them + // or reinterpret them lazily (as we do here). + return CastRetrievedVal(getBindingForObjCIvar(B, IVR), IVR, T); + } + + if (const VarRegion *VR = dyn_cast<VarRegion>(R)) { + // FIXME: Here we actually perform an implicit conversion from the loaded + // value to the variable type. What we should model is stores to variables + // that blow past the extent of the variable. If the address of the + // variable is reinterpretted, it is possible we stored a different value + // that could fit within the variable. Either we need to cast these when + // storing them or reinterpret them lazily (as we do here). + return CastRetrievedVal(getBindingForVar(B, VR), VR, T); + } + + const SVal *V = B.lookup(R, BindingKey::Direct); + + // Check if the region has a binding. + if (V) + return *V; + + // The location does not have a bound value. This means that it has + // the value it had upon its creation and/or entry to the analyzed + // function/method. These are either symbolic values or 'undefined'. + if (R->hasStackNonParametersStorage()) { + // All stack variables are considered to have undefined values + // upon creation. All heap allocated blocks are considered to + // have undefined values as well unless they are explicitly bound + // to specific values. + return UndefinedVal(); + } + + // All other values are symbolic. + return svalBuilder.getRegionValueSymbolVal(R); +} + +static QualType getUnderlyingType(const SubRegion *R) { + QualType RegionTy; + if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R)) + RegionTy = TVR->getValueType(); + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) + RegionTy = SR->getSymbol()->getType(); + + return RegionTy; +} + +/// Checks to see if store \p B has a lazy binding for region \p R. +/// +/// If \p AllowSubregionBindings is \c false, a lazy binding will be rejected +/// if there are additional bindings within \p R. +/// +/// Note that unlike RegionStoreManager::findLazyBinding, this will not search +/// for lazy bindings for super-regions of \p R. +static Optional<nonloc::LazyCompoundVal> +getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B, + const SubRegion *R, bool AllowSubregionBindings) { + Optional<SVal> V = B.getDefaultBinding(R); + if (!V) + return None; + + Optional<nonloc::LazyCompoundVal> LCV = V->getAs<nonloc::LazyCompoundVal>(); + if (!LCV) + return None; + + // If the LCV is for a subregion, the types might not match, and we shouldn't + // reuse the binding. + QualType RegionTy = getUnderlyingType(R); + if (!RegionTy.isNull() && + !RegionTy->isVoidPointerType()) { + QualType SourceRegionTy = LCV->getRegion()->getValueType(); + if (!SVB.getContext().hasSameUnqualifiedType(RegionTy, SourceRegionTy)) + return None; + } + + if (!AllowSubregionBindings) { + // If there are any other bindings within this region, we shouldn't reuse + // the top-level binding. + SmallVector<BindingPair, 16> Bindings; + collectSubRegionBindings(Bindings, SVB, *B.lookup(R->getBaseRegion()), R, + /*IncludeAllDefaultBindings=*/true); + if (Bindings.size() > 1) + return None; + } + + return *LCV; +} + + +std::pair<Store, const SubRegion *> +RegionStoreManager::findLazyBinding(RegionBindingsConstRef B, + const SubRegion *R, + const SubRegion *originalRegion) { + if (originalRegion != R) { + if (Optional<nonloc::LazyCompoundVal> V = + getExistingLazyBinding(svalBuilder, B, R, true)) + return std::make_pair(V->getStore(), V->getRegion()); + } + + typedef std::pair<Store, const SubRegion *> StoreRegionPair; + StoreRegionPair Result = StoreRegionPair(); + + if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) { + Result = findLazyBinding(B, cast<SubRegion>(ER->getSuperRegion()), + originalRegion); + + if (Result.second) + Result.second = MRMgr.getElementRegionWithSuper(ER, Result.second); + + } else if (const FieldRegion *FR = dyn_cast<FieldRegion>(R)) { + Result = findLazyBinding(B, cast<SubRegion>(FR->getSuperRegion()), + originalRegion); + + if (Result.second) + Result.second = MRMgr.getFieldRegionWithSuper(FR, Result.second); + + } else if (const CXXBaseObjectRegion *BaseReg = + dyn_cast<CXXBaseObjectRegion>(R)) { + // C++ base object region is another kind of region that we should blast + // through to look for lazy compound value. It is like a field region. + Result = findLazyBinding(B, cast<SubRegion>(BaseReg->getSuperRegion()), + originalRegion); + + if (Result.second) + Result.second = MRMgr.getCXXBaseObjectRegionWithSuper(BaseReg, + Result.second); + } + + return Result; +} + +SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B, + const ElementRegion* R) { + // We do not currently model bindings of the CompoundLiteralregion. + if (isa<CompoundLiteralRegion>(R->getBaseRegion())) + return UnknownVal(); + + // Check if the region has a binding. + if (const Optional<SVal> &V = B.getDirectBinding(R)) + return *V; + + const MemRegion* superR = R->getSuperRegion(); + + // Check if the region is an element region of a string literal. + if (const StringRegion *StrR = dyn_cast<StringRegion>(superR)) { + // FIXME: Handle loads from strings where the literal is treated as + // an integer, e.g., *((unsigned int*)"hello") + QualType T = Ctx.getAsArrayType(StrR->getValueType())->getElementType(); + if (!Ctx.hasSameUnqualifiedType(T, R->getElementType())) + return UnknownVal(); + + const StringLiteral *Str = StrR->getStringLiteral(); + SVal Idx = R->getIndex(); + if (Optional<nonloc::ConcreteInt> CI = Idx.getAs<nonloc::ConcreteInt>()) { + int64_t i = CI->getValue().getSExtValue(); + // Abort on string underrun. This can be possible by arbitrary + // clients of getBindingForElement(). + if (i < 0) + return UndefinedVal(); + int64_t length = Str->getLength(); + // Technically, only i == length is guaranteed to be null. + // However, such overflows should be caught before reaching this point; + // the only time such an access would be made is if a string literal was + // used to initialize a larger array. + char c = (i >= length) ? '\0' : Str->getCodeUnit(i); + return svalBuilder.makeIntVal(c, T); + } + } else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) { + // Check if the containing array is const and has an initialized value. + 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 auto *InitList = dyn_cast<InitListExpr>(Init)) { + // The array index has to be known. + if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) { + int64_t i = CI->getValue().getSExtValue(); + // If it is known that the index is out of bounds, we can return + // an undefined value. + if (i < 0) + return UndefinedVal(); + + if (auto CAT = Ctx.getAsConstantArrayType(VD->getType())) + if (CAT->getSize().sle(i)) + return UndefinedVal(); + + // If there is a list, but no init, it must be zero. + if (i >= InitList->getNumInits()) + return svalBuilder.makeZeroVal(R->getElementType()); + + if (const Expr *ElemInit = InitList->getInit(i)) + if (Optional<SVal> V = svalBuilder.getConstantVal(ElemInit)) + return *V; + } + } + } + } + } + + // Check for loads from a code text region. For such loads, just give up. + if (isa<CodeTextRegion>(superR)) + return UnknownVal(); + + // Handle the case where we are indexing into a larger scalar object. + // For example, this handles: + // int x = ... + // char *y = &x; + // return *y; + // FIXME: This is a hack, and doesn't do anything really intelligent yet. + const RegionRawOffset &O = R->getAsArrayOffset(); + + // If we cannot reason about the offset, return an unknown value. + if (!O.getRegion()) + return UnknownVal(); + + if (const TypedValueRegion *baseR = + dyn_cast_or_null<TypedValueRegion>(O.getRegion())) { + QualType baseT = baseR->getValueType(); + if (baseT->isScalarType()) { + QualType elemT = R->getElementType(); + if (elemT->isScalarType()) { + if (Ctx.getTypeSizeInChars(baseT) >= Ctx.getTypeSizeInChars(elemT)) { + if (const Optional<SVal> &V = B.getDirectBinding(superR)) { + if (SymbolRef parentSym = V->getAsSymbol()) + return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); + + if (V->isUnknownOrUndef()) + return *V; + // Other cases: give up. We are indexing into a larger object + // that has some value, but we don't know how to handle that yet. + return UnknownVal(); + } + } + } + } + } + return getBindingForFieldOrElementCommon(B, R, R->getElementType()); +} + +SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B, + const FieldRegion* R) { + + // Check if the region has a binding. + if (const Optional<SVal> &V = B.getDirectBinding(R)) + return *V; + + // Is the field declared constant and has an in-class initializer? + const FieldDecl *FD = R->getDecl(); + QualType Ty = FD->getType(); + if (Ty.isConstQualified()) + if (const Expr *Init = FD->getInClassInitializer()) + if (Optional<SVal> V = svalBuilder.getConstantVal(Init)) + return *V; + + // If the containing record was initialized, try to get its constant value. + const MemRegion* superR = R->getSuperRegion(); + if (const auto *VR = dyn_cast<VarRegion>(superR)) { + const VarDecl *VD = VR->getDecl(); + QualType RecordVarTy = VD->getType(); + 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 auto *InitList = dyn_cast<InitListExpr>(Init)) { + if (Index < InitList->getNumInits()) { + if (const Expr *FieldInit = InitList->getInit(Index)) + if (Optional<SVal> V = svalBuilder.getConstantVal(FieldInit)) + return *V; + } else { + return svalBuilder.makeZeroVal(Ty); + } + } + } + + return getBindingForFieldOrElementCommon(B, R, Ty); +} + +Optional<SVal> +RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindingsConstRef B, + const MemRegion *superR, + const TypedValueRegion *R, + QualType Ty) { + + if (const Optional<SVal> &D = B.getDefaultBinding(superR)) { + const SVal &val = D.getValue(); + if (SymbolRef parentSym = val.getAsSymbol()) + return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); + + if (val.isZeroConstant()) + return svalBuilder.makeZeroVal(Ty); + + if (val.isUnknownOrUndef()) + return val; + + // Lazy bindings are usually handled through getExistingLazyBinding(). + // We should unify these two code paths at some point. + if (val.getAs<nonloc::LazyCompoundVal>() || + val.getAs<nonloc::CompoundVal>()) + return val; + + llvm_unreachable("Unknown default value"); + } + + return None; +} + +SVal RegionStoreManager::getLazyBinding(const SubRegion *LazyBindingRegion, + RegionBindingsRef LazyBinding) { + SVal Result; + if (const ElementRegion *ER = dyn_cast<ElementRegion>(LazyBindingRegion)) + Result = getBindingForElement(LazyBinding, ER); + else + Result = getBindingForField(LazyBinding, + cast<FieldRegion>(LazyBindingRegion)); + + // FIXME: This is a hack to deal with RegionStore's inability to distinguish a + // default value for /part/ of an aggregate from a default value for the + // /entire/ aggregate. The most common case of this is when struct Outer + // has as its first member a struct Inner, which is copied in from a stack + // variable. In this case, even if the Outer's default value is symbolic, 0, + // or unknown, it gets overridden by the Inner's default value of undefined. + // + // This is a general problem -- if the Inner is zero-initialized, the Outer + // will now look zero-initialized. The proper way to solve this is with a + // new version of RegionStore that tracks the extent of a binding as well + // as the offset. + // + // This hack only takes care of the undefined case because that can very + // quickly result in a warning. + if (Result.isUndef()) + Result = UnknownVal(); + + return Result; +} + +SVal +RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B, + const TypedValueRegion *R, + QualType Ty) { + + // At this point we have already checked in either getBindingForElement or + // getBindingForField if 'R' has a direct binding. + + // Lazy binding? + Store lazyBindingStore = nullptr; + const SubRegion *lazyBindingRegion = nullptr; + std::tie(lazyBindingStore, lazyBindingRegion) = findLazyBinding(B, R, R); + if (lazyBindingRegion) + return getLazyBinding(lazyBindingRegion, + getRegionBindings(lazyBindingStore)); + + // Record whether or not we see a symbolic index. That can completely + // be out of scope of our lookup. + bool hasSymbolicIndex = false; + + // FIXME: This is a hack to deal with RegionStore's inability to distinguish a + // default value for /part/ of an aggregate from a default value for the + // /entire/ aggregate. The most common case of this is when struct Outer + // has as its first member a struct Inner, which is copied in from a stack + // variable. In this case, even if the Outer's default value is symbolic, 0, + // or unknown, it gets overridden by the Inner's default value of undefined. + // + // This is a general problem -- if the Inner is zero-initialized, the Outer + // will now look zero-initialized. The proper way to solve this is with a + // new version of RegionStore that tracks the extent of a binding as well + // as the offset. + // + // This hack only takes care of the undefined case because that can very + // quickly result in a warning. + bool hasPartialLazyBinding = false; + + const SubRegion *SR = R; + while (SR) { + const MemRegion *Base = SR->getSuperRegion(); + if (Optional<SVal> D = getBindingForDerivedDefaultValue(B, Base, R, Ty)) { + if (D->getAs<nonloc::LazyCompoundVal>()) { + hasPartialLazyBinding = true; + break; + } + + return *D; + } + + if (const ElementRegion *ER = dyn_cast<ElementRegion>(Base)) { + NonLoc index = ER->getIndex(); + if (!index.isConstant()) + hasSymbolicIndex = true; + } + + // If our super region is a field or element itself, walk up the region + // hierarchy to see if there is a default value installed in an ancestor. + SR = dyn_cast<SubRegion>(Base); + } + + if (R->hasStackNonParametersStorage()) { + if (isa<ElementRegion>(R)) { + // Currently we don't reason specially about Clang-style vectors. Check + // if superR is a vector and if so return Unknown. + if (const TypedValueRegion *typedSuperR = + dyn_cast<TypedValueRegion>(R->getSuperRegion())) { + if (typedSuperR->getValueType()->isVectorType()) + return UnknownVal(); + } + } + + // FIXME: We also need to take ElementRegions with symbolic indexes into + // account. This case handles both directly accessing an ElementRegion + // with a symbolic offset, but also fields within an element with + // a symbolic offset. + if (hasSymbolicIndex) + return UnknownVal(); + + if (!hasPartialLazyBinding) + return UndefinedVal(); + } + + // All other values are symbolic. + return svalBuilder.getRegionValueSymbolVal(R); +} + +SVal RegionStoreManager::getBindingForObjCIvar(RegionBindingsConstRef B, + const ObjCIvarRegion* R) { + // Check if the region has a binding. + if (const Optional<SVal> &V = B.getDirectBinding(R)) + return *V; + + const MemRegion *superR = R->getSuperRegion(); + + // Check if the super region has a default binding. + if (const Optional<SVal> &V = B.getDefaultBinding(superR)) { + if (SymbolRef parentSym = V->getAsSymbol()) + return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); + + // Other cases: give up. + return UnknownVal(); + } + + return getBindingForLazySymbol(R); +} + +SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B, + const VarRegion *R) { + + // Check if the region has a binding. + if (const Optional<SVal> &V = B.getDirectBinding(R)) + return *V; + + // Lazily derive a value for the VarRegion. + const VarDecl *VD = R->getDecl(); + const MemSpaceRegion *MS = R->getMemorySpace(); + + // Arguments are always symbolic. + if (isa<StackArgumentsSpaceRegion>(MS)) + return svalBuilder.getRegionValueSymbolVal(R); + + // Is 'VD' declared constant? If so, retrieve the constant value. + if (VD->getType().isConstQualified()) { + if (const Expr *Init = VD->getInit()) { + if (Optional<SVal> V = svalBuilder.getConstantVal(Init)) + return *V; + + // If the variable is const qualified and has an initializer but + // we couldn't evaluate initializer to a value, treat the value as + // unknown. + return UnknownVal(); + } + } + + // This must come after the check for constants because closure-captured + // constant variables may appear in UnknownSpaceRegion. + if (isa<UnknownSpaceRegion>(MS)) + return svalBuilder.getRegionValueSymbolVal(R); + + if (isa<GlobalsSpaceRegion>(MS)) { + QualType T = VD->getType(); + + // Function-scoped static variables are default-initialized to 0; if they + // have an initializer, it would have been processed by now. + // FIXME: This is only true when we're starting analysis from main(). + // We're losing a lot of coverage here. + if (isa<StaticGlobalSpaceRegion>(MS)) + return svalBuilder.makeZeroVal(T); + + if (Optional<SVal> V = getBindingForDerivedDefaultValue(B, MS, R, T)) { + assert(!V->getAs<nonloc::LazyCompoundVal>()); + return V.getValue(); + } + + return svalBuilder.getRegionValueSymbolVal(R); + } + + return UndefinedVal(); +} + +SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) { + // All other values are symbolic. + return svalBuilder.getRegionValueSymbolVal(R); +} + +const RegionStoreManager::SValListTy & +RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) { + // First, check the cache. + LazyBindingsMapTy::iterator I = LazyBindingsMap.find(LCV.getCVData()); + if (I != LazyBindingsMap.end()) + return I->second; + + // If we don't have a list of values cached, start constructing it. + SValListTy List; + + const SubRegion *LazyR = LCV.getRegion(); + RegionBindingsRef B = getRegionBindings(LCV.getStore()); + + // If this region had /no/ bindings at the time, there are no interesting + // values to return. + const ClusterBindings *Cluster = B.lookup(LazyR->getBaseRegion()); + if (!Cluster) + return (LazyBindingsMap[LCV.getCVData()] = std::move(List)); + + SmallVector<BindingPair, 32> Bindings; + collectSubRegionBindings(Bindings, svalBuilder, *Cluster, LazyR, + /*IncludeAllDefaultBindings=*/true); + for (SmallVectorImpl<BindingPair>::const_iterator I = Bindings.begin(), + E = Bindings.end(); + I != E; ++I) { + SVal V = I->second; + if (V.isUnknownOrUndef() || V.isConstant()) + continue; + + if (Optional<nonloc::LazyCompoundVal> InnerLCV = + V.getAs<nonloc::LazyCompoundVal>()) { + const SValListTy &InnerList = getInterestingValues(*InnerLCV); + List.insert(List.end(), InnerList.begin(), InnerList.end()); + continue; + } + + List.push_back(V); + } + + return (LazyBindingsMap[LCV.getCVData()] = std::move(List)); +} + +NonLoc RegionStoreManager::createLazyBinding(RegionBindingsConstRef B, + const TypedValueRegion *R) { + if (Optional<nonloc::LazyCompoundVal> V = + getExistingLazyBinding(svalBuilder, B, R, false)) + return *V; + + return svalBuilder.makeLazyCompoundVal(StoreRef(B.asStore(), *this), R); +} + +static bool isRecordEmpty(const RecordDecl *RD) { + if (!RD->field_empty()) + return false; + if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(RD)) + return CRD->getNumBases() == 0; + return true; +} + +SVal RegionStoreManager::getBindingForStruct(RegionBindingsConstRef B, + const TypedValueRegion *R) { + const RecordDecl *RD = R->getValueType()->castAs<RecordType>()->getDecl(); + if (!RD->getDefinition() || isRecordEmpty(RD)) + return UnknownVal(); + + return createLazyBinding(B, R); +} + +SVal RegionStoreManager::getBindingForArray(RegionBindingsConstRef B, + const TypedValueRegion *R) { + assert(Ctx.getAsConstantArrayType(R->getValueType()) && + "Only constant array types can have compound bindings."); + + return createLazyBinding(B, R); +} + +bool RegionStoreManager::includedInBindings(Store store, + const MemRegion *region) const { + RegionBindingsRef B = getRegionBindings(store); + region = region->getBaseRegion(); + + // Quick path: if the base is the head of a cluster, the region is live. + if (B.lookup(region)) + return true; + + // Slow path: if the region is the VALUE of any binding, it is live. + for (RegionBindingsRef::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) { + const ClusterBindings &Cluster = RI.getData(); + for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end(); + CI != CE; ++CI) { + const SVal &D = CI.getData(); + if (const MemRegion *R = D.getAsRegion()) + if (R->getBaseRegion() == region) + return true; + } + } + + return false; +} + +//===----------------------------------------------------------------------===// +// Binding values to regions. +//===----------------------------------------------------------------------===// + +StoreRef RegionStoreManager::killBinding(Store ST, Loc L) { + if (Optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>()) + if (const MemRegion* R = LV->getRegion()) + return StoreRef(getRegionBindings(ST).removeBinding(R) + .asImmutableMap() + .getRootWithoutRetain(), + *this); + + return StoreRef(ST, *this); +} + +RegionBindingsRef +RegionStoreManager::bind(RegionBindingsConstRef B, Loc L, SVal V) { + if (L.getAs<loc::ConcreteInt>()) + return B; + + // If we get here, the location should be a region. + const MemRegion *R = L.castAs<loc::MemRegionVal>().getRegion(); + + // Check if the region is a struct region. + if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R)) { + QualType Ty = TR->getValueType(); + if (Ty->isArrayType()) + return bindArray(B, TR, V); + if (Ty->isStructureOrClassType()) + return bindStruct(B, TR, V); + if (Ty->isVectorType()) + return bindVector(B, TR, V); + if (Ty->isUnionType()) + return bindAggregate(B, TR, V); + } + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) { + // Binding directly to a symbolic region should be treated as binding + // to element 0. + QualType T = SR->getSymbol()->getType(); + if (T->isAnyPointerType() || T->isReferenceType()) + T = T->getPointeeType(); + + R = GetElementZeroRegion(SR, T); + } + + assert((!isa<CXXThisRegion>(R) || !B.lookup(R)) && + "'this' pointer is not an l-value and is not assignable"); + + // Clear out bindings that may overlap with this binding. + RegionBindingsRef NewB = removeSubRegionBindings(B, cast<SubRegion>(R)); + return NewB.addBinding(BindingKey::Make(R, BindingKey::Direct), V); +} + +RegionBindingsRef +RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B, + const MemRegion *R, + QualType T) { + SVal V; + + if (Loc::isLocType(T)) + V = svalBuilder.makeNull(); + else if (T->isIntegralOrEnumerationType()) + V = svalBuilder.makeZeroVal(T); + else if (T->isStructureOrClassType() || T->isArrayType()) { + // Set the default value to a zero constant when it is a structure + // or array. The type doesn't really matter. + V = svalBuilder.makeZeroVal(Ctx.IntTy); + } + else { + // We can't represent values of this type, but we still need to set a value + // to record that the region has been initialized. + // If this assertion ever fires, a new case should be added above -- we + // should know how to default-initialize any value we can symbolicate. + assert(!SymbolManager::canSymbolicate(T) && "This type is representable"); + V = UnknownVal(); + } + + return B.addBinding(R, BindingKey::Default, V); +} + +RegionBindingsRef +RegionStoreManager::bindArray(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal Init) { + + const ArrayType *AT =cast<ArrayType>(Ctx.getCanonicalType(R->getValueType())); + QualType ElementTy = AT->getElementType(); + Optional<uint64_t> Size; + + if (const ConstantArrayType* CAT = dyn_cast<ConstantArrayType>(AT)) + Size = CAT->getSize().getZExtValue(); + + // Check if the init expr is a literal. If so, bind the rvalue instead. + // FIXME: It's not responsibility of the Store to transform this lvalue + // to rvalue. ExprEngine or maybe even CFG should do this before binding. + if (Optional<loc::MemRegionVal> MRV = Init.getAs<loc::MemRegionVal>()) { + SVal V = getBinding(B.asStore(), *MRV, R->getValueType()); + return bindAggregate(B, R, V); + } + + // Handle lazy compound values. + if (Init.getAs<nonloc::LazyCompoundVal>()) + return bindAggregate(B, R, Init); + + if (Init.isUnknown()) + return bindAggregate(B, R, UnknownVal()); + + // Remaining case: explicit compound values. + const nonloc::CompoundVal& CV = Init.castAs<nonloc::CompoundVal>(); + nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); + uint64_t i = 0; + + RegionBindingsRef NewB(B); + + for (; Size.hasValue() ? i < Size.getValue() : true ; ++i, ++VI) { + // The init list might be shorter than the array length. + if (VI == VE) + break; + + const NonLoc &Idx = svalBuilder.makeArrayIndex(i); + const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, Ctx); + + if (ElementTy->isStructureOrClassType()) + NewB = bindStruct(NewB, ER, *VI); + else if (ElementTy->isArrayType()) + NewB = bindArray(NewB, ER, *VI); + else + NewB = bind(NewB, loc::MemRegionVal(ER), *VI); + } + + // If the init list is shorter than the array length (or the array has + // variable length), set the array default value. Values that are already set + // are not overwritten. + if (!Size.hasValue() || i < Size.getValue()) + NewB = setImplicitDefaultValue(NewB, R, ElementTy); + + return NewB; +} + +RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal V) { + QualType T = R->getValueType(); + assert(T->isVectorType()); + const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs. + + // Handle lazy compound values and symbolic values. + if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>()) + return bindAggregate(B, R, V); + + // We may get non-CompoundVal accidentally due to imprecise cast logic or + // that we are binding symbolic struct value. Kill the field values, and if + // the value is symbolic go and bind it as a "default" binding. + if (!V.getAs<nonloc::CompoundVal>()) { + return bindAggregate(B, R, UnknownVal()); + } + + QualType ElemType = VT->getElementType(); + nonloc::CompoundVal CV = V.castAs<nonloc::CompoundVal>(); + nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); + unsigned index = 0, numElements = VT->getNumElements(); + RegionBindingsRef NewB(B); + + for ( ; index != numElements ; ++index) { + if (VI == VE) + break; + + NonLoc Idx = svalBuilder.makeArrayIndex(index); + const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx); + + if (ElemType->isArrayType()) + NewB = bindArray(NewB, ER, *VI); + else if (ElemType->isStructureOrClassType()) + NewB = bindStruct(NewB, ER, *VI); + else + NewB = bind(NewB, loc::MemRegionVal(ER), *VI); + } + return NewB; +} + +Optional<RegionBindingsRef> +RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B, + const TypedValueRegion *R, + const RecordDecl *RD, + nonloc::LazyCompoundVal LCV) { + FieldVector Fields; + + if (const CXXRecordDecl *Class = dyn_cast<CXXRecordDecl>(RD)) + if (Class->getNumBases() != 0 || Class->getNumVBases() != 0) + return None; + + for (const auto *FD : RD->fields()) { + if (FD->isUnnamedBitfield()) + continue; + + // If there are too many fields, or if any of the fields are aggregates, + // just use the LCV as a default binding. + if (Fields.size() == SmallStructLimit) + return None; + + QualType Ty = FD->getType(); + if (!(Ty->isScalarType() || Ty->isReferenceType())) + return None; + + Fields.push_back(FD); + } + + RegionBindingsRef NewB = B; + + for (FieldVector::iterator I = Fields.begin(), E = Fields.end(); I != E; ++I){ + const FieldRegion *SourceFR = MRMgr.getFieldRegion(*I, LCV.getRegion()); + SVal V = getBindingForField(getRegionBindings(LCV.getStore()), SourceFR); + + const FieldRegion *DestFR = MRMgr.getFieldRegion(*I, R); + NewB = bind(NewB, loc::MemRegionVal(DestFR), V); + } + + return NewB; +} + +RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B, + const TypedValueRegion* R, + SVal V) { + if (!Features.supportsFields()) + return B; + + QualType T = R->getValueType(); + assert(T->isStructureOrClassType()); + + const RecordType* RT = T->getAs<RecordType>(); + const RecordDecl *RD = RT->getDecl(); + + if (!RD->isCompleteDefinition()) + return B; + + // Handle lazy compound values and symbolic values. + if (Optional<nonloc::LazyCompoundVal> LCV = + V.getAs<nonloc::LazyCompoundVal>()) { + if (Optional<RegionBindingsRef> NewB = tryBindSmallStruct(B, R, RD, *LCV)) + return *NewB; + return bindAggregate(B, R, V); + } + if (V.getAs<nonloc::SymbolVal>()) + return bindAggregate(B, R, V); + + // We may get non-CompoundVal accidentally due to imprecise cast logic or + // that we are binding symbolic struct value. Kill the field values, and if + // the value is symbolic go and bind it as a "default" binding. + if (V.isUnknown() || !V.getAs<nonloc::CompoundVal>()) + return bindAggregate(B, R, UnknownVal()); + + 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); + + for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) { + + if (VI == VE) + break; + + // Skip any unnamed bitfields to stay in sync with the initializers. + if (FI->isUnnamedBitfield()) + continue; + + QualType FTy = FI->getType(); + const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); + + if (FTy->isArrayType()) + NewB = bindArray(NewB, FR, *VI); + else if (FTy->isStructureOrClassType()) + NewB = bindStruct(NewB, FR, *VI); + else + NewB = bind(NewB, loc::MemRegionVal(FR), *VI); + ++VI; + } + + // There may be fewer values in the initialize list than the fields of struct. + if (FI != FE) { + NewB = NewB.addBinding(R, BindingKey::Default, + svalBuilder.makeIntVal(0, false)); + } + + return NewB; +} + +RegionBindingsRef +RegionStoreManager::bindAggregate(RegionBindingsConstRef B, + const TypedRegion *R, + SVal Val) { + // Remove the old bindings, using 'R' as the root of all regions + // we will invalidate. Then add the new binding. + return removeSubRegionBindings(B, R).addBinding(R, BindingKey::Default, Val); +} + +//===----------------------------------------------------------------------===// +// State pruning. +//===----------------------------------------------------------------------===// + +namespace { +class RemoveDeadBindingsWorker + : public ClusterAnalysis<RemoveDeadBindingsWorker> { + using ChildrenListTy = SmallVector<const SymbolDerived *, 4>; + using MapParentsToDerivedTy = llvm::DenseMap<SymbolRef, ChildrenListTy>; + + MapParentsToDerivedTy ParentsToDerived; + SymbolReaper &SymReaper; + const StackFrameContext *CurrentLCtx; + +public: + RemoveDeadBindingsWorker(RegionStoreManager &rm, + ProgramStateManager &stateMgr, + RegionBindingsRef b, SymbolReaper &symReaper, + const StackFrameContext *LCtx) + : ClusterAnalysis<RemoveDeadBindingsWorker>(rm, stateMgr, b), + SymReaper(symReaper), CurrentLCtx(LCtx) {} + + // Called by ClusterAnalysis. + void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C); + void VisitCluster(const MemRegion *baseR, const ClusterBindings *C); + using ClusterAnalysis<RemoveDeadBindingsWorker>::VisitCluster; + + using ClusterAnalysis::AddToWorkList; + + bool AddToWorkList(const MemRegion *R); + + void VisitBinding(SVal V); + +private: + void populateWorklistFromSymbol(SymbolRef s); +}; +} + +bool RemoveDeadBindingsWorker::AddToWorkList(const MemRegion *R) { + const MemRegion *BaseR = R->getBaseRegion(); + return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR)); +} + +void RemoveDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, + const ClusterBindings &C) { + + if (const VarRegion *VR = dyn_cast<VarRegion>(baseR)) { + if (SymReaper.isLive(VR)) + AddToWorkList(baseR, &C); + + return; + } + + if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) { + if (SymReaper.isLive(SR->getSymbol())) { + AddToWorkList(SR, &C); + } else if (const auto *SD = dyn_cast<SymbolDerived>(SR->getSymbol())) { + ParentsToDerived[SD->getParentSymbol()].push_back(SD); + } + + return; + } + + if (isa<NonStaticGlobalSpaceRegion>(baseR)) { + AddToWorkList(baseR, &C); + return; + } + + // 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()); + const StackFrameContext *RegCtx = StackReg->getStackFrame(); + if (CurrentLCtx && + (RegCtx == CurrentLCtx || RegCtx->isParentOf(CurrentLCtx))) + AddToWorkList(TR, &C); + } +} + +void RemoveDeadBindingsWorker::VisitCluster(const MemRegion *baseR, + const ClusterBindings *C) { + if (!C) + return; + + // Mark the symbol for any SymbolicRegion with live bindings as live itself. + // This means we should continue to track that symbol. + if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(baseR)) + SymReaper.markLive(SymR->getSymbol()); + + for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) { + // Element index of a binding key is live. + SymReaper.markElementIndicesLive(I.getKey().getRegion()); + + VisitBinding(I.getData()); + } +} + +void RemoveDeadBindingsWorker::VisitBinding(SVal V) { + // Is it a LazyCompoundVal? All referenced regions are live as well. + if (Optional<nonloc::LazyCompoundVal> LCS = + V.getAs<nonloc::LazyCompoundVal>()) { + + const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS); + + for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(), + E = Vals.end(); + I != E; ++I) + VisitBinding(*I); + + return; + } + + // 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. + if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) { + BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(), + E = BR->referenced_vars_end(); + for ( ; I != E; ++I) + AddToWorkList(I.getCapturedRegion()); + } + } + + + // 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); + + 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); + + if (B.contains(SR)) + AddToWorkList(SR); + + const SymbolicRegion *SHR = + RM.getRegionManager().getSymbolicHeapRegion(SD); + if (B.contains(SHR)) + AddToWorkList(SHR); + } + } +} + +StoreRef RegionStoreManager::removeDeadBindings(Store store, + const StackFrameContext *LCtx, + SymbolReaper& SymReaper) { + RegionBindingsRef B = getRegionBindings(store); + RemoveDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx); + W.GenerateClusters(); + + // Enqueue the region roots onto the worklist. + for (SymbolReaper::region_iterator I = SymReaper.region_begin(), + E = SymReaper.region_end(); I != E; ++I) { + W.AddToWorkList(*I); + } + + W.RunWorkList(); + + // 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 + // as well as update DSymbols with the set symbols that are now dead. + for (RegionBindingsRef::iterator I = B.begin(), E = B.end(); I != E; ++I) { + const MemRegion *Base = I.getKey(); + + // If the cluster has been visited, we know the region has been marked. + // Otherwise, remove the dead entry. + if (!W.isVisited(Base)) + B = B.remove(Base); + } + + return StoreRef(B.asStore(), *this); +} + +//===----------------------------------------------------------------------===// +// Utility methods. +//===----------------------------------------------------------------------===// + +void RegionStoreManager::print(Store store, raw_ostream &OS, + const char* nl) { + RegionBindingsRef B = getRegionBindings(store); + OS << "Store (direct and default bindings), " + << B.asStore() + << " :" << nl; + B.dump(OS, nl); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp new file mode 100644 index 000000000000..2e40cc33381c --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp @@ -0,0 +1,1229 @@ +//== 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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp new file mode 100644 index 000000000000..6c0d487c8a87 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -0,0 +1,655 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines SValBuilder, the base class for all (complete) SValBuilder +// implementations. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Stmt.h" +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/None.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include <cassert> +#include <tuple> + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Basic SVal creation. +//===----------------------------------------------------------------------===// + +void SValBuilder::anchor() {} + +DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) { + if (Loc::isLocType(type)) + return makeNull(); + + if (type->isIntegralOrEnumerationType()) + return makeIntVal(0, type); + + if (type->isArrayType() || type->isRecordType() || type->isVectorType() || + type->isAnyComplexType()) + return makeCompoundVal(type, BasicVals.getEmptySValList()); + + // FIXME: Handle floats. + return UnknownVal(); +} + +NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APSInt& rhs, QualType type) { + // The Environment ensures we always get a persistent APSInt in + // BasicValueFactory, so we don't need to get the APSInt from + // BasicValueFactory again. + assert(lhs); + assert(!Loc::isLocType(type)); + return nonloc::SymbolVal(SymMgr.getSymIntExpr(lhs, op, rhs, type)); +} + +NonLoc SValBuilder::makeNonLoc(const llvm::APSInt& lhs, + BinaryOperator::Opcode op, const SymExpr *rhs, + QualType type) { + assert(rhs); + assert(!Loc::isLocType(type)); + return nonloc::SymbolVal(SymMgr.getIntSymExpr(lhs, op, rhs, type)); +} + +NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const SymExpr *rhs, QualType type) { + assert(lhs && rhs); + assert(!Loc::isLocType(type)); + return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type)); +} + +NonLoc SValBuilder::makeNonLoc(const SymExpr *operand, + QualType fromTy, QualType toTy) { + assert(operand); + assert(!Loc::isLocType(toTy)); + return nonloc::SymbolVal(SymMgr.getCastSymbol(operand, fromTy, toTy)); +} + +SVal SValBuilder::convertToArrayIndex(SVal val) { + if (val.isUnknownOrUndef()) + return val; + + // Common case: we have an appropriately sized integer. + if (Optional<nonloc::ConcreteInt> CI = val.getAs<nonloc::ConcreteInt>()) { + const llvm::APSInt& I = CI->getValue(); + if (I.getBitWidth() == ArrayIndexWidth && I.isSigned()) + return val; + } + + return evalCastFromNonLoc(val.castAs<NonLoc>(), ArrayIndexTy); +} + +nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){ + return makeTruthVal(boolean->getValue()); +} + +DefinedOrUnknownSVal +SValBuilder::getRegionValueSymbolVal(const TypedValueRegion *region) { + QualType T = region->getValueType(); + + if (T->isNullPtrType()) + return makeZeroVal(T); + + if (!SymbolManager::canSymbolicate(T)) + return UnknownVal(); + + SymbolRef sym = SymMgr.getRegionValueSymbol(region); + + if (Loc::isLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const void *SymbolTag, + const Expr *Ex, + const LocationContext *LCtx, + unsigned Count) { + QualType T = Ex->getType(); + + if (T->isNullPtrType()) + return makeZeroVal(T); + + // Compute the type of the result. If the expression is not an R-value, the + // result should be a location. + QualType ExType = Ex->getType(); + if (Ex->isGLValue()) + T = LCtx->getAnalysisDeclContext()->getASTContext().getPointerType(ExType); + + return conjureSymbolVal(SymbolTag, Ex, LCtx, T, Count); +} + +DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const void *symbolTag, + const Expr *expr, + const LocationContext *LCtx, + QualType type, + unsigned count) { + if (type->isNullPtrType()) + return makeZeroVal(type); + + if (!SymbolManager::canSymbolicate(type)) + return UnknownVal(); + + SymbolRef sym = SymMgr.conjureSymbol(expr, LCtx, type, count, symbolTag); + + if (Loc::isLocType(type)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const Stmt *stmt, + const LocationContext *LCtx, + QualType type, + unsigned visitCount) { + if (type->isNullPtrType()) + return makeZeroVal(type); + + if (!SymbolManager::canSymbolicate(type)) + return UnknownVal(); + + SymbolRef sym = SymMgr.conjureSymbol(stmt, LCtx, type, visitCount); + + if (Loc::isLocType(type)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedOrUnknownSVal +SValBuilder::getConjuredHeapSymbolVal(const Expr *E, + const LocationContext *LCtx, + unsigned VisitCount) { + QualType T = E->getType(); + assert(Loc::isLocType(T)); + assert(SymbolManager::canSymbolicate(T)); + if (T->isNullPtrType()) + return makeZeroVal(T); + + SymbolRef sym = SymMgr.conjureSymbol(E, LCtx, T, VisitCount); + return loc::MemRegionVal(MemMgr.getSymbolicHeapRegion(sym)); +} + +DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag, + const MemRegion *region, + const Expr *expr, QualType type, + const LocationContext *LCtx, + unsigned count) { + assert(SymbolManager::canSymbolicate(type) && "Invalid metadata symbol type"); + + SymbolRef sym = + SymMgr.getMetadataSymbol(region, expr, type, LCtx, count, symbolTag); + + if (Loc::isLocType(type)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedOrUnknownSVal +SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, + const TypedValueRegion *region) { + QualType T = region->getValueType(); + + if (T->isNullPtrType()) + return makeZeroVal(T); + + if (!SymbolManager::canSymbolicate(T)) + return UnknownVal(); + + SymbolRef sym = SymMgr.getDerivedSymbol(parentSymbol, region); + + if (Loc::isLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) { + assert(!DD || isa<CXXMethodDecl>(DD) || isa<FieldDecl>(DD)); + + if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(DD)) { + // Sema treats pointers to static member functions as have function pointer + // type, so return a function pointer for the method. + // We don't need to play a similar trick for static member fields + // because these are represented as plain VarDecls and not FieldDecls + // in the AST. + if (MD->isStatic()) + return getFunctionPointer(MD); + } + + return nonloc::PointerToMember(DD); +} + +DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) { + return loc::MemRegionVal(MemMgr.getFunctionCodeRegion(func)); +} + +DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block, + CanQualType locTy, + const LocationContext *locContext, + unsigned blockCount) { + const BlockCodeRegion *BC = + MemMgr.getBlockCodeRegion(block, locTy, locContext->getAnalysisDeclContext()); + const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, locContext, + blockCount); + return loc::MemRegionVal(BD); +} + +/// Return a memory region for the 'this' object reference. +loc::MemRegionVal SValBuilder::getCXXThis(const CXXMethodDecl *D, + const StackFrameContext *SFC) { + return loc::MemRegionVal( + getRegionManager().getCXXThisRegion(D->getThisType(), SFC)); +} + +/// Return a memory region for the 'this' object reference. +loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D, + const StackFrameContext *SFC) { + const Type *T = D->getTypeForDecl(); + QualType PT = getContext().getPointerType(QualType(T, 0)); + return loc::MemRegionVal(getRegionManager().getCXXThisRegion(PT, SFC)); +} + +Optional<SVal> SValBuilder::getConstantVal(const Expr *E) { + E = E->IgnoreParens(); + + switch (E->getStmtClass()) { + // Handle expressions that we treat differently from the AST's constant + // evaluator. + case Stmt::AddrLabelExprClass: + return makeLoc(cast<AddrLabelExpr>(E)); + + case Stmt::CXXScalarValueInitExprClass: + case Stmt::ImplicitValueInitExprClass: + return makeZeroVal(E->getType()); + + case Stmt::ObjCStringLiteralClass: { + const auto *SL = cast<ObjCStringLiteral>(E); + return makeLoc(getRegionManager().getObjCStringRegion(SL)); + } + + case Stmt::StringLiteralClass: { + const auto *SL = cast<StringLiteral>(E); + return makeLoc(getRegionManager().getStringRegion(SL)); + } + + // Fast-path some expressions to avoid the overhead of going through the AST's + // constant evaluator + case Stmt::CharacterLiteralClass: { + const auto *C = cast<CharacterLiteral>(E); + return makeIntVal(C->getValue(), C->getType()); + } + + case Stmt::CXXBoolLiteralExprClass: + return makeBoolVal(cast<CXXBoolLiteralExpr>(E)); + + case Stmt::TypeTraitExprClass: { + const auto *TE = cast<TypeTraitExpr>(E); + return makeTruthVal(TE->getValue(), TE->getType()); + } + + case Stmt::IntegerLiteralClass: + return makeIntVal(cast<IntegerLiteral>(E)); + + case Stmt::ObjCBoolLiteralExprClass: + return makeBoolVal(cast<ObjCBoolLiteralExpr>(E)); + + case Stmt::CXXNullPtrLiteralExprClass: + return makeNull(); + + case Stmt::CStyleCastExprClass: + case Stmt::CXXFunctionalCastExprClass: + case Stmt::CXXConstCastExprClass: + case Stmt::CXXReinterpretCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::ImplicitCastExprClass: { + const auto *CE = cast<CastExpr>(E); + switch (CE->getCastKind()) { + default: + break; + case CK_ArrayToPointerDecay: + case CK_IntegralToPointer: + case CK_NoOp: + case CK_BitCast: { + const Expr *SE = CE->getSubExpr(); + Optional<SVal> Val = getConstantVal(SE); + if (!Val) + return None; + return evalCast(*Val, CE->getType(), SE->getType()); + } + } + // FALLTHROUGH + LLVM_FALLTHROUGH; + } + + // If we don't have a special case, fall back to the AST's constant evaluator. + default: { + // Don't try to come up with a value for materialized temporaries. + if (E->isGLValue()) + return None; + + ASTContext &Ctx = getContext(); + Expr::EvalResult Result; + if (E->EvaluateAsInt(Result, Ctx)) + return makeIntVal(Result.Val.getInt()); + + if (Loc::isLocType(E->getType())) + if (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)) + return makeNull(); + + return None; + } + } +} + +SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op, + NonLoc LHS, NonLoc RHS, + QualType ResultTy) { + const SymExpr *symLHS = LHS.getAsSymExpr(); + const SymExpr *symRHS = RHS.getAsSymExpr(); + + // TODO: When the Max Complexity is reached, we should conjure a symbol + // instead of generating an Unknown value and propagate the taint info to it. + const unsigned MaxComp = StateMgr.getOwningEngine() + .getAnalysisManager() + .options.MaxSymbolComplexity; + + if (symLHS && symRHS && + (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp) + return makeNonLoc(symLHS, Op, symRHS, ResultTy); + + if (symLHS && symLHS->computeComplexity() < MaxComp) + if (Optional<nonloc::ConcreteInt> rInt = RHS.getAs<nonloc::ConcreteInt>()) + return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy); + + if (symRHS && symRHS->computeComplexity() < MaxComp) + if (Optional<nonloc::ConcreteInt> lInt = LHS.getAs<nonloc::ConcreteInt>()) + return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy); + + return UnknownVal(); +} + +SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op, + SVal lhs, SVal rhs, QualType type) { + if (lhs.isUndef() || rhs.isUndef()) + return UndefinedVal(); + + if (lhs.isUnknown() || rhs.isUnknown()) + return UnknownVal(); + + if (lhs.getAs<nonloc::LazyCompoundVal>() || + rhs.getAs<nonloc::LazyCompoundVal>()) { + return UnknownVal(); + } + + if (Optional<Loc> LV = lhs.getAs<Loc>()) { + if (Optional<Loc> RV = rhs.getAs<Loc>()) + return evalBinOpLL(state, op, *LV, *RV, type); + + return evalBinOpLN(state, op, *LV, rhs.castAs<NonLoc>(), type); + } + + if (Optional<Loc> RV = rhs.getAs<Loc>()) { + // Support pointer arithmetic where the addend is on the left + // and the pointer on the right. + assert(op == BO_Add); + + // Commute the operands. + return evalBinOpLN(state, op, *RV, lhs.castAs<NonLoc>(), type); + } + + return evalBinOpNN(state, op, lhs.castAs<NonLoc>(), rhs.castAs<NonLoc>(), + type); +} + +ConditionTruthVal SValBuilder::areEqual(ProgramStateRef state, SVal lhs, + SVal rhs) { + return state->isNonNull(evalEQ(state, lhs, rhs)); +} + +SVal SValBuilder::evalEQ(ProgramStateRef state, SVal lhs, SVal rhs) { + return evalBinOp(state, BO_EQ, lhs, rhs, getConditionType()); +} + +DefinedOrUnknownSVal SValBuilder::evalEQ(ProgramStateRef state, + DefinedOrUnknownSVal lhs, + DefinedOrUnknownSVal rhs) { + return evalEQ(state, static_cast<SVal>(lhs), static_cast<SVal>(rhs)) + .castAs<DefinedOrUnknownSVal>(); +} + +/// Recursively check if the pointer types are equal modulo const, volatile, +/// and restrict qualifiers. Also, assume that all types are similar to 'void'. +/// Assumes the input types are canonical. +static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy, + QualType FromTy) { + while (Context.UnwrapSimilarTypes(ToTy, FromTy)) { + Qualifiers Quals1, Quals2; + ToTy = Context.getUnqualifiedArrayType(ToTy, Quals1); + FromTy = Context.getUnqualifiedArrayType(FromTy, Quals2); + + // Make sure that non-cvr-qualifiers the other qualifiers (e.g., address + // spaces) are identical. + Quals1.removeCVRQualifiers(); + Quals2.removeCVRQualifiers(); + if (Quals1 != Quals2) + return false; + } + + // If we are casting to void, the 'From' value can be used to represent the + // 'To' value. + // + // FIXME: Doing this after unwrapping the types doesn't make any sense. A + // cast from 'int**' to 'void**' is not special in the way that a cast from + // 'int*' to 'void*' is. + if (ToTy->isVoidType()) + return true; + + if (ToTy != FromTy) + return false; + + return true; +} + +// Handles casts of type CK_IntegralCast. +// At the moment, this function will redirect to evalCast, except when the range +// of the original value is known to be greater than the max of the target type. +SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, + QualType castTy, QualType originalTy) { + // No truncations if target type is big enough. + if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy)) + return evalCast(val, castTy, originalTy); + + const SymExpr *se = val.getAsSymbolicExpression(); + if (!se) // Let evalCast handle non symbolic expressions. + return evalCast(val, castTy, originalTy); + + // Find the maximum value of the target type. + APSIntType ToType(getContext().getTypeSize(castTy), + castTy->isUnsignedIntegerType()); + llvm::APSInt ToTypeMax = ToType.getMaxValue(); + NonLoc ToTypeMaxVal = + makeIntVal(ToTypeMax.isUnsigned() ? ToTypeMax.getZExtValue() + : ToTypeMax.getSExtValue(), + castTy) + .castAs<NonLoc>(); + // Check the range of the symbol being casted against the maximum value of the + // target type. + NonLoc FromVal = val.castAs<NonLoc>(); + QualType CmpTy = getConditionType(); + NonLoc CompVal = + evalBinOpNN(state, BO_LE, FromVal, ToTypeMaxVal, CmpTy).castAs<NonLoc>(); + ProgramStateRef IsNotTruncated, IsTruncated; + std::tie(IsNotTruncated, IsTruncated) = state->assume(CompVal); + if (!IsNotTruncated && IsTruncated) { + // Symbol is truncated so we evaluate it as a cast. + NonLoc CastVal = makeNonLoc(se, originalTy, castTy); + return CastVal; + } + return evalCast(val, castTy, originalTy); +} + +// FIXME: should rewrite according to the cast kind. +SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { + castTy = Context.getCanonicalType(castTy); + originalTy = Context.getCanonicalType(originalTy); + if (val.isUnknownOrUndef() || castTy == originalTy) + return val; + + if (castTy->isBooleanType()) { + if (val.isUnknownOrUndef()) + return val; + if (val.isConstant()) + return makeTruthVal(!val.isZeroConstant(), castTy); + if (!Loc::isLocType(originalTy) && + !originalTy->isIntegralOrEnumerationType() && + !originalTy->isMemberPointerType()) + return UnknownVal(); + if (SymbolRef Sym = val.getAsSymbol(true)) { + BasicValueFactory &BVF = getBasicValueFactory(); + // FIXME: If we had a state here, we could see if the symbol is known to + // be zero, but we don't. + return makeNonLoc(Sym, BO_NE, BVF.getValue(0, Sym->getType()), castTy); + } + // Loc values are not always true, they could be weakly linked functions. + if (Optional<Loc> L = val.getAs<Loc>()) + return evalCastFromLoc(*L, castTy); + + Loc L = val.castAs<nonloc::LocAsInteger>().getLoc(); + return evalCastFromLoc(L, castTy); + } + + // For const casts, casts to void, just propagate the value. + if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType()) + if (shouldBeModeledWithNoOp(Context, Context.getPointerType(castTy), + Context.getPointerType(originalTy))) + return val; + + // Check for casts from pointers to integers. + if (castTy->isIntegralOrEnumerationType() && Loc::isLocType(originalTy)) + return evalCastFromLoc(val.castAs<Loc>(), castTy); + + // Check for casts from integers to pointers. + if (Loc::isLocType(castTy) && originalTy->isIntegralOrEnumerationType()) { + if (Optional<nonloc::LocAsInteger> LV = val.getAs<nonloc::LocAsInteger>()) { + if (const MemRegion *R = LV->getLoc().getAsRegion()) { + StoreManager &storeMgr = StateMgr.getStoreManager(); + R = storeMgr.castRegion(R, castTy); + return R ? SVal(loc::MemRegionVal(R)) : UnknownVal(); + } + return LV->getLoc(); + } + return dispatchCast(val, castTy); + } + + // Just pass through function and block pointers. + if (originalTy->isBlockPointerType() || originalTy->isFunctionPointerType()) { + assert(Loc::isLocType(castTy)); + return val; + } + + // Check for casts from array type to another type. + if (const auto *arrayT = + dyn_cast<ArrayType>(originalTy.getCanonicalType())) { + // We will always decay to a pointer. + QualType elemTy = arrayT->getElementType(); + val = StateMgr.ArrayToPointer(val.castAs<Loc>(), elemTy); + + // Are we casting from an array to a pointer? If so just pass on + // the decayed value. + if (castTy->isPointerType() || castTy->isReferenceType()) + return val; + + // Are we casting from an array to an integer? If so, cast the decayed + // pointer value to an integer. + assert(castTy->isIntegralOrEnumerationType()); + + // FIXME: Keep these here for now in case we decide soon that we + // need the original decayed type. + // QualType elemTy = cast<ArrayType>(originalTy)->getElementType(); + // QualType pointerTy = C.getPointerType(elemTy); + return evalCastFromLoc(val.castAs<Loc>(), castTy); + } + + // Check for casts from a region to a specific type. + if (const MemRegion *R = val.getAsRegion()) { + // Handle other casts of locations to integers. + if (castTy->isIntegralOrEnumerationType()) + return evalCastFromLoc(loc::MemRegionVal(R), castTy); + + // FIXME: We should handle the case where we strip off view layers to get + // to a desugared type. + if (!Loc::isLocType(castTy)) { + // FIXME: There can be gross cases where one casts the result of a function + // (that returns a pointer) to some other value that happens to fit + // within that pointer value. We currently have no good way to + // model such operations. When this happens, the underlying operation + // is that the caller is reasoning about bits. Conceptually we are + // layering a "view" of a location on top of those bits. Perhaps + // we need to be more lazy about mutual possible views, even on an + // SVal? This may be necessary for bit-level reasoning as well. + return UnknownVal(); + } + + // We get a symbolic function pointer for a dereference of a function + // pointer, but it is of function type. Example: + + // struct FPRec { + // void (*my_func)(int * x); + // }; + // + // int bar(int x); + // + // int f1_a(struct FPRec* foo) { + // int x; + // (*foo->my_func)(&x); + // return bar(x)+1; // no-warning + // } + + assert(Loc::isLocType(originalTy) || originalTy->isFunctionType() || + originalTy->isBlockPointerType() || castTy->isReferenceType()); + + StoreManager &storeMgr = StateMgr.getStoreManager(); + + // Delegate to store manager to get the result of casting a region to a + // different type. If the MemRegion* returned is NULL, this expression + // Evaluates to UnknownVal. + R = storeMgr.castRegion(R, castTy); + return R ? SVal(loc::MemRegionVal(R)) : UnknownVal(); + } + + return dispatchCast(val, castTy); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp new file mode 100644 index 000000000000..933c5c330072 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp @@ -0,0 +1,384 @@ +//===- RValues.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. +// +//===----------------------------------------------------------------------===// +// +// This file defines SVal, Loc, and NonLoc, classes that represent +// abstract r-values for use with path-sensitive value tracking. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Symbol iteration within an SVal. +//===----------------------------------------------------------------------===// + +//===----------------------------------------------------------------------===// +// Utility methods. +//===----------------------------------------------------------------------===// + +bool SVal::hasConjuredSymbol() const { + if (Optional<nonloc::SymbolVal> SV = getAs<nonloc::SymbolVal>()) { + SymbolRef sym = SV->getSymbol(); + if (isa<SymbolConjured>(sym)) + return true; + } + + if (Optional<loc::MemRegionVal> RV = getAs<loc::MemRegionVal>()) { + const MemRegion *R = RV->getRegion(); + if (const auto *SR = dyn_cast<SymbolicRegion>(R)) { + SymbolRef sym = SR->getSymbol(); + if (isa<SymbolConjured>(sym)) + return true; + } + } + + return false; +} + +const FunctionDecl *SVal::getAsFunctionDecl() const { + if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { + const MemRegion* R = X->getRegion(); + if (const FunctionCodeRegion *CTR = R->getAs<FunctionCodeRegion>()) + if (const auto *FD = dyn_cast<FunctionDecl>(CTR->getDecl())) + return FD; + } + + if (auto X = getAs<nonloc::PointerToMember>()) { + if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(X->getDecl())) + return MD; + } + return nullptr; +} + +/// If this SVal is a location (subclasses Loc) and wraps a symbol, +/// return that SymbolRef. Otherwise return 0. +/// +/// Implicit casts (ex: void* -> char*) can turn Symbolic region into Element +/// region. If that is the case, gets the underlining region. +/// When IncludeBaseRegions is set to true and the SubRegion is non-symbolic, +/// the first symbolic parent region is returned. +SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const { + // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? + if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) + return X->getLoc().getAsLocSymbol(IncludeBaseRegions); + + if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) { + const MemRegion *R = X->getRegion(); + if (const SymbolicRegion *SymR = IncludeBaseRegions ? + R->getSymbolicBase() : + dyn_cast<SymbolicRegion>(R->StripCasts())) + return SymR->getSymbol(); + } + return nullptr; +} + +/// Get the symbol in the SVal or its base region. +SymbolRef SVal::getLocSymbolInBase() const { + Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>(); + + if (!X) + return nullptr; + + const MemRegion *R = X->getRegion(); + + while (const auto *SR = dyn_cast<SubRegion>(R)) { + if (const auto *SymR = dyn_cast<SymbolicRegion>(SR)) + return SymR->getSymbol(); + else + R = SR->getSuperRegion(); + } + + return nullptr; +} + +// TODO: The next 3 functions have to be simplified. + +/// If this SVal wraps a symbol return that SymbolRef. +/// Otherwise, return 0. +/// +/// Casts are ignored during lookup. +/// \param IncludeBaseRegions The boolean that controls whether the search +/// should continue to the base regions if the region is not symbolic. +SymbolRef SVal::getAsSymbol(bool IncludeBaseRegions) const { + // FIXME: should we consider SymbolRef wrapped in CodeTextRegion? + if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>()) + return X->getSymbol(); + + return getAsLocSymbol(IncludeBaseRegions); +} + +/// getAsSymbolicExpression - If this Sval wraps a symbolic expression then +/// return that expression. Otherwise return NULL. +const SymExpr *SVal::getAsSymbolicExpression() const { + if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>()) + return X->getSymbol(); + + return getAsSymbol(); +} + +const SymExpr* SVal::getAsSymExpr() const { + const SymExpr* Sym = getAsSymbol(); + if (!Sym) + Sym = getAsSymbolicExpression(); + return Sym; +} + +const MemRegion *SVal::getAsRegion() const { + if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) + return X->getRegion(); + + if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>()) + return X->getLoc().getAsRegion(); + + return nullptr; +} + +const MemRegion *loc::MemRegionVal::stripCasts(bool StripBaseCasts) const { + const MemRegion *R = getRegion(); + return R ? R->StripCasts(StripBaseCasts) : nullptr; +} + +const void *nonloc::LazyCompoundVal::getStore() const { + return static_cast<const LazyCompoundValData*>(Data)->getStore(); +} + +const TypedValueRegion *nonloc::LazyCompoundVal::getRegion() const { + return static_cast<const LazyCompoundValData*>(Data)->getRegion(); +} + +bool nonloc::PointerToMember::isNullMemberPointer() const { + return getPTMData().isNull(); +} + +const DeclaratorDecl *nonloc::PointerToMember::getDecl() const { + const auto PTMD = this->getPTMData(); + if (PTMD.isNull()) + return nullptr; + + const DeclaratorDecl *DD = nullptr; + if (PTMD.is<const DeclaratorDecl *>()) + DD = PTMD.get<const DeclaratorDecl *>(); + else + DD = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl(); + + return DD; +} + +//===----------------------------------------------------------------------===// +// Other Iterators. +//===----------------------------------------------------------------------===// + +nonloc::CompoundVal::iterator nonloc::CompoundVal::begin() const { + return getValue()->begin(); +} + +nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { + return getValue()->end(); +} + +nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const { + const PTMDataType PTMD = getPTMData(); + if (PTMD.is<const DeclaratorDecl *>()) + return {}; + return PTMD.get<const PointerToMemberData *>()->begin(); +} + +nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const { + const PTMDataType PTMD = getPTMData(); + if (PTMD.is<const DeclaratorDecl *>()) + return {}; + return PTMD.get<const PointerToMemberData *>()->end(); +} + +//===----------------------------------------------------------------------===// +// Useful predicates. +//===----------------------------------------------------------------------===// + +bool SVal::isConstant() const { + return getAs<nonloc::ConcreteInt>() || getAs<loc::ConcreteInt>(); +} + +bool SVal::isConstant(int I) const { + if (Optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>()) + return LV->getValue() == I; + if (Optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>()) + return NV->getValue() == I; + return false; +} + +bool SVal::isZeroConstant() const { + return isConstant(0); +} + +//===----------------------------------------------------------------------===// +// Transfer function dispatch for Non-Locs. +//===----------------------------------------------------------------------===// + +SVal nonloc::ConcreteInt::evalBinOp(SValBuilder &svalBuilder, + BinaryOperator::Opcode Op, + const nonloc::ConcreteInt& R) const { + const llvm::APSInt* X = + svalBuilder.getBasicValueFactory().evalAPSInt(Op, getValue(), R.getValue()); + + if (X) + return nonloc::ConcreteInt(*X); + else + return UndefinedVal(); +} + +nonloc::ConcreteInt +nonloc::ConcreteInt::evalComplement(SValBuilder &svalBuilder) const { + return svalBuilder.makeIntVal(~getValue()); +} + +nonloc::ConcreteInt +nonloc::ConcreteInt::evalMinus(SValBuilder &svalBuilder) const { + return svalBuilder.makeIntVal(-getValue()); +} + +//===----------------------------------------------------------------------===// +// Transfer function dispatch for Locs. +//===----------------------------------------------------------------------===// + +SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals, + BinaryOperator::Opcode Op, + const loc::ConcreteInt& R) const { + assert(BinaryOperator::isComparisonOp(Op) || Op == BO_Sub); + + const llvm::APSInt *X = BasicVals.evalAPSInt(Op, getValue(), R.getValue()); + + if (X) + return nonloc::ConcreteInt(*X); + else + return UndefinedVal(); +} + +//===----------------------------------------------------------------------===// +// Pretty-Printing. +//===----------------------------------------------------------------------===// + +LLVM_DUMP_METHOD void SVal::dump() const { dumpToStream(llvm::errs()); } + +void SVal::dumpToStream(raw_ostream &os) const { + switch (getBaseKind()) { + case UnknownValKind: + os << "Unknown"; + break; + case NonLocKind: + castAs<NonLoc>().dumpToStream(os); + break; + case LocKind: + castAs<Loc>().dumpToStream(os); + break; + case UndefinedValKind: + os << "Undefined"; + break; + } +} + +void NonLoc::dumpToStream(raw_ostream &os) const { + switch (getSubKind()) { + case nonloc::ConcreteIntKind: { + const auto &Value = castAs<nonloc::ConcreteInt>().getValue(); + os << Value << ' ' << (Value.isSigned() ? 'S' : 'U') + << Value.getBitWidth() << 'b'; + break; + } + case nonloc::SymbolValKind: + os << castAs<nonloc::SymbolVal>().getSymbol(); + break; + + case nonloc::LocAsIntegerKind: { + const nonloc::LocAsInteger& C = castAs<nonloc::LocAsInteger>(); + os << C.getLoc() << " [as " << C.getNumBits() << " bit integer]"; + break; + } + case nonloc::CompoundValKind: { + const nonloc::CompoundVal& C = castAs<nonloc::CompoundVal>(); + os << "compoundVal{"; + bool first = true; + for (const auto &I : C) { + if (first) { + os << ' '; first = false; + } + else + os << ", "; + + I.dumpToStream(os); + } + os << "}"; + break; + } + case nonloc::LazyCompoundValKind: { + const nonloc::LazyCompoundVal &C = castAs<nonloc::LazyCompoundVal>(); + os << "lazyCompoundVal{" << const_cast<void *>(C.getStore()) + << ',' << C.getRegion() + << '}'; + break; + } + case nonloc::PointerToMemberKind: { + os << "pointerToMember{"; + const nonloc::PointerToMember &CastRes = + castAs<nonloc::PointerToMember>(); + if (CastRes.getDecl()) + os << "|" << CastRes.getDecl()->getQualifiedNameAsString() << "|"; + bool first = true; + for (const auto &I : CastRes) { + if (first) { + os << ' '; first = false; + } + else + os << ", "; + + os << (*I).getType().getAsString(); + } + + os << '}'; + break; + } + default: + assert(false && "Pretty-printed not implemented for this NonLoc."); + break; + } +} + +void Loc::dumpToStream(raw_ostream &os) const { + switch (getSubKind()) { + case loc::ConcreteIntKind: + os << castAs<loc::ConcreteInt>().getValue().getZExtValue() << " (Loc)"; + break; + case loc::GotoLabelKind: + os << "&&" << castAs<loc::GotoLabel>().getLabel()->getName(); + break; + case loc::MemRegionValKind: + os << '&' << castAs<loc::MemRegionVal>().getRegion()->getString(); + break; + default: + llvm_unreachable("Pretty-printing not implemented for this Loc."); + } +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp new file mode 100644 index 000000000000..fecbc0001079 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp @@ -0,0 +1,349 @@ +//===--- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines the SarifDiagnostics object. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/Version.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/Support/JSON.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace clang; +using namespace ento; + +namespace { +class SarifDiagnostics : public PathDiagnosticConsumer { + std::string OutputFile; + +public: + SarifDiagnostics(AnalyzerOptions &, const std::string &Output) + : OutputFile(Output) {} + ~SarifDiagnostics() override = default; + + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + FilesMade *FM) override; + + StringRef getName() const override { return "SarifDiagnostics"; } + PathGenerationScheme getGenerationScheme() const override { return Minimal; } + bool supportsLogicalOpControlFlow() const override { return true; } + bool supportsCrossFileDiagnostics() const override { return true; } +}; +} // end anonymous namespace + +void ento::createSarifDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, + const std::string &Output, + const Preprocessor &) { + C.push_back(new SarifDiagnostics(AnalyzerOpts, Output)); +} + +static StringRef getFileName(const FileEntry &FE) { + StringRef Filename = FE.tryGetRealPathName(); + if (Filename.empty()) + Filename = FE.getName(); + return Filename; +} + +static std::string percentEncodeURICharacter(char C) { + // RFC 3986 claims alpha, numeric, and this handful of + // characters are not reserved for the path component and + // should be written out directly. Otherwise, percent + // encode the character and write that out instead of the + // reserved character. + if (llvm::isAlnum(C) || + StringRef::npos != StringRef("-._~:@!$&'()*+,;=").find(C)) + return std::string(&C, 1); + return "%" + llvm::toHex(StringRef(&C, 1)); +} + +static std::string fileNameToURI(StringRef Filename) { + llvm::SmallString<32> Ret = StringRef("file://"); + + // Get the root name to see if it has a URI authority. + StringRef Root = sys::path::root_name(Filename); + if (Root.startswith("//")) { + // There is an authority, so add it to the URI. + Ret += Root.drop_front(2).str(); + } else if (!Root.empty()) { + // There is no authority, so end the component and add the root to the URI. + Ret += Twine("/" + Root).str(); + } + + auto Iter = sys::path::begin(Filename), End = sys::path::end(Filename); + assert(Iter != End && "Expected there to be a non-root path component."); + // Add the rest of the path components, encoding any reserved characters; + // we skip past the first path component, as it was handled it above. + std::for_each(++Iter, End, [&Ret](StringRef Component) { + // For reasons unknown to me, we may get a backslash with Windows native + // paths for the initial backslash following the drive component, which + // we need to ignore as a URI path part. + if (Component == "\\") + return; + + // Add the separator between the previous path part and the one being + // currently processed. + Ret += "/"; + + // URI encode the part. + for (char C : Component) { + Ret += percentEncodeURICharacter(C); + } + }); + + return Ret.str().str(); +} + +static json::Object createFileLocation(const FileEntry &FE) { + return json::Object{{"uri", fileNameToURI(getFileName(FE))}}; +} + +static json::Object createFile(const FileEntry &FE) { + return json::Object{{"fileLocation", createFileLocation(FE)}, + {"roles", json::Array{"resultFile"}}, + {"length", FE.getSize()}, + {"mimeType", "text/plain"}}; +} + +static json::Object createFileLocation(const FileEntry &FE, + json::Array &Files) { + std::string FileURI = fileNameToURI(getFileName(FE)); + + // See if the Files array contains this URI already. If it does not, create + // a new file object to add to the array. + auto I = llvm::find_if(Files, [&](const json::Value &File) { + if (const json::Object *Obj = File.getAsObject()) { + if (const json::Object *FileLoc = Obj->getObject("fileLocation")) { + Optional<StringRef> URI = FileLoc->getString("uri"); + return URI && URI->equals(FileURI); + } + } + return false; + }); + + // Calculate the index within the file location array so it can be stored in + // the JSON object. + auto Index = static_cast<unsigned>(std::distance(Files.begin(), I)); + if (I == Files.end()) + Files.push_back(createFile(FE)); + + return json::Object{{"uri", FileURI}, {"fileIndex", Index}}; +} + +static json::Object createTextRegion(SourceRange R, const SourceManager &SM) { + return json::Object{ + {"startLine", SM.getExpansionLineNumber(R.getBegin())}, + {"endLine", SM.getExpansionLineNumber(R.getEnd())}, + {"startColumn", SM.getExpansionColumnNumber(R.getBegin())}, + {"endColumn", SM.getExpansionColumnNumber(R.getEnd())}}; +} + +static json::Object createPhysicalLocation(SourceRange R, const FileEntry &FE, + const SourceManager &SMgr, + json::Array &Files) { + return json::Object{{{"fileLocation", createFileLocation(FE, Files)}, + {"region", createTextRegion(R, SMgr)}}}; +} + +enum class Importance { Important, Essential, Unimportant }; + +static StringRef importanceToStr(Importance I) { + switch (I) { + case Importance::Important: + return "important"; + case Importance::Essential: + return "essential"; + case Importance::Unimportant: + return "unimportant"; + } + llvm_unreachable("Fully covered switch is not so fully covered"); +} + +static json::Object createThreadFlowLocation(json::Object &&Location, + Importance I) { + return json::Object{{"location", std::move(Location)}, + {"importance", importanceToStr(I)}}; +} + +static json::Object createMessage(StringRef Text) { + return json::Object{{"text", Text.str()}}; +} + +static json::Object createLocation(json::Object &&PhysicalLocation, + StringRef Message = "") { + json::Object Ret{{"physicalLocation", std::move(PhysicalLocation)}}; + if (!Message.empty()) + Ret.insert({"message", createMessage(Message)}); + return Ret; +} + +static Importance calculateImportance(const PathDiagnosticPiece &Piece) { + switch (Piece.getKind()) { + case PathDiagnosticPiece::Kind::Call: + case PathDiagnosticPiece::Kind::Macro: + case PathDiagnosticPiece::Kind::Note: + // FIXME: What should be reported here? + break; + case PathDiagnosticPiece::Kind::Event: + return Piece.getTagStr() == "ConditionBRVisitor" ? Importance::Important + : Importance::Essential; + case PathDiagnosticPiece::Kind::ControlFlow: + return Importance::Unimportant; + } + return Importance::Unimportant; +} + +static json::Object createThreadFlow(const PathPieces &Pieces, + json::Array &Files) { + const SourceManager &SMgr = Pieces.front()->getLocation().getManager(); + json::Array Locations; + for (const auto &Piece : Pieces) { + const PathDiagnosticLocation &P = Piece->getLocation(); + Locations.push_back(createThreadFlowLocation( + createLocation(createPhysicalLocation(P.asRange(), + *P.asLocation().getFileEntry(), + SMgr, Files), + Piece->getString()), + calculateImportance(*Piece))); + } + return json::Object{{"locations", std::move(Locations)}}; +} + +static json::Object createCodeFlow(const PathPieces &Pieces, + json::Array &Files) { + return json::Object{ + {"threadFlows", json::Array{createThreadFlow(Pieces, Files)}}}; +} + +static json::Object createTool() { + return json::Object{{"name", "clang"}, + {"fullName", "clang static analyzer"}, + {"language", "en-US"}, + {"version", getClangFullVersion()}}; +} + +static json::Object createResult(const PathDiagnostic &Diag, json::Array &Files, + const StringMap<unsigned> &RuleMapping) { + const PathPieces &Path = Diag.path.flatten(false); + const SourceManager &SMgr = Path.front()->getLocation().getManager(); + + auto Iter = RuleMapping.find(Diag.getCheckName()); + assert(Iter != RuleMapping.end() && "Rule ID is not in the array index map?"); + + return json::Object{ + {"message", createMessage(Diag.getVerboseDescription())}, + {"codeFlows", json::Array{createCodeFlow(Path, Files)}}, + {"locations", + json::Array{createLocation(createPhysicalLocation( + Diag.getLocation().asRange(), + *Diag.getLocation().asLocation().getFileEntry(), SMgr, Files))}}, + {"ruleIndex", Iter->getValue()}, + {"ruleId", Diag.getCheckName()}}; +} + +static StringRef getRuleDescription(StringRef CheckName) { + return llvm::StringSwitch<StringRef>(CheckName) +#define GET_CHECKERS +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ + .Case(FULLNAME, HELPTEXT) +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER +#undef GET_CHECKERS + ; +} + +static StringRef getRuleHelpURIStr(StringRef CheckName) { + return llvm::StringSwitch<StringRef>(CheckName) +#define GET_CHECKERS +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ + .Case(FULLNAME, DOC_URI) +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER +#undef GET_CHECKERS + ; +} + +static json::Object createRule(const PathDiagnostic &Diag) { + StringRef CheckName = Diag.getCheckName(); + json::Object Ret{ + {"fullDescription", createMessage(getRuleDescription(CheckName))}, + {"name", createMessage(CheckName)}, + {"id", CheckName}}; + + std::string RuleURI = getRuleHelpURIStr(CheckName); + if (!RuleURI.empty()) + Ret["helpUri"] = RuleURI; + + return Ret; +} + +static json::Array createRules(std::vector<const PathDiagnostic *> &Diags, + StringMap<unsigned> &RuleMapping) { + json::Array Rules; + llvm::StringSet<> Seen; + + llvm::for_each(Diags, [&](const PathDiagnostic *D) { + StringRef RuleID = D->getCheckName(); + std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(RuleID); + if (P.second) { + RuleMapping[RuleID] = Rules.size(); // Maps RuleID to an Array Index. + Rules.push_back(createRule(*D)); + } + }); + + return Rules; +} + +static json::Object createResources(std::vector<const PathDiagnostic *> &Diags, + StringMap<unsigned> &RuleMapping) { + return json::Object{{"rules", createRules(Diags, RuleMapping)}}; +} + +static json::Object createRun(std::vector<const PathDiagnostic *> &Diags) { + json::Array Results, Files; + StringMap<unsigned> RuleMapping; + json::Object Resources = createResources(Diags, RuleMapping); + + llvm::for_each(Diags, [&](const PathDiagnostic *D) { + Results.push_back(createResult(*D, Files, RuleMapping)); + }); + + return json::Object{{"tool", createTool()}, + {"resources", std::move(Resources)}, + {"results", std::move(Results)}, + {"files", std::move(Files)}}; +} + +void SarifDiagnostics::FlushDiagnosticsImpl( + std::vector<const PathDiagnostic *> &Diags, FilesMade *) { + // We currently overwrite the file if it already exists. However, it may be + // useful to add a feature someday that allows the user to append a run to an + // existing SARIF file. One danger from that approach is that the size of the + // file can become large very quickly, so decoding into JSON to append a run + // may be an expensive operation. + std::error_code EC; + llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text); + if (EC) { + llvm::errs() << "warning: could not create file: " << EC.message() << '\n'; + return; + } + json::Object Sarif{ + {"$schema", + "http://json.schemastore.org/sarif-2.0.0-csd.2.beta.2018-11-28"}, + {"version", "2.0.0-csd.2.beta.2018-11-28"}, + {"runs", json::Array{createRun(Diags)}}}; + OS << llvm::formatv("{0:2}", json::Value(std::move(Sarif))); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp new file mode 100644 index 000000000000..adb40178f5b1 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp @@ -0,0 +1,133 @@ +//== SimpleConstraintManager.cpp --------------------------------*- 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 SimpleConstraintManager, a class that provides a +// simplified constraint manager interface, compared to ConstraintManager. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" + +namespace clang { + +namespace ento { + +SimpleConstraintManager::~SimpleConstraintManager() {} + +ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State, + DefinedSVal Cond, + bool Assumption) { + // If we have a Loc value, cast it to a bool NonLoc first. + if (Optional<Loc> LV = Cond.getAs<Loc>()) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + QualType T; + const MemRegion *MR = LV->getAsRegion(); + if (const TypedRegion *TR = dyn_cast_or_null<TypedRegion>(MR)) + T = TR->getLocationType(); + else + T = SVB.getContext().VoidPtrTy; + + Cond = SVB.evalCast(*LV, SVB.getContext().BoolTy, T).castAs<DefinedSVal>(); + } + + return assume(State, Cond.castAs<NonLoc>(), Assumption); +} + +ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State, + NonLoc Cond, bool Assumption) { + State = assumeAux(State, Cond, Assumption); + if (NotifyAssumeClients && SU) + return SU->processAssume(State, Cond, Assumption); + return State; +} + +ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State, + NonLoc Cond, + bool Assumption) { + + // We cannot reason about SymSymExprs, and can only reason about some + // SymIntExprs. + if (!canReasonAbout(Cond)) { + // Just add the constraint to the expression without trying to simplify. + SymbolRef Sym = Cond.getAsSymExpr(); + assert(Sym); + return assumeSymUnsupported(State, Sym, Assumption); + } + + switch (Cond.getSubKind()) { + default: + llvm_unreachable("'Assume' not implemented for this NonLoc"); + + case nonloc::SymbolValKind: { + nonloc::SymbolVal SV = Cond.castAs<nonloc::SymbolVal>(); + SymbolRef Sym = SV.getSymbol(); + assert(Sym); + return assumeSym(State, Sym, Assumption); + } + + case nonloc::ConcreteIntKind: { + bool b = Cond.castAs<nonloc::ConcreteInt>().getValue() != 0; + bool isFeasible = b ? Assumption : !Assumption; + return isFeasible ? State : nullptr; + } + + case nonloc::PointerToMemberKind: { + bool IsNull = !Cond.castAs<nonloc::PointerToMember>().isNullMemberPointer(); + bool IsFeasible = IsNull ? Assumption : !Assumption; + return IsFeasible ? State : nullptr; + } + + case nonloc::LocAsIntegerKind: + return assume(State, Cond.castAs<nonloc::LocAsInteger>().getLoc(), + Assumption); + } // end switch +} + +ProgramStateRef SimpleConstraintManager::assumeInclusiveRange( + ProgramStateRef State, NonLoc Value, const llvm::APSInt &From, + const llvm::APSInt &To, bool InRange) { + + assert(From.isUnsigned() == To.isUnsigned() && + From.getBitWidth() == To.getBitWidth() && + "Values should have same types!"); + + if (!canReasonAbout(Value)) { + // Just add the constraint to the expression without trying to simplify. + SymbolRef Sym = Value.getAsSymExpr(); + assert(Sym); + return assumeSymInclusiveRange(State, Sym, From, To, InRange); + } + + switch (Value.getSubKind()) { + default: + llvm_unreachable("'assumeInclusiveRange' is not implemented" + "for this NonLoc"); + + case nonloc::LocAsIntegerKind: + case nonloc::SymbolValKind: { + if (SymbolRef Sym = Value.getAsSymbol()) + return assumeSymInclusiveRange(State, Sym, From, To, InRange); + return State; + } // end switch + + case nonloc::ConcreteIntKind: { + const llvm::APSInt &IntVal = Value.castAs<nonloc::ConcreteInt>().getValue(); + bool IsInRange = IntVal >= From && IntVal <= To; + bool isFeasible = (IsInRange == InRange); + return isFeasible ? State : nullptr; + } + } // end switch +} + +} // end of namespace ento + +} // end of namespace clang diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp new file mode 100644 index 000000000000..fc57cecac9cb --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -0,0 +1,1343 @@ +// 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines SimpleSValBuilder, a basic implementation of SValBuilder. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class SimpleSValBuilder : public SValBuilder { +protected: + SVal dispatchCast(SVal val, QualType castTy) override; + SVal evalCastFromNonLoc(NonLoc val, QualType castTy) override; + SVal evalCastFromLoc(Loc val, QualType castTy) override; + +public: + SimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, + ProgramStateManager &stateMgr) + : SValBuilder(alloc, context, stateMgr) {} + ~SimpleSValBuilder() override {} + + SVal evalMinus(NonLoc val) override; + SVal evalComplement(NonLoc val) override; + SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op, + NonLoc lhs, NonLoc rhs, QualType resultTy) override; + SVal evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op, + Loc lhs, Loc rhs, QualType resultTy) override; + SVal evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op, + Loc lhs, NonLoc rhs, QualType resultTy) override; + + /// getKnownValue - evaluates a given SVal. If the SVal has only one possible + /// (integer) value, that value is returned. Otherwise, returns NULL. + const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V) override; + + /// Recursively descends into symbolic expressions and replaces symbols + /// with their known values (in the sense of the getKnownValue() method). + SVal simplifySVal(ProgramStateRef State, SVal V) override; + + SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op, + const llvm::APSInt &RHS, QualType resultTy); +}; +} // end anonymous namespace + +SValBuilder *ento::createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, + ASTContext &context, + ProgramStateManager &stateMgr) { + return new SimpleSValBuilder(alloc, context, stateMgr); +} + +//===----------------------------------------------------------------------===// +// Transfer function for Casts. +//===----------------------------------------------------------------------===// + +SVal SimpleSValBuilder::dispatchCast(SVal Val, QualType CastTy) { + assert(Val.getAs<Loc>() || Val.getAs<NonLoc>()); + return Val.getAs<Loc>() ? evalCastFromLoc(Val.castAs<Loc>(), CastTy) + : evalCastFromNonLoc(Val.castAs<NonLoc>(), CastTy); +} + +SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) { + bool isLocType = Loc::isLocType(castTy); + if (val.getAs<nonloc::PointerToMember>()) + return val; + + if (Optional<nonloc::LocAsInteger> LI = val.getAs<nonloc::LocAsInteger>()) { + if (isLocType) + return LI->getLoc(); + // FIXME: Correctly support promotions/truncations. + unsigned castSize = Context.getIntWidth(castTy); + if (castSize == LI->getNumBits()) + return val; + return makeLocAsInteger(LI->getLoc(), castSize); + } + + if (const SymExpr *se = val.getAsSymbolicExpression()) { + QualType T = Context.getCanonicalType(se->getType()); + // If types are the same or both are integers, ignore the cast. + // FIXME: Remove this hack when we support symbolic truncation/extension. + // HACK: If both castTy and T are integers, ignore the cast. This is + // not a permanent solution. Eventually we want to precisely handle + // extension/truncation of symbolic integers. This prevents us from losing + // precision when we assign 'x = y' and 'y' is symbolic and x and y are + // different integer types. + if (haveSameType(T, castTy)) + return val; + + if (!isLocType) + return makeNonLoc(se, T, castTy); + return UnknownVal(); + } + + // If value is a non-integer constant, produce unknown. + if (!val.getAs<nonloc::ConcreteInt>()) + return UnknownVal(); + + // Handle casts to a boolean type. + if (castTy->isBooleanType()) { + bool b = val.castAs<nonloc::ConcreteInt>().getValue().getBoolValue(); + return makeTruthVal(b, castTy); + } + + // Only handle casts from integers to integers - if val is an integer constant + // being cast to a non-integer type, produce unknown. + if (!isLocType && !castTy->isIntegralOrEnumerationType()) + return UnknownVal(); + + llvm::APSInt i = val.castAs<nonloc::ConcreteInt>().getValue(); + BasicVals.getAPSIntType(castTy).apply(i); + + if (isLocType) + return makeIntLocVal(i); + else + return makeIntVal(i); +} + +SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) { + + // Casts from pointers -> pointers, just return the lval. + // + // Casts from pointers -> references, just return the lval. These + // can be introduced by the frontend for corner cases, e.g + // casting from va_list* to __builtin_va_list&. + // + if (Loc::isLocType(castTy) || castTy->isReferenceType()) + return val; + + // FIXME: Handle transparent unions where a value can be "transparently" + // lifted into a union type. + if (castTy->isUnionType()) + return UnknownVal(); + + // Casting a Loc to a bool will almost always be true, + // unless this is a weak function or a symbolic region. + if (castTy->isBooleanType()) { + switch (val.getSubKind()) { + case loc::MemRegionValKind: { + const MemRegion *R = val.castAs<loc::MemRegionVal>().getRegion(); + if (const FunctionCodeRegion *FTR = dyn_cast<FunctionCodeRegion>(R)) + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FTR->getDecl())) + if (FD->isWeak()) + // FIXME: Currently we are using an extent symbol here, + // because there are no generic region address metadata + // symbols to use, only content metadata. + return nonloc::SymbolVal(SymMgr.getExtentSymbol(FTR)); + + if (const SymbolicRegion *SymR = R->getSymbolicBase()) + return makeNonLoc(SymR->getSymbol(), BO_NE, + BasicVals.getZeroWithPtrWidth(), castTy); + + // FALL-THROUGH + LLVM_FALLTHROUGH; + } + + case loc::GotoLabelKind: + // Labels and non-symbolic memory regions are always true. + return makeTruthVal(true, castTy); + } + } + + if (castTy->isIntegralOrEnumerationType()) { + unsigned BitWidth = Context.getIntWidth(castTy); + + if (!val.getAs<loc::ConcreteInt>()) + return makeLocAsInteger(val, BitWidth); + + llvm::APSInt i = val.castAs<loc::ConcreteInt>().getValue(); + BasicVals.getAPSIntType(castTy).apply(i); + return makeIntVal(i); + } + + // All other cases: return 'UnknownVal'. This includes casting pointers + // to floats, which is probably badness it itself, but this is a good + // intermediate solution until we do something better. + return UnknownVal(); +} + +//===----------------------------------------------------------------------===// +// Transfer function for unary operators. +//===----------------------------------------------------------------------===// + +SVal SimpleSValBuilder::evalMinus(NonLoc val) { + switch (val.getSubKind()) { + case nonloc::ConcreteIntKind: + return val.castAs<nonloc::ConcreteInt>().evalMinus(*this); + default: + return UnknownVal(); + } +} + +SVal SimpleSValBuilder::evalComplement(NonLoc X) { + switch (X.getSubKind()) { + case nonloc::ConcreteIntKind: + return X.castAs<nonloc::ConcreteInt>().evalComplement(*this); + default: + return UnknownVal(); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function for binary operators. +//===----------------------------------------------------------------------===// + +SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS, + BinaryOperator::Opcode op, + const llvm::APSInt &RHS, + QualType resultTy) { + bool isIdempotent = false; + + // Check for a few special cases with known reductions first. + switch (op) { + default: + // We can't reduce this case; just treat it normally. + break; + case BO_Mul: + // a*0 and a*1 + if (RHS == 0) + return makeIntVal(0, resultTy); + else if (RHS == 1) + isIdempotent = true; + break; + case BO_Div: + // a/0 and a/1 + if (RHS == 0) + // This is also handled elsewhere. + return UndefinedVal(); + else if (RHS == 1) + isIdempotent = true; + break; + case BO_Rem: + // a%0 and a%1 + if (RHS == 0) + // This is also handled elsewhere. + return UndefinedVal(); + else if (RHS == 1) + return makeIntVal(0, resultTy); + break; + case BO_Add: + case BO_Sub: + case BO_Shl: + case BO_Shr: + case BO_Xor: + // a+0, a-0, a<<0, a>>0, a^0 + if (RHS == 0) + isIdempotent = true; + break; + case BO_And: + // a&0 and a&(~0) + if (RHS == 0) + return makeIntVal(0, resultTy); + else if (RHS.isAllOnesValue()) + isIdempotent = true; + break; + case BO_Or: + // a|0 and a|(~0) + if (RHS == 0) + isIdempotent = true; + else if (RHS.isAllOnesValue()) { + const llvm::APSInt &Result = BasicVals.Convert(resultTy, RHS); + return nonloc::ConcreteInt(Result); + } + break; + } + + // Idempotent ops (like a*1) can still change the type of an expression. + // Wrap the LHS up in a NonLoc again and let evalCastFromNonLoc do the + // dirty work. + if (isIdempotent) + return evalCastFromNonLoc(nonloc::SymbolVal(LHS), resultTy); + + // If we reach this point, the expression cannot be simplified. + // Make a SymbolVal for the entire expression, after converting the RHS. + const llvm::APSInt *ConvertedRHS = &RHS; + if (BinaryOperator::isComparisonOp(op)) { + // We're looking for a type big enough to compare the symbolic value + // with the given constant. + // FIXME: This is an approximation of Sema::UsualArithmeticConversions. + ASTContext &Ctx = getContext(); + QualType SymbolType = LHS->getType(); + uint64_t ValWidth = RHS.getBitWidth(); + uint64_t TypeWidth = Ctx.getTypeSize(SymbolType); + + if (ValWidth < TypeWidth) { + // If the value is too small, extend it. + ConvertedRHS = &BasicVals.Convert(SymbolType, RHS); + } else if (ValWidth == TypeWidth) { + // If the value is signed but the symbol is unsigned, do the comparison + // in unsigned space. [C99 6.3.1.8] + // (For the opposite case, the value is already unsigned.) + if (RHS.isSigned() && !SymbolType->isSignedIntegerOrEnumerationType()) + ConvertedRHS = &BasicVals.Convert(SymbolType, RHS); + } + } else + ConvertedRHS = &BasicVals.Convert(resultTy, RHS); + + return makeNonLoc(LHS, op, *ConvertedRHS, resultTy); +} + +// See if Sym is known to be a relation Rel with Bound. +static bool isInRelation(BinaryOperator::Opcode Rel, SymbolRef Sym, + llvm::APSInt Bound, ProgramStateRef State) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + SVal Result = + SVB.evalBinOpNN(State, Rel, nonloc::SymbolVal(Sym), + nonloc::ConcreteInt(Bound), SVB.getConditionType()); + if (auto DV = Result.getAs<DefinedSVal>()) { + return !State->assume(*DV, false); + } + return false; +} + +// See if Sym is known to be within [min/4, max/4], where min and max +// are the bounds of the symbol's integral type. With such symbols, +// some manipulations can be performed without the risk of overflow. +// assume() doesn't cause infinite recursion because we should be dealing +// with simpler symbols on every recursive call. +static bool isWithinConstantOverflowBounds(SymbolRef Sym, + ProgramStateRef State) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + BasicValueFactory &BV = SVB.getBasicValueFactory(); + + QualType T = Sym->getType(); + assert(T->isSignedIntegerOrEnumerationType() && + "This only works with signed integers!"); + APSIntType AT = BV.getAPSIntType(T); + + llvm::APSInt Max = AT.getMaxValue() / AT.getValue(4), Min = -Max; + return isInRelation(BO_LE, Sym, Max, State) && + isInRelation(BO_GE, Sym, Min, State); +} + +// Same for the concrete integers: see if I is within [min/4, max/4]. +static bool isWithinConstantOverflowBounds(llvm::APSInt I) { + APSIntType AT(I); + assert(!AT.isUnsigned() && + "This only works with signed integers!"); + + llvm::APSInt Max = AT.getMaxValue() / AT.getValue(4), Min = -Max; + return (I <= Max) && (I >= -Max); +} + +static std::pair<SymbolRef, llvm::APSInt> +decomposeSymbol(SymbolRef Sym, BasicValueFactory &BV) { + if (const auto *SymInt = dyn_cast<SymIntExpr>(Sym)) + if (BinaryOperator::isAdditiveOp(SymInt->getOpcode())) + return std::make_pair(SymInt->getLHS(), + (SymInt->getOpcode() == BO_Add) ? + (SymInt->getRHS()) : + (-SymInt->getRHS())); + + // Fail to decompose: "reduce" the problem to the "$x + 0" case. + return std::make_pair(Sym, BV.getValue(0, Sym->getType())); +} + +// Simplify "(LSym + LInt) Op (RSym + RInt)" assuming all values are of the +// same signed integral type and no overflows occur (which should be checked +// by the caller). +static NonLoc doRearrangeUnchecked(ProgramStateRef State, + BinaryOperator::Opcode Op, + SymbolRef LSym, llvm::APSInt LInt, + SymbolRef RSym, llvm::APSInt RInt) { + SValBuilder &SVB = State->getStateManager().getSValBuilder(); + BasicValueFactory &BV = SVB.getBasicValueFactory(); + SymbolManager &SymMgr = SVB.getSymbolManager(); + + QualType SymTy = LSym->getType(); + assert(SymTy == RSym->getType() && + "Symbols are not of the same type!"); + assert(APSIntType(LInt) == BV.getAPSIntType(SymTy) && + "Integers are not of the same type as symbols!"); + assert(APSIntType(RInt) == BV.getAPSIntType(SymTy) && + "Integers are not of the same type as symbols!"); + + QualType ResultTy; + if (BinaryOperator::isComparisonOp(Op)) + ResultTy = SVB.getConditionType(); + else if (BinaryOperator::isAdditiveOp(Op)) + ResultTy = SymTy; + else + llvm_unreachable("Operation not suitable for unchecked rearrangement!"); + + // FIXME: Can we use assume() without getting into an infinite recursion? + if (LSym == RSym) + return SVB.evalBinOpNN(State, Op, nonloc::ConcreteInt(LInt), + nonloc::ConcreteInt(RInt), ResultTy) + .castAs<NonLoc>(); + + SymbolRef ResultSym = nullptr; + BinaryOperator::Opcode ResultOp; + llvm::APSInt ResultInt; + if (BinaryOperator::isComparisonOp(Op)) { + // Prefer comparing to a non-negative number. + // FIXME: Maybe it'd be better to have consistency in + // "$x - $y" vs. "$y - $x" because those are solver's keys. + if (LInt > RInt) { + ResultSym = SymMgr.getSymSymExpr(RSym, BO_Sub, LSym, SymTy); + ResultOp = BinaryOperator::reverseComparisonOp(Op); + ResultInt = LInt - RInt; // Opposite order! + } else { + ResultSym = SymMgr.getSymSymExpr(LSym, BO_Sub, RSym, SymTy); + ResultOp = Op; + ResultInt = RInt - LInt; // Opposite order! + } + } else { + ResultSym = SymMgr.getSymSymExpr(LSym, Op, RSym, SymTy); + ResultInt = (Op == BO_Add) ? (LInt + RInt) : (LInt - RInt); + ResultOp = BO_Add; + // Bring back the cosmetic difference. + if (ResultInt < 0) { + ResultInt = -ResultInt; + ResultOp = BO_Sub; + } else if (ResultInt == 0) { + // Shortcut: Simplify "$x + 0" to "$x". + return nonloc::SymbolVal(ResultSym); + } + } + const llvm::APSInt &PersistentResultInt = BV.getValue(ResultInt); + return nonloc::SymbolVal( + SymMgr.getSymIntExpr(ResultSym, ResultOp, PersistentResultInt, ResultTy)); +} + +// Rearrange if symbol type matches the result type and if the operator is a +// comparison operator, both symbol and constant must be within constant +// overflow bounds. +static bool shouldRearrange(ProgramStateRef State, BinaryOperator::Opcode Op, + SymbolRef Sym, llvm::APSInt Int, QualType Ty) { + return Sym->getType() == Ty && + (!BinaryOperator::isComparisonOp(Op) || + (isWithinConstantOverflowBounds(Sym, State) && + isWithinConstantOverflowBounds(Int))); +} + +static Optional<NonLoc> tryRearrange(ProgramStateRef State, + BinaryOperator::Opcode Op, NonLoc Lhs, + NonLoc Rhs, QualType ResultTy) { + ProgramStateManager &StateMgr = State->getStateManager(); + SValBuilder &SVB = StateMgr.getSValBuilder(); + + // We expect everything to be of the same type - this type. + QualType SingleTy; + + auto &Opts = + StateMgr.getOwningEngine().getAnalysisManager().getAnalyzerOptions(); + + // FIXME: After putting complexity threshold to the symbols we can always + // rearrange additive operations but rearrange comparisons only if + // option is set. + if(!Opts.ShouldAggressivelySimplifyBinaryOperation) + return None; + + SymbolRef LSym = Lhs.getAsSymbol(); + if (!LSym) + return None; + + if (BinaryOperator::isComparisonOp(Op)) { + SingleTy = LSym->getType(); + if (ResultTy != SVB.getConditionType()) + return None; + // Initialize SingleTy later with a symbol's type. + } else if (BinaryOperator::isAdditiveOp(Op)) { + SingleTy = ResultTy; + if (LSym->getType() != SingleTy) + return None; + } else { + // Don't rearrange other operations. + return None; + } + + assert(!SingleTy.isNull() && "We should have figured out the type by now!"); + + // Rearrange signed symbolic expressions only + if (!SingleTy->isSignedIntegerOrEnumerationType()) + return None; + + SymbolRef RSym = Rhs.getAsSymbol(); + if (!RSym || RSym->getType() != SingleTy) + return None; + + BasicValueFactory &BV = State->getBasicVals(); + llvm::APSInt LInt, RInt; + std::tie(LSym, LInt) = decomposeSymbol(LSym, BV); + std::tie(RSym, RInt) = decomposeSymbol(RSym, BV); + if (!shouldRearrange(State, Op, LSym, LInt, SingleTy) || + !shouldRearrange(State, Op, RSym, RInt, SingleTy)) + return None; + + // We know that no overflows can occur anymore. + return doRearrangeUnchecked(State, Op, LSym, LInt, RSym, RInt); +} + +SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state, + BinaryOperator::Opcode op, + NonLoc lhs, NonLoc rhs, + QualType resultTy) { + NonLoc InputLHS = lhs; + NonLoc InputRHS = rhs; + + // Handle trivial case where left-side and right-side are the same. + if (lhs == rhs) + switch (op) { + default: + break; + case BO_EQ: + case BO_LE: + case BO_GE: + return makeTruthVal(true, resultTy); + case BO_LT: + case BO_GT: + case BO_NE: + return makeTruthVal(false, resultTy); + case BO_Xor: + case BO_Sub: + if (resultTy->isIntegralOrEnumerationType()) + return makeIntVal(0, resultTy); + return evalCastFromNonLoc(makeIntVal(0, /*Unsigned=*/false), resultTy); + case BO_Or: + case BO_And: + return evalCastFromNonLoc(lhs, resultTy); + } + + while (1) { + switch (lhs.getSubKind()) { + default: + return makeSymExprValNN(op, lhs, rhs, resultTy); + case nonloc::PointerToMemberKind: { + assert(rhs.getSubKind() == nonloc::PointerToMemberKind && + "Both SVals should have pointer-to-member-type"); + auto LPTM = lhs.castAs<nonloc::PointerToMember>(), + RPTM = rhs.castAs<nonloc::PointerToMember>(); + auto LPTMD = LPTM.getPTMData(), RPTMD = RPTM.getPTMData(); + switch (op) { + case BO_EQ: + return makeTruthVal(LPTMD == RPTMD, resultTy); + case BO_NE: + return makeTruthVal(LPTMD != RPTMD, resultTy); + default: + return UnknownVal(); + } + } + case nonloc::LocAsIntegerKind: { + Loc lhsL = lhs.castAs<nonloc::LocAsInteger>().getLoc(); + switch (rhs.getSubKind()) { + case nonloc::LocAsIntegerKind: + // FIXME: at the moment the implementation + // of modeling "pointers as integers" is not complete. + if (!BinaryOperator::isComparisonOp(op)) + return UnknownVal(); + return evalBinOpLL(state, op, lhsL, + rhs.castAs<nonloc::LocAsInteger>().getLoc(), + resultTy); + case nonloc::ConcreteIntKind: { + // FIXME: at the moment the implementation + // of modeling "pointers as integers" is not complete. + if (!BinaryOperator::isComparisonOp(op)) + return UnknownVal(); + // Transform the integer into a location and compare. + // FIXME: This only makes sense for comparisons. If we want to, say, + // 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); + return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy); + } + default: + switch (op) { + case BO_EQ: + return makeTruthVal(false, resultTy); + case BO_NE: + return makeTruthVal(true, resultTy); + default: + // This case also handles pointer arithmetic. + return makeSymExprValNN(op, InputLHS, InputRHS, resultTy); + } + } + } + case nonloc::ConcreteIntKind: { + llvm::APSInt LHSValue = lhs.castAs<nonloc::ConcreteInt>().getValue(); + + // If we're dealing with two known constants, just perform the operation. + if (const llvm::APSInt *KnownRHSValue = getKnownValue(state, rhs)) { + llvm::APSInt RHSValue = *KnownRHSValue; + if (BinaryOperator::isComparisonOp(op)) { + // We're looking for a type big enough to compare the two values. + // FIXME: This is not correct. char + short will result in a promotion + // to int. Unfortunately we have lost types by this point. + APSIntType CompareType = std::max(APSIntType(LHSValue), + APSIntType(RHSValue)); + CompareType.apply(LHSValue); + CompareType.apply(RHSValue); + } else if (!BinaryOperator::isShiftOp(op)) { + APSIntType IntType = BasicVals.getAPSIntType(resultTy); + IntType.apply(LHSValue); + IntType.apply(RHSValue); + } + + const llvm::APSInt *Result = + BasicVals.evalAPSInt(op, LHSValue, RHSValue); + if (!Result) + return UndefinedVal(); + + return nonloc::ConcreteInt(*Result); + } + + // Swap the left and right sides and flip the operator if doing so + // allows us to better reason about the expression (this is a form + // of expression canonicalization). + // While we're at it, catch some special cases for non-commutative ops. + switch (op) { + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + op = BinaryOperator::reverseComparisonOp(op); + LLVM_FALLTHROUGH; + case BO_EQ: + case BO_NE: + case BO_Add: + case BO_Mul: + case BO_And: + case BO_Xor: + case BO_Or: + std::swap(lhs, rhs); + continue; + case BO_Shr: + // (~0)>>a + if (LHSValue.isAllOnesValue() && LHSValue.isSigned()) + return evalCastFromNonLoc(lhs, resultTy); + LLVM_FALLTHROUGH; + case BO_Shl: + // 0<<a and 0>>a + if (LHSValue == 0) + return evalCastFromNonLoc(lhs, resultTy); + return makeSymExprValNN(op, InputLHS, InputRHS, resultTy); + default: + return makeSymExprValNN(op, InputLHS, InputRHS, resultTy); + } + } + case nonloc::SymbolValKind: { + // We only handle LHS as simple symbols or SymIntExprs. + SymbolRef Sym = lhs.castAs<nonloc::SymbolVal>().getSymbol(); + + // LHS is a symbolic expression. + if (const SymIntExpr *symIntExpr = dyn_cast<SymIntExpr>(Sym)) { + + // Is this a logical not? (!x is represented as x == 0.) + if (op == BO_EQ && rhs.isZeroConstant()) { + // We know how to negate certain expressions. Simplify them here. + + BinaryOperator::Opcode opc = symIntExpr->getOpcode(); + switch (opc) { + default: + // We don't know how to negate this operation. + // Just handle it as if it were a normal comparison to 0. + break; + case BO_LAnd: + case BO_LOr: + llvm_unreachable("Logical operators handled by branching logic."); + case BO_Assign: + case BO_MulAssign: + case BO_DivAssign: + case BO_RemAssign: + case BO_AddAssign: + case BO_SubAssign: + case BO_ShlAssign: + case BO_ShrAssign: + case BO_AndAssign: + case BO_XorAssign: + case BO_OrAssign: + case BO_Comma: + llvm_unreachable("'=' and ',' operators handled by ExprEngine."); + case BO_PtrMemD: + case BO_PtrMemI: + llvm_unreachable("Pointer arithmetic not handled here."); + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + case BO_EQ: + case BO_NE: + assert(resultTy->isBooleanType() || + resultTy == getConditionType()); + assert(symIntExpr->getType()->isBooleanType() || + getContext().hasSameUnqualifiedType(symIntExpr->getType(), + getConditionType())); + // Negate the comparison and make a value. + opc = BinaryOperator::negateComparisonOp(opc); + return makeNonLoc(symIntExpr->getLHS(), opc, + symIntExpr->getRHS(), resultTy); + } + } + + // For now, only handle expressions whose RHS is a constant. + if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) { + // If both the LHS and the current expression are additive, + // fold their constants and try again. + if (BinaryOperator::isAdditiveOp(op)) { + BinaryOperator::Opcode lop = symIntExpr->getOpcode(); + if (BinaryOperator::isAdditiveOp(lop)) { + // Convert the two constants to a common type, then combine them. + + // resultTy may not be the best type to convert to, but it's + // probably the best choice in expressions with mixed type + // (such as x+1U+2LL). The rules for implicit conversions should + // choose a reasonable type to preserve the expression, and will + // at least match how the value is going to be used. + APSIntType IntType = BasicVals.getAPSIntType(resultTy); + const llvm::APSInt &first = IntType.convert(symIntExpr->getRHS()); + const llvm::APSInt &second = IntType.convert(*RHSValue); + + const llvm::APSInt *newRHS; + if (lop == op) + newRHS = BasicVals.evalAPSInt(BO_Add, first, second); + else + newRHS = BasicVals.evalAPSInt(BO_Sub, first, second); + + assert(newRHS && "Invalid operation despite common type!"); + rhs = nonloc::ConcreteInt(*newRHS); + lhs = nonloc::SymbolVal(symIntExpr->getLHS()); + op = lop; + continue; + } + } + + // Otherwise, make a SymIntExpr out of the expression. + return MakeSymIntVal(symIntExpr, op, *RHSValue, resultTy); + } + } + + // Does the symbolic expression simplify to a constant? + // If so, "fold" the constant by setting 'lhs' to a ConcreteInt + // and try again. + SVal simplifiedLhs = simplifySVal(state, lhs); + if (simplifiedLhs != lhs) + if (auto simplifiedLhsAsNonLoc = simplifiedLhs.getAs<NonLoc>()) { + lhs = *simplifiedLhsAsNonLoc; + continue; + } + + // Is the RHS a constant? + if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) + return MakeSymIntVal(Sym, op, *RHSValue, resultTy); + + if (Optional<NonLoc> V = tryRearrange(state, op, lhs, rhs, resultTy)) + return *V; + + // Give up -- this is not a symbolic expression we can handle. + return makeSymExprValNN(op, InputLHS, InputRHS, resultTy); + } + } + } +} + +static SVal evalBinOpFieldRegionFieldRegion(const FieldRegion *LeftFR, + const FieldRegion *RightFR, + BinaryOperator::Opcode op, + QualType resultTy, + SimpleSValBuilder &SVB) { + // Only comparisons are meaningful here! + if (!BinaryOperator::isComparisonOp(op)) + return UnknownVal(); + + // Next, see if the two FRs have the same super-region. + // FIXME: This doesn't handle casts yet, and simply stripping the casts + // doesn't help. + if (LeftFR->getSuperRegion() != RightFR->getSuperRegion()) + return UnknownVal(); + + const FieldDecl *LeftFD = LeftFR->getDecl(); + const FieldDecl *RightFD = RightFR->getDecl(); + const RecordDecl *RD = LeftFD->getParent(); + + // Make sure the two FRs are from the same kind of record. Just in case! + // FIXME: This is probably where inheritance would be a problem. + if (RD != RightFD->getParent()) + return UnknownVal(); + + // We know for sure that the two fields are not the same, since that + // would have given us the same SVal. + if (op == BO_EQ) + return SVB.makeTruthVal(false, resultTy); + if (op == BO_NE) + return SVB.makeTruthVal(true, resultTy); + + // Iterate through the fields and see which one comes first. + // [C99 6.7.2.1.13] "Within a structure object, the non-bit-field + // members and the units in which bit-fields reside have addresses that + // increase in the order in which they are declared." + bool leftFirst = (op == BO_LT || op == BO_LE); + for (const auto *I : RD->fields()) { + if (I == LeftFD) + return SVB.makeTruthVal(leftFirst, resultTy); + if (I == RightFD) + return SVB.makeTruthVal(!leftFirst, resultTy); + } + + llvm_unreachable("Fields not found in parent record's definition"); +} + +// FIXME: all this logic will change if/when we have MemRegion::getLocation(). +SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state, + BinaryOperator::Opcode op, + Loc lhs, Loc rhs, + QualType resultTy) { + // Only comparisons and subtractions are valid operations on two pointers. + // See [C99 6.5.5 through 6.5.14] or [C++0x 5.6 through 5.15]. + // However, if a pointer is casted to an integer, evalBinOpNN may end up + // calling this function with another operation (PR7527). We don't attempt to + // model this for now, but it could be useful, particularly when the + // "location" is actually an integer value that's been passed through a void*. + if (!(BinaryOperator::isComparisonOp(op) || op == BO_Sub)) + return UnknownVal(); + + // Special cases for when both sides are identical. + if (lhs == rhs) { + switch (op) { + default: + llvm_unreachable("Unimplemented operation for two identical values"); + case BO_Sub: + return makeZeroVal(resultTy); + case BO_EQ: + case BO_LE: + case BO_GE: + return makeTruthVal(true, resultTy); + case BO_NE: + case BO_LT: + case BO_GT: + return makeTruthVal(false, resultTy); + } + } + + switch (lhs.getSubKind()) { + default: + llvm_unreachable("Ordering not implemented for this Loc."); + + case loc::GotoLabelKind: + // The only thing we know about labels is that they're non-null. + if (rhs.isZeroConstant()) { + switch (op) { + default: + break; + case BO_Sub: + return evalCastFromLoc(lhs, resultTy); + case BO_EQ: + case BO_LE: + case BO_LT: + return makeTruthVal(false, resultTy); + case BO_NE: + case BO_GT: + case BO_GE: + return makeTruthVal(true, resultTy); + } + } + // There may be two labels for the same location, and a function region may + // have the same address as a label at the start of the function (depending + // on the ABI). + // FIXME: we can probably do a comparison against other MemRegions, though. + // FIXME: is there a way to tell if two labels refer to the same location? + return UnknownVal(); + + case loc::ConcreteIntKind: { + // If one of the operands is a symbol and the other is a constant, + // build an expression for use by the constraint manager. + if (SymbolRef rSym = rhs.getAsLocSymbol()) { + // We can only build expressions with symbols on the left, + // so we need a reversible operator. + if (!BinaryOperator::isComparisonOp(op) || op == BO_Cmp) + return UnknownVal(); + + const llvm::APSInt &lVal = lhs.castAs<loc::ConcreteInt>().getValue(); + op = BinaryOperator::reverseComparisonOp(op); + return makeNonLoc(rSym, op, lVal, resultTy); + } + + // If both operands are constants, just perform the operation. + if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { + SVal ResultVal = + lhs.castAs<loc::ConcreteInt>().evalBinOp(BasicVals, op, *rInt); + if (Optional<NonLoc> Result = ResultVal.getAs<NonLoc>()) + return evalCastFromNonLoc(*Result, resultTy); + + assert(!ResultVal.getAs<Loc>() && "Loc-Loc ops should not produce Locs"); + return UnknownVal(); + } + + // Special case comparisons against NULL. + // This must come after the test if the RHS is a symbol, which is used to + // build constraints. The address of any non-symbolic region is guaranteed + // to be non-NULL, as is any label. + assert(rhs.getAs<loc::MemRegionVal>() || rhs.getAs<loc::GotoLabel>()); + if (lhs.isZeroConstant()) { + switch (op) { + default: + break; + case BO_EQ: + case BO_GT: + case BO_GE: + return makeTruthVal(false, resultTy); + case BO_NE: + case BO_LT: + case BO_LE: + return makeTruthVal(true, resultTy); + } + } + + // Comparing an arbitrary integer to a region or label address is + // completely unknowable. + return UnknownVal(); + } + case loc::MemRegionValKind: { + if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) { + // If one of the operands is a symbol and the other is a constant, + // build an expression for use by the constraint manager. + if (SymbolRef lSym = lhs.getAsLocSymbol(true)) { + if (BinaryOperator::isComparisonOp(op)) + return MakeSymIntVal(lSym, op, rInt->getValue(), resultTy); + return UnknownVal(); + } + // Special case comparisons to NULL. + // This must come after the test if the LHS is a symbol, which is used to + // build constraints. The address of any non-symbolic region is guaranteed + // to be non-NULL. + if (rInt->isZeroConstant()) { + if (op == BO_Sub) + return evalCastFromLoc(lhs, resultTy); + + if (BinaryOperator::isComparisonOp(op)) { + QualType boolType = getContext().BoolTy; + NonLoc l = evalCastFromLoc(lhs, boolType).castAs<NonLoc>(); + NonLoc r = makeTruthVal(false, boolType).castAs<NonLoc>(); + return evalBinOpNN(state, op, l, r, resultTy); + } + } + + // Comparing a region to an arbitrary integer is completely unknowable. + return UnknownVal(); + } + + // Get both values as regions, if possible. + const MemRegion *LeftMR = lhs.getAsRegion(); + assert(LeftMR && "MemRegionValKind SVal doesn't have a region!"); + + const MemRegion *RightMR = rhs.getAsRegion(); + if (!RightMR) + // The RHS is probably a label, which in theory could address a region. + // FIXME: we can probably make a more useful statement about non-code + // regions, though. + return UnknownVal(); + + const MemRegion *LeftBase = LeftMR->getBaseRegion(); + const MemRegion *RightBase = RightMR->getBaseRegion(); + const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace(); + const MemSpaceRegion *RightMS = RightBase->getMemorySpace(); + const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion(); + + // If the two regions are from different known memory spaces they cannot be + // equal. Also, assume that no symbolic region (whose memory space is + // unknown) is on the stack. + if (LeftMS != RightMS && + ((LeftMS != UnknownMS && RightMS != UnknownMS) || + (isa<StackSpaceRegion>(LeftMS) || isa<StackSpaceRegion>(RightMS)))) { + switch (op) { + default: + return UnknownVal(); + case BO_EQ: + return makeTruthVal(false, resultTy); + case BO_NE: + return makeTruthVal(true, resultTy); + } + } + + // If both values wrap regions, see if they're from different base regions. + // Note, heap base symbolic regions are assumed to not alias with + // each other; for example, we assume that malloc returns different address + // on each invocation. + // FIXME: ObjC object pointers always reside on the heap, but currently + // we treat their memory space as unknown, because symbolic pointers + // to ObjC objects may alias. There should be a way to construct + // possibly-aliasing heap-based regions. For instance, MacOSXApiChecker + // guesses memory space for ObjC object pointers manually instead of + // relying on us. + if (LeftBase != RightBase && + ((!isa<SymbolicRegion>(LeftBase) && !isa<SymbolicRegion>(RightBase)) || + (isa<HeapSpaceRegion>(LeftMS) || isa<HeapSpaceRegion>(RightMS))) ){ + switch (op) { + default: + return UnknownVal(); + case BO_EQ: + return makeTruthVal(false, resultTy); + case BO_NE: + return makeTruthVal(true, resultTy); + } + } + + // Handle special cases for when both regions are element regions. + const ElementRegion *RightER = dyn_cast<ElementRegion>(RightMR); + const ElementRegion *LeftER = dyn_cast<ElementRegion>(LeftMR); + if (RightER && LeftER) { + // Next, see if the two ERs have the same super-region and matching types. + // FIXME: This should do something useful even if the types don't match, + // though if both indexes are constant the RegionRawOffset path will + // give the correct answer. + if (LeftER->getSuperRegion() == RightER->getSuperRegion() && + LeftER->getElementType() == RightER->getElementType()) { + // Get the left index and cast it to the correct type. + // If the index is unknown or undefined, bail out here. + SVal LeftIndexVal = LeftER->getIndex(); + Optional<NonLoc> LeftIndex = LeftIndexVal.getAs<NonLoc>(); + if (!LeftIndex) + return UnknownVal(); + LeftIndexVal = evalCastFromNonLoc(*LeftIndex, ArrayIndexTy); + LeftIndex = LeftIndexVal.getAs<NonLoc>(); + if (!LeftIndex) + return UnknownVal(); + + // Do the same for the right index. + SVal RightIndexVal = RightER->getIndex(); + Optional<NonLoc> RightIndex = RightIndexVal.getAs<NonLoc>(); + if (!RightIndex) + return UnknownVal(); + RightIndexVal = evalCastFromNonLoc(*RightIndex, ArrayIndexTy); + RightIndex = RightIndexVal.getAs<NonLoc>(); + if (!RightIndex) + return UnknownVal(); + + // Actually perform the operation. + // evalBinOpNN expects the two indexes to already be the right type. + return evalBinOpNN(state, op, *LeftIndex, *RightIndex, resultTy); + } + } + + // Special handling of the FieldRegions, even with symbolic offsets. + const FieldRegion *RightFR = dyn_cast<FieldRegion>(RightMR); + const FieldRegion *LeftFR = dyn_cast<FieldRegion>(LeftMR); + if (RightFR && LeftFR) { + SVal R = evalBinOpFieldRegionFieldRegion(LeftFR, RightFR, op, resultTy, + *this); + if (!R.isUnknown()) + return R; + } + + // Compare the regions using the raw offsets. + RegionOffset LeftOffset = LeftMR->getAsOffset(); + RegionOffset RightOffset = RightMR->getAsOffset(); + + if (LeftOffset.getRegion() != nullptr && + LeftOffset.getRegion() == RightOffset.getRegion() && + !LeftOffset.hasSymbolicOffset() && !RightOffset.hasSymbolicOffset()) { + int64_t left = LeftOffset.getOffset(); + int64_t right = RightOffset.getOffset(); + + switch (op) { + default: + return UnknownVal(); + case BO_LT: + return makeTruthVal(left < right, resultTy); + case BO_GT: + return makeTruthVal(left > right, resultTy); + case BO_LE: + return makeTruthVal(left <= right, resultTy); + case BO_GE: + return makeTruthVal(left >= right, resultTy); + case BO_EQ: + return makeTruthVal(left == right, resultTy); + case BO_NE: + return makeTruthVal(left != right, resultTy); + } + } + + // At this point we're not going to get a good answer, but we can try + // conjuring an expression instead. + SymbolRef LHSSym = lhs.getAsLocSymbol(); + SymbolRef RHSSym = rhs.getAsLocSymbol(); + if (LHSSym && RHSSym) + return makeNonLoc(LHSSym, op, RHSSym, resultTy); + + // If we get here, we have no way of comparing the regions. + return UnknownVal(); + } + } +} + +SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state, + BinaryOperator::Opcode op, + Loc lhs, NonLoc rhs, QualType resultTy) { + if (op >= BO_PtrMemD && op <= BO_PtrMemI) { + if (auto PTMSV = rhs.getAs<nonloc::PointerToMember>()) { + if (PTMSV->isNullMemberPointer()) + return UndefinedVal(); + if (const FieldDecl *FD = PTMSV->getDeclAs<FieldDecl>()) { + SVal Result = lhs; + + for (const auto &I : *PTMSV) + Result = StateMgr.getStoreManager().evalDerivedToBase( + Result, I->getType(),I->isVirtual()); + return state->getLValue(FD, Result); + } + } + + return rhs; + } + + assert(!BinaryOperator::isComparisonOp(op) && + "arguments to comparison ops must be of the same type"); + + // Special case: rhs is a zero constant. + if (rhs.isZeroConstant()) + return lhs; + + // Perserve the null pointer so that it can be found by the DerefChecker. + if (lhs.isZeroConstant()) + return lhs; + + // We are dealing with pointer arithmetic. + + // Handle pointer arithmetic on constant values. + if (Optional<nonloc::ConcreteInt> rhsInt = rhs.getAs<nonloc::ConcreteInt>()) { + if (Optional<loc::ConcreteInt> lhsInt = lhs.getAs<loc::ConcreteInt>()) { + const llvm::APSInt &leftI = lhsInt->getValue(); + assert(leftI.isUnsigned()); + llvm::APSInt rightI(rhsInt->getValue(), /* isUnsigned */ true); + + // Convert the bitwidth of rightI. This should deal with overflow + // since we are dealing with concrete values. + rightI = rightI.extOrTrunc(leftI.getBitWidth()); + + // Offset the increment by the pointer size. + llvm::APSInt Multiplicand(rightI.getBitWidth(), /* isUnsigned */ true); + QualType pointeeType = resultTy->getPointeeType(); + Multiplicand = getContext().getTypeSizeInChars(pointeeType).getQuantity(); + rightI *= Multiplicand; + + // Compute the adjusted pointer. + switch (op) { + case BO_Add: + rightI = leftI + rightI; + break; + case BO_Sub: + rightI = leftI - rightI; + break; + default: + llvm_unreachable("Invalid pointer arithmetic operation"); + } + return loc::ConcreteInt(getBasicValueFactory().getValue(rightI)); + } + } + + // Handle cases where 'lhs' is a region. + if (const MemRegion *region = lhs.getAsRegion()) { + rhs = convertToArrayIndex(rhs).castAs<NonLoc>(); + SVal index = UnknownVal(); + const SubRegion *superR = nullptr; + // We need to know the type of the pointer in order to add an integer to it. + // Depending on the type, different amount of bytes is added. + QualType elementType; + + if (const ElementRegion *elemReg = dyn_cast<ElementRegion>(region)) { + assert(op == BO_Add || op == BO_Sub); + index = evalBinOpNN(state, op, elemReg->getIndex(), rhs, + getArrayIndexType()); + superR = cast<SubRegion>(elemReg->getSuperRegion()); + elementType = elemReg->getElementType(); + } + else if (isa<SubRegion>(region)) { + assert(op == BO_Add || op == BO_Sub); + index = (op == BO_Add) ? rhs : evalMinus(rhs); + superR = cast<SubRegion>(region); + // TODO: Is this actually reliable? Maybe improving our MemRegion + // hierarchy to provide typed regions for all non-void pointers would be + // better. For instance, we cannot extend this towards LocAsInteger + // operations, where result type of the expression is integer. + if (resultTy->isAnyPointerType()) + elementType = resultTy->getPointeeType(); + } + + // Represent arithmetic on void pointers as arithmetic on char pointers. + // It is fine when a TypedValueRegion of char value type represents + // a void pointer. Note that arithmetic on void pointers is a GCC extension. + if (elementType->isVoidType()) + elementType = getContext().CharTy; + + if (Optional<NonLoc> indexV = index.getAs<NonLoc>()) { + return loc::MemRegionVal(MemMgr.getElementRegion(elementType, *indexV, + superR, getContext())); + } + } + return UnknownVal(); +} + +const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state, + SVal V) { + V = simplifySVal(state, V); + if (V.isUnknownOrUndef()) + return nullptr; + + if (Optional<loc::ConcreteInt> X = V.getAs<loc::ConcreteInt>()) + return &X->getValue(); + + if (Optional<nonloc::ConcreteInt> X = V.getAs<nonloc::ConcreteInt>()) + return &X->getValue(); + + if (SymbolRef Sym = V.getAsSymbol()) + return state->getConstraintManager().getSymVal(state, Sym); + + // FIXME: Add support for SymExprs. + return nullptr; +} + +SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) { + // For now, this function tries to constant-fold symbols inside a + // nonloc::SymbolVal, and does nothing else. More simplifications should + // be possible, such as constant-folding an index in an ElementRegion. + + class Simplifier : public FullSValVisitor<Simplifier, SVal> { + ProgramStateRef State; + SValBuilder &SVB; + + // Cache results for the lifetime of the Simplifier. Results change every + // time new constraints are added to the program state, which is the whole + // point of simplifying, and for that very reason it's pointless to maintain + // the same cache for the duration of the whole analysis. + llvm::DenseMap<SymbolRef, SVal> Cached; + + static bool isUnchanged(SymbolRef Sym, SVal Val) { + return Sym == Val.getAsSymbol(); + } + + SVal cache(SymbolRef Sym, SVal V) { + Cached[Sym] = V; + return V; + } + + SVal skip(SymbolRef Sym) { + return cache(Sym, SVB.makeSymbolVal(Sym)); + } + + public: + Simplifier(ProgramStateRef State) + : State(State), SVB(State->getStateManager().getSValBuilder()) {} + + SVal VisitSymbolData(const SymbolData *S) { + // No cache here. + if (const llvm::APSInt *I = + SVB.getKnownValue(State, SVB.makeSymbolVal(S))) + return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I) + : (SVal)SVB.makeIntVal(*I); + return SVB.makeSymbolVal(S); + } + + // TODO: Support SymbolCast. Support IntSymExpr when/if we actually + // start producing them. + + SVal VisitSymIntExpr(const SymIntExpr *S) { + auto I = Cached.find(S); + if (I != Cached.end()) + return I->second; + + SVal LHS = Visit(S->getLHS()); + if (isUnchanged(S->getLHS(), LHS)) + return skip(S); + + SVal RHS; + // By looking at the APSInt in the right-hand side of S, we cannot + // figure out if it should be treated as a Loc or as a NonLoc. + // So make our guess by recalling that we cannot multiply pointers + // or compare a pointer to an integer. + if (Loc::isLocType(S->getLHS()->getType()) && + BinaryOperator::isComparisonOp(S->getOpcode())) { + // The usual conversion of $sym to &SymRegion{$sym}, as they have + // the same meaning for Loc-type symbols, but the latter form + // is preferred in SVal computations for being Loc itself. + if (SymbolRef Sym = LHS.getAsSymbol()) { + assert(Loc::isLocType(Sym->getType())); + LHS = SVB.makeLoc(Sym); + } + RHS = SVB.makeIntLocVal(S->getRHS()); + } else { + RHS = SVB.makeIntVal(S->getRHS()); + } + + return cache( + S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType())); + } + + SVal VisitSymSymExpr(const SymSymExpr *S) { + auto I = Cached.find(S); + if (I != Cached.end()) + return I->second; + + // For now don't try to simplify mixed Loc/NonLoc expressions + // because they often appear from LocAsInteger operations + // and we don't know how to combine a LocAsInteger + // with a concrete value. + if (Loc::isLocType(S->getLHS()->getType()) != + Loc::isLocType(S->getRHS()->getType())) + return skip(S); + + SVal LHS = Visit(S->getLHS()); + SVal RHS = Visit(S->getRHS()); + if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS)) + return skip(S); + + return cache( + S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType())); + } + + SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); } + + SVal VisitMemRegion(const MemRegion *R) { return loc::MemRegionVal(R); } + + SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) { + // Simplification is much more costly than computing complexity. + // For high complexity, it may be not worth it. + return Visit(V.getSymbol()); + } + + SVal VisitSVal(SVal V) { return V; } + }; + + // A crude way of preventing this function from calling itself from evalBinOp. + static bool isReentering = false; + if (isReentering) + return V; + + isReentering = true; + SVal SimplifiedV = Simplifier(State).Visit(V); + isReentering = false; + + return SimplifiedV; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp new file mode 100644 index 000000000000..4fa937d9658d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp @@ -0,0 +1,561 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defined the types Store and StoreManager. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/CXXInheritance.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Type.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include <cassert> +#include <cstdint> + +using namespace clang; +using namespace ento; + +StoreManager::StoreManager(ProgramStateManager &stateMgr) + : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr), + MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {} + +StoreRef StoreManager::enterStackFrame(Store OldStore, + const CallEvent &Call, + const StackFrameContext *LCtx) { + StoreRef Store = StoreRef(OldStore, *this); + + SmallVector<CallEvent::FrameBindingTy, 16> InitialBindings; + Call.getInitialStackFrameContents(LCtx, InitialBindings); + + for (const auto &I : InitialBindings) + Store = Bind(Store.getStore(), I.first, I.second); + + return Store; +} + +const ElementRegion *StoreManager::MakeElementRegion(const SubRegion *Base, + QualType EleTy, + uint64_t index) { + NonLoc idx = svalBuilder.makeArrayIndex(index); + return MRMgr.getElementRegion(EleTy, idx, Base, svalBuilder.getContext()); +} + +const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R, + QualType T) { + NonLoc idx = svalBuilder.makeZeroArrayIndex(); + assert(!T.isNull()); + return MRMgr.getElementRegion(T, idx, R, Ctx); +} + +const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) { + ASTContext &Ctx = StateMgr.getContext(); + + // Handle casts to Objective-C objects. + if (CastToTy->isObjCObjectPointerType()) + return R->StripCasts(); + + if (CastToTy->isBlockPointerType()) { + // FIXME: We may need different solutions, depending on the symbol + // involved. Blocks can be casted to/from 'id', as they can be treated + // as Objective-C objects. This could possibly be handled by enhancing + // our reasoning of downcasts of symbolic objects. + if (isa<CodeTextRegion>(R) || isa<SymbolicRegion>(R)) + return R; + + // We don't know what to make of it. Return a NULL region, which + // will be interpreted as UnknownVal. + return nullptr; + } + + // Now assume we are casting from pointer to pointer. Other cases should + // already be handled. + QualType PointeeTy = CastToTy->getPointeeType(); + QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); + + // Handle casts to void*. We just pass the region through. + if (CanonPointeeTy.getLocalUnqualifiedType() == Ctx.VoidTy) + return R; + + // Handle casts from compatible types. + if (R->isBoundable()) + if (const auto *TR = dyn_cast<TypedValueRegion>(R)) { + QualType ObjTy = Ctx.getCanonicalType(TR->getValueType()); + if (CanonPointeeTy == ObjTy) + return R; + } + + // Process region cast according to the kind of the region being cast. + switch (R->getKind()) { + case MemRegion::CXXThisRegionKind: + case MemRegion::CodeSpaceRegionKind: + case MemRegion::StackLocalsSpaceRegionKind: + case MemRegion::StackArgumentsSpaceRegionKind: + case MemRegion::HeapSpaceRegionKind: + case MemRegion::UnknownSpaceRegionKind: + case MemRegion::StaticGlobalSpaceRegionKind: + case MemRegion::GlobalInternalSpaceRegionKind: + case MemRegion::GlobalSystemSpaceRegionKind: + case MemRegion::GlobalImmutableSpaceRegionKind: { + llvm_unreachable("Invalid region cast"); + } + + case MemRegion::FunctionCodeRegionKind: + case MemRegion::BlockCodeRegionKind: + case MemRegion::BlockDataRegionKind: + case MemRegion::StringRegionKind: + // FIXME: Need to handle arbitrary downcasts. + case MemRegion::SymbolicRegionKind: + case MemRegion::AllocaRegionKind: + case MemRegion::CompoundLiteralRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + case MemRegion::ObjCStringRegionKind: + case MemRegion::VarRegionKind: + case MemRegion::CXXTempObjectRegionKind: + case MemRegion::CXXBaseObjectRegionKind: + case MemRegion::CXXDerivedObjectRegionKind: + return MakeElementRegion(cast<SubRegion>(R), PointeeTy); + + case MemRegion::ElementRegionKind: { + // If we are casting from an ElementRegion to another type, the + // algorithm is as follows: + // + // (1) Compute the "raw offset" of the ElementRegion from the + // base region. This is done by calling 'getAsRawOffset()'. + // + // (2a) If we get a 'RegionRawOffset' after calling + // 'getAsRawOffset()', determine if the absolute offset + // can be exactly divided into chunks of the size of the + // casted-pointee type. If so, create a new ElementRegion with + // the pointee-cast type as the new ElementType and the index + // being the offset divded by the chunk size. If not, create + // a new ElementRegion at offset 0 off the raw offset region. + // + // (2b) If we don't a get a 'RegionRawOffset' after calling + // 'getAsRawOffset()', it means that we are at offset 0. + // + // FIXME: Handle symbolic raw offsets. + + const ElementRegion *elementR = cast<ElementRegion>(R); + const RegionRawOffset &rawOff = elementR->getAsArrayOffset(); + const MemRegion *baseR = rawOff.getRegion(); + + // If we cannot compute a raw offset, throw up our hands and return + // a NULL MemRegion*. + if (!baseR) + return nullptr; + + CharUnits off = rawOff.getOffset(); + + if (off.isZero()) { + // Edge case: we are at 0 bytes off the beginning of baseR. We + // check to see if type we are casting to is the same as the base + // region. If so, just return the base region. + if (const auto *TR = dyn_cast<TypedValueRegion>(baseR)) { + QualType ObjTy = Ctx.getCanonicalType(TR->getValueType()); + QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy); + if (CanonPointeeTy == ObjTy) + return baseR; + } + + // Otherwise, create a new ElementRegion at offset 0. + return MakeElementRegion(cast<SubRegion>(baseR), PointeeTy); + } + + // We have a non-zero offset from the base region. We want to determine + // if the offset can be evenly divided by sizeof(PointeeTy). If so, + // we create an ElementRegion whose index is that value. Otherwise, we + // create two ElementRegions, one that reflects a raw offset and the other + // that reflects the cast. + + // Compute the index for the new ElementRegion. + int64_t newIndex = 0; + const MemRegion *newSuperR = nullptr; + + // We can only compute sizeof(PointeeTy) if it is a complete type. + if (!PointeeTy->isIncompleteType()) { + // Compute the size in **bytes**. + CharUnits pointeeTySize = Ctx.getTypeSizeInChars(PointeeTy); + if (!pointeeTySize.isZero()) { + // Is the offset a multiple of the size? If so, we can layer the + // ElementRegion (with elementType == PointeeTy) directly on top of + // the base region. + if (off % pointeeTySize == 0) { + newIndex = off / pointeeTySize; + newSuperR = baseR; + } + } + } + + if (!newSuperR) { + // Create an intermediate ElementRegion to represent the raw byte. + // This will be the super region of the final ElementRegion. + newSuperR = MakeElementRegion(cast<SubRegion>(baseR), Ctx.CharTy, + off.getQuantity()); + } + + return MakeElementRegion(cast<SubRegion>(newSuperR), PointeeTy, newIndex); + } + } + + llvm_unreachable("unreachable"); +} + +static bool regionMatchesCXXRecordType(SVal V, QualType Ty) { + const MemRegion *MR = V.getAsRegion(); + if (!MR) + return true; + + const auto *TVR = dyn_cast<TypedValueRegion>(MR); + if (!TVR) + return true; + + const CXXRecordDecl *RD = TVR->getValueType()->getAsCXXRecordDecl(); + if (!RD) + return true; + + const CXXRecordDecl *Expected = Ty->getPointeeCXXRecordDecl(); + if (!Expected) + Expected = Ty->getAsCXXRecordDecl(); + + return Expected->getCanonicalDecl() == RD->getCanonicalDecl(); +} + +SVal StoreManager::evalDerivedToBase(SVal Derived, const CastExpr *Cast) { + // Sanity check to avoid doing the wrong thing in the face of + // reinterpret_cast. + if (!regionMatchesCXXRecordType(Derived, Cast->getSubExpr()->getType())) + return UnknownVal(); + + // Walk through the cast path to create nested CXXBaseRegions. + SVal Result = Derived; + for (CastExpr::path_const_iterator I = Cast->path_begin(), + E = Cast->path_end(); + I != E; ++I) { + Result = evalDerivedToBase(Result, (*I)->getType(), (*I)->isVirtual()); + } + return Result; +} + +SVal StoreManager::evalDerivedToBase(SVal Derived, const CXXBasePath &Path) { + // Walk through the path to create nested CXXBaseRegions. + SVal Result = Derived; + for (const auto &I : Path) + Result = evalDerivedToBase(Result, I.Base->getType(), + I.Base->isVirtual()); + return Result; +} + +SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType, + bool IsVirtual) { + const MemRegion *DerivedReg = Derived.getAsRegion(); + if (!DerivedReg) + return Derived; + + const CXXRecordDecl *BaseDecl = BaseType->getPointeeCXXRecordDecl(); + if (!BaseDecl) + BaseDecl = BaseType->getAsCXXRecordDecl(); + assert(BaseDecl && "not a C++ object?"); + + if (const auto *AlreadyDerivedReg = + dyn_cast<CXXDerivedObjectRegion>(DerivedReg)) { + if (const auto *SR = + dyn_cast<SymbolicRegion>(AlreadyDerivedReg->getSuperRegion())) + if (SR->getSymbol()->getType()->getPointeeCXXRecordDecl() == BaseDecl) + return loc::MemRegionVal(SR); + + DerivedReg = AlreadyDerivedReg->getSuperRegion(); + } + + const MemRegion *BaseReg = MRMgr.getCXXBaseObjectRegion( + BaseDecl, cast<SubRegion>(DerivedReg), IsVirtual); + + return loc::MemRegionVal(BaseReg); +} + +/// Returns the static type of the given region, if it represents a C++ class +/// object. +/// +/// This handles both fully-typed regions, where the dynamic type is known, and +/// symbolic regions, where the dynamic type is merely bounded (and even then, +/// only ostensibly!), but does not take advantage of any dynamic type info. +static const CXXRecordDecl *getCXXRecordType(const MemRegion *MR) { + if (const auto *TVR = dyn_cast<TypedValueRegion>(MR)) + return TVR->getValueType()->getAsCXXRecordDecl(); + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) + return SR->getSymbol()->getType()->getPointeeCXXRecordDecl(); + return nullptr; +} + +SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType, + bool &Failed) { + Failed = false; + + const MemRegion *MR = Base.getAsRegion(); + if (!MR) + return UnknownVal(); + + // Assume the derived class is a pointer or a reference to a CXX record. + TargetType = TargetType->getPointeeType(); + assert(!TargetType.isNull()); + const CXXRecordDecl *TargetClass = TargetType->getAsCXXRecordDecl(); + if (!TargetClass && !TargetType->isVoidType()) + return UnknownVal(); + + // Drill down the CXXBaseObject chains, which represent upcasts (casts from + // derived to base). + while (const CXXRecordDecl *MRClass = getCXXRecordType(MR)) { + // If found the derived class, the cast succeeds. + if (MRClass == TargetClass) + return loc::MemRegionVal(MR); + + // We skip over incomplete types. They must be the result of an earlier + // reinterpret_cast, as one can only dynamic_cast between types in the same + // class hierarchy. + if (!TargetType->isVoidType() && MRClass->hasDefinition()) { + // Static upcasts are marked as DerivedToBase casts by Sema, so this will + // only happen when multiple or virtual inheritance is involved. + CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true, + /*DetectVirtual=*/false); + if (MRClass->isDerivedFrom(TargetClass, Paths)) + return evalDerivedToBase(loc::MemRegionVal(MR), Paths.front()); + } + + if (const auto *BaseR = dyn_cast<CXXBaseObjectRegion>(MR)) { + // Drill down the chain to get the derived classes. + MR = BaseR->getSuperRegion(); + continue; + } + + // If this is a cast to void*, return the region. + if (TargetType->isVoidType()) + return loc::MemRegionVal(MR); + + // Strange use of reinterpret_cast can give us paths we don't reason + // about well, by putting in ElementRegions where we'd expect + // CXXBaseObjectRegions. If it's a valid reinterpret_cast (i.e. if the + // derived class has a zero offset from the base class), then it's safe + // to strip the cast; if it's invalid, -Wreinterpret-base-class should + // catch it. In the interest of performance, the analyzer will silently + // do the wrong thing in the invalid case (because offsets for subregions + // will be wrong). + const MemRegion *Uncasted = MR->StripCasts(/*IncludeBaseCasts=*/false); + if (Uncasted == MR) { + // We reached the bottom of the hierarchy and did not find the derived + // class. We must be casting the base to derived, so the cast should + // fail. + break; + } + + MR = Uncasted; + } + + // If we're casting a symbolic base pointer to a derived class, use + // CXXDerivedObjectRegion to represent the cast. If it's a pointer to an + // unrelated type, it must be a weird reinterpret_cast and we have to + // be fine with ElementRegion. TODO: Should we instead make + // Derived{TargetClass, Element{SourceClass, SR}}? + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) { + QualType T = SR->getSymbol()->getType(); + const CXXRecordDecl *SourceClass = T->getPointeeCXXRecordDecl(); + if (TargetClass && SourceClass && TargetClass->isDerivedFrom(SourceClass)) + return loc::MemRegionVal( + MRMgr.getCXXDerivedObjectRegion(TargetClass, SR)); + return loc::MemRegionVal(GetElementZeroRegion(SR, TargetType)); + } + + // We failed if the region we ended up with has perfect type info. + Failed = isa<TypedValueRegion>(MR); + return UnknownVal(); +} + +/// CastRetrievedVal - Used by subclasses of StoreManager to implement +/// implicit casts that arise from loads from regions that are reinterpreted +/// as another region. +SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R, + QualType castTy) { + if (castTy.isNull() || V.isUnknownOrUndef()) + return V; + + // The dispatchCast() call below would convert the int into a float. + // What we want, however, is a bit-by-bit reinterpretation of the int + // as a float, which usually yields nothing garbage. For now skip casts + // from ints to floats. + // TODO: What other combinations of types are affected? + if (castTy->isFloatingType()) { + SymbolRef Sym = V.getAsSymbol(); + if (Sym && !Sym->getType()->isFloatingType()) + return UnknownVal(); + } + + // When retrieving symbolic pointer and expecting a non-void pointer, + // wrap them into element regions of the expected type if necessary. + // SValBuilder::dispatchCast() doesn't do that, but it is necessary to + // make sure that the retrieved value makes sense, because there's no other + // cast in the AST that would tell us to cast it to the correct pointer type. + // We might need to do that for non-void pointers as well. + // FIXME: We really need a single good function to perform casts for us + // correctly every time we need it. + if (castTy->isPointerType() && !castTy->isVoidPointerType()) + if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(V.getAsRegion())) + if (SR->getSymbol()->getType().getCanonicalType() != + castTy.getCanonicalType()) + return loc::MemRegionVal(castRegion(SR, castTy)); + + return svalBuilder.dispatchCast(V, castTy); +} + +SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) { + if (Base.isUnknownOrUndef()) + return Base; + + Loc BaseL = Base.castAs<Loc>(); + const SubRegion* BaseR = nullptr; + + switch (BaseL.getSubKind()) { + case loc::MemRegionValKind: + BaseR = cast<SubRegion>(BaseL.castAs<loc::MemRegionVal>().getRegion()); + break; + + case loc::GotoLabelKind: + // These are anormal cases. Flag an undefined value. + return UndefinedVal(); + + case loc::ConcreteIntKind: + // While these seem funny, this can happen through casts. + // FIXME: What we should return is the field offset, not base. For example, + // add the field offset to the integer value. That way things + // like this work properly: &(((struct foo *) 0xa)->f) + // However, that's not easy to fix without reducing our abilities + // to catch null pointer dereference. Eg., ((struct foo *)0x0)->f = 7 + // is a null dereference even though we're dereferencing offset of f + // rather than null. Coming up with an approach that computes offsets + // over null pointers properly while still being able to catch null + // dereferences might be worth it. + return Base; + + default: + llvm_unreachable("Unhandled Base."); + } + + // NOTE: We must have this check first because ObjCIvarDecl is a subclass + // of FieldDecl. + if (const auto *ID = dyn_cast<ObjCIvarDecl>(D)) + return loc::MemRegionVal(MRMgr.getObjCIvarRegion(ID, BaseR)); + + return loc::MemRegionVal(MRMgr.getFieldRegion(cast<FieldDecl>(D), BaseR)); +} + +SVal StoreManager::getLValueIvar(const ObjCIvarDecl *decl, SVal base) { + return getLValueFieldOrIvar(decl, base); +} + +SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset, + SVal Base) { + // If the base is an unknown or undefined value, just return it back. + // FIXME: For absolute pointer addresses, we just return that value back as + // well, although in reality we should return the offset added to that + // value. See also the similar FIXME in getLValueFieldOrIvar(). + if (Base.isUnknownOrUndef() || Base.getAs<loc::ConcreteInt>()) + return Base; + + if (Base.getAs<loc::GotoLabel>()) + return UnknownVal(); + + const SubRegion *BaseRegion = + Base.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>(); + + // Pointer of any type can be cast and used as array base. + const auto *ElemR = dyn_cast<ElementRegion>(BaseRegion); + + // Convert the offset to the appropriate size and signedness. + Offset = svalBuilder.convertToArrayIndex(Offset).castAs<NonLoc>(); + + if (!ElemR) { + // If the base region is not an ElementRegion, create one. + // This can happen in the following example: + // + // char *p = __builtin_alloc(10); + // p[1] = 8; + // + // Observe that 'p' binds to an AllocaRegion. + return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset, + BaseRegion, Ctx)); + } + + SVal BaseIdx = ElemR->getIndex(); + + if (!BaseIdx.getAs<nonloc::ConcreteInt>()) + return UnknownVal(); + + const llvm::APSInt &BaseIdxI = + BaseIdx.castAs<nonloc::ConcreteInt>().getValue(); + + // Only allow non-integer offsets if the base region has no offset itself. + // FIXME: This is a somewhat arbitrary restriction. We should be using + // SValBuilder here to add the two offsets without checking their types. + if (!Offset.getAs<nonloc::ConcreteInt>()) { + if (isa<ElementRegion>(BaseRegion->StripCasts())) + return UnknownVal(); + + return loc::MemRegionVal(MRMgr.getElementRegion( + elementType, Offset, cast<SubRegion>(ElemR->getSuperRegion()), Ctx)); + } + + const llvm::APSInt& OffI = Offset.castAs<nonloc::ConcreteInt>().getValue(); + assert(BaseIdxI.isSigned()); + + // Compute the new index. + nonloc::ConcreteInt NewIdx(svalBuilder.getBasicValueFactory().getValue(BaseIdxI + + OffI)); + + // Construct the new ElementRegion. + const SubRegion *ArrayR = cast<SubRegion>(ElemR->getSuperRegion()); + return loc::MemRegionVal(MRMgr.getElementRegion(elementType, NewIdx, ArrayR, + Ctx)); +} + +StoreManager::BindingsHandler::~BindingsHandler() = default; + +bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr, + Store store, + const MemRegion* R, + SVal val) { + SymbolRef SymV = val.getAsLocSymbol(); + if (!SymV || SymV != Sym) + return true; + + if (Binding) { + First = false; + return false; + } + else + Binding = R; + + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp new file mode 100644 index 000000000000..350f4b8bb3a2 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp @@ -0,0 +1,14 @@ +//== 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. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h" + +using namespace clang::ento; + +void SubEngine::anchor() { } diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp new file mode 100644 index 000000000000..66273f099a38 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp @@ -0,0 +1,567 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines SymbolManager, a class that manages symbolic values +// created for use by ExprEngine and related classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Expr.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/AnalysisDeclContext.h" +#include "clang/Basic/LLVM.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/Compiler.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/Support/raw_ostream.h" +#include <cassert> + +using namespace clang; +using namespace ento; + +void SymExpr::anchor() {} + +LLVM_DUMP_METHOD void SymExpr::dump() const { + dumpToStream(llvm::errs()); +} + +void SymIntExpr::dumpToStream(raw_ostream &os) const { + os << '('; + getLHS()->dumpToStream(os); + os << ") " + << BinaryOperator::getOpcodeStr(getOpcode()) << ' '; + if (getRHS().isUnsigned()) + os << getRHS().getZExtValue(); + else + os << getRHS().getSExtValue(); + if (getRHS().isUnsigned()) + os << 'U'; +} + +void IntSymExpr::dumpToStream(raw_ostream &os) const { + if (getLHS().isUnsigned()) + os << getLHS().getZExtValue(); + else + os << getLHS().getSExtValue(); + if (getLHS().isUnsigned()) + os << 'U'; + os << ' ' + << BinaryOperator::getOpcodeStr(getOpcode()) + << " ("; + getRHS()->dumpToStream(os); + os << ')'; +} + +void SymSymExpr::dumpToStream(raw_ostream &os) const { + os << '('; + getLHS()->dumpToStream(os); + os << ") " + << BinaryOperator::getOpcodeStr(getOpcode()) + << " ("; + getRHS()->dumpToStream(os); + os << ')'; +} + +void SymbolCast::dumpToStream(raw_ostream &os) const { + os << '(' << ToTy.getAsString() << ") ("; + Operand->dumpToStream(os); + os << ')'; +} + +void SymbolConjured::dumpToStream(raw_ostream &os) const { + os << "conj_$" << getSymbolID() << '{' << T.getAsString() << ", LC" + << LCtx->getID(); + if (S) + os << ", S" << S->getID(LCtx->getDecl()->getASTContext()); + else + os << ", no stmt"; + os << ", #" << Count << '}'; +} + +void SymbolDerived::dumpToStream(raw_ostream &os) const { + os << "derived_$" << getSymbolID() << '{' + << getParentSymbol() << ',' << getRegion() << '}'; +} + +void SymbolExtent::dumpToStream(raw_ostream &os) const { + os << "extent_$" << getSymbolID() << '{' << getRegion() << '}'; +} + +void SymbolMetadata::dumpToStream(raw_ostream &os) const { + os << "meta_$" << getSymbolID() << '{' + << getRegion() << ',' << T.getAsString() << '}'; +} + +void SymbolData::anchor() {} + +void SymbolRegionValue::dumpToStream(raw_ostream &os) const { + os << "reg_$" << getSymbolID() + << '<' << getType().getAsString() << ' ' << R << '>'; +} + +bool SymExpr::symbol_iterator::operator==(const symbol_iterator &X) const { + return itr == X.itr; +} + +bool SymExpr::symbol_iterator::operator!=(const symbol_iterator &X) const { + return itr != X.itr; +} + +SymExpr::symbol_iterator::symbol_iterator(const SymExpr *SE) { + itr.push_back(SE); +} + +SymExpr::symbol_iterator &SymExpr::symbol_iterator::operator++() { + assert(!itr.empty() && "attempting to iterate on an 'end' iterator"); + expand(); + return *this; +} + +SymbolRef SymExpr::symbol_iterator::operator*() { + assert(!itr.empty() && "attempting to dereference an 'end' iterator"); + return itr.back(); +} + +void SymExpr::symbol_iterator::expand() { + const SymExpr *SE = itr.pop_back_val(); + + switch (SE->getKind()) { + case SymExpr::SymbolRegionValueKind: + case SymExpr::SymbolConjuredKind: + case SymExpr::SymbolDerivedKind: + case SymExpr::SymbolExtentKind: + case SymExpr::SymbolMetadataKind: + return; + case SymExpr::SymbolCastKind: + itr.push_back(cast<SymbolCast>(SE)->getOperand()); + return; + case SymExpr::SymIntExprKind: + itr.push_back(cast<SymIntExpr>(SE)->getLHS()); + return; + case SymExpr::IntSymExprKind: + itr.push_back(cast<IntSymExpr>(SE)->getRHS()); + return; + case SymExpr::SymSymExprKind: { + const auto *x = cast<SymSymExpr>(SE); + itr.push_back(x->getLHS()); + itr.push_back(x->getRHS()); + return; + } + } + llvm_unreachable("unhandled expansion case"); +} + +const SymbolRegionValue* +SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) { + llvm::FoldingSetNodeID profile; + SymbolRegionValue::Profile(profile, R); + void *InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate<SymbolRegionValue>(); + new (SD) SymbolRegionValue(SymbolCounter, R); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast<SymbolRegionValue>(SD); +} + +const SymbolConjured* SymbolManager::conjureSymbol(const Stmt *E, + const LocationContext *LCtx, + QualType T, + unsigned Count, + const void *SymbolTag) { + llvm::FoldingSetNodeID profile; + SymbolConjured::Profile(profile, E, T, Count, LCtx, SymbolTag); + void *InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate<SymbolConjured>(); + new (SD) SymbolConjured(SymbolCounter, E, LCtx, T, Count, SymbolTag); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast<SymbolConjured>(SD); +} + +const SymbolDerived* +SymbolManager::getDerivedSymbol(SymbolRef parentSymbol, + const TypedValueRegion *R) { + llvm::FoldingSetNodeID profile; + SymbolDerived::Profile(profile, parentSymbol, R); + void *InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate<SymbolDerived>(); + new (SD) SymbolDerived(SymbolCounter, parentSymbol, R); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast<SymbolDerived>(SD); +} + +const SymbolExtent* +SymbolManager::getExtentSymbol(const SubRegion *R) { + llvm::FoldingSetNodeID profile; + SymbolExtent::Profile(profile, R); + void *InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate<SymbolExtent>(); + new (SD) SymbolExtent(SymbolCounter, R); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast<SymbolExtent>(SD); +} + +const SymbolMetadata * +SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt *S, QualType T, + const LocationContext *LCtx, + unsigned Count, const void *SymbolTag) { + llvm::FoldingSetNodeID profile; + SymbolMetadata::Profile(profile, R, S, T, LCtx, Count, SymbolTag); + void *InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate<SymbolMetadata>(); + new (SD) SymbolMetadata(SymbolCounter, R, S, T, LCtx, Count, SymbolTag); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast<SymbolMetadata>(SD); +} + +const SymbolCast* +SymbolManager::getCastSymbol(const SymExpr *Op, + QualType From, QualType To) { + llvm::FoldingSetNodeID ID; + SymbolCast::Profile(ID, Op, From, To); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + if (!data) { + data = (SymbolCast*) BPAlloc.Allocate<SymbolCast>(); + new (data) SymbolCast(Op, From, To); + DataSet.InsertNode(data, InsertPos); + } + + return cast<SymbolCast>(data); +} + +const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs, + BinaryOperator::Opcode op, + const llvm::APSInt& v, + QualType t) { + llvm::FoldingSetNodeID ID; + SymIntExpr::Profile(ID, lhs, op, v, t); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!data) { + data = (SymIntExpr*) BPAlloc.Allocate<SymIntExpr>(); + new (data) SymIntExpr(lhs, op, v, t); + DataSet.InsertNode(data, InsertPos); + } + + return cast<SymIntExpr>(data); +} + +const IntSymExpr *SymbolManager::getIntSymExpr(const llvm::APSInt& lhs, + BinaryOperator::Opcode op, + const SymExpr *rhs, + QualType t) { + llvm::FoldingSetNodeID ID; + IntSymExpr::Profile(ID, lhs, op, rhs, t); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!data) { + data = (IntSymExpr*) BPAlloc.Allocate<IntSymExpr>(); + new (data) IntSymExpr(lhs, op, rhs, t); + DataSet.InsertNode(data, InsertPos); + } + + return cast<IntSymExpr>(data); +} + +const SymSymExpr *SymbolManager::getSymSymExpr(const SymExpr *lhs, + BinaryOperator::Opcode op, + const SymExpr *rhs, + QualType t) { + llvm::FoldingSetNodeID ID; + SymSymExpr::Profile(ID, lhs, op, rhs, t); + void *InsertPos; + SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!data) { + data = (SymSymExpr*) BPAlloc.Allocate<SymSymExpr>(); + new (data) SymSymExpr(lhs, op, rhs, t); + DataSet.InsertNode(data, InsertPos); + } + + return cast<SymSymExpr>(data); +} + +QualType SymbolConjured::getType() const { + return T; +} + +QualType SymbolDerived::getType() const { + return R->getValueType(); +} + +QualType SymbolExtent::getType() const { + ASTContext &Ctx = R->getMemRegionManager()->getContext(); + return Ctx.getSizeType(); +} + +QualType SymbolMetadata::getType() const { + return T; +} + +QualType SymbolRegionValue::getType() const { + return R->getValueType(); +} + +SymbolManager::~SymbolManager() { + llvm::DeleteContainerSeconds(SymbolDependencies); +} + +bool SymbolManager::canSymbolicate(QualType T) { + T = T.getCanonicalType(); + + if (Loc::isLocType(T)) + return true; + + if (T->isIntegralOrEnumerationType()) + return true; + + if (T->isRecordType() && !T->isUnionType()) + return true; + + return false; +} + +void SymbolManager::addSymbolDependency(const SymbolRef Primary, + const SymbolRef Dependent) { + SymbolDependTy::iterator I = SymbolDependencies.find(Primary); + SymbolRefSmallVectorTy *dependencies = nullptr; + if (I == SymbolDependencies.end()) { + dependencies = new SymbolRefSmallVectorTy(); + SymbolDependencies[Primary] = dependencies; + } else { + dependencies = I->second; + } + dependencies->push_back(Dependent); +} + +const SymbolRefSmallVectorTy *SymbolManager::getDependentSymbols( + const SymbolRef Primary) { + SymbolDependTy::const_iterator I = SymbolDependencies.find(Primary); + if (I == SymbolDependencies.end()) + return nullptr; + return I->second; +} + +void SymbolReaper::markDependentsLive(SymbolRef sym) { + // Do not mark dependents more then once. + SymbolMapTy::iterator LI = TheLiving.find(sym); + assert(LI != TheLiving.end() && "The primary symbol is not live."); + if (LI->second == HaveMarkedDependents) + return; + LI->second = HaveMarkedDependents; + + if (const SymbolRefSmallVectorTy *Deps = SymMgr.getDependentSymbols(sym)) { + for (const auto I : *Deps) { + if (TheLiving.find(I) != TheLiving.end()) + continue; + markLive(I); + } + } +} + +void SymbolReaper::markLive(SymbolRef sym) { + TheLiving[sym] = NotProcessed; + markDependentsLive(sym); +} + +void SymbolReaper::markLive(const MemRegion *region) { + RegionRoots.insert(region); + markElementIndicesLive(region); +} + +void SymbolReaper::markElementIndicesLive(const MemRegion *region) { + for (auto SR = dyn_cast<SubRegion>(region); SR; + SR = dyn_cast<SubRegion>(SR->getSuperRegion())) { + if (const auto ER = dyn_cast<ElementRegion>(SR)) { + SVal Idx = ER->getIndex(); + for (auto SI = Idx.symbol_begin(), SE = Idx.symbol_end(); SI != SE; ++SI) + markLive(*SI); + } + } +} + +void SymbolReaper::markInUse(SymbolRef sym) { + if (isa<SymbolMetadata>(sym)) + MetadataInUse.insert(sym); +} + +bool SymbolReaper::isLiveRegion(const MemRegion *MR) { + if (RegionRoots.count(MR)) + return true; + + MR = MR->getBaseRegion(); + + if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) + return isLive(SR->getSymbol()); + + if (const auto *VR = dyn_cast<VarRegion>(MR)) + return isLive(VR, true); + + // FIXME: This is a gross over-approximation. What we really need is a way to + // tell if anything still refers to this region. Unlike SymbolicRegions, + // AllocaRegions don't have associated symbols, though, so we don't actually + // have a way to track their liveness. + if (isa<AllocaRegion>(MR)) + return true; + + if (isa<CXXThisRegion>(MR)) + return true; + + if (isa<MemSpaceRegion>(MR)) + return true; + + if (isa<CodeTextRegion>(MR)) + return true; + + return false; +} + +bool SymbolReaper::isLive(SymbolRef sym) { + if (TheLiving.count(sym)) { + markDependentsLive(sym); + return true; + } + + bool KnownLive; + + switch (sym->getKind()) { + case SymExpr::SymbolRegionValueKind: + KnownLive = isLiveRegion(cast<SymbolRegionValue>(sym)->getRegion()); + break; + case SymExpr::SymbolConjuredKind: + KnownLive = false; + break; + case SymExpr::SymbolDerivedKind: + KnownLive = isLive(cast<SymbolDerived>(sym)->getParentSymbol()); + break; + case SymExpr::SymbolExtentKind: + KnownLive = isLiveRegion(cast<SymbolExtent>(sym)->getRegion()); + break; + case SymExpr::SymbolMetadataKind: + KnownLive = MetadataInUse.count(sym) && + isLiveRegion(cast<SymbolMetadata>(sym)->getRegion()); + if (KnownLive) + MetadataInUse.erase(sym); + break; + case SymExpr::SymIntExprKind: + KnownLive = isLive(cast<SymIntExpr>(sym)->getLHS()); + break; + case SymExpr::IntSymExprKind: + KnownLive = isLive(cast<IntSymExpr>(sym)->getRHS()); + break; + case SymExpr::SymSymExprKind: + KnownLive = isLive(cast<SymSymExpr>(sym)->getLHS()) && + isLive(cast<SymSymExpr>(sym)->getRHS()); + break; + case SymExpr::SymbolCastKind: + KnownLive = isLive(cast<SymbolCast>(sym)->getOperand()); + break; + } + + if (KnownLive) + markLive(sym); + + return KnownLive; +} + +bool +SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const { + if (LCtx == nullptr) + return false; + + if (LCtx != ELCtx) { + // If the reaper's location context is a parent of the expression's + // location context, then the expression value is now "out of scope". + if (LCtx->isParentOf(ELCtx)) + return false; + return true; + } + + // If no statement is provided, everything is this and parent contexts is live. + if (!Loc) + return true; + + return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal); +} + +bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{ + const StackFrameContext *VarContext = VR->getStackFrame(); + + if (!VarContext) + return true; + + if (!LCtx) + return false; + const StackFrameContext *CurrentContext = LCtx->getStackFrame(); + + if (VarContext == CurrentContext) { + // If no statement is provided, everything is live. + if (!Loc) + return true; + + if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl())) + return true; + + if (!includeStoreBindings) + return false; + + unsigned &cachedQuery = + const_cast<SymbolReaper *>(this)->includedRegionCache[VR]; + + if (cachedQuery) { + return cachedQuery == 1; + } + + // Query the store to see if the region occurs in any live bindings. + if (Store store = reapedStore.getStore()) { + bool hasRegion = + reapedStore.getStoreManager().includedInBindings(store, VR); + cachedQuery = hasRegion ? 1 : 2; + return hasRegion; + } + + return false; + } + + return VarContext->isParentOf(CurrentContext); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TaintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TaintManager.cpp new file mode 100644 index 000000000000..c34b0ca1839d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TaintManager.cpp @@ -0,0 +1,23 @@ +//== 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/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/WorkList.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/WorkList.cpp new file mode 100644 index 000000000000..e705393cb83a --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/WorkList.cpp @@ -0,0 +1,314 @@ +//===- 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. +// +//===----------------------------------------------------------------------===// +// +// Defines different worklist implementations for the static analyzer. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h" +#include "llvm/ADT/PriorityQueue.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/Statistic.h" +#include <deque> +#include <vector> + +using namespace clang; +using namespace ento; + +#define DEBUG_TYPE "WorkList" + +STATISTIC(MaxQueueSize, "Maximum size of the worklist"); +STATISTIC(MaxReachableSize, "Maximum size of auxiliary worklist set"); + +//===----------------------------------------------------------------------===// +// Worklist classes for exploration of reachable states. +//===----------------------------------------------------------------------===// + +namespace { + +class DFS : public WorkList { + SmallVector<WorkListUnit, 20> Stack; + +public: + bool hasWork() const override { + return !Stack.empty(); + } + + void enqueue(const WorkListUnit& U) override { + Stack.push_back(U); + } + + WorkListUnit dequeue() override { + assert(!Stack.empty()); + const WorkListUnit& U = Stack.back(); + Stack.pop_back(); // This technically "invalidates" U, but we are fine. + return U; + } +}; + +class BFS : public WorkList { + std::deque<WorkListUnit> Queue; + +public: + bool hasWork() const override { + return !Queue.empty(); + } + + void enqueue(const WorkListUnit& U) override { + Queue.push_back(U); + } + + WorkListUnit dequeue() override { + WorkListUnit U = Queue.front(); + Queue.pop_front(); + return U; + } +}; + +} // namespace + +// Place the dstor for WorkList here because it contains virtual member +// functions, and we the code for the dstor generated in one compilation unit. +WorkList::~WorkList() = default; + +std::unique_ptr<WorkList> WorkList::makeDFS() { + return llvm::make_unique<DFS>(); +} + +std::unique_ptr<WorkList> WorkList::makeBFS() { + return llvm::make_unique<BFS>(); +} + +namespace { + + class BFSBlockDFSContents : public WorkList { + std::deque<WorkListUnit> Queue; + SmallVector<WorkListUnit, 20> Stack; + + public: + bool hasWork() const override { + return !Queue.empty() || !Stack.empty(); + } + + void enqueue(const WorkListUnit& U) override { + if (U.getNode()->getLocation().getAs<BlockEntrance>()) + Queue.push_front(U); + else + Stack.push_back(U); + } + + WorkListUnit dequeue() override { + // Process all basic blocks to completion. + if (!Stack.empty()) { + const WorkListUnit& U = Stack.back(); + Stack.pop_back(); // This technically "invalidates" U, but we are fine. + return U; + } + + assert(!Queue.empty()); + // Don't use const reference. The subsequent pop_back() might make it + // unsafe. + WorkListUnit U = Queue.front(); + Queue.pop_front(); + return U; + } + }; + +} // namespace + +std::unique_ptr<WorkList> WorkList::makeBFSBlockDFSContents() { + return llvm::make_unique<BFSBlockDFSContents>(); +} + +namespace { + +class UnexploredFirstStack : public WorkList { + /// Stack of nodes known to have statements we have not traversed yet. + SmallVector<WorkListUnit, 20> StackUnexplored; + + /// Stack of all other nodes. + SmallVector<WorkListUnit, 20> StackOthers; + + using BlockID = unsigned; + using LocIdentifier = std::pair<BlockID, const StackFrameContext *>; + + llvm::DenseSet<LocIdentifier> Reachable; + +public: + bool hasWork() const override { + return !(StackUnexplored.empty() && StackOthers.empty()); + } + + void enqueue(const WorkListUnit &U) override { + const ExplodedNode *N = U.getNode(); + auto BE = N->getLocation().getAs<BlockEntrance>(); + + if (!BE) { + // Assume the choice of the order of the preceding block entrance was + // correct. + StackUnexplored.push_back(U); + } else { + LocIdentifier LocId = std::make_pair( + BE->getBlock()->getBlockID(), + N->getLocationContext()->getStackFrame()); + auto InsertInfo = Reachable.insert(LocId); + + if (InsertInfo.second) { + StackUnexplored.push_back(U); + } else { + StackOthers.push_back(U); + } + } + MaxReachableSize.updateMax(Reachable.size()); + MaxQueueSize.updateMax(StackUnexplored.size() + StackOthers.size()); + } + + WorkListUnit dequeue() override { + if (!StackUnexplored.empty()) { + WorkListUnit &U = StackUnexplored.back(); + StackUnexplored.pop_back(); + return U; + } else { + WorkListUnit &U = StackOthers.back(); + StackOthers.pop_back(); + return U; + } + } +}; + +} // namespace + +std::unique_ptr<WorkList> WorkList::makeUnexploredFirst() { + return llvm::make_unique<UnexploredFirstStack>(); +} + +namespace { +class UnexploredFirstPriorityQueue : public WorkList { + using BlockID = unsigned; + using LocIdentifier = std::pair<BlockID, const StackFrameContext *>; + + // How many times each location was visited. + // Is signed because we negate it later in order to have a reversed + // comparison. + using VisitedTimesMap = llvm::DenseMap<LocIdentifier, int>; + + // Compare by number of times the location was visited first (negated + // to prefer less often visited locations), then by insertion time (prefer + // expanding nodes inserted sooner first). + using QueuePriority = std::pair<int, unsigned long>; + using QueueItem = std::pair<WorkListUnit, QueuePriority>; + + struct ExplorationComparator { + bool operator() (const QueueItem &LHS, const QueueItem &RHS) { + return LHS.second < RHS.second; + } + }; + + // Number of inserted nodes, used to emulate DFS ordering in the priority + // queue when insertions are equal. + unsigned long Counter = 0; + + // Number of times a current location was reached. + VisitedTimesMap NumReached; + + // The top item is the largest one. + llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, ExplorationComparator> + queue; + +public: + bool hasWork() const override { + return !queue.empty(); + } + + void enqueue(const WorkListUnit &U) override { + const ExplodedNode *N = U.getNode(); + unsigned NumVisited = 0; + if (auto BE = N->getLocation().getAs<BlockEntrance>()) { + LocIdentifier LocId = std::make_pair( + BE->getBlock()->getBlockID(), + N->getLocationContext()->getStackFrame()); + NumVisited = NumReached[LocId]++; + } + + queue.push(std::make_pair(U, std::make_pair(-NumVisited, ++Counter))); + } + + WorkListUnit dequeue() override { + QueueItem U = queue.top(); + queue.pop(); + return U.first; + } +}; +} // namespace + +std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityQueue() { + return llvm::make_unique<UnexploredFirstPriorityQueue>(); +} + +namespace { +class UnexploredFirstPriorityLocationQueue : public WorkList { + using LocIdentifier = const CFGBlock *; + + // How many times each location was visited. + // Is signed because we negate it later in order to have a reversed + // comparison. + using VisitedTimesMap = llvm::DenseMap<LocIdentifier, int>; + + // Compare by number of times the location was visited first (negated + // to prefer less often visited locations), then by insertion time (prefer + // expanding nodes inserted sooner first). + using QueuePriority = std::pair<int, unsigned long>; + using QueueItem = std::pair<WorkListUnit, QueuePriority>; + + struct ExplorationComparator { + bool operator() (const QueueItem &LHS, const QueueItem &RHS) { + return LHS.second < RHS.second; + } + }; + + // Number of inserted nodes, used to emulate DFS ordering in the priority + // queue when insertions are equal. + unsigned long Counter = 0; + + // Number of times a current location was reached. + VisitedTimesMap NumReached; + + // The top item is the largest one. + llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, ExplorationComparator> + queue; + +public: + bool hasWork() const override { + return !queue.empty(); + } + + void enqueue(const WorkListUnit &U) override { + const ExplodedNode *N = U.getNode(); + unsigned NumVisited = 0; + if (auto BE = N->getLocation().getAs<BlockEntrance>()) + NumVisited = NumReached[BE->getBlock()]++; + + queue.push(std::make_pair(U, std::make_pair(-NumVisited, ++Counter))); + } + + WorkListUnit dequeue() override { + QueueItem U = queue.top(); + queue.pop(); + return U.first; + } + +}; + +} + +std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityLocationQueue() { + return llvm::make_unique<UnexploredFirstPriorityLocationQueue>(); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp new file mode 100644 index 000000000000..c4729f969f33 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp @@ -0,0 +1,841 @@ +//== 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 +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp new file mode 100644 index 000000000000..d87937d9b63d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp @@ -0,0 +1,771 @@ +//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// "Meta" ASTConsumer for running different source analyses. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#include "ModelInjector.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/CFG.h" +#include "clang/Analysis/CallGraph.h" +#include "clang/Analysis/CodeInjector.h" +#include "clang/Basic/SourceManager.h" +#include "clang/CrossTU/CrossTranslationUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/Statistic.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> +#include <queue> +#include <utility> + +using namespace clang; +using namespace ento; + +#define DEBUG_TYPE "AnalysisConsumer" + +STATISTIC(NumFunctionTopLevel, "The # of functions at top level."); +STATISTIC(NumFunctionsAnalyzed, + "The # of functions and blocks analyzed (as top level " + "with inlining turned on)."); +STATISTIC(NumBlocksInAnalyzedFunctions, + "The # of basic blocks in the analyzed functions."); +STATISTIC(NumVisitedBlocksInAnalyzedFunctions, + "The # of visited basic blocks in the analyzed functions."); +STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks."); +STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function."); + +//===----------------------------------------------------------------------===// +// Special PathDiagnosticConsumers. +//===----------------------------------------------------------------------===// + +void ento::createPlistHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, + const std::string &prefix, + const Preprocessor &PP) { + createHTMLDiagnosticConsumer(AnalyzerOpts, C, + llvm::sys::path::parent_path(prefix), PP); + createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP); +} + +void ento::createTextPathDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts, + PathDiagnosticConsumers &C, + const std::string &Prefix, + const clang::Preprocessor &PP) { + llvm_unreachable("'text' consumer should be enabled on ClangDiags"); +} + +namespace { +class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer { + DiagnosticsEngine &Diag; + bool IncludePath; +public: + ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag) + : Diag(Diag), IncludePath(false) {} + ~ClangDiagPathDiagConsumer() override {} + StringRef getName() const override { return "ClangDiags"; } + + bool supportsLogicalOpControlFlow() const override { return true; } + bool supportsCrossFileDiagnostics() const override { return true; } + + PathGenerationScheme getGenerationScheme() const override { + return IncludePath ? Minimal : None; + } + + void enablePaths() { + IncludePath = true; + } + + void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags, + FilesMade *filesMade) override { + unsigned WarnID = Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0"); + unsigned NoteID = Diag.getCustomDiagID(DiagnosticsEngine::Note, "%0"); + + for (std::vector<const PathDiagnostic*>::iterator I = Diags.begin(), + E = Diags.end(); I != E; ++I) { + const PathDiagnostic *PD = *I; + SourceLocation WarnLoc = PD->getLocation().asLocation(); + Diag.Report(WarnLoc, WarnID) << PD->getShortDescription() + << PD->path.back()->getRanges(); + + // First, add extra notes, even if paths should not be included. + for (const auto &Piece : PD->path) { + if (!isa<PathDiagnosticNotePiece>(Piece.get())) + continue; + + SourceLocation NoteLoc = Piece->getLocation().asLocation(); + Diag.Report(NoteLoc, NoteID) << Piece->getString() + << Piece->getRanges(); + } + + if (!IncludePath) + continue; + + // Then, add the path notes if necessary. + PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true); + for (const auto &Piece : FlatPath) { + if (isa<PathDiagnosticNotePiece>(Piece.get())) + continue; + + SourceLocation NoteLoc = Piece->getLocation().asLocation(); + Diag.Report(NoteLoc, NoteID) << Piece->getString() + << Piece->getRanges(); + } + } + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// AnalysisConsumer declaration. +//===----------------------------------------------------------------------===// + +namespace { + +class AnalysisConsumer : public AnalysisASTConsumer, + public RecursiveASTVisitor<AnalysisConsumer> { + enum { + AM_None = 0, + AM_Syntax = 0x1, + AM_Path = 0x2 + }; + typedef unsigned AnalysisMode; + + /// Mode of the analyzes while recursively visiting Decls. + AnalysisMode RecVisitorMode; + /// Bug Reporter to use while recursively visiting Decls. + BugReporter *RecVisitorBR; + + std::vector<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns; + +public: + ASTContext *Ctx; + const Preprocessor &PP; + const std::string OutDir; + AnalyzerOptionsRef Opts; + ArrayRef<std::string> Plugins; + CodeInjector *Injector; + cross_tu::CrossTranslationUnitContext CTU; + + /// Stores the declarations from the local translation unit. + /// Note, we pre-compute the local declarations at parse time as an + /// optimization to make sure we do not deserialize everything from disk. + /// The local declaration to all declarations ratio might be very small when + /// working with a PCH file. + SetOfDecls LocalTUDecls; + + // Set of PathDiagnosticConsumers. Owned by AnalysisManager. + PathDiagnosticConsumers PathConsumers; + + StoreManagerCreator CreateStoreMgr; + ConstraintManagerCreator CreateConstraintMgr; + + std::unique_ptr<CheckerManager> checkerMgr; + std::unique_ptr<AnalysisManager> Mgr; + + /// Time the analyzes time of each translation unit. + std::unique_ptr<llvm::TimerGroup> AnalyzerTimers; + std::unique_ptr<llvm::Timer> TUTotalTimer; + + /// The information about analyzed functions shared throughout the + /// translation unit. + FunctionSummariesTy FunctionSummaries; + + AnalysisConsumer(CompilerInstance &CI, const std::string &outdir, + AnalyzerOptionsRef opts, ArrayRef<std::string> plugins, + CodeInjector *injector) + : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr), + PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)), + Plugins(plugins), Injector(injector), CTU(CI) { + DigestAnalyzerOptions(); + if (Opts->PrintStats || Opts->ShouldSerializeStats) { + AnalyzerTimers = llvm::make_unique<llvm::TimerGroup>( + "analyzer", "Analyzer timers"); + TUTotalTimer = llvm::make_unique<llvm::Timer>( + "time", "Analyzer total time", *AnalyzerTimers); + llvm::EnableStatistics(/* PrintOnExit= */ false); + } + } + + ~AnalysisConsumer() override { + if (Opts->PrintStats) { + llvm::PrintStatistics(); + } + } + + void DigestAnalyzerOptions() { + if (Opts->AnalysisDiagOpt != PD_NONE) { + // Create the PathDiagnosticConsumer. + ClangDiagPathDiagConsumer *clangDiags = + new ClangDiagPathDiagConsumer(PP.getDiagnostics()); + PathConsumers.push_back(clangDiags); + + if (Opts->AnalysisDiagOpt == PD_TEXT) { + clangDiags->enablePaths(); + + } else if (!OutDir.empty()) { + switch (Opts->AnalysisDiagOpt) { + default: +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \ + case PD_##NAME: \ + CREATEFN(*Opts.get(), PathConsumers, OutDir, PP); \ + break; +#include "clang/StaticAnalyzer/Core/Analyses.def" + } + } + } + + // Create the analyzer component creators. + switch (Opts->AnalysisStoreOpt) { + default: + llvm_unreachable("Unknown store manager."); +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \ + case NAME##Model: CreateStoreMgr = CREATEFN; break; +#include "clang/StaticAnalyzer/Core/Analyses.def" + } + + switch (Opts->AnalysisConstraintsOpt) { + default: + llvm_unreachable("Unknown constraint manager."); +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \ + case NAME##Model: CreateConstraintMgr = CREATEFN; break; +#include "clang/StaticAnalyzer/Core/Analyses.def" + } + } + + void DisplayFunction(const Decl *D, AnalysisMode Mode, + ExprEngine::InliningModes IMode) { + if (!Opts->AnalyzerDisplayProgress) + return; + + SourceManager &SM = Mgr->getASTContext().getSourceManager(); + PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); + if (Loc.isValid()) { + llvm::errs() << "ANALYZE"; + + if (Mode == AM_Syntax) + llvm::errs() << " (Syntax)"; + else if (Mode == AM_Path) { + llvm::errs() << " (Path, "; + switch (IMode) { + case ExprEngine::Inline_Minimal: + llvm::errs() << " Inline_Minimal"; + break; + case ExprEngine::Inline_Regular: + llvm::errs() << " Inline_Regular"; + break; + } + llvm::errs() << ")"; + } + else + assert(Mode == (AM_Syntax | AM_Path) && "Unexpected mode!"); + + llvm::errs() << ": " << Loc.getFilename() << ' ' + << getFunctionName(D) << '\n'; + } + } + + void Initialize(ASTContext &Context) override { + Ctx = &Context; + checkerMgr = createCheckerManager( + *Ctx, *Opts, Plugins, CheckerRegistrationFns, PP.getDiagnostics()); + + Mgr = llvm::make_unique<AnalysisManager>( + *Ctx, PP.getDiagnostics(), PathConsumers, CreateStoreMgr, + CreateConstraintMgr, checkerMgr.get(), *Opts, Injector); + } + + /// Store the top level decls in the set to be processed later on. + /// (Doing this pre-processing avoids deserialization of data from PCH.) + bool HandleTopLevelDecl(DeclGroupRef D) override; + void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override; + + void HandleTranslationUnit(ASTContext &C) override; + + /// Determine which inlining mode should be used when this function is + /// analyzed. This allows to redefine the default inlining policies when + /// analyzing a given function. + ExprEngine::InliningModes + getInliningModeForFunction(const Decl *D, const SetOfConstDecls &Visited); + + /// Build the call graph for all the top level decls of this TU and + /// use it to define the order in which the functions should be visited. + void HandleDeclsCallGraph(const unsigned LocalTUDeclsSize); + + /// Run analyzes(syntax or path sensitive) on the given function. + /// \param Mode - determines if we are requesting syntax only or path + /// sensitive only analysis. + /// \param VisitedCallees - The output parameter, which is populated with the + /// set of functions which should be considered analyzed after analyzing the + /// given root function. + void HandleCode(Decl *D, AnalysisMode Mode, + ExprEngine::InliningModes IMode = ExprEngine::Inline_Minimal, + SetOfConstDecls *VisitedCallees = nullptr); + + void RunPathSensitiveChecks(Decl *D, + ExprEngine::InliningModes IMode, + SetOfConstDecls *VisitedCallees); + + /// Visitors for the RecursiveASTVisitor. + bool shouldWalkTypesOfTypeLocs() const { return false; } + + /// Handle callbacks for arbitrary Decls. + bool VisitDecl(Decl *D) { + AnalysisMode Mode = getModeForDecl(D, RecVisitorMode); + if (Mode & AM_Syntax) + checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR); + return true; + } + + bool VisitFunctionDecl(FunctionDecl *FD) { + IdentifierInfo *II = FD->getIdentifier(); + if (II && II->getName().startswith("__inline")) + return true; + + // We skip function template definitions, as their semantics is + // only determined when they are instantiated. + if (FD->isThisDeclarationADefinition() && + !FD->isDependentContext()) { + assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false); + HandleCode(FD, RecVisitorMode); + } + return true; + } + + bool VisitObjCMethodDecl(ObjCMethodDecl *MD) { + if (MD->isThisDeclarationADefinition()) { + assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false); + HandleCode(MD, RecVisitorMode); + } + return true; + } + + bool VisitBlockDecl(BlockDecl *BD) { + if (BD->hasBody()) { + assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false); + // Since we skip function template definitions, we should skip blocks + // declared in those functions as well. + if (!BD->isDependentContext()) { + HandleCode(BD, RecVisitorMode); + } + } + return true; + } + + void AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) override { + PathConsumers.push_back(Consumer); + } + + void AddCheckerRegistrationFn(std::function<void(CheckerRegistry&)> Fn) override { + CheckerRegistrationFns.push_back(std::move(Fn)); + } + +private: + void storeTopLevelDecls(DeclGroupRef DG); + std::string getFunctionName(const Decl *D); + + /// Check if we should skip (not analyze) the given function. + AnalysisMode getModeForDecl(Decl *D, AnalysisMode Mode); + void runAnalysisOnTranslationUnit(ASTContext &C); + + /// Print \p S to stderr if \c Opts->AnalyzerDisplayProgress is set. + void reportAnalyzerProgress(StringRef S); +}; +} // end anonymous namespace + + +//===----------------------------------------------------------------------===// +// AnalysisConsumer implementation. +//===----------------------------------------------------------------------===// +bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) { + storeTopLevelDecls(DG); + return true; +} + +void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) { + storeTopLevelDecls(DG); +} + +void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) { + for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) { + + // Skip ObjCMethodDecl, wait for the objc container to avoid + // analyzing twice. + if (isa<ObjCMethodDecl>(*I)) + continue; + + LocalTUDecls.push_back(*I); + } +} + +static bool shouldSkipFunction(const Decl *D, + const SetOfConstDecls &Visited, + const SetOfConstDecls &VisitedAsTopLevel) { + if (VisitedAsTopLevel.count(D)) + return true; + + // We want to re-analyse the functions as top level in the following cases: + // - The 'init' methods should be reanalyzed because + // ObjCNonNilReturnValueChecker assumes that '[super init]' never returns + // 'nil' and unless we analyze the 'init' functions as top level, we will + // not catch errors within defensive code. + // - We want to reanalyze all ObjC methods as top level to report Retain + // Count naming convention errors more aggressively. + if (isa<ObjCMethodDecl>(D)) + return false; + // We also want to reanalyze all C++ copy and move assignment operators to + // separately check the two cases where 'this' aliases with the parameter and + // where it may not. (cplusplus.SelfAssignmentChecker) + if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) { + if (MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()) + return false; + } + + // Otherwise, if we visited the function before, do not reanalyze it. + return Visited.count(D); +} + +ExprEngine::InliningModes +AnalysisConsumer::getInliningModeForFunction(const Decl *D, + const SetOfConstDecls &Visited) { + // We want to reanalyze all ObjC methods as top level to report Retain + // Count naming convention errors more aggressively. But we should tune down + // inlining when reanalyzing an already inlined function. + if (Visited.count(D) && isa<ObjCMethodDecl>(D)) { + const ObjCMethodDecl *ObjCM = cast<ObjCMethodDecl>(D); + if (ObjCM->getMethodFamily() != OMF_init) + return ExprEngine::Inline_Minimal; + } + + return ExprEngine::Inline_Regular; +} + +void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) { + // Build the Call Graph by adding all the top level declarations to the graph. + // Note: CallGraph can trigger deserialization of more items from a pch + // (though HandleInterestingDecl); triggering additions to LocalTUDecls. + // We rely on random access to add the initially processed Decls to CG. + CallGraph CG; + for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { + CG.addToCallGraph(LocalTUDecls[i]); + } + + // Walk over all of the call graph nodes in topological order, so that we + // analyze parents before the children. Skip the functions inlined into + // the previously processed functions. Use external Visited set to identify + // inlined functions. The topological order allows the "do not reanalyze + // previously inlined function" performance heuristic to be triggered more + // often. + SetOfConstDecls Visited; + SetOfConstDecls VisitedAsTopLevel; + llvm::ReversePostOrderTraversal<clang::CallGraph*> RPOT(&CG); + for (llvm::ReversePostOrderTraversal<clang::CallGraph*>::rpo_iterator + I = RPOT.begin(), E = RPOT.end(); I != E; ++I) { + NumFunctionTopLevel++; + + CallGraphNode *N = *I; + Decl *D = N->getDecl(); + + // Skip the abstract root node. + if (!D) + continue; + + // Skip the functions which have been processed already or previously + // inlined. + if (shouldSkipFunction(D, Visited, VisitedAsTopLevel)) + continue; + + // Analyze the function. + SetOfConstDecls VisitedCallees; + + HandleCode(D, AM_Path, getInliningModeForFunction(D, Visited), + (Mgr->options.InliningMode == All ? nullptr : &VisitedCallees)); + + // Add the visited callees to the global visited set. + for (const Decl *Callee : VisitedCallees) + // Decls from CallGraph are already canonical. But Decls coming from + // CallExprs may be not. We should canonicalize them manually. + Visited.insert(isa<ObjCMethodDecl>(Callee) ? Callee + : Callee->getCanonicalDecl()); + VisitedAsTopLevel.insert(D); + } +} + +static bool isBisonFile(ASTContext &C) { + const SourceManager &SM = C.getSourceManager(); + FileID FID = SM.getMainFileID(); + StringRef Buffer = SM.getBuffer(FID)->getBuffer(); + if (Buffer.startswith("/* A Bison parser, made by")) + return true; + return false; +} + +void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) { + BugReporter BR(*Mgr); + TranslationUnitDecl *TU = C.getTranslationUnitDecl(); + checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR); + + // Run the AST-only checks using the order in which functions are defined. + // If inlining is not turned on, use the simplest function order for path + // sensitive analyzes as well. + RecVisitorMode = AM_Syntax; + if (!Mgr->shouldInlineCall()) + RecVisitorMode |= AM_Path; + RecVisitorBR = &BR; + + // Process all the top level declarations. + // + // Note: TraverseDecl may modify LocalTUDecls, but only by appending more + // entries. Thus we don't use an iterator, but rely on LocalTUDecls + // random access. By doing so, we automatically compensate for iterators + // possibly being invalidated, although this is a bit slower. + const unsigned LocalTUDeclsSize = LocalTUDecls.size(); + for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) { + TraverseDecl(LocalTUDecls[i]); + } + + if (Mgr->shouldInlineCall()) + HandleDeclsCallGraph(LocalTUDeclsSize); + + // After all decls handled, run checkers on the entire TranslationUnit. + checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR); + + RecVisitorBR = nullptr; +} + +void AnalysisConsumer::reportAnalyzerProgress(StringRef S) { + if (Opts->AnalyzerDisplayProgress) + llvm::errs() << S; +} + +void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { + + // Don't run the actions if an error has occurred with parsing the file. + DiagnosticsEngine &Diags = PP.getDiagnostics(); + if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) + return; + + if (TUTotalTimer) TUTotalTimer->startTimer(); + + if (isBisonFile(C)) { + reportAnalyzerProgress("Skipping bison-generated file\n"); + } else if (Opts->DisableAllChecks) { + + // Don't analyze if the user explicitly asked for no checks to be performed + // on this file. + reportAnalyzerProgress("All checks are disabled using a supplied option\n"); + } else { + // Otherwise, just run the analysis. + runAnalysisOnTranslationUnit(C); + } + + if (TUTotalTimer) TUTotalTimer->stopTimer(); + + // Count how many basic blocks we have not covered. + NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks(); + NumVisitedBlocksInAnalyzedFunctions = + FunctionSummaries.getTotalNumVisitedBasicBlocks(); + if (NumBlocksInAnalyzedFunctions > 0) + PercentReachableBlocks = + (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) / + NumBlocksInAnalyzedFunctions; + + // Explicitly destroy the PathDiagnosticConsumer. This will flush its output. + // FIXME: This should be replaced with something that doesn't rely on + // side-effects in PathDiagnosticConsumer's destructor. This is required when + // used with option -disable-free. + Mgr.reset(); +} + +std::string AnalysisConsumer::getFunctionName(const Decl *D) { + std::string Str; + llvm::raw_string_ostream OS(Str); + + if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + OS << FD->getQualifiedNameAsString(); + + // In C++, there are overloads. + if (Ctx->getLangOpts().CPlusPlus) { + OS << '('; + for (const auto &P : FD->parameters()) { + if (P != *FD->param_begin()) + OS << ", "; + OS << P->getType().getAsString(); + } + OS << ')'; + } + + } else if (isa<BlockDecl>(D)) { + PresumedLoc Loc = Ctx->getSourceManager().getPresumedLoc(D->getLocation()); + + if (Loc.isValid()) { + OS << "block (line: " << Loc.getLine() << ", col: " << Loc.getColumn() + << ')'; + } + + } else if (const ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(D)) { + + // FIXME: copy-pasted from CGDebugInfo.cpp. + OS << (OMD->isInstanceMethod() ? '-' : '+') << '['; + const DeclContext *DC = OMD->getDeclContext(); + if (const auto *OID = dyn_cast<ObjCImplementationDecl>(DC)) { + OS << OID->getName(); + } else if (const auto *OID = dyn_cast<ObjCInterfaceDecl>(DC)) { + OS << OID->getName(); + } else if (const auto *OC = dyn_cast<ObjCCategoryDecl>(DC)) { + if (OC->IsClassExtension()) { + OS << OC->getClassInterface()->getName(); + } else { + OS << OC->getIdentifier()->getNameStart() << '(' + << OC->getIdentifier()->getNameStart() << ')'; + } + } else if (const auto *OCD = dyn_cast<ObjCCategoryImplDecl>(DC)) { + OS << OCD->getClassInterface()->getName() << '(' + << OCD->getName() << ')'; + } else if (isa<ObjCProtocolDecl>(DC)) { + // We can extract the type of the class from the self pointer. + if (ImplicitParamDecl *SelfDecl = OMD->getSelfDecl()) { + QualType ClassTy = + cast<ObjCObjectPointerType>(SelfDecl->getType())->getPointeeType(); + ClassTy.print(OS, PrintingPolicy(LangOptions())); + } + } + OS << ' ' << OMD->getSelector().getAsString() << ']'; + + } + + return OS.str(); +} + +AnalysisConsumer::AnalysisMode +AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) { + if (!Opts->AnalyzeSpecificFunction.empty() && + getFunctionName(D) != Opts->AnalyzeSpecificFunction) + return AM_None; + + // Unless -analyze-all is specified, treat decls differently depending on + // where they came from: + // - Main source file: run both path-sensitive and non-path-sensitive checks. + // - Header files: run non-path-sensitive checks only. + // - System headers: don't run any checks. + SourceManager &SM = Ctx->getSourceManager(); + const Stmt *Body = D->getBody(); + SourceLocation SL = Body ? Body->getBeginLoc() : D->getLocation(); + SL = SM.getExpansionLoc(SL); + + if (!Opts->AnalyzeAll && !Mgr->isInCodeFile(SL)) { + if (SL.isInvalid() || SM.isInSystemHeader(SL)) + return AM_None; + return Mode & ~AM_Path; + } + + return Mode; +} + +void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode, + ExprEngine::InliningModes IMode, + SetOfConstDecls *VisitedCallees) { + if (!D->hasBody()) + return; + Mode = getModeForDecl(D, Mode); + if (Mode == AM_None) + return; + + // Clear the AnalysisManager of old AnalysisDeclContexts. + Mgr->ClearContexts(); + // Ignore autosynthesized code. + if (Mgr->getAnalysisDeclContext(D)->isBodyAutosynthesized()) + return; + + DisplayFunction(D, Mode, IMode); + CFG *DeclCFG = Mgr->getCFG(D); + if (DeclCFG) + MaxCFGSize.updateMax(DeclCFG->size()); + + BugReporter BR(*Mgr); + + if (Mode & AM_Syntax) + checkerMgr->runCheckersOnASTBody(D, *Mgr, BR); + if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) { + RunPathSensitiveChecks(D, IMode, VisitedCallees); + if (IMode != ExprEngine::Inline_Minimal) + NumFunctionsAnalyzed++; + } +} + +//===----------------------------------------------------------------------===// +// Path-sensitive checking. +//===----------------------------------------------------------------------===// + +void AnalysisConsumer::RunPathSensitiveChecks(Decl *D, + ExprEngine::InliningModes IMode, + SetOfConstDecls *VisitedCallees) { + // Construct the analysis engine. First check if the CFG is valid. + // FIXME: Inter-procedural analysis will need to handle invalid CFGs. + if (!Mgr->getCFG(D)) + return; + + // See if the LiveVariables analysis scales. + if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>()) + return; + + ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode); + + // Execute the worklist algorithm. + Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D), + Mgr->options.MaxNodesPerTopLevelFunction); + + if (!Mgr->options.DumpExplodedGraphTo.empty()) + Eng.DumpGraph(Mgr->options.TrimGraph, Mgr->options.DumpExplodedGraphTo); + + // Visualize the exploded graph. + if (Mgr->options.visualizeExplodedGraphWithGraphViz) + Eng.ViewGraph(Mgr->options.TrimGraph); + + // Display warnings. + Eng.getBugReporter().FlushReports(); +} + +//===----------------------------------------------------------------------===// +// AnalysisConsumer creation. +//===----------------------------------------------------------------------===// + +std::unique_ptr<AnalysisASTConsumer> +ento::CreateAnalysisConsumer(CompilerInstance &CI) { + // Disable the effects of '-Werror' when using the AnalysisConsumer. + CI.getPreprocessor().getDiagnostics().setWarningsAsErrors(false); + + AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts(); + bool hasModelPath = analyzerOpts->Config.count("model-path") > 0; + + return llvm::make_unique<AnalysisConsumer>( + CI, CI.getFrontendOpts().OutputFile, analyzerOpts, + CI.getFrontendOpts().Plugins, + hasModelPath ? new ModelInjector(CI) : nullptr); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp new file mode 100644 index 000000000000..1c31c35b75e4 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp @@ -0,0 +1,137 @@ +//===--- CheckerRegistration.cpp - Registration for the Analyzer Checkers -===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Defines the registration function for the analyzer checkers. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Support/FormattedStream.h" +#include "llvm/Support/raw_ostream.h" +#include <memory> + +using namespace clang; +using namespace ento; + +std::unique_ptr<CheckerManager> ento::createCheckerManager( + ASTContext &context, + AnalyzerOptions &opts, + ArrayRef<std::string> plugins, + ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns, + DiagnosticsEngine &diags) { + auto checkerMgr = llvm::make_unique<CheckerManager>(context, opts); + + CheckerRegistry allCheckers(plugins, diags); + + for (const auto &Fn : checkerRegistrationFns) + Fn(allCheckers); + + allCheckers.initializeManager(*checkerMgr, opts); + allCheckers.validateCheckerOptions(opts); + checkerMgr->finishedCheckerRegistration(); + + return checkerMgr; +} + +void ento::printCheckerHelp(raw_ostream &out, ArrayRef<std::string> plugins, + DiagnosticsEngine &diags) { + out << "OVERVIEW: Clang Static Analyzer Checkers List\n\n"; + out << "USAGE: -analyzer-checker <CHECKER or PACKAGE,...>\n\n"; + + CheckerRegistry(plugins, diags).printHelp(out); +} + +void ento::printEnabledCheckerList(raw_ostream &out, + ArrayRef<std::string> plugins, + const AnalyzerOptions &opts, + DiagnosticsEngine &diags) { + out << "OVERVIEW: Clang Static Analyzer Enabled Checkers List\n\n"; + + CheckerRegistry(plugins, diags).printList(out, opts); +} + +void ento::printAnalyzerConfigList(raw_ostream &out) { + out << "OVERVIEW: Clang Static Analyzer -analyzer-config Option List\n\n"; + out << "USAGE: clang -cc1 [CLANG_OPTIONS] -analyzer-config " + "<OPTION1=VALUE,OPTION2=VALUE,...>\n\n"; + out << " clang -cc1 [CLANG_OPTIONS] -analyzer-config OPTION1=VALUE, " + "-analyzer-config OPTION2=VALUE, ...\n\n"; + out << " clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang" + "<OPTION1=VALUE,OPTION2=VALUE,...>\n\n"; + out << " clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang " + "OPTION1=VALUE, -Xclang -analyzer-config -Xclang " + "OPTION2=VALUE, ...\n\n"; + out << "OPTIONS:\n\n"; + + using OptionAndDescriptionTy = std::pair<StringRef, std::string>; + OptionAndDescriptionTy PrintableOptions[] = { +#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \ + { \ + CMDFLAG, \ + llvm::Twine(llvm::Twine() + "(" + \ + (StringRef(#TYPE) == "StringRef" ? "string" : #TYPE ) + \ + ") " DESC \ + " (default: " #DEFAULT_VAL ")").str() \ + }, + +#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \ + SHALLOW_VAL, DEEP_VAL) \ + { \ + CMDFLAG, \ + llvm::Twine(llvm::Twine() + "(" + \ + (StringRef(#TYPE) == "StringRef" ? "string" : #TYPE ) + \ + ") " DESC \ + " (default: " #SHALLOW_VAL " in shallow mode, " #DEEP_VAL \ + " in deep mode)").str() \ + }, +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def" +#undef ANALYZER_OPTION +#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE + }; + + llvm::sort(PrintableOptions, [](const OptionAndDescriptionTy &LHS, + const OptionAndDescriptionTy &RHS) { + return LHS.first < RHS.first; + }); + + constexpr size_t MinLineWidth = 70; + constexpr size_t PadForOpt = 2; + constexpr size_t OptionWidth = 30; + constexpr size_t PadForDesc = PadForOpt + OptionWidth; + static_assert(MinLineWidth > PadForDesc, "MinLineWidth must be greater!"); + + llvm::formatted_raw_ostream FOut(out); + + for (const auto &Pair : PrintableOptions) { + FOut.PadToColumn(PadForOpt) << Pair.first; + + // If the buffer's length is greater then PadForDesc, print a newline. + if (FOut.getColumn() > PadForDesc) + FOut << '\n'; + + FOut.PadToColumn(PadForDesc); + + for (char C : Pair.second) { + if (FOut.getColumn() > MinLineWidth && C == ' ') { + FOut << '\n'; + FOut.PadToColumn(PadForDesc); + continue; + } + FOut << C; + } + FOut << "\n\n"; + } +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp new file mode 100644 index 000000000000..620c0e588906 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp @@ -0,0 +1,247 @@ +//===- CheckerRegistry.cpp - Maintains all available checkers -------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/Basic/LLVM.h" +#include "clang/Frontend/FrontendDiagnostic.h" +#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" +#include "clang/StaticAnalyzer/Core/CheckerManager.h" +#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/SetVector.h" +#include "llvm/ADT/StringMap.h" +#include "llvm/ADT/StringRef.h" +#include "llvm/Support/DynamicLibrary.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/raw_ostream.h" +#include <algorithm> + +using namespace clang; +using namespace ento; +using llvm::sys::DynamicLibrary; + +using RegisterCheckersFn = void (*)(CheckerRegistry &); + +static bool isCompatibleAPIVersion(const char *versionString) { + // If the version string is null, it's not an analyzer plugin. + if (!versionString) + return false; + + // For now, none of the static analyzer API is considered stable. + // Versions must match exactly. + return strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0; +} + +CheckerRegistry::CheckerRegistry(ArrayRef<std::string> plugins, + DiagnosticsEngine &diags) : Diags(diags) { +#define GET_CHECKERS +#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \ + addChecker(register##CLASS, FULLNAME, HELPTEXT, DOC_URI); +#include "clang/StaticAnalyzer/Checkers/Checkers.inc" +#undef CHECKER +#undef GET_CHECKERS + + for (ArrayRef<std::string>::iterator i = plugins.begin(), e = plugins.end(); + i != e; ++i) { + // Get access to the plugin. + std::string err; + DynamicLibrary lib = DynamicLibrary::getPermanentLibrary(i->c_str(), &err); + if (!lib.isValid()) { + diags.Report(diag::err_fe_unable_to_load_plugin) << *i << err; + continue; + } + + // See if it's compatible with this build of clang. + const char *pluginAPIVersion = + (const char *) lib.getAddressOfSymbol("clang_analyzerAPIVersionString"); + if (!isCompatibleAPIVersion(pluginAPIVersion)) { + Diags.Report(diag::warn_incompatible_analyzer_plugin_api) + << llvm::sys::path::filename(*i); + Diags.Report(diag::note_incompatible_analyzer_plugin_api) + << CLANG_ANALYZER_API_VERSION_STRING + << pluginAPIVersion; + continue; + } + + // Register its checkers. + RegisterCheckersFn registerPluginCheckers = + (RegisterCheckersFn) (intptr_t) lib.getAddressOfSymbol( + "clang_registerCheckers"); + if (registerPluginCheckers) + registerPluginCheckers(*this); + } +} + +static constexpr char PackageSeparator = '.'; + +static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a, + const CheckerRegistry::CheckerInfo &b) { + return a.FullName < b.FullName; +} + +static bool isInPackage(const CheckerRegistry::CheckerInfo &checker, + StringRef packageName) { + // Does the checker's full name have the package as a prefix? + if (!checker.FullName.startswith(packageName)) + return false; + + // Is the package actually just the name of a specific checker? + if (checker.FullName.size() == packageName.size()) + return true; + + // Is the checker in the package (or a subpackage)? + if (checker.FullName[packageName.size()] == PackageSeparator) + return true; + + return false; +} + +CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers( + const AnalyzerOptions &Opts) const { + + assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) && + "In order to efficiently gather checkers, this function expects them " + "to be already sorted!"); + + CheckerInfoSet enabledCheckers; + const auto end = Checkers.cend(); + + for (const std::pair<std::string, bool> &opt : Opts.CheckersControlList) { + // Use a binary search to find the possible start of the package. + CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.first, "", ""); + auto firstRelatedChecker = + std::lower_bound(Checkers.cbegin(), end, packageInfo, checkerNameLT); + + if (firstRelatedChecker == end || + !isInPackage(*firstRelatedChecker, opt.first)) { + Diags.Report(diag::err_unknown_analyzer_checker) << opt.first; + Diags.Report(diag::note_suggest_disabling_all_checkers); + return {}; + } + + // See how large the package is. + // If the package doesn't exist, assume the option refers to a single + // checker. + size_t size = 1; + llvm::StringMap<size_t>::const_iterator packageSize = + Packages.find(opt.first); + if (packageSize != Packages.end()) + size = packageSize->getValue(); + + // Step through all the checkers in the package. + for (auto lastRelatedChecker = firstRelatedChecker+size; + firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker) + if (opt.second) + enabledCheckers.insert(&*firstRelatedChecker); + else + enabledCheckers.remove(&*firstRelatedChecker); + } + + return enabledCheckers; +} + +void CheckerRegistry::addChecker(InitializationFunction Fn, StringRef Name, + StringRef Desc, StringRef DocsUri) { + Checkers.emplace_back(Fn, Name, Desc, DocsUri); + + // Record the presence of the checker in its packages. + StringRef packageName, leafName; + std::tie(packageName, leafName) = Name.rsplit(PackageSeparator); + while (!leafName.empty()) { + Packages[packageName] += 1; + std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator); + } +} + +void CheckerRegistry::initializeManager(CheckerManager &checkerMgr, + const AnalyzerOptions &Opts) const { + // Sort checkers for efficient collection. + llvm::sort(Checkers, checkerNameLT); + + // Collect checkers enabled by the options. + CheckerInfoSet enabledCheckers = getEnabledCheckers(Opts); + + // Initialize the CheckerManager with all enabled checkers. + for (const auto *i : enabledCheckers) { + checkerMgr.setCurrentCheckName(CheckName(i->FullName)); + i->Initialize(checkerMgr); + } +} + +void CheckerRegistry::validateCheckerOptions( + const AnalyzerOptions &opts) const { + for (const auto &config : opts.Config) { + size_t pos = config.getKey().find(':'); + if (pos == StringRef::npos) + continue; + + bool hasChecker = false; + StringRef checkerName = config.getKey().substr(0, pos); + for (const auto &checker : Checkers) { + if (checker.FullName.startswith(checkerName) && + (checker.FullName.size() == pos || checker.FullName[pos] == '.')) { + hasChecker = true; + break; + } + } + if (!hasChecker) + Diags.Report(diag::err_unknown_analyzer_checker) << checkerName; + } +} + +void CheckerRegistry::printHelp(raw_ostream &out, + size_t maxNameChars) const { + // FIXME: Alphabetical sort puts 'experimental' in the middle. + // Would it be better to name it '~experimental' or something else + // that's ASCIIbetically last? + llvm::sort(Checkers, checkerNameLT); + + // FIXME: Print available packages. + + out << "CHECKERS:\n"; + + // Find the maximum option length. + size_t optionFieldWidth = 0; + for (const auto &i : Checkers) { + // Limit the amount of padding we are willing to give up for alignment. + // Package.Name Description [Hidden] + size_t nameLength = i.FullName.size(); + if (nameLength <= maxNameChars) + optionFieldWidth = std::max(optionFieldWidth, nameLength); + } + + const size_t initialPad = 2; + for (const auto &i : Checkers) { + out.indent(initialPad) << i.FullName; + + int pad = optionFieldWidth - i.FullName.size(); + + // Break on long option names. + if (pad < 0) { + out << '\n'; + pad = optionFieldWidth + initialPad; + } + out.indent(pad + 2) << i.Desc; + + out << '\n'; + } +} + +void CheckerRegistry::printList(raw_ostream &out, + const AnalyzerOptions &opts) const { + // Sort checkers for efficient collection. + llvm::sort(Checkers, checkerNameLT); + + // Collect checkers enabled by the options. + CheckerInfoSet enabledCheckers = getEnabledCheckers(opts); + + for (const auto *i : enabledCheckers) + out << i->FullName << '\n'; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp new file mode 100644 index 000000000000..b33608042ce3 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp @@ -0,0 +1,28 @@ +//===--- FrontendActions.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" +#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h" +#include "clang/StaticAnalyzer/Frontend/ModelConsumer.h" +using namespace clang; +using namespace ento; + +std::unique_ptr<ASTConsumer> +AnalysisAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) { + return CreateAnalysisConsumer(CI); +} + +ParseModelFileAction::ParseModelFileAction(llvm::StringMap<Stmt *> &Bodies) + : Bodies(Bodies) {} + +std::unique_ptr<ASTConsumer> +ParseModelFileAction::CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return llvm::make_unique<ModelConsumer>(Bodies); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp new file mode 100644 index 000000000000..60825ef7411d --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp @@ -0,0 +1,42 @@ +//===--- ModelConsumer.cpp - ASTConsumer for consuming model files --------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file implements an ASTConsumer for consuming model files. +/// +/// This ASTConsumer handles the AST of a parsed model file. All top level +/// function definitions will be collected from that model file for later +/// retrieval during the static analysis. The body of these functions will not +/// be injected into the ASTUnit of the analyzed translation unit. It will be +/// available through the BodyFarm which is utilized by the AnalysisDeclContext +/// class. +/// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Frontend/ModelConsumer.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclGroup.h" + +using namespace clang; +using namespace ento; + +ModelConsumer::ModelConsumer(llvm::StringMap<Stmt *> &Bodies) + : Bodies(Bodies) {} + +bool ModelConsumer::HandleTopLevelDecl(DeclGroupRef D) { + for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) { + + // Only interested in definitions. + const FunctionDecl *func = llvm::dyn_cast<FunctionDecl>(*I); + if (func && func->hasBody()) { + Bodies.insert(std::make_pair(func->getName(), func->getBody())); + } + } + return true; +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp new file mode 100644 index 000000000000..b1927c8401d6 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp @@ -0,0 +1,115 @@ +//===-- ModelInjector.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "ModelInjector.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/IdentifierTable.h" +#include "clang/Basic/Stack.h" +#include "clang/Frontend/ASTUnit.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendAction.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Serialization/ASTReader.h" +#include "clang/StaticAnalyzer/Frontend/FrontendActions.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/Support/CrashRecoveryContext.h" +#include "llvm/Support/FileSystem.h" +#include <utility> + +using namespace clang; +using namespace ento; + +ModelInjector::ModelInjector(CompilerInstance &CI) : CI(CI) {} + +Stmt *ModelInjector::getBody(const FunctionDecl *D) { + onBodySynthesis(D); + return Bodies[D->getName()]; +} + +Stmt *ModelInjector::getBody(const ObjCMethodDecl *D) { + onBodySynthesis(D); + return Bodies[D->getName()]; +} + +void ModelInjector::onBodySynthesis(const NamedDecl *D) { + + // FIXME: what about overloads? Declarations can be used as keys but what + // about file name index? Mangled names may not be suitable for that either. + if (Bodies.count(D->getName()) != 0) + return; + + SourceManager &SM = CI.getSourceManager(); + FileID mainFileID = SM.getMainFileID(); + + AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts(); + llvm::StringRef modelPath = analyzerOpts->ModelPath; + + llvm::SmallString<128> fileName; + + if (!modelPath.empty()) + fileName = + llvm::StringRef(modelPath.str() + "/" + D->getName().str() + ".model"); + else + fileName = llvm::StringRef(D->getName().str() + ".model"); + + if (!llvm::sys::fs::exists(fileName.str())) { + Bodies[D->getName()] = nullptr; + return; + } + + auto Invocation = std::make_shared<CompilerInvocation>(CI.getInvocation()); + + FrontendOptions &FrontendOpts = Invocation->getFrontendOpts(); + InputKind IK = InputKind::CXX; // FIXME + FrontendOpts.Inputs.clear(); + FrontendOpts.Inputs.emplace_back(fileName, IK); + FrontendOpts.DisableFree = true; + + Invocation->getDiagnosticOpts().VerifyDiagnostics = 0; + + // Modules are parsed by a separate CompilerInstance, so this code mimics that + // behavior for models + CompilerInstance Instance(CI.getPCHContainerOperations()); + Instance.setInvocation(std::move(Invocation)); + Instance.createDiagnostics( + new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()), + /*ShouldOwnClient=*/true); + + Instance.getDiagnostics().setSourceManager(&SM); + + Instance.setVirtualFileSystem(&CI.getVirtualFileSystem()); + + // The instance wants to take ownership, however DisableFree frontend option + // is set to true to avoid double free issues + Instance.setFileManager(&CI.getFileManager()); + Instance.setSourceManager(&SM); + Instance.setPreprocessor(CI.getPreprocessorPtr()); + Instance.setASTContext(&CI.getASTContext()); + + Instance.getPreprocessor().InitializeForModelFile(); + + ParseModelFileAction parseModelFile(Bodies); + + llvm::CrashRecoveryContext CRC; + + CRC.RunSafelyOnThread([&]() { Instance.ExecuteAction(parseModelFile); }, + DesiredStackSize); + + Instance.getPreprocessor().FinalizeForModelFile(); + + Instance.resetAndLeakSourceManager(); + Instance.resetAndLeakFileManager(); + Instance.resetAndLeakPreprocessor(); + + // The preprocessor enters to the main file id when parsing is started, so + // the main file id is changed to the model file during parsing and it needs + // to be reset to the former main file id after parsing of the model file + // is done. + SM.setMainFileID(mainFileID); +} diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h new file mode 100644 index 000000000000..b1b6de9ef9d9 --- /dev/null +++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h @@ -0,0 +1,70 @@ +//===-- ModelInjector.h -----------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// This file defines the clang::ento::ModelInjector class which implements the +/// clang::CodeInjector interface. This class is responsible for injecting +/// function definitions that were synthesized from model files. +/// +/// Model files allow definitions of functions to be lazily constituted for functions +/// which lack bodies in the original source code. This allows the analyzer +/// to more precisely analyze code that calls such functions, analyzing the +/// artificial definitions (which typically approximate the semantics of the +/// called function) when called by client code. These definitions are +/// reconstituted lazily, on-demand, by the static analyzer engine. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_SA_FRONTEND_MODELINJECTOR_H +#define LLVM_CLANG_SA_FRONTEND_MODELINJECTOR_H + +#include "clang/Analysis/CodeInjector.h" +#include "llvm/ADT/StringMap.h" + +namespace clang { + +class CompilerInstance; +class ASTUnit; +class ASTReader; +class NamedDecl; +class Module; + +namespace ento { +class ModelInjector : public CodeInjector { +public: + ModelInjector(CompilerInstance &CI); + Stmt *getBody(const FunctionDecl *D) override; + Stmt *getBody(const ObjCMethodDecl *D) override; + +private: + /// Synthesize a body for a declaration + /// + /// This method first looks up the appropriate model file based on the + /// model-path configuration option and the name of the declaration that is + /// looked up. If no model were synthesized yet for a function with that name + /// it will create a new compiler instance to parse the model file using the + /// ASTContext, Preprocessor, SourceManager of the original compiler instance. + /// The former resources are shared between the two compiler instance, so the + /// newly created instance have to "leak" these objects, since they are owned + /// by the original instance. + /// + /// The model-path should be either an absolute path or relative to the + /// working directory of the compiler. + void onBodySynthesis(const NamedDecl *D); + + CompilerInstance &CI; + + // FIXME: double memoization is redundant, with memoization both here and in + // BodyFarm. + llvm::StringMap<Stmt *> Bodies; +}; +} +} + +#endif |