From: Ted Kremenek Date: Thu, 23 Dec 2010 19:38:26 +0000 (+0000) Subject: Chris Lattner has strong opinions about directory X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=21142581d55918beed544a757e4af3bb865b1812;p=clang Chris Lattner has strong opinions about directory layout. :) Rename the 'EntoSA' directories to 'StaticAnalyzer'. Internally we will still use the 'ento' namespace for the analyzer engine (unless there are further sabre rattlings...). git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@122514 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/examples/PrintFunctionNames/CMakeLists.txt b/examples/PrintFunctionNames/CMakeLists.txt index ee451877c6..edfbcb5ce1 100644 --- a/examples/PrintFunctionNames/CMakeLists.txt +++ b/examples/PrintFunctionNames/CMakeLists.txt @@ -10,8 +10,8 @@ set( LLVM_USED_LIBS clangCodeGen clangParse clangSema - clangEntoCheckers - clangEntoCore + clangStaticAnalyzerCheckers + clangStaticAnalyzerCore clangAnalysis clangIndex clangRewrite diff --git a/examples/clang-interpreter/CMakeLists.txt b/examples/clang-interpreter/CMakeLists.txt index c8c3c2be2d..21c5afdc81 100644 --- a/examples/clang-interpreter/CMakeLists.txt +++ b/examples/clang-interpreter/CMakeLists.txt @@ -6,8 +6,8 @@ set(LLVM_USED_LIBS clangDriver clangCodeGen clangSema - clangEntoCheckers - clangEntoCore + clangStaticAnalyzerCheckers + clangStaticAnalyzerCore clangIndex clangAnalysis clangRewrite diff --git a/examples/clang-interpreter/Makefile b/examples/clang-interpreter/Makefile index 86bc9ca142..24a2be864d 100644 --- a/examples/clang-interpreter/Makefile +++ b/examples/clang-interpreter/Makefile @@ -18,7 +18,7 @@ TOOL_NO_EXPORTS = 1 LINK_COMPONENTS := jit interpreter nativecodegen bitreader bitwriter ipo \ selectiondag asmparser USEDLIBS = clangFrontend.a clangSerialization.a clangDriver.a clangCodeGen.a \ - clangSema.a clangEntoCheckers.a clangEntoCore.a clangAnalysis.a clangRewrite.a \ + clangSema.a clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a clangAnalysis.a clangRewrite.a \ clangAST.a clangParse.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/Makefile diff --git a/examples/wpa/CMakeLists.txt b/examples/wpa/CMakeLists.txt index 0100aa2b98..e3aaf1d919 100644 --- a/examples/wpa/CMakeLists.txt +++ b/examples/wpa/CMakeLists.txt @@ -7,8 +7,8 @@ set(LLVM_USED_LIBS clangSema clangAnalysis clangSerialization - clangEntoCheckers - clangEntoCore + clangStaticAnalyzerCheckers + clangStaticAnalyzerCore clangRewrite clangAST clangParse diff --git a/examples/wpa/Makefile b/examples/wpa/Makefile index 4fbba9d30f..af0f843149 100644 --- a/examples/wpa/Makefile +++ b/examples/wpa/Makefile @@ -16,7 +16,7 @@ NO_INSTALL = 1 TOOL_NO_EXPORTS = 1 LINK_COMPONENTS := asmparser bitreader mc core -USEDLIBS = clangEntoCheckers.a clangEntoCore.a clangIndex.a clangFrontend.a clangDriver.a \ +USEDLIBS = clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a clangIndex.a clangFrontend.a clangDriver.a \ clangSema.a clangAnalysis.a clangSerialization.a \ clangAST.a clangParse.a clangLex.a clangBasic.a diff --git a/include/clang/EntoSA/AnalysisConsumer.h b/include/clang/StaticAnalyzer/AnalysisConsumer.h similarity index 100% rename from include/clang/EntoSA/AnalysisConsumer.h rename to include/clang/StaticAnalyzer/AnalysisConsumer.h diff --git a/include/clang/EntoSA/BugReporter/BugReporter.h b/include/clang/StaticAnalyzer/BugReporter/BugReporter.h similarity index 99% rename from include/clang/EntoSA/BugReporter/BugReporter.h rename to include/clang/StaticAnalyzer/BugReporter/BugReporter.h index c879d3c94e..a5b1980d1e 100644 --- a/include/clang/EntoSA/BugReporter/BugReporter.h +++ b/include/clang/StaticAnalyzer/BugReporter/BugReporter.h @@ -16,7 +16,7 @@ #define LLVM_CLANG_GR_BUGREPORTER #include "clang/Basic/SourceLocation.h" -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/ImmutableSet.h" diff --git a/include/clang/EntoSA/BugReporter/BugType.h b/include/clang/StaticAnalyzer/BugReporter/BugType.h similarity index 97% rename from include/clang/EntoSA/BugReporter/BugType.h rename to include/clang/StaticAnalyzer/BugReporter/BugType.h index 1eee141964..6e6ec54918 100644 --- a/include/clang/EntoSA/BugReporter/BugType.h +++ b/include/clang/StaticAnalyzer/BugReporter/BugType.h @@ -14,7 +14,7 @@ #ifndef LLVM_CLANG_ANALYSIS_BUGTYPE #define LLVM_CLANG_ANALYSIS_BUGTYPE -#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" #include "llvm/ADT/FoldingSet.h" #include diff --git a/include/clang/EntoSA/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/BugReporter/PathDiagnostic.h similarity index 100% rename from include/clang/EntoSA/BugReporter/PathDiagnostic.h rename to include/clang/StaticAnalyzer/BugReporter/PathDiagnostic.h diff --git a/include/clang/EntoSA/Checkers/DereferenceChecker.h b/include/clang/StaticAnalyzer/Checkers/DereferenceChecker.h similarity index 100% rename from include/clang/EntoSA/Checkers/DereferenceChecker.h rename to include/clang/StaticAnalyzer/Checkers/DereferenceChecker.h diff --git a/include/clang/EntoSA/Checkers/LocalCheckers.h b/include/clang/StaticAnalyzer/Checkers/LocalCheckers.h similarity index 100% rename from include/clang/EntoSA/Checkers/LocalCheckers.h rename to include/clang/StaticAnalyzer/Checkers/LocalCheckers.h diff --git a/include/clang/StaticAnalyzer/EntoSA/AnalysisConsumer.h b/include/clang/StaticAnalyzer/EntoSA/AnalysisConsumer.h new file mode 100644 index 0000000000..86f6c727a4 --- /dev/null +++ b/include/clang/StaticAnalyzer/EntoSA/AnalysisConsumer.h @@ -0,0 +1,39 @@ +//===--- AnalysisConsumer.h - Front-end Analysis Engine Hooks ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This header contains the functions necessary for a front-end to run various +// analyses. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_ANALYSISCONSUMER_H +#define LLVM_CLANG_GR_ANALYSISCONSUMER_H + +#include + +namespace clang { + +class AnalyzerOptions; +class ASTConsumer; +class Preprocessor; + +namespace ento { + +/// CreateAnalysisConsumer - Creates an ASTConsumer to run various code +/// analysis passes. (The set of analyses run is controlled by command-line +/// options.) +ASTConsumer* CreateAnalysisConsumer(const Preprocessor &pp, + const std::string &output, + const AnalyzerOptions& Opts); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/EntoSA/BugReporter/BugReporter.h b/include/clang/StaticAnalyzer/EntoSA/BugReporter/BugReporter.h new file mode 100644 index 0000000000..a5b1980d1e --- /dev/null +++ b/include/clang/StaticAnalyzer/EntoSA/BugReporter/BugReporter.h @@ -0,0 +1,486 @@ +//===--- BugReporter.h - Generate PathDiagnostics --------------*- 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 BugReporter, a utility class for generating +// PathDiagnostics for analyses based on GRState. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_BUGREPORTER +#define LLVM_CLANG_GR_BUGREPORTER + +#include "clang/Basic/SourceLocation.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/ADT/SmallSet.h" +#include + +namespace clang { + +class ASTContext; +class Diagnostic; +class Stmt; +class ParentMap; + +namespace ento { + +class PathDiagnostic; +class PathDiagnosticPiece; +class PathDiagnosticClient; +class ExplodedNode; +class ExplodedGraph; +class BugReporter; +class BugReporterContext; +class ExprEngine; +class GRState; +class BugType; + +//===----------------------------------------------------------------------===// +// Interface for individual bug reports. +//===----------------------------------------------------------------------===// + +class BugReporterVisitor : public llvm::FoldingSetNode { +public: + virtual ~BugReporterVisitor(); + virtual PathDiagnosticPiece* VisitNode(const ExplodedNode* N, + const ExplodedNode* PrevN, + BugReporterContext& BRC) = 0; + + virtual bool isOwnedByReporterContext() { return true; } + virtual void Profile(llvm::FoldingSetNodeID &ID) const = 0; +}; + +// FIXME: Combine this with RangedBugReport and remove RangedBugReport. +class BugReport : public BugReporterVisitor { +protected: + BugType& BT; + std::string ShortDescription; + std::string Description; + const ExplodedNode *ErrorNode; + mutable SourceRange R; + +protected: + friend class BugReporter; + friend class BugReportEquivClass; + + virtual void Profile(llvm::FoldingSetNodeID& hash) const { + hash.AddInteger(getLocation().getRawEncoding()); + hash.AddString(Description); + } + +public: + class NodeResolver { + public: + virtual ~NodeResolver() {} + virtual const ExplodedNode* + getOriginalNode(const ExplodedNode* N) = 0; + }; + + BugReport(BugType& bt, llvm::StringRef desc, const ExplodedNode *errornode) + : BT(bt), Description(desc), ErrorNode(errornode) {} + + BugReport(BugType& bt, llvm::StringRef shortDesc, llvm::StringRef desc, + const ExplodedNode *errornode) + : BT(bt), ShortDescription(shortDesc), Description(desc), + ErrorNode(errornode) {} + + virtual ~BugReport(); + + virtual bool isOwnedByReporterContext() { return false; } + + const BugType& getBugType() const { return BT; } + BugType& getBugType() { return BT; } + + // FIXME: Perhaps this should be moved into a subclass? + const ExplodedNode* getErrorNode() const { return ErrorNode; } + + // FIXME: Do we need this? Maybe getLocation() should return a ProgramPoint + // object. + // FIXME: If we do need it, we can probably just make it private to + // BugReporter. + const Stmt* getStmt() const; + + const llvm::StringRef getDescription() const { return Description; } + + const llvm::StringRef getShortDescription() const { + return ShortDescription.empty() ? Description : ShortDescription; + } + + // FIXME: Is this needed? + virtual std::pair getExtraDescriptiveText() { + return std::make_pair((const char**)0,(const char**)0); + } + + // FIXME: Perhaps move this into a subclass. + virtual PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, + const ExplodedNode* N); + + /// getLocation - Return the "definitive" location of the reported bug. + /// While a bug can span an entire path, usually there is a specific + /// location that can be used to identify where the key issue occured. + /// This location is used by clients rendering diagnostics. + virtual SourceLocation getLocation() const; + + typedef const SourceRange *ranges_iterator; + + /// getRanges - Returns the source ranges associated with this bug. + virtual std::pair getRanges() const; + + virtual PathDiagnosticPiece* VisitNode(const ExplodedNode* N, + const ExplodedNode* PrevN, + BugReporterContext& BR); + + virtual void registerInitialVisitors(BugReporterContext& BRC, + const ExplodedNode* N) {} +}; + +//===----------------------------------------------------------------------===// +// BugTypes (collections of related reports). +//===----------------------------------------------------------------------===// + +class BugReportEquivClass : public llvm::FoldingSetNode { + // List of *owned* BugReport objects. + std::list Reports; + + friend class BugReporter; + void AddReport(BugReport* R) { Reports.push_back(R); } +public: + BugReportEquivClass(BugReport* R) { Reports.push_back(R); } + ~BugReportEquivClass(); + + void Profile(llvm::FoldingSetNodeID& ID) const { + assert(!Reports.empty()); + (*Reports.begin())->Profile(ID); + } + + class iterator { + std::list::iterator impl; + public: + iterator(std::list::iterator i) : impl(i) {} + iterator& operator++() { ++impl; return *this; } + bool operator==(const iterator& I) const { return I.impl == impl; } + bool operator!=(const iterator& I) const { return I.impl != impl; } + BugReport* operator*() const { return *impl; } + BugReport* operator->() const { return *impl; } + }; + + class const_iterator { + std::list::const_iterator impl; + public: + const_iterator(std::list::const_iterator i) : impl(i) {} + const_iterator& operator++() { ++impl; return *this; } + bool operator==(const const_iterator& I) const { return I.impl == impl; } + bool operator!=(const const_iterator& I) const { return I.impl != impl; } + const BugReport* operator*() const { return *impl; } + const BugReport* operator->() const { return *impl; } + }; + + iterator begin() { return iterator(Reports.begin()); } + iterator end() { return iterator(Reports.end()); } + + const_iterator begin() const { return const_iterator(Reports.begin()); } + const_iterator end() const { return const_iterator(Reports.end()); } +}; + + +//===----------------------------------------------------------------------===// +// Specialized subclasses of BugReport. +//===----------------------------------------------------------------------===// + +// FIXME: Collapse this with the default BugReport class. +class RangedBugReport : public BugReport { + llvm::SmallVector Ranges; +public: + RangedBugReport(BugType& D, llvm::StringRef description, + ExplodedNode *errornode) + : BugReport(D, description, errornode) {} + + RangedBugReport(BugType& D, llvm::StringRef shortDescription, + llvm::StringRef description, ExplodedNode *errornode) + : BugReport(D, shortDescription, description, errornode) {} + + ~RangedBugReport(); + + // FIXME: Move this out of line. + void addRange(SourceRange R) { + assert(R.isValid()); + Ranges.push_back(R); + } + + virtual std::pair getRanges() const { + return std::make_pair(Ranges.begin(), Ranges.end()); + } +}; + +class EnhancedBugReport : public RangedBugReport { +public: + typedef void (*VisitorCreator)(BugReporterContext &BRcC, const void *data, + const ExplodedNode *N); + +private: + typedef std::vector > Creators; + Creators creators; + +public: + EnhancedBugReport(BugType& D, llvm::StringRef description, + ExplodedNode *errornode) + : RangedBugReport(D, description, errornode) {} + + EnhancedBugReport(BugType& D, llvm::StringRef shortDescription, + llvm::StringRef description, ExplodedNode *errornode) + : RangedBugReport(D, shortDescription, description, errornode) {} + + ~EnhancedBugReport() {} + + void registerInitialVisitors(BugReporterContext& BRC, const ExplodedNode* N) { + for (Creators::iterator I = creators.begin(), E = creators.end(); I!=E; ++I) + I->first(BRC, I->second, N); + } + + void addVisitorCreator(VisitorCreator creator, const void *data) { + creators.push_back(std::make_pair(creator, data)); + } +}; + +//===----------------------------------------------------------------------===// +// BugReporter and friends. +//===----------------------------------------------------------------------===// + +class BugReporterData { +public: + virtual ~BugReporterData(); + virtual Diagnostic& getDiagnostic() = 0; + virtual PathDiagnosticClient* getPathDiagnosticClient() = 0; + virtual ASTContext& getASTContext() = 0; + virtual SourceManager& getSourceManager() = 0; +}; + +class BugReporter { +public: + enum Kind { BaseBRKind, GRBugReporterKind }; + +private: + typedef llvm::ImmutableSet BugTypesTy; + BugTypesTy::Factory F; + BugTypesTy BugTypes; + + const Kind kind; + BugReporterData& D; + + void FlushReport(BugReportEquivClass& EQ); + +protected: + BugReporter(BugReporterData& d, Kind k) : BugTypes(F.getEmptySet()), kind(k), + D(d) {} + +public: + BugReporter(BugReporterData& d) : BugTypes(F.getEmptySet()), kind(BaseBRKind), + D(d) {} + virtual ~BugReporter(); + + void FlushReports(); + + Kind getKind() const { return kind; } + + Diagnostic& getDiagnostic() { + return D.getDiagnostic(); + } + + PathDiagnosticClient* getPathDiagnosticClient() { + return D.getPathDiagnosticClient(); + } + + typedef BugTypesTy::iterator iterator; + iterator begin() { return BugTypes.begin(); } + iterator end() { return BugTypes.end(); } + + ASTContext& getContext() { return D.getASTContext(); } + + SourceManager& getSourceManager() { return D.getSourceManager(); } + + virtual void GeneratePathDiagnostic(PathDiagnostic& pathDiagnostic, + llvm::SmallVectorImpl &bugReports) {} + + void Register(BugType *BT); + + void EmitReport(BugReport *R); + + void EmitBasicReport(llvm::StringRef BugName, llvm::StringRef BugStr, + SourceLocation Loc, + SourceRange* RangeBeg, unsigned NumRanges); + + void EmitBasicReport(llvm::StringRef BugName, llvm::StringRef BugCategory, + llvm::StringRef BugStr, SourceLocation Loc, + SourceRange* RangeBeg, unsigned NumRanges); + + + void EmitBasicReport(llvm::StringRef BugName, llvm::StringRef BugStr, + SourceLocation Loc) { + EmitBasicReport(BugName, BugStr, Loc, 0, 0); + } + + void EmitBasicReport(llvm::StringRef BugName, llvm::StringRef BugCategory, + llvm::StringRef BugStr, SourceLocation Loc) { + EmitBasicReport(BugName, BugCategory, BugStr, Loc, 0, 0); + } + + void EmitBasicReport(llvm::StringRef BugName, llvm::StringRef BugStr, + SourceLocation Loc, SourceRange R) { + EmitBasicReport(BugName, BugStr, Loc, &R, 1); + } + + void EmitBasicReport(llvm::StringRef BugName, llvm::StringRef Category, + llvm::StringRef BugStr, SourceLocation Loc, + SourceRange R) { + EmitBasicReport(BugName, Category, BugStr, Loc, &R, 1); + } + + static bool classof(const BugReporter* R) { return true; } +}; + +// FIXME: Get rid of GRBugReporter. It's the wrong abstraction. +class GRBugReporter : public BugReporter { + ExprEngine& Eng; + llvm::SmallSet NotableSymbols; +public: + GRBugReporter(BugReporterData& d, ExprEngine& eng) + : BugReporter(d, GRBugReporterKind), Eng(eng) {} + + virtual ~GRBugReporter(); + + /// getEngine - Return the analysis engine used to analyze a given + /// function or method. + ExprEngine &getEngine() { return Eng; } + + /// getGraph - Get the exploded graph created by the analysis engine + /// for the analyzed method or function. + ExplodedGraph &getGraph(); + + /// getStateManager - Return the state manager used by the analysis + /// engine. + GRStateManager &getStateManager(); + + virtual void GeneratePathDiagnostic(PathDiagnostic &pathDiagnostic, + llvm::SmallVectorImpl &bugReports); + + void addNotableSymbol(SymbolRef Sym) { + NotableSymbols.insert(Sym); + } + + bool isNotable(SymbolRef Sym) const { + return (bool) NotableSymbols.count(Sym); + } + + /// classof - Used by isa<>, cast<>, and dyn_cast<>. + static bool classof(const BugReporter* R) { + return R->getKind() == GRBugReporterKind; + } +}; + +class BugReporterContext { + GRBugReporter &BR; + // Not the most efficient data structure, but we use an ImmutableList for the + // Callbacks because it is safe to make additions to list during iteration. + llvm::ImmutableList::Factory F; + llvm::ImmutableList Callbacks; + llvm::FoldingSet CallbacksSet; +public: + BugReporterContext(GRBugReporter& br) : BR(br), Callbacks(F.getEmptyList()) {} + virtual ~BugReporterContext(); + + void addVisitor(BugReporterVisitor* visitor); + + typedef llvm::ImmutableList::iterator visitor_iterator; + visitor_iterator visitor_begin() { return Callbacks.begin(); } + visitor_iterator visitor_end() { return Callbacks.end(); } + + GRBugReporter& getBugReporter() { return BR; } + + ExplodedGraph &getGraph() { return BR.getGraph(); } + + void addNotableSymbol(SymbolRef Sym) { + // FIXME: For now forward to GRBugReporter. + BR.addNotableSymbol(Sym); + } + + bool isNotable(SymbolRef Sym) const { + // FIXME: For now forward to GRBugReporter. + return BR.isNotable(Sym); + } + + GRStateManager& getStateManager() { + return BR.getStateManager(); + } + + SValBuilder& getSValBuilder() { + return getStateManager().getSValBuilder(); + } + + ASTContext& getASTContext() { + return BR.getContext(); + } + + SourceManager& getSourceManager() { + return BR.getSourceManager(); + } + + virtual BugReport::NodeResolver& getNodeResolver() = 0; +}; + +class DiagBugReport : public RangedBugReport { + std::list Strs; + FullSourceLoc L; +public: + DiagBugReport(BugType& D, llvm::StringRef desc, FullSourceLoc l) : + RangedBugReport(D, desc, 0), L(l) {} + + virtual ~DiagBugReport() {} + + // FIXME: Move out-of-line (virtual function). + SourceLocation getLocation() const { return L; } + + void addString(llvm::StringRef s) { Strs.push_back(s); } + + typedef std::list::const_iterator str_iterator; + str_iterator str_begin() const { return Strs.begin(); } + str_iterator str_end() const { return Strs.end(); } +}; + +//===----------------------------------------------------------------------===// +//===----------------------------------------------------------------------===// + +namespace bugreporter { + +const Stmt *GetDerefExpr(const ExplodedNode *N); +const Stmt *GetDenomExpr(const ExplodedNode *N); +const Stmt *GetCalleeExpr(const ExplodedNode *N); +const Stmt *GetRetValExpr(const ExplodedNode *N); + +void registerTrackNullOrUndefValue(BugReporterContext& BRC, const void *stmt, + const ExplodedNode* N); + +void registerFindLastStore(BugReporterContext& BRC, const void *memregion, + const ExplodedNode *N); + +void registerNilReceiverVisitor(BugReporterContext &BRC); + +void registerVarDeclsLastStore(BugReporterContext &BRC, const void *stmt, + const ExplodedNode *N); + +} // end namespace clang::bugreporter + +//===----------------------------------------------------------------------===// + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/EntoSA/BugReporter/BugType.h b/include/clang/StaticAnalyzer/EntoSA/BugReporter/BugType.h new file mode 100644 index 0000000000..6e6ec54918 --- /dev/null +++ b/include/clang/StaticAnalyzer/EntoSA/BugReporter/BugType.h @@ -0,0 +1,76 @@ +//===--- BugType.h - Bug Information Desciption ----------------*- 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 BugType, a class representing a bug type. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_ANALYSIS_BUGTYPE +#define LLVM_CLANG_ANALYSIS_BUGTYPE + +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "llvm/ADT/FoldingSet.h" +#include + +namespace clang { + +namespace ento { + +class ExplodedNode; +class ExprEngine; + +class BugType { +private: + const std::string Name; + const std::string Category; + llvm::FoldingSet EQClasses; + friend class BugReporter; + bool SuppressonSink; +public: + BugType(llvm::StringRef name, llvm::StringRef cat) + : Name(name), Category(cat), SuppressonSink(false) {} + virtual ~BugType(); + + // FIXME: Should these be made strings as well? + llvm::StringRef getName() const { return Name; } + llvm::StringRef getCategory() const { return Category; } + + /// isSuppressOnSink - Returns true if bug reports associated with this bug + /// type should be suppressed if the end node of the report is post-dominated + /// by a sink node. + bool isSuppressOnSink() const { return SuppressonSink; } + void setSuppressOnSink(bool x) { SuppressonSink = x; } + + virtual void FlushReports(BugReporter& BR); + + typedef llvm::FoldingSet::iterator iterator; + iterator begin() { return EQClasses.begin(); } + iterator end() { return EQClasses.end(); } + + typedef llvm::FoldingSet::const_iterator const_iterator; + const_iterator begin() const { return EQClasses.begin(); } + const_iterator end() const { return EQClasses.end(); } +}; + +class BuiltinBug : public BugType { + const std::string desc; +public: + BuiltinBug(const char *name, const char *description) + : BugType(name, "Logic error"), desc(description) {} + + BuiltinBug(const char *name) + : BugType(name, "Logic error"), desc(name) {} + + llvm::StringRef getDescription() const { return desc; } +}; + +} // end GR namespace + +} // end clang namespace +#endif diff --git a/include/clang/StaticAnalyzer/EntoSA/BugReporter/PathDiagnostic.h b/include/clang/StaticAnalyzer/EntoSA/BugReporter/PathDiagnostic.h new file mode 100644 index 0000000000..6d53c097d2 --- /dev/null +++ b/include/clang/StaticAnalyzer/EntoSA/BugReporter/PathDiagnostic.h @@ -0,0 +1,500 @@ +//===--- PathDiagnostic.h - Path-Specific Diagnostic Handling ---*- 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 PathDiagnostic-related interfaces. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_PATH_DIAGNOSTIC_H +#define LLVM_CLANG_PATH_DIAGNOSTIC_H + +#include "clang/Basic/Diagnostic.h" +#include "llvm/ADT/FoldingSet.h" +#include +#include +#include +#include + +namespace clang { + +class Decl; +class SourceManager; +class Stmt; + +namespace ento { + +//===----------------------------------------------------------------------===// +// High-level interface for handlers of path-sensitive diagnostics. +//===----------------------------------------------------------------------===// + +class PathDiagnostic; + +class PathDiagnosticClient : public DiagnosticClient { +public: + PathDiagnosticClient() {} + + virtual ~PathDiagnosticClient() {} + + virtual void + FlushDiagnostics(llvm::SmallVectorImpl *FilesMade = 0) = 0; + + void FlushDiagnostics(llvm::SmallVectorImpl &FilesMade) { + FlushDiagnostics(&FilesMade); + } + + virtual llvm::StringRef getName() const = 0; + + virtual void HandleDiagnostic(Diagnostic::Level DiagLevel, + const DiagnosticInfo &Info); + virtual void HandlePathDiagnostic(const PathDiagnostic* D) = 0; + + enum PathGenerationScheme { Minimal, Extensive }; + virtual PathGenerationScheme getGenerationScheme() const { return Minimal; } + virtual bool supportsLogicalOpControlFlow() const { return false; } + virtual bool supportsAllBlockEdges() const { return false; } + virtual bool useVerboseDescription() const { return true; } +}; + +//===----------------------------------------------------------------------===// +// Path-sensitive diagnostics. +//===----------------------------------------------------------------------===// + +class PathDiagnosticRange : public SourceRange { +public: + const bool isPoint; + + PathDiagnosticRange(const SourceRange &R, bool isP = false) + : SourceRange(R), isPoint(isP) {} +}; + +class PathDiagnosticLocation { +private: + enum Kind { RangeK, SingleLocK, StmtK, DeclK } K; + SourceRange R; + const Stmt *S; + const Decl *D; + const SourceManager *SM; +public: + PathDiagnosticLocation() + : K(SingleLocK), S(0), D(0), SM(0) {} + + PathDiagnosticLocation(FullSourceLoc L) + : K(SingleLocK), R(L, L), S(0), D(0), SM(&L.getManager()) {} + + PathDiagnosticLocation(const Stmt *s, const SourceManager &sm) + : K(StmtK), S(s), D(0), SM(&sm) {} + + PathDiagnosticLocation(SourceRange r, const SourceManager &sm) + : K(RangeK), R(r), S(0), D(0), SM(&sm) {} + + PathDiagnosticLocation(const Decl *d, const SourceManager &sm) + : K(DeclK), S(0), D(d), SM(&sm) {} + + bool operator==(const PathDiagnosticLocation &X) const { + return K == X.K && R == X.R && S == X.S && D == X.D; + } + + bool operator!=(const PathDiagnosticLocation &X) const { + return K != X.K || R != X.R || S != X.S || D != X.D;; + } + + PathDiagnosticLocation& operator=(const PathDiagnosticLocation &X) { + K = X.K; + R = X.R; + S = X.S; + D = X.D; + SM = X.SM; + return *this; + } + + bool isValid() const { + return SM != 0; + } + + const SourceManager& getSourceManager() const { assert(isValid());return *SM;} + + FullSourceLoc asLocation() const; + PathDiagnosticRange asRange() const; + const Stmt *asStmt() const { assert(isValid()); return S; } + const Decl *asDecl() const { assert(isValid()); return D; } + + bool hasRange() const { return K == StmtK || K == RangeK || K == DeclK; } + + void invalidate() { + *this = PathDiagnosticLocation(); + } + + void flatten(); + + const SourceManager& getManager() const { assert(isValid()); return *SM; } + + void Profile(llvm::FoldingSetNodeID &ID) const; +}; + +class PathDiagnosticLocationPair { +private: + PathDiagnosticLocation Start, End; +public: + PathDiagnosticLocationPair(const PathDiagnosticLocation &start, + const PathDiagnosticLocation &end) + : Start(start), End(end) {} + + const PathDiagnosticLocation &getStart() const { return Start; } + const PathDiagnosticLocation &getEnd() const { return End; } + + void flatten() { + Start.flatten(); + End.flatten(); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + Start.Profile(ID); + End.Profile(ID); + } +}; + +//===----------------------------------------------------------------------===// +// Path "pieces" for path-sensitive diagnostics. +//===----------------------------------------------------------------------===// + +class PathDiagnosticPiece { +public: + enum Kind { ControlFlow, Event, Macro }; + enum DisplayHint { Above, Below }; + +private: + const std::string str; + std::vector FixItHints; + const Kind kind; + const DisplayHint Hint; + std::vector ranges; + + // Do not implement: + PathDiagnosticPiece(); + PathDiagnosticPiece(const PathDiagnosticPiece &P); + PathDiagnosticPiece& operator=(const PathDiagnosticPiece &P); + +protected: + PathDiagnosticPiece(llvm::StringRef s, Kind k, DisplayHint hint = Below); + + PathDiagnosticPiece(Kind k, DisplayHint hint = Below); + +public: + virtual ~PathDiagnosticPiece(); + + const std::string& getString() const { return str; } + + /// getDisplayHint - Return a hint indicating where the diagnostic should + /// be displayed by the PathDiagnosticClient. + DisplayHint getDisplayHint() const { return Hint; } + + virtual PathDiagnosticLocation getLocation() const = 0; + virtual void flattenLocations() = 0; + + Kind getKind() const { return kind; } + + void addRange(SourceRange R) { ranges.push_back(R); } + + void addRange(SourceLocation B, SourceLocation E) { + ranges.push_back(SourceRange(B,E)); + } + + void addFixItHint(const FixItHint& Hint) { + FixItHints.push_back(Hint); + } + + typedef const SourceRange* range_iterator; + + range_iterator ranges_begin() const { + return ranges.empty() ? NULL : &ranges[0]; + } + + range_iterator ranges_end() const { + return ranges_begin() + ranges.size(); + } + + typedef const FixItHint *fixit_iterator; + + fixit_iterator fixit_begin() const { + return FixItHints.empty()? 0 : &FixItHints[0]; + } + + fixit_iterator fixit_end() const { + return FixItHints.empty()? 0 + : &FixItHints[0] + FixItHints.size(); + } + + static inline bool classof(const PathDiagnosticPiece* P) { + return true; + } + + virtual void Profile(llvm::FoldingSetNodeID &ID) const; +}; + +class PathDiagnosticSpotPiece : public PathDiagnosticPiece { +private: + PathDiagnosticLocation Pos; +public: + PathDiagnosticSpotPiece(const PathDiagnosticLocation &pos, + llvm::StringRef s, + PathDiagnosticPiece::Kind k, + bool addPosRange = true) + : PathDiagnosticPiece(s, k), Pos(pos) { + assert(Pos.asLocation().isValid() && + "PathDiagnosticSpotPiece's must have a valid location."); + if (addPosRange && Pos.hasRange()) addRange(Pos.asRange()); + } + + PathDiagnosticLocation getLocation() const { return Pos; } + virtual void flattenLocations() { Pos.flatten(); } + + virtual void Profile(llvm::FoldingSetNodeID &ID) const; +}; + +class PathDiagnosticEventPiece : public PathDiagnosticSpotPiece { + +public: + PathDiagnosticEventPiece(const PathDiagnosticLocation &pos, + llvm::StringRef s, bool addPosRange = true) + : PathDiagnosticSpotPiece(pos, s, Event, addPosRange) {} + + ~PathDiagnosticEventPiece(); + + static inline bool classof(const PathDiagnosticPiece* P) { + return P->getKind() == Event; + } +}; + +class PathDiagnosticControlFlowPiece : public PathDiagnosticPiece { + std::vector LPairs; +public: + PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, + const PathDiagnosticLocation &endPos, + llvm::StringRef s) + : PathDiagnosticPiece(s, ControlFlow) { + LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); + } + + PathDiagnosticControlFlowPiece(const PathDiagnosticLocation &startPos, + const PathDiagnosticLocation &endPos) + : PathDiagnosticPiece(ControlFlow) { + LPairs.push_back(PathDiagnosticLocationPair(startPos, endPos)); + } + + ~PathDiagnosticControlFlowPiece(); + + PathDiagnosticLocation getStartLocation() const { + assert(!LPairs.empty() && + "PathDiagnosticControlFlowPiece needs at least one location."); + return LPairs[0].getStart(); + } + + PathDiagnosticLocation getEndLocation() const { + assert(!LPairs.empty() && + "PathDiagnosticControlFlowPiece needs at least one location."); + return LPairs[0].getEnd(); + } + + void push_back(const PathDiagnosticLocationPair &X) { LPairs.push_back(X); } + + virtual PathDiagnosticLocation getLocation() const { + return getStartLocation(); + } + + typedef std::vector::iterator iterator; + iterator begin() { return LPairs.begin(); } + iterator end() { return LPairs.end(); } + + virtual void flattenLocations() { + for (iterator I=begin(), E=end(); I!=E; ++I) I->flatten(); + } + + typedef std::vector::const_iterator + const_iterator; + const_iterator begin() const { return LPairs.begin(); } + const_iterator end() const { return LPairs.end(); } + + static inline bool classof(const PathDiagnosticPiece* P) { + return P->getKind() == ControlFlow; + } + + virtual void Profile(llvm::FoldingSetNodeID &ID) const; +}; + +class PathDiagnosticMacroPiece : public PathDiagnosticSpotPiece { + std::vector SubPieces; +public: + PathDiagnosticMacroPiece(const PathDiagnosticLocation &pos) + : PathDiagnosticSpotPiece(pos, "", Macro) {} + + ~PathDiagnosticMacroPiece(); + + bool containsEvent() const; + + void push_back(PathDiagnosticPiece* P) { SubPieces.push_back(P); } + + typedef std::vector::iterator iterator; + iterator begin() { return SubPieces.begin(); } + iterator end() { return SubPieces.end(); } + + virtual void flattenLocations() { + PathDiagnosticSpotPiece::flattenLocations(); + for (iterator I=begin(), E=end(); I!=E; ++I) (*I)->flattenLocations(); + } + + typedef std::vector::const_iterator const_iterator; + const_iterator begin() const { return SubPieces.begin(); } + const_iterator end() const { return SubPieces.end(); } + + static inline bool classof(const PathDiagnosticPiece* P) { + return P->getKind() == Macro; + } + + virtual void Profile(llvm::FoldingSetNodeID &ID) const; +}; + +/// PathDiagnostic - PathDiagnostic objects represent a single path-sensitive +/// diagnostic. It represents an ordered-collection of PathDiagnosticPieces, +/// each which represent the pieces of the path. +class PathDiagnostic : public llvm::FoldingSetNode { + std::deque path; + unsigned Size; + std::string BugType; + std::string Desc; + std::string Category; + std::deque OtherDesc; + +public: + PathDiagnostic(); + + PathDiagnostic(llvm::StringRef bugtype, llvm::StringRef desc, + llvm::StringRef category); + + ~PathDiagnostic(); + + llvm::StringRef getDescription() const { return Desc; } + llvm::StringRef getBugType() const { return BugType; } + llvm::StringRef getCategory() const { return Category; } + + typedef std::deque::const_iterator meta_iterator; + meta_iterator meta_begin() const { return OtherDesc.begin(); } + meta_iterator meta_end() const { return OtherDesc.end(); } + void addMeta(llvm::StringRef s) { OtherDesc.push_back(s); } + + PathDiagnosticLocation getLocation() const { + assert(Size > 0 && "getLocation() requires a non-empty PathDiagnostic."); + return rbegin()->getLocation(); + } + + void push_front(PathDiagnosticPiece* piece) { + assert(piece); + path.push_front(piece); + ++Size; + } + + void push_back(PathDiagnosticPiece* piece) { + assert(piece); + path.push_back(piece); + ++Size; + } + + PathDiagnosticPiece* back() { + return path.back(); + } + + const PathDiagnosticPiece* back() const { + return path.back(); + } + + unsigned size() const { return Size; } + bool empty() const { return Size == 0; } + + void resetPath(bool deletePieces = true); + + class iterator { + public: + typedef std::deque::iterator ImplTy; + + typedef PathDiagnosticPiece value_type; + typedef value_type& reference; + typedef value_type* pointer; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + private: + ImplTy I; + + public: + iterator(const ImplTy& i) : I(i) {} + + bool operator==(const iterator& X) const { return I == X.I; } + bool operator!=(const iterator& X) const { return I != X.I; } + + PathDiagnosticPiece& operator*() const { return **I; } + PathDiagnosticPiece* operator->() const { return *I; } + + iterator& operator++() { ++I; return *this; } + iterator& operator--() { --I; return *this; } + }; + + class const_iterator { + public: + typedef std::deque::const_iterator ImplTy; + + typedef const PathDiagnosticPiece value_type; + typedef value_type& reference; + typedef value_type* pointer; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + private: + ImplTy I; + + public: + const_iterator(const ImplTy& i) : I(i) {} + + bool operator==(const const_iterator& X) const { return I == X.I; } + bool operator!=(const const_iterator& X) const { return I != X.I; } + + reference operator*() const { return **I; } + pointer operator->() const { return *I; } + + const_iterator& operator++() { ++I; return *this; } + const_iterator& operator--() { --I; return *this; } + }; + + typedef std::reverse_iterator reverse_iterator; + typedef std::reverse_iterator const_reverse_iterator; + + // forward iterator creation methods. + + iterator begin() { return path.begin(); } + iterator end() { return path.end(); } + + const_iterator begin() const { return path.begin(); } + const_iterator end() const { return path.end(); } + + // reverse iterator creation methods. + reverse_iterator rbegin() { return reverse_iterator(end()); } + const_reverse_iterator rbegin() const{ return const_reverse_iterator(end()); } + reverse_iterator rend() { return reverse_iterator(begin()); } + const_reverse_iterator rend() const { return const_reverse_iterator(begin());} + + void flattenLocations() { + for (iterator I = begin(), E = end(); I != E; ++I) I->flattenLocations(); + } + + void Profile(llvm::FoldingSetNodeID &ID) const; +}; + +} // end GR namespace + +} //end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/EntoSA/Checkers/DereferenceChecker.h b/include/clang/StaticAnalyzer/EntoSA/Checkers/DereferenceChecker.h new file mode 100644 index 0000000000..f9cce9ca9c --- /dev/null +++ b/include/clang/StaticAnalyzer/EntoSA/Checkers/DereferenceChecker.h @@ -0,0 +1,35 @@ +//== NullDerefChecker.h - 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 and UndefDerefChecker, two builtin checks +// in ExprEngine that check for null and undefined pointers at loads +// and stores. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_DEREFCHECKER +#define LLVM_CLANG_GR_DEREFCHECKER + +#include + +namespace clang { + +namespace ento { + +class ExprEngine; +class ExplodedNode; + +std::pair +GetImplicitNullDereferences(ExprEngine &Eng); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/EntoSA/Checkers/LocalCheckers.h b/include/clang/StaticAnalyzer/EntoSA/Checkers/LocalCheckers.h new file mode 100644 index 0000000000..e0f002b6d0 --- /dev/null +++ b/include/clang/StaticAnalyzer/EntoSA/Checkers/LocalCheckers.h @@ -0,0 +1,67 @@ +//==- LocalCheckers.h - Intra-Procedural+Flow-Sensitive 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 the interface to call a set of intra-procedural (local) +// checkers that use flow/path-sensitive analyses to find bugs. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_LOCALCHECKERS_H +#define LLVM_CLANG_GR_LOCALCHECKERS_H + +namespace clang { + +class CFG; +class Decl; +class Diagnostic; +class ASTContext; +class LangOptions; +class ParentMap; +class LiveVariables; +class ObjCImplementationDecl; +class LangOptions; +class TranslationUnitDecl; + +namespace ento { + +class PathDiagnosticClient; +class TransferFuncs; +class BugType; +class BugReporter; +class ExprEngine; + +void CheckDeadStores(CFG &cfg, LiveVariables &L, ParentMap &map, + BugReporter& BR); + +TransferFuncs* MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, + const LangOptions& lopts); + +void CheckObjCDealloc(const ObjCImplementationDecl* D, const LangOptions& L, + BugReporter& BR); + +void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID, + BugReporter& BR); + +void CheckObjCUnusedIvar(const ObjCImplementationDecl *D, BugReporter& BR); + +void RegisterAppleChecks(ExprEngine& Eng, const Decl &D); +void RegisterExperimentalChecks(ExprEngine &Eng); +void RegisterExperimentalInternalChecks(ExprEngine &Eng); + +void CheckLLVMConventions(TranslationUnitDecl &TU, BugReporter &BR); +void CheckSecuritySyntaxOnly(const Decl *D, BugReporter &BR); +void CheckSizeofPointer(const Decl *D, BugReporter &BR); + +void RegisterCallInliner(ExprEngine &Eng); + +} // end GR namespace + +} // end namespace clang + +#endif diff --git a/include/clang/EntoSA/FrontendActions.h b/include/clang/StaticAnalyzer/EntoSA/FrontendActions.h similarity index 100% rename from include/clang/EntoSA/FrontendActions.h rename to include/clang/StaticAnalyzer/EntoSA/FrontendActions.h diff --git a/include/clang/EntoSA/ManagerRegistry.h b/include/clang/StaticAnalyzer/EntoSA/ManagerRegistry.h similarity index 96% rename from include/clang/EntoSA/ManagerRegistry.h rename to include/clang/StaticAnalyzer/EntoSA/ManagerRegistry.h index 43657a5471..1b84980428 100644 --- a/include/clang/EntoSA/ManagerRegistry.h +++ b/include/clang/StaticAnalyzer/EntoSA/ManagerRegistry.h @@ -14,7 +14,7 @@ #ifndef LLVM_CLANG_GR_MANAGER_REGISTRY_H #define LLVM_CLANG_GR_MANAGER_REGISTRY_H -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" namespace clang { diff --git a/include/clang/EntoSA/PathDiagnosticClients.h b/include/clang/StaticAnalyzer/EntoSA/PathDiagnosticClients.h similarity index 100% rename from include/clang/EntoSA/PathDiagnosticClients.h rename to include/clang/StaticAnalyzer/EntoSA/PathDiagnosticClients.h diff --git a/include/clang/EntoSA/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/AnalysisManager.h similarity index 98% rename from include/clang/EntoSA/PathSensitive/AnalysisManager.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/AnalysisManager.h index 95d954ec86..ba86ed8ba2 100644 --- a/include/clang/EntoSA/PathSensitive/AnalysisManager.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/AnalysisManager.h @@ -16,8 +16,8 @@ #define LLVM_CLANG_GR_ANALYSISMANAGER_H #include "clang/Analysis/AnalysisContext.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" namespace clang { diff --git a/include/clang/EntoSA/PathSensitive/BasicValueFactory.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/BasicValueFactory.h similarity index 99% rename from include/clang/EntoSA/PathSensitive/BasicValueFactory.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/BasicValueFactory.h index 061508ecbd..4cac8b6417 100644 --- a/include/clang/EntoSA/PathSensitive/BasicValueFactory.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/BasicValueFactory.h @@ -16,7 +16,7 @@ #ifndef LLVM_CLANG_GR_BASICVALUEFACTORY_H #define LLVM_CLANG_GR_BASICVALUEFACTORY_H -#include "clang/EntoSA/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" #include "clang/AST/ASTContext.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/APSInt.h" diff --git a/include/clang/EntoSA/PathSensitive/BlockCounter.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/BlockCounter.h similarity index 100% rename from include/clang/EntoSA/PathSensitive/BlockCounter.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/BlockCounter.h diff --git a/include/clang/EntoSA/PathSensitive/Checker.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Checker.h similarity index 99% rename from include/clang/EntoSA/PathSensitive/Checker.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/Checker.h index a536955c95..b2103a2426 100644 --- a/include/clang/EntoSA/PathSensitive/Checker.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Checker.h @@ -16,7 +16,7 @@ #define LLVM_CLANG_GR_CHECKER #include "clang/Analysis/Support/SaveAndRestore.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" //===----------------------------------------------------------------------===// // Checker interface. diff --git a/include/clang/EntoSA/PathSensitive/CheckerHelpers.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerHelpers.h similarity index 100% rename from include/clang/EntoSA/PathSensitive/CheckerHelpers.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerHelpers.h diff --git a/include/clang/EntoSA/PathSensitive/CheckerVisitor.def b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerVisitor.def similarity index 100% rename from include/clang/EntoSA/PathSensitive/CheckerVisitor.def rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerVisitor.def diff --git a/include/clang/EntoSA/PathSensitive/CheckerVisitor.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerVisitor.h similarity index 92% rename from include/clang/EntoSA/PathSensitive/CheckerVisitor.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerVisitor.h index 62ce943cdb..68677f5503 100644 --- a/include/clang/EntoSA/PathSensitive/CheckerVisitor.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerVisitor.h @@ -13,7 +13,7 @@ #ifndef LLVM_CLANG_GR_CHECKERVISITOR #define LLVM_CLANG_GR_CHECKERVISITOR -#include "clang/EntoSA/PathSensitive/Checker.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" namespace clang { @@ -51,7 +51,7 @@ public: case Stmt::NAME ## Class:\ static_cast(this)->PreVisit ## NAME(C,static_cast(S));\ break; -#include "clang/EntoSA/PathSensitive/CheckerVisitor.def" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.def" } } @@ -70,7 +70,7 @@ case Stmt::NAME ## Class:\ static_cast(this)->\ PostVisit ## NAME(C,static_cast(S));\ break; -#include "clang/EntoSA/PathSensitive/CheckerVisitor.def" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.def" } } @@ -101,7 +101,7 @@ void PreVisit ## NAME(CheckerContext &C, const NAME* S) {\ void PostVisit ## NAME(CheckerContext &C, const NAME* S) {\ static_cast(this)->PostVisit ## FALLBACK(C, S);\ } -#include "clang/EntoSA/PathSensitive/CheckerVisitor.def" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.def" }; } // end GR namespace diff --git a/include/clang/EntoSA/PathSensitive/ConstraintManager.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ConstraintManager.h similarity index 97% rename from include/clang/EntoSA/PathSensitive/ConstraintManager.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/ConstraintManager.h index b9e723ca4a..81f4558e6a 100644 --- a/include/clang/EntoSA/PathSensitive/ConstraintManager.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ConstraintManager.h @@ -15,7 +15,7 @@ #define LLVM_CLANG_GR_CONSTRAINT_MANAGER_H // FIXME: Typedef LiveSymbolsTy/DeadSymbolsTy at a more appropriate place. -#include "clang/EntoSA/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/PathSensitive/Store.h" namespace llvm { class APSInt; diff --git a/include/clang/EntoSA/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CoreEngine.h similarity index 98% rename from include/clang/EntoSA/PathSensitive/CoreEngine.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/CoreEngine.h index 45e7fdc645..3d63982d73 100644 --- a/include/clang/EntoSA/PathSensitive/CoreEngine.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CoreEngine.h @@ -16,10 +16,10 @@ #define LLVM_CLANG_GR_COREENGINE #include "clang/AST/Expr.h" -#include "clang/EntoSA/PathSensitive/ExplodedGraph.h" -#include "clang/EntoSA/PathSensitive/WorkList.h" -#include "clang/EntoSA/PathSensitive/BlockCounter.h" -#include "clang/EntoSA/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/PathSensitive/WorkList.h" +#include "clang/StaticAnalyzer/PathSensitive/BlockCounter.h" +#include "clang/StaticAnalyzer/PathSensitive/SubEngine.h" #include "llvm/ADT/OwningPtr.h" namespace clang { diff --git a/include/clang/EntoSA/PathSensitive/Environment.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Environment.h similarity index 96% rename from include/clang/EntoSA/PathSensitive/Environment.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/Environment.h index 1e88e1605e..8f2ebce670 100644 --- a/include/clang/EntoSA/PathSensitive/Environment.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Environment.h @@ -14,8 +14,8 @@ #ifndef LLVM_CLANG_GR_ENVIRONMENT_H #define LLVM_CLANG_GR_ENVIRONMENT_H -#include "clang/EntoSA/PathSensitive/Store.h" -#include "clang/EntoSA/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" #include "llvm/ADT/ImmutableMap.h" namespace clang { diff --git a/include/clang/EntoSA/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExplodedGraph.h similarity index 100% rename from include/clang/EntoSA/PathSensitive/ExplodedGraph.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExplodedGraph.h diff --git a/include/clang/EntoSA/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngine.h similarity index 98% rename from include/clang/EntoSA/PathSensitive/ExprEngine.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngine.h index d10e76b202..407691a20b 100644 --- a/include/clang/EntoSA/PathSensitive/ExprEngine.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngine.h @@ -16,12 +16,12 @@ #ifndef LLVM_CLANG_GR_EXPRENGINE #define LLVM_CLANG_GR_EXPRENGINE -#include "clang/EntoSA/PathSensitive/AnalysisManager.h" -#include "clang/EntoSA/PathSensitive/SubEngine.h" -#include "clang/EntoSA/PathSensitive/CoreEngine.h" -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/PathSensitive/TransferFuncs.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" #include "clang/AST/Type.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/ExprCXX.h" diff --git a/include/clang/EntoSA/PathSensitive/ExprEngineBuilders.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngineBuilders.h similarity index 97% rename from include/clang/EntoSA/PathSensitive/ExprEngineBuilders.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngineBuilders.h index 91df6121a7..965ec3e1c3 100644 --- a/include/clang/EntoSA/PathSensitive/ExprEngineBuilders.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngineBuilders.h @@ -14,7 +14,7 @@ #ifndef LLVM_CLANG_GR_EXPRENGINE_BUILDERS #define LLVM_CLANG_GR_EXPRENGINE_BUILDERS -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" #include "clang/Analysis/Support/SaveAndRestore.h" namespace clang { diff --git a/include/clang/EntoSA/PathSensitive/GRState.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/GRState.h similarity index 99% rename from include/clang/EntoSA/PathSensitive/GRState.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/GRState.h index 8e2ab07299..dcdb6e26cb 100644 --- a/include/clang/EntoSA/PathSensitive/GRState.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/GRState.h @@ -14,10 +14,10 @@ #ifndef LLVM_CLANG_GR_VALUESTATE_H #define LLVM_CLANG_GR_VALUESTATE_H -#include "clang/EntoSA/PathSensitive/ConstraintManager.h" -#include "clang/EntoSA/PathSensitive/Environment.h" -#include "clang/EntoSA/PathSensitive/Store.h" -#include "clang/EntoSA/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/PathSensitive/ConstraintManager.h" +#include "clang/StaticAnalyzer/PathSensitive/Environment.h" +#include "clang/StaticAnalyzer/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/PathSensitive/SValBuilder.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/Support/Casting.h" diff --git a/include/clang/EntoSA/PathSensitive/GRStateTrait.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/GRStateTrait.h similarity index 100% rename from include/clang/EntoSA/PathSensitive/GRStateTrait.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/GRStateTrait.h diff --git a/include/clang/EntoSA/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/MemRegion.h similarity index 99% rename from include/clang/EntoSA/PathSensitive/MemRegion.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/MemRegion.h index c9c72ab33e..33779a9fd5 100644 --- a/include/clang/EntoSA/PathSensitive/MemRegion.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/MemRegion.h @@ -18,7 +18,7 @@ #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" -#include "clang/EntoSA/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" #include "llvm/Support/Casting.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/ADT/FoldingSet.h" diff --git a/include/clang/EntoSA/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SValBuilder.h similarity index 97% rename from include/clang/EntoSA/PathSensitive/SValBuilder.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/SValBuilder.h index f4c168dc0b..0705510ccd 100644 --- a/include/clang/EntoSA/PathSensitive/SValBuilder.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SValBuilder.h @@ -17,9 +17,9 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprCXX.h" -#include "clang/EntoSA/PathSensitive/SVals.h" -#include "clang/EntoSA/PathSensitive/BasicValueFactory.h" -#include "clang/EntoSA/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" namespace clang { diff --git a/include/clang/EntoSA/PathSensitive/SVals.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SVals.h similarity index 99% rename from include/clang/EntoSA/PathSensitive/SVals.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/SVals.h index acbf1d4413..d4d846954e 100644 --- a/include/clang/EntoSA/PathSensitive/SVals.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SVals.h @@ -15,7 +15,7 @@ #ifndef LLVM_CLANG_GR_RVALUE_H #define LLVM_CLANG_GR_RVALUE_H -#include "clang/EntoSA/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" #include "llvm/Support/Casting.h" #include "llvm/ADT/ImmutableList.h" diff --git a/include/clang/EntoSA/PathSensitive/Store.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Store.h similarity index 98% rename from include/clang/EntoSA/PathSensitive/Store.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/Store.h index 83b113834e..c48f129feb 100644 --- a/include/clang/EntoSA/PathSensitive/Store.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Store.h @@ -14,8 +14,8 @@ #ifndef LLVM_CLANG_GR_STORE_H #define LLVM_CLANG_GR_STORE_H -#include "clang/EntoSA/PathSensitive/MemRegion.h" -#include "clang/EntoSA/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/PathSensitive/SValBuilder.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/Optional.h" diff --git a/include/clang/EntoSA/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SubEngine.h similarity index 98% rename from include/clang/EntoSA/PathSensitive/SubEngine.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/SubEngine.h index bddd5e950f..8672bd1a02 100644 --- a/include/clang/EntoSA/PathSensitive/SubEngine.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SubEngine.h @@ -13,7 +13,7 @@ #ifndef LLVM_CLANG_GR_SUBENGINE_H #define LLVM_CLANG_GR_SUBENGINE_H -#include "clang/EntoSA/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" namespace clang { diff --git a/include/clang/EntoSA/PathSensitive/SummaryManager.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SummaryManager.h similarity index 100% rename from include/clang/EntoSA/PathSensitive/SummaryManager.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/SummaryManager.h diff --git a/include/clang/EntoSA/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SymbolManager.h similarity index 100% rename from include/clang/EntoSA/PathSensitive/SymbolManager.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/SymbolManager.h diff --git a/include/clang/EntoSA/PathSensitive/TransferFuncs.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/TransferFuncs.h similarity index 96% rename from include/clang/EntoSA/PathSensitive/TransferFuncs.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/TransferFuncs.h index ac5b4a6d17..22e3a4d3c0 100644 --- a/include/clang/EntoSA/PathSensitive/TransferFuncs.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/TransferFuncs.h @@ -15,8 +15,8 @@ #ifndef LLVM_CLANG_GR_TRANSFERFUNCS #define LLVM_CLANG_GR_TRANSFERFUNCS -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" #include namespace clang { diff --git a/include/clang/EntoSA/PathSensitive/WorkList.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/WorkList.h similarity index 97% rename from include/clang/EntoSA/PathSensitive/WorkList.h rename to include/clang/StaticAnalyzer/EntoSA/PathSensitive/WorkList.h index 6f98587235..be909061cd 100644 --- a/include/clang/EntoSA/PathSensitive/WorkList.h +++ b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/WorkList.h @@ -15,7 +15,7 @@ #ifndef LLVM_CLANG_GR_WORKLIST #define LLVM_CLANG_GR_WORKLIST -#include "clang/EntoSA/PathSensitive/BlockCounter.h" +#include "clang/StaticAnalyzer/PathSensitive/BlockCounter.h" #include namespace clang { diff --git a/include/clang/StaticAnalyzer/FrontendActions.h b/include/clang/StaticAnalyzer/FrontendActions.h new file mode 100644 index 0000000000..e3867a2a24 --- /dev/null +++ b/include/clang/StaticAnalyzer/FrontendActions.h @@ -0,0 +1,33 @@ +//===-- FrontendActions.h - Useful Frontend Actions -------------*- 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_GR_FRONTENDACTIONS_H +#define LLVM_CLANG_GR_FRONTENDACTIONS_H + +#include "clang/Frontend/FrontendAction.h" + +namespace clang { + +namespace ento { + +//===----------------------------------------------------------------------===// +// AST Consumer Actions +//===----------------------------------------------------------------------===// + +class AnalysisAction : public ASTFrontendAction { +protected: + virtual ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile); +}; + +} // end GR namespace + +} // end namespace clang + +#endif diff --git a/include/clang/StaticAnalyzer/ManagerRegistry.h b/include/clang/StaticAnalyzer/ManagerRegistry.h new file mode 100644 index 0000000000..1b84980428 --- /dev/null +++ b/include/clang/StaticAnalyzer/ManagerRegistry.h @@ -0,0 +1,58 @@ +//===-- ManagerRegistry.h - Pluggable analyzer module registry --*- 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 ManagerRegistry and Register* classes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_MANAGER_REGISTRY_H +#define LLVM_CLANG_GR_MANAGER_REGISTRY_H + +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" + +namespace clang { + +namespace ento { + +/// ManagerRegistry - This class records manager creators registered at +/// runtime. The information is communicated to AnalysisManager through static +/// members. Better design is expected. + +class ManagerRegistry { +public: + static StoreManagerCreator StoreMgrCreator; + static ConstraintManagerCreator ConstraintMgrCreator; +}; + +/// RegisterConstraintManager - This class is used to setup the constraint +/// manager of the static analyzer. The constructor takes a creator function +/// pointer for creating the constraint manager. +/// +/// It is used like this: +/// +/// class MyConstraintManager {}; +/// ConstraintManager* CreateMyConstraintManager(GRStateManager& statemgr) { +/// return new MyConstraintManager(statemgr); +/// } +/// RegisterConstraintManager X(CreateMyConstraintManager); + +class RegisterConstraintManager { +public: + RegisterConstraintManager(ConstraintManagerCreator CMC) { + assert(ManagerRegistry::ConstraintMgrCreator == 0 + && "ConstraintMgrCreator already set!"); + ManagerRegistry::ConstraintMgrCreator = CMC; + } +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathDiagnosticClients.h b/include/clang/StaticAnalyzer/PathDiagnosticClients.h new file mode 100644 index 0000000000..2713e31fc0 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathDiagnosticClients.h @@ -0,0 +1,42 @@ +//===--- PathDiagnosticClients.h - Path Diagnostic Clients ------*- 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 interface to create different path diagostic clients. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_PATH_DIAGNOSTIC_CLIENTS_H +#define LLVM_CLANG_GR_PATH_DIAGNOSTIC_CLiENTS_H + +#include + +namespace clang { + +class Preprocessor; + +namespace ento { + +class PathDiagnosticClient; + +PathDiagnosticClient* +createHTMLDiagnosticClient(const std::string& prefix, const Preprocessor &PP); + +PathDiagnosticClient* +createPlistDiagnosticClient(const std::string& prefix, const Preprocessor &PP, + PathDiagnosticClient *SubPD = 0); + +PathDiagnosticClient* +createTextPathDiagnosticClient(const std::string& prefix, + const Preprocessor &PP); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/PathSensitive/AnalysisManager.h new file mode 100644 index 0000000000..ba86ed8ba2 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/AnalysisManager.h @@ -0,0 +1,208 @@ +//== AnalysisManager.h - Path sensitive analysis data manager ------*- 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 AnalysisManager class that manages the data and policy +// for path sensitive analysis. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_ANALYSISMANAGER_H +#define LLVM_CLANG_GR_ANALYSISMANAGER_H + +#include "clang/Analysis/AnalysisContext.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" + +namespace clang { + +namespace idx { + class Indexer; + class TranslationUnit; +} + +namespace ento { + +class AnalysisManager : public BugReporterData { + AnalysisContextManager AnaCtxMgr; + LocationContextManager LocCtxMgr; + + ASTContext &Ctx; + Diagnostic &Diags; + const LangOptions &LangInfo; + + llvm::OwningPtr PD; + + // Configurable components creators. + StoreManagerCreator CreateStoreMgr; + ConstraintManagerCreator CreateConstraintMgr; + + /// \brief Provide function definitions in other translation units. This is + /// NULL if we don't have multiple translation units. AnalysisManager does + /// not own the Indexer. + idx::Indexer *Idxer; + + enum AnalysisScope { ScopeTU, ScopeDecl } AScope; + + // The maximum number of exploded nodes the analyzer will generate. + unsigned MaxNodes; + + // The maximum number of times the analyzer visit a block. + unsigned MaxVisit; + + bool VisualizeEGDot; + bool VisualizeEGUbi; + bool PurgeDead; + + /// EargerlyAssume - A flag indicating how the engine should handle + // expressions such as: 'x = (y != 0)'. When this flag is true then + // the subexpression 'y != 0' will be eagerly assumed to be true or false, + // thus evaluating it to the integers 0 or 1 respectively. The upside + // is that this can increase analysis precision until we have a better way + // to lazily evaluate such logic. The downside is that it eagerly + // bifurcates paths. + bool EagerlyAssume; + bool TrimGraph; + bool InlineCall; + +public: + AnalysisManager(ASTContext &ctx, Diagnostic &diags, + const LangOptions &lang, PathDiagnosticClient *pd, + StoreManagerCreator storemgr, + ConstraintManagerCreator constraintmgr, + idx::Indexer *idxer, + unsigned maxnodes, unsigned maxvisit, + bool vizdot, bool vizubi, bool purge, bool eager, bool trim, + bool inlinecall, bool useUnoptimizedCFG, + bool addImplicitDtors, bool addInitializers) + + : AnaCtxMgr(useUnoptimizedCFG, addImplicitDtors, addInitializers), + Ctx(ctx), Diags(diags), LangInfo(lang), PD(pd), + CreateStoreMgr(storemgr), CreateConstraintMgr(constraintmgr),Idxer(idxer), + AScope(ScopeDecl), MaxNodes(maxnodes), MaxVisit(maxvisit), + VisualizeEGDot(vizdot), VisualizeEGUbi(vizubi), PurgeDead(purge), + EagerlyAssume(eager), TrimGraph(trim), InlineCall(inlinecall) {} + + ~AnalysisManager() { FlushDiagnostics(); } + + void ClearContexts() { + LocCtxMgr.clear(); + AnaCtxMgr.clear(); + } + + AnalysisContextManager& getAnalysisContextManager() { + return AnaCtxMgr; + } + + StoreManagerCreator getStoreManagerCreator() { + return CreateStoreMgr; + } + + ConstraintManagerCreator getConstraintManagerCreator() { + return CreateConstraintMgr; + } + + idx::Indexer *getIndexer() const { return Idxer; } + + virtual ASTContext &getASTContext() { + return Ctx; + } + + virtual SourceManager &getSourceManager() { + return getASTContext().getSourceManager(); + } + + virtual Diagnostic &getDiagnostic() { + return Diags; + } + + const LangOptions &getLangOptions() const { + return LangInfo; + } + + virtual PathDiagnosticClient *getPathDiagnosticClient() { + return PD.get(); + } + + void FlushDiagnostics() { + if (PD.get()) + PD->FlushDiagnostics(); + } + + unsigned getMaxNodes() const { return MaxNodes; } + + unsigned getMaxVisit() const { return MaxVisit; } + + bool shouldVisualizeGraphviz() const { return VisualizeEGDot; } + + bool shouldVisualizeUbigraph() const { return VisualizeEGUbi; } + + bool shouldVisualize() const { + return VisualizeEGDot || VisualizeEGUbi; + } + + bool shouldTrimGraph() const { return TrimGraph; } + + bool shouldPurgeDead() const { return PurgeDead; } + + bool shouldEagerlyAssume() const { return EagerlyAssume; } + + bool shouldInlineCall() const { return InlineCall; } + + bool hasIndexer() const { return Idxer != 0; } + + AnalysisContext *getAnalysisContextInAnotherTU(const Decl *D); + + CFG *getCFG(Decl const *D) { + return AnaCtxMgr.getContext(D)->getCFG(); + } + + LiveVariables *getLiveVariables(Decl const *D) { + return AnaCtxMgr.getContext(D)->getLiveVariables(); + } + + ParentMap &getParentMap(Decl const *D) { + return AnaCtxMgr.getContext(D)->getParentMap(); + } + + AnalysisContext *getAnalysisContext(const Decl *D) { + return AnaCtxMgr.getContext(D); + } + + AnalysisContext *getAnalysisContext(const Decl *D, idx::TranslationUnit *TU) { + return AnaCtxMgr.getContext(D, TU); + } + + const StackFrameContext *getStackFrame(AnalysisContext *Ctx, + LocationContext const *Parent, + const Stmt *S, + const CFGBlock *Blk, unsigned Idx) { + return LocCtxMgr.getStackFrame(Ctx, Parent, S, Blk, Idx); + } + + // Get the top level stack frame. + const StackFrameContext *getStackFrame(Decl const *D, + idx::TranslationUnit *TU) { + return LocCtxMgr.getStackFrame(AnaCtxMgr.getContext(D, TU), 0, 0, 0, 0); + } + + // Get a stack frame with parent. + StackFrameContext const *getStackFrame(const Decl *D, + LocationContext const *Parent, + const Stmt *S, + const CFGBlock *Blk, unsigned Idx) { + return LocCtxMgr.getStackFrame(AnaCtxMgr.getContext(D), Parent, S, + Blk,Idx); + } +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/BasicValueFactory.h b/include/clang/StaticAnalyzer/PathSensitive/BasicValueFactory.h new file mode 100644 index 0000000000..4cac8b6417 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/BasicValueFactory.h @@ -0,0 +1,201 @@ +//=== BasicValueFactory.h - Basic values for Path Sens analysis --*- 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 BasicValueFactory, a class that manages the lifetime +// of APSInt objects and symbolic constraints used by ExprEngine +// and related classes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_BASICVALUEFACTORY_H +#define LLVM_CLANG_GR_BASICVALUEFACTORY_H + +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" +#include "clang/AST/ASTContext.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/APSInt.h" +#include "llvm/ADT/ImmutableList.h" + +namespace clang { + +namespace ento { + + class GRState; + +class CompoundValData : public llvm::FoldingSetNode { + QualType T; + llvm::ImmutableList L; + +public: + CompoundValData(QualType t, llvm::ImmutableList l) + : T(t), L(l) {} + + typedef llvm::ImmutableList::iterator iterator; + iterator begin() const { return L.begin(); } + iterator end() const { return L.end(); } + + static void Profile(llvm::FoldingSetNodeID& ID, QualType T, + llvm::ImmutableList L); + + void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, T, L); } +}; + +class LazyCompoundValData : public llvm::FoldingSetNode { + const void *store; + const TypedRegion *region; +public: + LazyCompoundValData(const void *st, const TypedRegion *r) + : store(st), region(r) {} + + const void *getStore() const { return store; } + const TypedRegion *getRegion() const { return region; } + + static void Profile(llvm::FoldingSetNodeID& ID, const void *store, + const TypedRegion *region); + + void Profile(llvm::FoldingSetNodeID& ID) { Profile(ID, store, region); } +}; + +class BasicValueFactory { + typedef llvm::FoldingSet > + APSIntSetTy; + + ASTContext& Ctx; + llvm::BumpPtrAllocator& BPAlloc; + + APSIntSetTy APSIntSet; + void* PersistentSVals; + void* PersistentSValPairs; + + llvm::ImmutableList::Factory SValListFactory; + llvm::FoldingSet CompoundValDataSet; + llvm::FoldingSet LazyCompoundValDataSet; + +public: + BasicValueFactory(ASTContext& ctx, llvm::BumpPtrAllocator& Alloc) + : Ctx(ctx), BPAlloc(Alloc), PersistentSVals(0), PersistentSValPairs(0), + SValListFactory(Alloc) {} + + ~BasicValueFactory(); + + ASTContext& getContext() const { return Ctx; } + + const llvm::APSInt& getValue(const llvm::APSInt& X); + const llvm::APSInt& getValue(const llvm::APInt& X, bool isUnsigned); + const llvm::APSInt& getValue(uint64_t X, unsigned BitWidth, bool isUnsigned); + const llvm::APSInt& getValue(uint64_t X, QualType T); + + /// Convert - Create a new persistent APSInt with the same value as 'From' + /// but with the bitwidth and signedness of 'To'. + const llvm::APSInt &Convert(const llvm::APSInt& To, + const llvm::APSInt& From) { + + if (To.isUnsigned() == From.isUnsigned() && + To.getBitWidth() == From.getBitWidth()) + return From; + + return getValue(From.getSExtValue(), To.getBitWidth(), To.isUnsigned()); + } + + const llvm::APSInt &Convert(QualType T, const llvm::APSInt &From) { + assert(T->isIntegerType() || Loc::IsLocType(T)); + unsigned bitwidth = Ctx.getTypeSize(T); + bool isUnsigned = T->isUnsignedIntegerType() || Loc::IsLocType(T); + + if (isUnsigned == From.isUnsigned() && bitwidth == From.getBitWidth()) + return From; + + return getValue(From.getSExtValue(), bitwidth, isUnsigned); + } + + const llvm::APSInt& getIntValue(uint64_t X, bool isUnsigned) { + QualType T = isUnsigned ? Ctx.UnsignedIntTy : Ctx.IntTy; + return getValue(X, T); + } + + inline const llvm::APSInt& getMaxValue(const llvm::APSInt &v) { + return getValue(llvm::APSInt::getMaxValue(v.getBitWidth(), v.isUnsigned())); + } + + inline const llvm::APSInt& getMinValue(const llvm::APSInt &v) { + return getValue(llvm::APSInt::getMinValue(v.getBitWidth(), v.isUnsigned())); + } + + inline const llvm::APSInt& getMaxValue(QualType T) { + assert(T->isIntegerType() || Loc::IsLocType(T)); + bool isUnsigned = T->isUnsignedIntegerType() || Loc::IsLocType(T); + return getValue(llvm::APSInt::getMaxValue(Ctx.getTypeSize(T), isUnsigned)); + } + + inline const llvm::APSInt& getMinValue(QualType T) { + assert(T->isIntegerType() || Loc::IsLocType(T)); + bool isUnsigned = T->isUnsignedIntegerType() || Loc::IsLocType(T); + return getValue(llvm::APSInt::getMinValue(Ctx.getTypeSize(T), isUnsigned)); + } + + inline const llvm::APSInt& Add1(const llvm::APSInt& V) { + llvm::APSInt X = V; + ++X; + return getValue(X); + } + + inline const llvm::APSInt& Sub1(const llvm::APSInt& V) { + llvm::APSInt X = V; + --X; + return getValue(X); + } + + inline const llvm::APSInt& getZeroWithPtrWidth(bool isUnsigned = true) { + return getValue(0, Ctx.getTypeSize(Ctx.VoidPtrTy), isUnsigned); + } + + inline const llvm::APSInt &getIntWithPtrWidth(uint64_t X, bool isUnsigned) { + return getValue(X, Ctx.getTypeSize(Ctx.VoidPtrTy), isUnsigned); + } + + inline const llvm::APSInt& getTruthValue(bool b, QualType T) { + return getValue(b ? 1 : 0, Ctx.getTypeSize(T), false); + } + + inline const llvm::APSInt& getTruthValue(bool b) { + return getTruthValue(b, Ctx.IntTy); + } + + const CompoundValData *getCompoundValData(QualType T, + llvm::ImmutableList Vals); + + const LazyCompoundValData *getLazyCompoundValData(const void *store, + const TypedRegion *region); + + llvm::ImmutableList getEmptySValList() { + return SValListFactory.getEmptyList(); + } + + llvm::ImmutableList consVals(SVal X, llvm::ImmutableList L) { + return SValListFactory.add(X, L); + } + + const llvm::APSInt* evalAPSInt(BinaryOperator::Opcode Op, + const llvm::APSInt& V1, + const llvm::APSInt& V2); + + const std::pair& + getPersistentSValWithData(const SVal& V, uintptr_t Data); + + const std::pair& + getPersistentSValPair(const SVal& V1, const SVal& V2); + + const SVal* getPersistentSVal(SVal X); +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/BlockCounter.h b/include/clang/StaticAnalyzer/PathSensitive/BlockCounter.h new file mode 100644 index 0000000000..7d0fdfb5f1 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/BlockCounter.h @@ -0,0 +1,59 @@ +//==- 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_BLOCKCOUNTER +#define LLVM_CLANG_GR_BLOCKCOUNTER + +namespace llvm { + class BumpPtrAllocator; +} + +namespace clang { + +class StackFrameContext; + +namespace ento { + +class BlockCounter { + void* Data; + + BlockCounter(void* D) : Data(D) {} + +public: + BlockCounter() : Data(0) {} + + unsigned getNumVisited(const StackFrameContext *CallSite, + unsigned BlockID) const; + + class Factory { + void* F; + public: + Factory(llvm::BumpPtrAllocator& Alloc); + ~Factory(); + + BlockCounter GetEmptyCounter(); + BlockCounter IncrementCount(BlockCounter BC, + const StackFrameContext *CallSite, + unsigned BlockID); + }; + + friend class Factory; +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/Checker.h b/include/clang/StaticAnalyzer/PathSensitive/Checker.h new file mode 100644 index 0000000000..b2103a2426 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/Checker.h @@ -0,0 +1,309 @@ +//== Checker.h - Abstract interface 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 and CheckerVisitor, classes used for creating +// domain-specific checks. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_CHECKER +#define LLVM_CLANG_GR_CHECKER + +#include "clang/Analysis/Support/SaveAndRestore.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" + +//===----------------------------------------------------------------------===// +// Checker interface. +//===----------------------------------------------------------------------===// + +namespace clang { + +namespace ento { + +class CheckerContext { + ExplodedNodeSet &Dst; + StmtNodeBuilder &B; + ExprEngine &Eng; + ExplodedNode *Pred; + SaveAndRestore OldSink; + SaveAndRestore OldTag; + SaveAndRestore OldPointKind; + SaveOr OldHasGen; + const GRState *ST; + const Stmt *statement; + const unsigned size; +public: + bool *respondsToCallback; +public: + CheckerContext(ExplodedNodeSet &dst, StmtNodeBuilder &builder, + ExprEngine &eng, ExplodedNode *pred, + const void *tag, ProgramPoint::Kind K, + bool *respondsToCB = 0, + const Stmt *stmt = 0, const GRState *st = 0) + : Dst(dst), B(builder), Eng(eng), Pred(pred), + OldSink(B.BuildSinks), + OldTag(B.Tag, tag), + OldPointKind(B.PointKind, K), + OldHasGen(B.HasGeneratedNode), + ST(st), statement(stmt), size(Dst.size()), + respondsToCallback(respondsToCB) {} + + ~CheckerContext(); + + ExprEngine &getEngine() { + return Eng; + } + + AnalysisManager &getAnalysisManager() { + return Eng.getAnalysisManager(); + } + + ConstraintManager &getConstraintManager() { + return Eng.getConstraintManager(); + } + + StoreManager &getStoreManager() { + return Eng.getStoreManager(); + } + + ExplodedNodeSet &getNodeSet() { return Dst; } + StmtNodeBuilder &getNodeBuilder() { return B; } + ExplodedNode *&getPredecessor() { return Pred; } + const GRState *getState() { return ST ? ST : B.GetState(Pred); } + + ASTContext &getASTContext() { + return Eng.getContext(); + } + + BugReporter &getBugReporter() { + return Eng.getBugReporter(); + } + + SourceManager &getSourceManager() { + return getBugReporter().getSourceManager(); + } + + SValBuilder &getSValBuilder() { + return Eng.getSValBuilder(); + } + + ExplodedNode *generateNode(bool autoTransition = true) { + assert(statement && "Only transitions with statements currently supported"); + ExplodedNode *N = generateNodeImpl(statement, getState(), false); + if (N && autoTransition) + Dst.Add(N); + return N; + } + + ExplodedNode *generateNode(const Stmt *stmt, const GRState *state, + bool autoTransition = true) { + assert(state); + ExplodedNode *N = generateNodeImpl(stmt, state, false); + if (N && autoTransition) + addTransition(N); + return N; + } + + ExplodedNode *generateNode(const GRState *state, ExplodedNode *pred, + bool autoTransition = true) { + assert(statement && "Only transitions with statements currently supported"); + ExplodedNode *N = generateNodeImpl(statement, state, pred, false); + if (N && autoTransition) + addTransition(N); + return N; + } + + ExplodedNode *generateNode(const GRState *state, bool autoTransition = true) { + assert(statement && "Only transitions with statements currently supported"); + ExplodedNode *N = generateNodeImpl(statement, state, false); + if (N && autoTransition) + addTransition(N); + return N; + } + + ExplodedNode *generateSink(const Stmt *stmt, const GRState *state = 0) { + return generateNodeImpl(stmt, state ? state : getState(), true); + } + + ExplodedNode *generateSink(const GRState *state = 0) { + assert(statement && "Only transitions with statements currently supported"); + return generateNodeImpl(statement, state ? state : getState(), true); + } + + void addTransition(ExplodedNode *node) { + Dst.Add(node); + } + + void addTransition(const GRState *state) { + assert(state); + // If the 'state' is not new, we need to check if the cached state 'ST' + // is new. + if (state != getState() || (ST && ST != B.GetState(Pred))) + // state is new or equals to ST. + generateNode(state, true); + else + Dst.Add(Pred); + } + + // Generate a node with a new program point different from the one that will + // be created by the StmtNodeBuilder. + void addTransition(const GRState *state, ProgramPoint Loc) { + ExplodedNode *N = B.generateNode(Loc, state, Pred); + if (N) + addTransition(N); + } + + void EmitReport(BugReport *R) { + Eng.getBugReporter().EmitReport(R); + } + + AnalysisContext *getCurrentAnalysisContext() const { + return Pred->getLocationContext()->getAnalysisContext(); + } + +private: + ExplodedNode *generateNodeImpl(const Stmt* stmt, const GRState *state, + bool markAsSink) { + ExplodedNode *node = B.generateNode(stmt, state, Pred); + if (markAsSink && node) + node->markAsSink(); + return node; + } + + ExplodedNode *generateNodeImpl(const Stmt* stmt, const GRState *state, + ExplodedNode *pred, bool markAsSink) { + ExplodedNode *node = B.generateNode(stmt, state, pred); + if (markAsSink && node) + node->markAsSink(); + return node; + } +}; + +class Checker { +private: + friend class ExprEngine; + + // FIXME: Remove the 'tag' option. + void GR_Visit(ExplodedNodeSet &Dst, + StmtNodeBuilder &Builder, + ExprEngine &Eng, + const Stmt *S, + ExplodedNode *Pred, void *tag, bool isPrevisit, + bool& respondsToCallback) { + CheckerContext C(Dst, Builder, Eng, Pred, tag, + isPrevisit ? ProgramPoint::PreStmtKind : + ProgramPoint::PostStmtKind, &respondsToCallback, S); + if (isPrevisit) + _PreVisit(C, S); + else + _PostVisit(C, S); + } + + bool GR_evalNilReceiver(ExplodedNodeSet &Dst, StmtNodeBuilder &Builder, + ExprEngine &Eng, const ObjCMessageExpr *ME, + ExplodedNode *Pred, const GRState *state, void *tag) { + CheckerContext C(Dst, Builder, Eng, Pred, tag, ProgramPoint::PostStmtKind, + 0, ME, state); + return evalNilReceiver(C, ME); + } + + bool GR_evalCallExpr(ExplodedNodeSet &Dst, StmtNodeBuilder &Builder, + ExprEngine &Eng, const CallExpr *CE, + ExplodedNode *Pred, void *tag) { + CheckerContext C(Dst, Builder, Eng, Pred, tag, ProgramPoint::PostStmtKind, + 0, CE); + return evalCallExpr(C, CE); + } + + // FIXME: Remove the 'tag' option. + void GR_VisitBind(ExplodedNodeSet &Dst, + StmtNodeBuilder &Builder, ExprEngine &Eng, + const Stmt *StoreE, ExplodedNode *Pred, void *tag, + SVal location, SVal val, + bool isPrevisit) { + CheckerContext C(Dst, Builder, Eng, Pred, tag, + isPrevisit ? ProgramPoint::PreStmtKind : + ProgramPoint::PostStmtKind, 0, StoreE); + assert(isPrevisit && "Only previsit supported for now."); + PreVisitBind(C, StoreE, location, val); + } + + // FIXME: Remove the 'tag' option. + void GR_visitLocation(ExplodedNodeSet &Dst, + StmtNodeBuilder &Builder, + ExprEngine &Eng, + const Stmt *S, + ExplodedNode *Pred, const GRState *state, + SVal location, + void *tag, bool isLoad) { + CheckerContext C(Dst, Builder, Eng, Pred, tag, + isLoad ? ProgramPoint::PreLoadKind : + ProgramPoint::PreStoreKind, 0, S, state); + visitLocation(C, S, location); + } + + void GR_evalDeadSymbols(ExplodedNodeSet &Dst, StmtNodeBuilder &Builder, + ExprEngine &Eng, const Stmt *S, ExplodedNode *Pred, + SymbolReaper &SymReaper, void *tag) { + CheckerContext C(Dst, Builder, Eng, Pred, tag, + ProgramPoint::PostPurgeDeadSymbolsKind, 0, S); + evalDeadSymbols(C, SymReaper); + } + +public: + virtual ~Checker(); + virtual void _PreVisit(CheckerContext &C, const Stmt *S) {} + virtual void _PostVisit(CheckerContext &C, const Stmt *S) {} + virtual void visitLocation(CheckerContext &C, const Stmt *S, SVal location) {} + virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE, + SVal location, SVal val) {} + virtual void evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper) {} + virtual void evalEndPath(EndPathNodeBuilder &B, void *tag, + ExprEngine &Eng) {} + + virtual void MarkLiveSymbols(const GRState *state, SymbolReaper &SymReaper) {} + + virtual void VisitBranchCondition(BranchNodeBuilder &Builder, + ExprEngine &Eng, + const Stmt *Condition, void *tag) {} + + virtual bool evalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME) { + return false; + } + + virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE) { + return false; + } + + virtual const GRState *evalAssume(const GRState *state, SVal Cond, + bool Assumption, bool *respondsToCallback) { + *respondsToCallback = false; + return state; + } + + virtual bool WantsRegionChangeUpdate(const GRState *state) { return false; } + + virtual const GRState *EvalRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End, + bool *respondsToCallback) { + *respondsToCallback = false; + return state; + } + + virtual void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, + ExprEngine &Eng) {} +}; + +} // end GR namespace + +} // end clang namespace + +#endif + diff --git a/include/clang/StaticAnalyzer/PathSensitive/CheckerHelpers.h b/include/clang/StaticAnalyzer/PathSensitive/CheckerHelpers.h new file mode 100644 index 0000000000..005e0e5863 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/CheckerHelpers.h @@ -0,0 +1,44 @@ +//== CheckerHelpers.h - 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 CheckerVisitor. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_PATHSENSITIVE_CHECKERHELPERS +#define LLVM_CLANG_GR_PATHSENSITIVE_CHECKERHELPERS + +#include "clang/AST/Stmt.h" + +namespace clang { + +namespace ento { + +bool containsMacro(const Stmt *S); +bool containsEnum(const Stmt *S); +bool containsStaticLocal(const Stmt *S); +bool containsBuiltinOffsetOf(const Stmt *S); +template bool containsStmt(const Stmt *S) { + if (isa(S)) + return true; + + for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); + ++I) + if (const Stmt *child = *I) + if (containsStmt(child)) + return true; + + return false; +} + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/CheckerVisitor.def b/include/clang/StaticAnalyzer/PathSensitive/CheckerVisitor.def new file mode 100644 index 0000000000..3ee8071985 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/CheckerVisitor.def @@ -0,0 +1,43 @@ +//===-- CheckerVisitor.def - Metadata for CheckerVisitor ----------------*-===// +// +// 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 AST nodes accepted by the CheckerVisitor class. +// +//===---------------------------------------------------------------------===// + +#ifndef PREVISIT +#define PREVISIT(NODE, FALLBACK) +#endif + +#ifndef POSTVISIT +#define POSTVISIT(NODE, FALLBACK) +#endif + +PREVISIT(ArraySubscriptExpr, Stmt) +PREVISIT(BinaryOperator, Stmt) +PREVISIT(CallExpr, GenericCall) +PREVISIT(CStyleCastExpr, CastExpr) +PREVISIT(CXXFunctionalCastExpr, CastExpr) +PREVISIT(CXXOperatorCallExpr, GenericCall) +PREVISIT(CXXMemberCallExpr, GenericCall) +PREVISIT(DeclStmt, Stmt) +PREVISIT(ImplicitCastExpr, CastExpr) +PREVISIT(ObjCAtSynchronizedStmt, Stmt) +PREVISIT(ObjCMessageExpr, Stmt) +PREVISIT(ReturnStmt, Stmt) + +POSTVISIT(BlockExpr, Stmt) +POSTVISIT(BinaryOperator, Stmt) +POSTVISIT(CallExpr, GenericCall) +POSTVISIT(CXXOperatorCallExpr, GenericCall) +POSTVISIT(CXXMemberCallExpr, GenericCall) +POSTVISIT(ObjCMessageExpr, Stmt) + +#undef PREVISIT +#undef POSTVISIT diff --git a/include/clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h b/include/clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h new file mode 100644 index 0000000000..68677f5503 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h @@ -0,0 +1,111 @@ +//== CheckerVisitor.h - Abstract visitor 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 CheckerVisitor. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_CHECKERVISITOR +#define LLVM_CLANG_GR_CHECKERVISITOR +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" + +namespace clang { + +namespace ento { + +//===----------------------------------------------------------------------===// +// Checker visitor interface. Used by subclasses of Checker to specify their +// own checker visitor logic. +//===----------------------------------------------------------------------===// + +/// CheckerVisitor - This class implements a simple visitor for Stmt subclasses. +/// Since Expr derives from Stmt, this also includes support for visiting Exprs. +template +class CheckerVisitor : public Checker { +public: + virtual void _PreVisit(CheckerContext &C, const Stmt *S) { + PreVisit(C, S); + } + + virtual void _PostVisit(CheckerContext &C, const Stmt *S) { + PostVisit(C, S); + } + + void PreVisit(CheckerContext &C, const Stmt *S) { + switch (S->getStmtClass()) { + default: + assert(false && "Unsupport statement."); + return; + case Stmt::CompoundAssignOperatorClass: + static_cast(this)->PreVisitBinaryOperator(C, + static_cast(S)); + break; + +#define PREVISIT(NAME, FALLBACK) \ +case Stmt::NAME ## Class:\ +static_cast(this)->PreVisit ## NAME(C,static_cast(S));\ +break; +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.def" + } + } + + void PostVisit(CheckerContext &C, const Stmt *S) { + switch (S->getStmtClass()) { + default: + assert(false && "Unsupport statement."); + return; + case Stmt::CompoundAssignOperatorClass: + static_cast(this)->PostVisitBinaryOperator(C, + static_cast(S)); + break; + +#define POSTVISIT(NAME, FALLBACK) \ +case Stmt::NAME ## Class:\ +static_cast(this)->\ +PostVisit ## NAME(C,static_cast(S));\ +break; +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.def" + } + } + + void PreVisitGenericCall(CheckerContext &C, const CallExpr *CE) { + static_cast(this)->PreVisitStmt(C, CE); + } + void PostVisitGenericCall(CheckerContext &C, const CallExpr *CE) { + static_cast(this)->PostVisitStmt(C, CE); + } + + void PreVisitStmt(CheckerContext &C, const Stmt *S) { + *C.respondsToCallback = false; + } + + void PostVisitStmt(CheckerContext &C, const Stmt *S) { + *C.respondsToCallback = false; + } + + void PreVisitCastExpr(CheckerContext &C, const CastExpr *E) { + static_cast(this)->PreVisitStmt(C, E); + } + +#define PREVISIT(NAME, FALLBACK) \ +void PreVisit ## NAME(CheckerContext &C, const NAME* S) {\ + static_cast(this)->PreVisit ## FALLBACK(C, S);\ +} +#define POSTVISIT(NAME, FALLBACK) \ +void PostVisit ## NAME(CheckerContext &C, const NAME* S) {\ + static_cast(this)->PostVisit ## FALLBACK(C, S);\ +} +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.def" +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/ConstraintManager.h b/include/clang/StaticAnalyzer/PathSensitive/ConstraintManager.h new file mode 100644 index 0000000000..81f4558e6a --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/ConstraintManager.h @@ -0,0 +1,76 @@ +//== ConstraintManager.h - Constraints on symbolic 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 defined the interface to manage constraints on symbolic values. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_CONSTRAINT_MANAGER_H +#define LLVM_CLANG_GR_CONSTRAINT_MANAGER_H + +// FIXME: Typedef LiveSymbolsTy/DeadSymbolsTy at a more appropriate place. +#include "clang/StaticAnalyzer/PathSensitive/Store.h" + +namespace llvm { +class APSInt; +} + +namespace clang { + +namespace ento { + +class GRState; +class GRStateManager; +class SubEngine; +class SVal; + +class ConstraintManager { +public: + virtual ~ConstraintManager(); + virtual const GRState *assume(const GRState *state, DefinedSVal Cond, + bool Assumption) = 0; + + std::pair assumeDual(const GRState *state, + DefinedSVal Cond) { + return std::make_pair(assume(state, Cond, true), + assume(state, Cond, false)); + } + + virtual const llvm::APSInt* getSymVal(const GRState *state, + SymbolRef sym) const = 0; + + virtual bool isEqual(const GRState *state, SymbolRef sym, + const llvm::APSInt& V) const = 0; + + virtual const GRState *RemoveDeadBindings(const GRState *state, + SymbolReaper& SymReaper) = 0; + + virtual void print(const GRState *state, llvm::raw_ostream& Out, + const char* nl, const char *sep) = 0; + + virtual void EndPath(const GRState *state) {} + + /// canReasonAbout - Not all ConstraintManagers can accurately reason about + /// all SVal values. This method returns true if the ConstraintManager can + /// reasonably handle a given SVal value. This is typically queried by + /// ExprEngine to determine if the value should be replaced with a + /// conjured symbolic value in order to recover some precision. + virtual bool canReasonAbout(SVal X) const = 0; +}; + +ConstraintManager* CreateBasicConstraintManager(GRStateManager& statemgr, + SubEngine &subengine); +ConstraintManager* CreateRangeConstraintManager(GRStateManager& statemgr, + SubEngine &subengine); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/PathSensitive/CoreEngine.h new file mode 100644 index 0000000000..3d63982d73 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/CoreEngine.h @@ -0,0 +1,542 @@ +//==- CoreEngine.h - Path-Sensitive Dataflow Engine ----------------*- 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 generic engine for intraprocedural, path-sensitive, +// dataflow analysis via graph reachability. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_COREENGINE +#define LLVM_CLANG_GR_COREENGINE + +#include "clang/AST/Expr.h" +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/PathSensitive/WorkList.h" +#include "clang/StaticAnalyzer/PathSensitive/BlockCounter.h" +#include "clang/StaticAnalyzer/PathSensitive/SubEngine.h" +#include "llvm/ADT/OwningPtr.h" + +namespace clang { + +namespace ento { + +//===----------------------------------------------------------------------===// +/// CoreEngine - Implements the core logic of the graph-reachability +/// analysis. It traverses the CFG and generates the ExplodedGraph. +/// Program "states" are treated as opaque void pointers. +/// The template class CoreEngine (which subclasses CoreEngine) +/// provides the matching component to the engine that knows the actual types +/// for states. Note that this engine only dispatches to transfer functions +/// at the statement and block-level. The analyses themselves must implement +/// any transfer function logic and the sub-expression level (if any). +class CoreEngine { + friend class StmtNodeBuilder; + friend class BranchNodeBuilder; + friend class IndirectGotoNodeBuilder; + friend class SwitchNodeBuilder; + friend class EndPathNodeBuilder; + friend class CallEnterNodeBuilder; + friend class CallExitNodeBuilder; + +public: + typedef std::vector > + BlocksAborted; +private: + + SubEngine& SubEng; + + /// G - The simulation graph. Each node is a (location,state) pair. + llvm::OwningPtr G; + + /// WList - A set of queued nodes that need to be processed by the + /// worklist algorithm. It is up to the implementation of WList to decide + /// the order that nodes are processed. + WorkList* WList; + + /// BCounterFactory - A factory object for created BlockCounter objects. + /// These are used to record for key nodes in the ExplodedGraph the + /// number of times different CFGBlocks have been visited along a path. + BlockCounter::Factory BCounterFactory; + + /// The locations where we stopped doing work because we visited a location + /// too many times. + BlocksAborted blocksAborted; + + void generateNode(const ProgramPoint& Loc, const GRState* State, + ExplodedNode* Pred); + + void HandleBlockEdge(const BlockEdge& E, ExplodedNode* Pred); + void HandleBlockEntrance(const BlockEntrance& E, ExplodedNode* Pred); + void HandleBlockExit(const CFGBlock* B, ExplodedNode* Pred); + void HandlePostStmt(const CFGBlock* B, unsigned StmtIdx, ExplodedNode *Pred); + + void HandleBranch(const Stmt* Cond, const Stmt* Term, const CFGBlock* B, + ExplodedNode* Pred); + void HandleCallEnter(const CallEnter &L, const CFGBlock *Block, + unsigned Index, ExplodedNode *Pred); + void HandleCallExit(const CallExit &L, ExplodedNode *Pred); + + /// Get the initial state from the subengine. + const GRState* getInitialState(const LocationContext *InitLoc) { + return SubEng.getInitialState(InitLoc); + } + + void ProcessEndPath(EndPathNodeBuilder& Builder) { + SubEng.ProcessEndPath(Builder); + } + + void ProcessElement(const CFGElement E, StmtNodeBuilder& Builder) { + SubEng.ProcessElement(E, Builder); + } + + bool ProcessBlockEntrance(const CFGBlock* Blk, const ExplodedNode *Pred, + BlockCounter BC) { + return SubEng.ProcessBlockEntrance(Blk, Pred, BC); + } + + + void ProcessBranch(const Stmt* Condition, const Stmt* Terminator, + BranchNodeBuilder& Builder) { + SubEng.ProcessBranch(Condition, Terminator, Builder); + } + + + void ProcessIndirectGoto(IndirectGotoNodeBuilder& Builder) { + SubEng.ProcessIndirectGoto(Builder); + } + + + void ProcessSwitch(SwitchNodeBuilder& Builder) { + SubEng.ProcessSwitch(Builder); + } + + void ProcessCallEnter(CallEnterNodeBuilder &Builder) { + SubEng.ProcessCallEnter(Builder); + } + + void ProcessCallExit(CallExitNodeBuilder &Builder) { + SubEng.ProcessCallExit(Builder); + } + +private: + CoreEngine(const CoreEngine&); // Do not implement. + CoreEngine& operator=(const CoreEngine&); + +public: + /// Construct a CoreEngine object to analyze the provided CFG using + /// a DFS exploration of the exploded graph. + CoreEngine(SubEngine& subengine) + : SubEng(subengine), G(new ExplodedGraph()), + WList(WorkList::MakeBFS()), + BCounterFactory(G->getAllocator()) {} + + /// Construct a CoreEngine object to analyze the provided CFG and to + /// use the provided worklist object to execute the worklist algorithm. + /// The CoreEngine object assumes ownership of 'wlist'. + CoreEngine(WorkList* wlist, SubEngine& subengine) + : SubEng(subengine), G(new ExplodedGraph()), WList(wlist), + BCounterFactory(G->getAllocator()) {} + + ~CoreEngine() { + delete WList; + } + + /// getGraph - Returns the exploded graph. + ExplodedGraph& getGraph() { return *G.get(); } + + /// takeGraph - Returns the exploded graph. Ownership of the graph is + /// transfered to the caller. + ExplodedGraph* takeGraph() { return G.take(); } + + /// ExecuteWorkList - Run the worklist algorithm for a maximum number of + /// steps. Returns true if there is still simulation state on the worklist. + bool ExecuteWorkList(const LocationContext *L, unsigned Steps, + const GRState *InitState); + void ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps, + const GRState *InitState, + ExplodedNodeSet &Dst); + + // Functions for external checking of whether we have unfinished work + bool wasBlockAborted() const { return !blocksAborted.empty(); } + bool hasWorkRemaining() const { return wasBlockAborted() || WList->hasWork(); } + + WorkList *getWorkList() const { return WList; } + + BlocksAborted::const_iterator blocks_aborted_begin() const { + return blocksAborted.begin(); + } + BlocksAborted::const_iterator blocks_aborted_end() const { + return blocksAborted.end(); + } +}; + +class StmtNodeBuilder { + CoreEngine& Eng; + const CFGBlock& B; + const unsigned Idx; + ExplodedNode* Pred; + GRStateManager& Mgr; + +public: + bool PurgingDeadSymbols; + bool BuildSinks; + bool HasGeneratedNode; + ProgramPoint::Kind PointKind; + const void *Tag; + + const GRState* CleanedState; + + + typedef llvm::SmallPtrSet DeferredTy; + DeferredTy Deferred; + + void GenerateAutoTransition(ExplodedNode* N); + +public: + StmtNodeBuilder(const CFGBlock* b, unsigned idx, ExplodedNode* N, + CoreEngine* e, GRStateManager &mgr); + + ~StmtNodeBuilder(); + + ExplodedNode* getBasePredecessor() const { return Pred; } + + // FIXME: This should not be exposed. + WorkList *getWorkList() { return Eng.WList; } + + void SetCleanedState(const GRState* St) { + CleanedState = St; + } + + BlockCounter getBlockCounter() const { return Eng.WList->getBlockCounter();} + + unsigned getCurrentBlockCount() const { + return getBlockCounter().getNumVisited( + Pred->getLocationContext()->getCurrentStackFrame(), + B.getBlockID()); + } + + ExplodedNode* generateNode(PostStmt PP,const GRState* St,ExplodedNode* Pred) { + HasGeneratedNode = true; + return generateNodeInternal(PP, St, Pred); + } + + ExplodedNode* generateNode(const Stmt *S, const GRState *St, + ExplodedNode *Pred, ProgramPoint::Kind K) { + HasGeneratedNode = true; + + if (PurgingDeadSymbols) + K = ProgramPoint::PostPurgeDeadSymbolsKind; + + return generateNodeInternal(S, St, Pred, K, Tag); + } + + ExplodedNode* generateNode(const Stmt *S, const GRState *St, + ExplodedNode *Pred) { + return generateNode(S, St, Pred, PointKind); + } + + ExplodedNode *generateNode(const ProgramPoint &PP, const GRState* State, + ExplodedNode* Pred) { + HasGeneratedNode = true; + return generateNodeInternal(PP, State, Pred); + } + + ExplodedNode* + generateNodeInternal(const ProgramPoint &PP, const GRState* State, + ExplodedNode* Pred); + + ExplodedNode* + generateNodeInternal(const Stmt* S, const GRState* State, ExplodedNode* Pred, + ProgramPoint::Kind K = ProgramPoint::PostStmtKind, + const void *tag = 0); + + /// getStmt - Return the current block-level expression associated with + /// this builder. + const Stmt* getStmt() const { + CFGStmt CS = B[Idx].getAs(); + if (CS) + return CS.getStmt(); + else + return 0; + } + + /// getBlock - Return the CFGBlock associated with the block-level expression + /// of this builder. + const CFGBlock* getBlock() const { return &B; } + + unsigned getIndex() const { return Idx; } + + const GRState* GetState(ExplodedNode* Pred) const { + if (Pred == getBasePredecessor()) + return CleanedState; + else + return Pred->getState(); + } + + ExplodedNode* MakeNode(ExplodedNodeSet& Dst, const Stmt* S, + ExplodedNode* Pred, const GRState* St) { + return MakeNode(Dst, S, Pred, St, PointKind); + } + + ExplodedNode* MakeNode(ExplodedNodeSet& Dst, const Stmt* S,ExplodedNode* Pred, + const GRState* St, ProgramPoint::Kind K); + + ExplodedNode* MakeSinkNode(ExplodedNodeSet& Dst, const Stmt* S, + ExplodedNode* Pred, const GRState* St) { + bool Tmp = BuildSinks; + BuildSinks = true; + ExplodedNode* N = MakeNode(Dst, S, Pred, St); + BuildSinks = Tmp; + return N; + } +}; + +class BranchNodeBuilder { + CoreEngine& Eng; + const CFGBlock* Src; + const CFGBlock* DstT; + const CFGBlock* DstF; + ExplodedNode* Pred; + + typedef llvm::SmallVector DeferredTy; + DeferredTy Deferred; + + bool GeneratedTrue; + bool GeneratedFalse; + bool InFeasibleTrue; + bool InFeasibleFalse; + +public: + BranchNodeBuilder(const CFGBlock* src, const CFGBlock* dstT, + const CFGBlock* dstF, ExplodedNode* pred, CoreEngine* e) + : Eng(*e), Src(src), DstT(dstT), DstF(dstF), Pred(pred), + GeneratedTrue(false), GeneratedFalse(false), + InFeasibleTrue(!DstT), InFeasibleFalse(!DstF) {} + + ~BranchNodeBuilder(); + + ExplodedNode* getPredecessor() const { return Pred; } + + const ExplodedGraph& getGraph() const { return *Eng.G; } + + BlockCounter getBlockCounter() const { return Eng.WList->getBlockCounter();} + + ExplodedNode* generateNode(const GRState* State, bool branch); + + const CFGBlock* getTargetBlock(bool branch) const { + return branch ? DstT : DstF; + } + + void markInfeasible(bool branch) { + if (branch) + InFeasibleTrue = GeneratedTrue = true; + else + InFeasibleFalse = GeneratedFalse = true; + } + + bool isFeasible(bool branch) { + return branch ? !InFeasibleTrue : !InFeasibleFalse; + } + + const GRState* getState() const { + return getPredecessor()->getState(); + } +}; + +class IndirectGotoNodeBuilder { + CoreEngine& Eng; + const CFGBlock* Src; + const CFGBlock& DispatchBlock; + const Expr* E; + ExplodedNode* Pred; + +public: + IndirectGotoNodeBuilder(ExplodedNode* pred, const CFGBlock* src, + const Expr* e, const CFGBlock* dispatch, CoreEngine* eng) + : Eng(*eng), Src(src), DispatchBlock(*dispatch), E(e), Pred(pred) {} + + class iterator { + CFGBlock::const_succ_iterator I; + + friend class IndirectGotoNodeBuilder; + iterator(CFGBlock::const_succ_iterator i) : I(i) {} + public: + + iterator& operator++() { ++I; return *this; } + bool operator!=(const iterator& X) const { return I != X.I; } + + const LabelStmt* getLabel() const { + return llvm::cast((*I)->getLabel()); + } + + const CFGBlock* getBlock() const { + return *I; + } + }; + + iterator begin() { return iterator(DispatchBlock.succ_begin()); } + iterator end() { return iterator(DispatchBlock.succ_end()); } + + ExplodedNode* generateNode(const iterator& I, const GRState* State, + bool isSink = false); + + const Expr* getTarget() const { return E; } + + const GRState* getState() const { return Pred->State; } +}; + +class SwitchNodeBuilder { + CoreEngine& Eng; + const CFGBlock* Src; + const Expr* Condition; + ExplodedNode* Pred; + +public: + SwitchNodeBuilder(ExplodedNode* pred, const CFGBlock* src, + const Expr* condition, CoreEngine* eng) + : Eng(*eng), Src(src), Condition(condition), Pred(pred) {} + + class iterator { + CFGBlock::const_succ_reverse_iterator I; + + friend class SwitchNodeBuilder; + iterator(CFGBlock::const_succ_reverse_iterator i) : I(i) {} + + public: + iterator& operator++() { ++I; return *this; } + bool operator!=(const iterator &X) const { return I != X.I; } + bool operator==(const iterator &X) const { return I == X.I; } + + const CaseStmt* getCase() const { + return llvm::cast((*I)->getLabel()); + } + + const CFGBlock* getBlock() const { + return *I; + } + }; + + iterator begin() { return iterator(Src->succ_rbegin()+1); } + iterator end() { return iterator(Src->succ_rend()); } + + const SwitchStmt *getSwitch() const { + return llvm::cast(Src->getTerminator()); + } + + ExplodedNode* generateCaseStmtNode(const iterator& I, const GRState* State); + + ExplodedNode* generateDefaultCaseNode(const GRState* State, + bool isSink = false); + + const Expr* getCondition() const { return Condition; } + + const GRState* getState() const { return Pred->State; } +}; + +class EndPathNodeBuilder { + CoreEngine &Eng; + const CFGBlock& B; + ExplodedNode* Pred; + +public: + bool HasGeneratedNode; + +public: + EndPathNodeBuilder(const CFGBlock* b, ExplodedNode* N, CoreEngine* e) + : Eng(*e), B(*b), Pred(N), HasGeneratedNode(false) {} + + ~EndPathNodeBuilder(); + + WorkList &getWorkList() { return *Eng.WList; } + + ExplodedNode* getPredecessor() const { return Pred; } + + BlockCounter getBlockCounter() const { + return Eng.WList->getBlockCounter(); + } + + unsigned getCurrentBlockCount() const { + return getBlockCounter().getNumVisited( + Pred->getLocationContext()->getCurrentStackFrame(), + B.getBlockID()); + } + + ExplodedNode* generateNode(const GRState* State, const void *tag = 0, + ExplodedNode *P = 0); + + void GenerateCallExitNode(const GRState *state); + + const CFGBlock* getBlock() const { return &B; } + + const GRState* getState() const { + return getPredecessor()->getState(); + } +}; + +class CallEnterNodeBuilder { + CoreEngine &Eng; + + const ExplodedNode *Pred; + + // The call site. For implicit automatic object dtor, this is the trigger + // statement. + const Stmt *CE; + + // The context of the callee. + const StackFrameContext *CalleeCtx; + + // The parent block of the CallExpr. + const CFGBlock *Block; + + // The CFGBlock index of the CallExpr. + unsigned Index; + +public: + CallEnterNodeBuilder(CoreEngine &eng, const ExplodedNode *pred, + const Stmt *s, const StackFrameContext *callee, + const CFGBlock *blk, unsigned idx) + : Eng(eng), Pred(pred), CE(s), CalleeCtx(callee), Block(blk), Index(idx) {} + + const GRState *getState() const { return Pred->getState(); } + + const LocationContext *getLocationContext() const { + return Pred->getLocationContext(); + } + + const Stmt *getCallExpr() const { return CE; } + + const StackFrameContext *getCalleeContext() const { return CalleeCtx; } + + const CFGBlock *getBlock() const { return Block; } + + unsigned getIndex() const { return Index; } + + void generateNode(const GRState *state); +}; + +class CallExitNodeBuilder { + CoreEngine &Eng; + const ExplodedNode *Pred; + +public: + CallExitNodeBuilder(CoreEngine &eng, const ExplodedNode *pred) + : Eng(eng), Pred(pred) {} + + const ExplodedNode *getPredecessor() const { return Pred; } + + const GRState *getState() const { return Pred->getState(); } + + void generateNode(const GRState *state); +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/Environment.h b/include/clang/StaticAnalyzer/PathSensitive/Environment.h new file mode 100644 index 0000000000..8f2ebce670 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/Environment.h @@ -0,0 +1,106 @@ +//== Environment.h - Map from Stmt* to Locations/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 defined the Environment and EnvironmentManager classes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_ENVIRONMENT_H +#define LLVM_CLANG_GR_ENVIRONMENT_H + +#include "clang/StaticAnalyzer/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" +#include "llvm/ADT/ImmutableMap.h" + +namespace clang { + +class LiveVariables; + +namespace ento { + +class EnvironmentManager; +class SValBuilder; + +/// Environment - An immutable map from Stmts to their current +/// symbolic values (SVals). +/// +class Environment { +private: + friend class EnvironmentManager; + + // Type definitions. + typedef llvm::ImmutableMap BindingsTy; + + // Data. + BindingsTy ExprBindings; + + Environment(BindingsTy eb) + : ExprBindings(eb) {} + + SVal lookupExpr(const Stmt* E) const; + +public: + typedef BindingsTy::iterator iterator; + iterator begin() const { return ExprBindings.begin(); } + iterator end() const { return ExprBindings.end(); } + + + /// GetSVal - Fetches the current binding of the expression in the + /// Environment. + SVal getSVal(const Stmt* Ex, SValBuilder& svalBuilder) const; + + /// Profile - Profile the contents of an Environment object for use + /// in a FoldingSet. + static void Profile(llvm::FoldingSetNodeID& ID, const Environment* env) { + env->ExprBindings.Profile(ID); + } + + /// Profile - Used to profile the contents of this object for inclusion + /// in a FoldingSet. + void Profile(llvm::FoldingSetNodeID& ID) const { + Profile(ID, this); + } + + bool operator==(const Environment& RHS) const { + return ExprBindings == RHS.ExprBindings; + } +}; + +class EnvironmentManager { +private: + typedef Environment::BindingsTy::Factory FactoryTy; + FactoryTy F; + +public: + EnvironmentManager(llvm::BumpPtrAllocator& Allocator) : F(Allocator) {} + ~EnvironmentManager() {} + + Environment getInitialEnvironment() { + return Environment(F.getEmptyMap()); + } + + /// Bind the value 'V' to the statement 'S'. + Environment bindExpr(Environment Env, const Stmt *S, SVal V, + bool Invalidate); + + /// Bind the location 'location' and value 'V' to the statement 'S'. This + /// is used when simulating loads/stores. + Environment bindExprAndLocation(Environment Env, const Stmt *S, SVal location, + SVal V); + + Environment RemoveDeadBindings(Environment Env, + SymbolReaper &SymReaper, const GRState *ST, + llvm::SmallVectorImpl& RegionRoots); +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h new file mode 100644 index 0000000000..7f8c4c60c4 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h @@ -0,0 +1,435 @@ +//=-- ExplodedGraph.h - Local, Path-Sens. "Exploded Graph" -*- 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 template classes ExplodedNode and ExplodedGraph, +// which represent a path-sensitive, intra-procedural "exploded graph." +// See "Precise interprocedural dataflow analysis via graph reachability" +// by Reps, Horwitz, and Sagiv +// (http://portal.acm.org/citation.cfm?id=199462) for the definition of an +// exploded graph. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_EXPLODEDGRAPH +#define LLVM_CLANG_GR_EXPLODEDGRAPH + +#include "clang/Analysis/ProgramPoint.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/AST/Decl.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/SmallPtrSet.h" +#include "llvm/Support/Allocator.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/ADT/GraphTraits.h" +#include "llvm/ADT/DepthFirstIterator.h" +#include "llvm/Support/Casting.h" +#include "clang/Analysis/Support/BumpVector.h" + +namespace clang { + +class CFG; + +namespace ento { + +class GRState; +class ExplodedGraph; + +//===----------------------------------------------------------------------===// +// ExplodedGraph "implementation" classes. These classes are not typed to +// contain a specific kind of state. Typed-specialized versions are defined +// on top of these classes. +//===----------------------------------------------------------------------===// + +class ExplodedNode : public llvm::FoldingSetNode { + friend class ExplodedGraph; + friend class CoreEngine; + friend class StmtNodeBuilder; + friend class BranchNodeBuilder; + friend class IndirectGotoNodeBuilder; + friend class SwitchNodeBuilder; + friend class EndPathNodeBuilder; + + class NodeGroup { + enum { Size1 = 0x0, SizeOther = 0x1, AuxFlag = 0x2, Mask = 0x3 }; + uintptr_t P; + + unsigned getKind() const { + return P & 0x1; + } + + void* getPtr() const { + assert (!getFlag()); + return reinterpret_cast(P & ~Mask); + } + + ExplodedNode *getNode() const { + return reinterpret_cast(getPtr()); + } + + public: + NodeGroup() : P(0) {} + + ExplodedNode **begin() const; + + ExplodedNode **end() const; + + unsigned size() const; + + bool empty() const { return (P & ~Mask) == 0; } + + void addNode(ExplodedNode* N, ExplodedGraph &G); + + void setFlag() { + assert(P == 0); + P = AuxFlag; + } + + bool getFlag() const { + return P & AuxFlag ? true : false; + } + }; + + /// Location - The program location (within a function body) associated + /// with this node. + const ProgramPoint Location; + + /// State - The state associated with this node. + const GRState* State; + + /// Preds - The predecessors of this node. + NodeGroup Preds; + + /// Succs - The successors of this node. + NodeGroup Succs; + +public: + + explicit ExplodedNode(const ProgramPoint& loc, const GRState* state) + : Location(loc), State(state) {} + + /// getLocation - Returns the edge associated with the given node. + ProgramPoint getLocation() const { return Location; } + + const LocationContext *getLocationContext() const { + return getLocation().getLocationContext(); + } + + const Decl &getCodeDecl() const { return *getLocationContext()->getDecl(); } + + CFG &getCFG() const { return *getLocationContext()->getCFG(); } + + ParentMap &getParentMap() const {return getLocationContext()->getParentMap();} + + LiveVariables &getLiveVariables() const { + return *getLocationContext()->getLiveVariables(); + } + + const GRState* getState() const { return State; } + + template + const T* getLocationAs() const { return llvm::dyn_cast(&Location); } + + static void Profile(llvm::FoldingSetNodeID &ID, + const ProgramPoint& Loc, const GRState* state) { + ID.Add(Loc); + ID.AddPointer(state); + } + + void Profile(llvm::FoldingSetNodeID& ID) const { + Profile(ID, getLocation(), getState()); + } + + /// addPredeccessor - Adds a predecessor to the current node, and + /// in tandem add this node as a successor of the other node. + void addPredecessor(ExplodedNode* V, ExplodedGraph &G); + + unsigned succ_size() const { return Succs.size(); } + unsigned pred_size() const { return Preds.size(); } + bool succ_empty() const { return Succs.empty(); } + bool pred_empty() const { return Preds.empty(); } + + bool isSink() const { return Succs.getFlag(); } + void markAsSink() { Succs.setFlag(); } + + ExplodedNode* getFirstPred() { + return pred_empty() ? NULL : *(pred_begin()); + } + + const ExplodedNode* getFirstPred() const { + return const_cast(this)->getFirstPred(); + } + + // Iterators over successor and predecessor vertices. + typedef ExplodedNode** succ_iterator; + typedef const ExplodedNode* const * const_succ_iterator; + typedef ExplodedNode** pred_iterator; + typedef const ExplodedNode* const * const_pred_iterator; + + pred_iterator pred_begin() { return Preds.begin(); } + pred_iterator pred_end() { return Preds.end(); } + + const_pred_iterator pred_begin() const { + return const_cast(this)->pred_begin(); + } + const_pred_iterator pred_end() const { + return const_cast(this)->pred_end(); + } + + succ_iterator succ_begin() { return Succs.begin(); } + succ_iterator succ_end() { return Succs.end(); } + + const_succ_iterator succ_begin() const { + return const_cast(this)->succ_begin(); + } + const_succ_iterator succ_end() const { + return const_cast(this)->succ_end(); + } + + // For debugging. + +public: + + class Auditor { + public: + virtual ~Auditor(); + virtual void AddEdge(ExplodedNode* Src, ExplodedNode* Dst) = 0; + }; + + static void SetAuditor(Auditor* A); +}; + +// FIXME: Is this class necessary? +class InterExplodedGraphMap { + llvm::DenseMap M; + friend class ExplodedGraph; + +public: + ExplodedNode* getMappedNode(const ExplodedNode* N) const; + + InterExplodedGraphMap() {} + virtual ~InterExplodedGraphMap() {} +}; + +class ExplodedGraph { +protected: + friend class CoreEngine; + + // Type definitions. + typedef llvm::SmallVector RootsTy; + typedef llvm::SmallVector EndNodesTy; + + /// Roots - The roots of the simulation graph. Usually there will be only + /// one, but clients are free to establish multiple subgraphs within a single + /// SimulGraph. Moreover, these subgraphs can often merge when paths from + /// different roots reach the same state at the same program location. + RootsTy Roots; + + /// EndNodes - The nodes in the simulation graph which have been + /// specially marked as the endpoint of an abstract simulation path. + EndNodesTy EndNodes; + + /// Nodes - The nodes in the graph. + llvm::FoldingSet Nodes; + + /// BVC - Allocator and context for allocating nodes and their predecessor + /// and successor groups. + BumpVectorContext BVC; + + /// NumNodes - The number of nodes in the graph. + unsigned NumNodes; + +public: + /// getNode - Retrieve the node associated with a (Location,State) pair, + /// where the 'Location' is a ProgramPoint in the CFG. If no node for + /// this pair exists, it is created. IsNew is set to true if + /// the node was freshly created. + + ExplodedNode* getNode(const ProgramPoint& L, const GRState *State, + bool* IsNew = 0); + + ExplodedGraph* MakeEmptyGraph() const { + return new ExplodedGraph(); + } + + /// addRoot - Add an untyped node to the set of roots. + ExplodedNode* addRoot(ExplodedNode* V) { + Roots.push_back(V); + return V; + } + + /// addEndOfPath - Add an untyped node to the set of EOP nodes. + ExplodedNode* addEndOfPath(ExplodedNode* V) { + EndNodes.push_back(V); + return V; + } + + ExplodedGraph() : NumNodes(0) {} + + ~ExplodedGraph() {} + + unsigned num_roots() const { return Roots.size(); } + unsigned num_eops() const { return EndNodes.size(); } + + bool empty() const { return NumNodes == 0; } + unsigned size() const { return NumNodes; } + + // Iterators. + typedef ExplodedNode NodeTy; + typedef llvm::FoldingSet AllNodesTy; + typedef NodeTy** roots_iterator; + typedef NodeTy* const * const_roots_iterator; + typedef NodeTy** eop_iterator; + typedef NodeTy* const * const_eop_iterator; + typedef AllNodesTy::iterator node_iterator; + typedef AllNodesTy::const_iterator const_node_iterator; + + node_iterator nodes_begin() { return Nodes.begin(); } + + node_iterator nodes_end() { return Nodes.end(); } + + const_node_iterator nodes_begin() const { return Nodes.begin(); } + + const_node_iterator nodes_end() const { return Nodes.end(); } + + roots_iterator roots_begin() { return Roots.begin(); } + + roots_iterator roots_end() { return Roots.end(); } + + const_roots_iterator roots_begin() const { return Roots.begin(); } + + const_roots_iterator roots_end() const { return Roots.end(); } + + eop_iterator eop_begin() { return EndNodes.begin(); } + + eop_iterator eop_end() { return EndNodes.end(); } + + const_eop_iterator eop_begin() const { return EndNodes.begin(); } + + const_eop_iterator eop_end() const { return EndNodes.end(); } + + llvm::BumpPtrAllocator & getAllocator() { return BVC.getAllocator(); } + BumpVectorContext &getNodeAllocator() { return BVC; } + + typedef llvm::DenseMap NodeMap; + + std::pair + Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd, + llvm::DenseMap *InverseMap = 0) const; + + ExplodedGraph* TrimInternal(const ExplodedNode* const * NBeg, + const ExplodedNode* const * NEnd, + InterExplodedGraphMap *M, + llvm::DenseMap *InverseMap) const; +}; + +class ExplodedNodeSet { + typedef llvm::SmallPtrSet ImplTy; + ImplTy Impl; + +public: + ExplodedNodeSet(ExplodedNode* N) { + assert (N && !static_cast(N)->isSink()); + Impl.insert(N); + } + + ExplodedNodeSet() {} + + inline void Add(ExplodedNode* N) { + if (N && !static_cast(N)->isSink()) Impl.insert(N); + } + + ExplodedNodeSet& operator=(const ExplodedNodeSet &X) { + Impl = X.Impl; + return *this; + } + + typedef ImplTy::iterator iterator; + typedef ImplTy::const_iterator const_iterator; + + unsigned size() const { return Impl.size(); } + bool empty() const { return Impl.empty(); } + + void clear() { Impl.clear(); } + void insert(const ExplodedNodeSet &S) { + if (empty()) + Impl = S.Impl; + else + Impl.insert(S.begin(), S.end()); + } + + inline iterator begin() { return Impl.begin(); } + inline iterator end() { return Impl.end(); } + + inline const_iterator begin() const { return Impl.begin(); } + inline const_iterator end() const { return Impl.end(); } +}; + +} // end GR namespace + +} // end clang namespace + +// GraphTraits + +namespace llvm { + template<> struct GraphTraits { + typedef clang::ento::ExplodedNode NodeType; + typedef NodeType::succ_iterator ChildIteratorType; + typedef llvm::df_iterator nodes_iterator; + + static inline NodeType* getEntryNode(NodeType* N) { + return N; + } + + static inline ChildIteratorType child_begin(NodeType* N) { + return N->succ_begin(); + } + + static inline ChildIteratorType child_end(NodeType* N) { + return N->succ_end(); + } + + static inline nodes_iterator nodes_begin(NodeType* N) { + return df_begin(N); + } + + static inline nodes_iterator nodes_end(NodeType* N) { + return df_end(N); + } + }; + + template<> struct GraphTraits { + typedef const clang::ento::ExplodedNode NodeType; + typedef NodeType::const_succ_iterator ChildIteratorType; + typedef llvm::df_iterator nodes_iterator; + + static inline NodeType* getEntryNode(NodeType* N) { + return N; + } + + static inline ChildIteratorType child_begin(NodeType* N) { + return N->succ_begin(); + } + + static inline ChildIteratorType child_end(NodeType* N) { + return N->succ_end(); + } + + static inline nodes_iterator nodes_begin(NodeType* N) { + return df_begin(N); + } + + static inline nodes_iterator nodes_end(NodeType* N) { + return df_end(N); + } + }; + +} // end llvm namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h new file mode 100644 index 0000000000..407691a20b --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/ExprEngine.h @@ -0,0 +1,544 @@ +//===-- ExprEngine.h - Path-Sensitive Expression-Level Dataflow ---*- 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 meta-engine for path-sensitive dataflow analysis that +// is built on CoreEngine, but provides the boilerplate to execute transfer +// functions and build the ExplodedGraph at the expression level. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_EXPRENGINE +#define LLVM_CLANG_GR_EXPRENGINE + +#include "clang/StaticAnalyzer/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/AST/Type.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ExprCXX.h" +#include "clang/AST/StmtObjC.h" + +namespace clang { + +class ObjCForCollectionStmt; + +namespace ento { + +class AnalysisManager; +class Checker; + +class ExprEngine : public SubEngine { + AnalysisManager &AMgr; + + CoreEngine Engine; + + /// G - the simulation graph. + ExplodedGraph& G; + + /// Builder - The current StmtNodeBuilder which is used when building the + /// nodes for a given statement. + StmtNodeBuilder* Builder; + + /// StateMgr - Object that manages the data for all created states. + GRStateManager StateMgr; + + /// SymMgr - Object that manages the symbol information. + SymbolManager& SymMgr; + + /// svalBuilder - SValBuilder object that creates SVals from expressions. + SValBuilder &svalBuilder; + + /// EntryNode - The immediate predecessor node. + ExplodedNode* EntryNode; + + /// CleanedState - The state for EntryNode "cleaned" of all dead + /// variables and symbols (as determined by a liveness analysis). + const GRState* CleanedState; + + /// currentStmt - The current block-level statement. + const Stmt* currentStmt; + + // Obj-C Class Identifiers. + IdentifierInfo* NSExceptionII; + + // Obj-C Selectors. + Selector* NSExceptionInstanceRaiseSelectors; + Selector RaiseSel; + + enum CallbackKind { + PreVisitStmtCallback, + PostVisitStmtCallback, + ProcessAssumeCallback, + EvalRegionChangesCallback + }; + + typedef uint32_t CallbackTag; + + /// GetCallbackTag - Create a tag for a certain kind of callback. The 'Sub' + /// argument can be used to differentiate callbacks that depend on another + /// value from a small set of possibilities, such as statement classes. + static inline CallbackTag GetCallbackTag(CallbackKind K, uint32_t Sub = 0) { + assert(Sub == ((Sub << 8) >> 8) && "Tag sub-kind must fit into 24 bits"); + return K | (Sub << 8); + } + + typedef llvm::DenseMap CheckerMap; + typedef std::vector > CheckersOrdered; + typedef llvm::DenseMap CheckersOrderedCache; + + /// A registration map from checker tag to the index into the + /// ordered checkers vector. + CheckerMap CheckerM; + + /// An ordered vector of checkers that are called when evaluating + /// various expressions and statements. + CheckersOrdered Checkers; + + /// A map used for caching the checkers that respond to the callback for + /// a particular callback tag. + CheckersOrderedCache COCache; + + /// The BugReporter associated with this engine. It is important that + /// this object be placed at the very end of member variables so that its + /// destructor is called before the rest of the ExprEngine is destroyed. + GRBugReporter BR; + + llvm::OwningPtr TF; + +public: + ExprEngine(AnalysisManager &mgr, TransferFuncs *tf); + + ~ExprEngine(); + + void ExecuteWorkList(const LocationContext *L, unsigned Steps = 150000) { + Engine.ExecuteWorkList(L, Steps, 0); + } + + /// Execute the work list with an initial state. Nodes that reaches the exit + /// of the function are added into the Dst set, which represent the exit + /// state of the function call. + void ExecuteWorkListWithInitialState(const LocationContext *L, unsigned Steps, + const GRState *InitState, + ExplodedNodeSet &Dst) { + Engine.ExecuteWorkListWithInitialState(L, Steps, InitState, Dst); + } + + /// getContext - Return the ASTContext associated with this analysis. + ASTContext& getContext() const { return AMgr.getASTContext(); } + + virtual AnalysisManager &getAnalysisManager() { return AMgr; } + + SValBuilder &getSValBuilder() { return svalBuilder; } + + TransferFuncs& getTF() { return *TF; } + + BugReporter& getBugReporter() { return BR; } + + StmtNodeBuilder &getBuilder() { assert(Builder); return *Builder; } + + // FIXME: Remove once TransferFuncs is no longer referenced. + void setTransferFunction(TransferFuncs* tf); + + /// ViewGraph - Visualize the ExplodedGraph created by executing the + /// simulation. + void ViewGraph(bool trim = false); + + void ViewGraph(ExplodedNode** Beg, ExplodedNode** End); + + /// getInitialState - Return the initial state used for the root vertex + /// in the ExplodedGraph. + const GRState* getInitialState(const LocationContext *InitLoc); + + ExplodedGraph& getGraph() { return G; } + const ExplodedGraph& getGraph() const { return G; } + + template + void registerCheck(CHECKER *check) { + unsigned entry = Checkers.size(); + void *tag = CHECKER::getTag(); + Checkers.push_back(std::make_pair(tag, check)); + CheckerM[tag] = entry; + } + + Checker *lookupChecker(void *tag) const; + + template + CHECKER *getChecker() const { + return static_cast(lookupChecker(CHECKER::getTag())); + } + + /// ProcessElement - Called by CoreEngine. Used to generate new successor + /// nodes by processing the 'effects' of a CFG element. + void ProcessElement(const CFGElement E, StmtNodeBuilder& builder); + + void ProcessStmt(const CFGStmt S, StmtNodeBuilder &builder); + + void ProcessInitializer(const CFGInitializer I, StmtNodeBuilder &builder); + + void ProcessImplicitDtor(const CFGImplicitDtor D, StmtNodeBuilder &builder); + + void ProcessAutomaticObjDtor(const CFGAutomaticObjDtor D, + StmtNodeBuilder &builder); + void ProcessBaseDtor(const CFGBaseDtor D, StmtNodeBuilder &builder); + void ProcessMemberDtor(const CFGMemberDtor D, StmtNodeBuilder &builder); + void ProcessTemporaryDtor(const CFGTemporaryDtor D, + StmtNodeBuilder &builder); + + /// ProcessBlockEntrance - Called by CoreEngine when start processing + /// a CFGBlock. This method returns true if the analysis should continue + /// exploring the given path, and false otherwise. + bool ProcessBlockEntrance(const CFGBlock* B, const ExplodedNode *Pred, + BlockCounter BC); + + /// ProcessBranch - Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a branch condition. + void ProcessBranch(const Stmt* Condition, const Stmt* Term, + BranchNodeBuilder& builder); + + /// ProcessIndirectGoto - Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a computed goto jump. + void ProcessIndirectGoto(IndirectGotoNodeBuilder& builder); + + /// ProcessSwitch - Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a switch statement. + void ProcessSwitch(SwitchNodeBuilder& builder); + + /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path + /// nodes when the control reaches the end of a function. + void ProcessEndPath(EndPathNodeBuilder& builder); + + /// Generate the entry node of the callee. + void ProcessCallEnter(CallEnterNodeBuilder &builder); + + /// Generate the first post callsite node. + void ProcessCallExit(CallExitNodeBuilder &builder); + + /// Called by CoreEngine when the analysis worklist has terminated. + void ProcessEndWorklist(bool hasWorkRemaining); + + /// evalAssume - Callback function invoked by the ConstraintManager when + /// making assumptions about state values. + const GRState *ProcessAssume(const GRState *state, SVal cond,bool assumption); + + /// WantsRegionChangeUpdate - Called by GRStateManager to determine if a + /// region change should trigger a ProcessRegionChanges update. + bool WantsRegionChangeUpdate(const GRState* state); + + /// ProcessRegionChanges - Called by GRStateManager whenever a change is made + /// to the store. Used to update checkers that track region values. + const GRState* ProcessRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End); + + virtual GRStateManager& getStateManager() { return StateMgr; } + + StoreManager& getStoreManager() { return StateMgr.getStoreManager(); } + + ConstraintManager& getConstraintManager() { + return StateMgr.getConstraintManager(); + } + + // FIXME: Remove when we migrate over to just using SValBuilder. + BasicValueFactory& getBasicVals() { + return StateMgr.getBasicVals(); + } + const BasicValueFactory& getBasicVals() const { + return StateMgr.getBasicVals(); + } + + // FIXME: Remove when we migrate over to just using ValueManager. + SymbolManager& getSymbolManager() { return SymMgr; } + const SymbolManager& getSymbolManager() const { return SymMgr; } + + // Functions for external checking of whether we have unfinished work + bool wasBlockAborted() const { return Engine.wasBlockAborted(); } + bool hasEmptyWorkList() const { return !Engine.getWorkList()->hasWork(); } + bool hasWorkRemaining() const { + return wasBlockAborted() || Engine.getWorkList()->hasWork(); + } + + const CoreEngine &getCoreEngine() const { return Engine; } + +protected: + const GRState* GetState(ExplodedNode* N) { + return N == EntryNode ? CleanedState : N->getState(); + } + +public: + ExplodedNode* MakeNode(ExplodedNodeSet& Dst, const Stmt* S, + ExplodedNode* Pred, const GRState* St, + ProgramPoint::Kind K = ProgramPoint::PostStmtKind, + const void *tag = 0); + + /// CheckerVisit - Dispatcher for performing checker-specific logic + /// at specific statements. + void CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, ExplodedNodeSet &Src, + CallbackKind Kind); + + bool CheckerEvalCall(const CallExpr *CE, + ExplodedNodeSet &Dst, + ExplodedNode *Pred); + + void CheckerEvalNilReceiver(const ObjCMessageExpr *ME, + ExplodedNodeSet &Dst, + const GRState *state, + ExplodedNode *Pred); + + void CheckerVisitBind(const Stmt *StoreE, ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, SVal location, SVal val, + bool isPrevisit); + + /// Visit - Transfer function logic for all statements. Dispatches to + /// other functions that handle specific kinds of statements. + void Visit(const Stmt* S, ExplodedNode* Pred, ExplodedNodeSet& Dst); + + /// VisitArraySubscriptExpr - Transfer function for array accesses. + void VisitLvalArraySubscriptExpr(const ArraySubscriptExpr* Ex, + ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + /// VisitAsmStmt - Transfer function logic for inline asm. + void VisitAsmStmt(const AsmStmt* A, ExplodedNode* Pred, ExplodedNodeSet& Dst); + + void VisitAsmStmtHelperOutputs(const AsmStmt* A, + AsmStmt::const_outputs_iterator I, + AsmStmt::const_outputs_iterator E, + ExplodedNode* Pred, ExplodedNodeSet& Dst); + + void VisitAsmStmtHelperInputs(const AsmStmt* A, + AsmStmt::const_inputs_iterator I, + AsmStmt::const_inputs_iterator E, + ExplodedNode* Pred, ExplodedNodeSet& Dst); + + /// VisitBlockExpr - Transfer function logic for BlockExprs. + void VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitBinaryOperator - Transfer function logic for binary operators. + void VisitBinaryOperator(const BinaryOperator* B, ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + + /// VisitCall - Transfer function for function calls. + void VisitCall(const CallExpr* CE, ExplodedNode* Pred, + CallExpr::const_arg_iterator AI, + CallExpr::const_arg_iterator AE, + ExplodedNodeSet& Dst); + + /// VisitCast - Transfer function logic for all casts (implicit and explicit). + void VisitCast(const CastExpr *CastE, const Expr *Ex, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// VisitCompoundLiteralExpr - Transfer function logic for compound literals. + void VisitCompoundLiteralExpr(const CompoundLiteralExpr* CL, + ExplodedNode* Pred, ExplodedNodeSet& Dst); + + /// Transfer function logic for DeclRefExprs and BlockDeclRefExprs. + void VisitCommonDeclRefExpr(const Expr* DR, const NamedDecl *D, + ExplodedNode* Pred, ExplodedNodeSet& Dst); + + /// VisitDeclStmt - Transfer function logic for DeclStmts. + void VisitDeclStmt(const DeclStmt* DS, ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + /// VisitGuardedExpr - Transfer function logic for ?, __builtin_choose + void VisitGuardedExpr(const Expr* Ex, const Expr* L, const Expr* R, + ExplodedNode* Pred, ExplodedNodeSet& Dst); + + /// VisitCondInit - Transfer function for handling the initialization + /// of a condition variable in an IfStmt, SwitchStmt, etc. + void VisitCondInit(const VarDecl *VD, const Stmt *S, ExplodedNode *Pred, + ExplodedNodeSet& Dst); + + void VisitInitListExpr(const InitListExpr* E, ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + /// VisitLogicalExpr - Transfer function logic for '&&', '||' + void VisitLogicalExpr(const BinaryOperator* B, ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + /// VisitMemberExpr - Transfer function for member expressions. + void VisitMemberExpr(const MemberExpr* M, ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + /// Transfer function logic for ObjCAtSynchronizedStmts. + void VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + + /// Transfer function logic for computing the lvalue of an Objective-C ivar. + void VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr* DR, ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + /// VisitObjCForCollectionStmt - Transfer function logic for + /// ObjCForCollectionStmt. + void VisitObjCForCollectionStmt(const ObjCForCollectionStmt* S, + ExplodedNode* Pred, ExplodedNodeSet& Dst); + + void VisitObjCForCollectionStmtAux(const ObjCForCollectionStmt* S, + ExplodedNode* Pred, + ExplodedNodeSet& Dst, SVal ElementV); + + /// VisitObjCMessageExpr - Transfer function for ObjC message expressions. + void VisitObjCMessageExpr(const ObjCMessageExpr* ME, ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + /// VisitReturnStmt - Transfer function logic for return statements. + void VisitReturnStmt(const ReturnStmt* R, ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + /// VisitOffsetOfExpr - Transfer function for offsetof. + void VisitOffsetOfExpr(const OffsetOfExpr* Ex, ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + /// VisitSizeOfAlignOfExpr - Transfer function for sizeof. + void VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr* Ex, ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + /// VisitUnaryOperator - Transfer function logic for unary operators. + void VisitUnaryOperator(const UnaryOperator* B, ExplodedNode* Pred, + ExplodedNodeSet& Dst); + + void VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, + ExplodedNodeSet & Dst); + + void VisitCXXTemporaryObjectExpr(const CXXTemporaryObjectExpr *expr, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + VisitCXXConstructExpr(expr, 0, Pred, Dst); + } + + void VisitCXXConstructExpr(const CXXConstructExpr *E, const MemRegion *Dest, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + + void VisitCXXDestructor(const CXXDestructorDecl *DD, + const MemRegion *Dest, const Stmt *S, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + + void VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + void VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *C, + ExplodedNode *Pred, ExplodedNodeSet &Dst); + + void VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + void VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + void VisitAggExpr(const Expr *E, const MemRegion *Dest, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// Create a C++ temporary object for an rvalue. + void CreateCXXTemporaryObject(const Expr *Ex, ExplodedNode *Pred, + ExplodedNodeSet &Dst); + + /// Synthesize CXXThisRegion. + const CXXThisRegion *getCXXThisRegion(const CXXRecordDecl *RD, + const StackFrameContext *SFC); + + const CXXThisRegion *getCXXThisRegion(const CXXMethodDecl *decl, + const StackFrameContext *frameCtx); + + /// Evaluate arguments with a work list algorithm. + void evalArguments(ConstExprIterator AI, ConstExprIterator AE, + const FunctionProtoType *FnType, + ExplodedNode *Pred, ExplodedNodeSet &Dst, + bool FstArgAsLValue = false); + + /// Evaluate method call itself. Used for CXXMethodCallExpr and + /// CXXOperatorCallExpr. + void evalMethodCall(const CallExpr *MCE, const CXXMethodDecl *MD, + const Expr *ThisExpr, ExplodedNode *Pred, + ExplodedNodeSet &Src, ExplodedNodeSet &Dst); + + /// evalEagerlyAssume - Given the nodes in 'Src', eagerly assume symbolic + /// expressions of the form 'x != 0' and generate new nodes (stored in Dst) + /// with those assumptions. + void evalEagerlyAssume(ExplodedNodeSet& Dst, ExplodedNodeSet& Src, + const Expr *Ex); + + SVal evalMinus(SVal X) { + return X.isValid() ? svalBuilder.evalMinus(cast(X)) : X; + } + + SVal evalComplement(SVal X) { + return X.isValid() ? svalBuilder.evalComplement(cast(X)) : X; + } + +public: + + SVal evalBinOp(const GRState *state, BinaryOperator::Opcode op, + NonLoc L, NonLoc R, QualType T) { + return svalBuilder.evalBinOpNN(state, op, L, R, T); + } + + SVal evalBinOp(const GRState *state, BinaryOperator::Opcode op, + NonLoc L, SVal R, QualType T) { + return R.isValid() ? svalBuilder.evalBinOpNN(state,op,L, cast(R), T) : R; + } + + SVal evalBinOp(const GRState *ST, BinaryOperator::Opcode Op, + SVal LHS, SVal RHS, QualType T) { + return svalBuilder.evalBinOp(ST, Op, LHS, RHS, T); + } + +protected: + void evalObjCMessageExpr(ExplodedNodeSet& Dst, const ObjCMessageExpr* ME, + ExplodedNode* Pred, const GRState *state) { + assert (Builder && "StmtNodeBuilder must be defined."); + getTF().evalObjCMessageExpr(Dst, *this, *Builder, ME, Pred, state); + } + + const GRState* MarkBranch(const GRState* St, const Stmt* Terminator, + bool branchTaken); + + /// evalBind - Handle the semantics of binding a value to a specific location. + /// This method is used by evalStore, VisitDeclStmt, and others. + void evalBind(ExplodedNodeSet& Dst, const Stmt* StoreE, ExplodedNode* Pred, + const GRState* St, SVal location, SVal Val, + bool atDeclInit = false); + +public: + // FIXME: 'tag' should be removed, and a LocationContext should be used + // instead. + // FIXME: Comment on the meaning of the arguments, when 'St' may not + // be the same as Pred->state, and when 'location' may not be the + // same as state->getLValue(Ex). + /// Simulate a read of the result of Ex. + void evalLoad(ExplodedNodeSet& Dst, const Expr* Ex, ExplodedNode* Pred, + const GRState* St, SVal location, const void *tag = 0, + QualType LoadTy = QualType()); + + // FIXME: 'tag' should be removed, and a LocationContext should be used + // instead. + void evalStore(ExplodedNodeSet& Dst, const Expr* AssignE, const Expr* StoreE, + ExplodedNode* Pred, const GRState* St, SVal TargetLV, SVal Val, + const void *tag = 0); +private: + void evalLoadCommon(ExplodedNodeSet& Dst, const Expr* Ex, ExplodedNode* Pred, + const GRState* St, SVal location, const void *tag, + QualType LoadTy); + + // FIXME: 'tag' should be removed, and a LocationContext should be used + // instead. + void evalLocation(ExplodedNodeSet &Dst, const Stmt *S, ExplodedNode* Pred, + const GRState* St, SVal location, + const void *tag, bool isLoad); + + bool InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, ExplodedNode *Pred); +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/ExprEngineBuilders.h b/include/clang/StaticAnalyzer/PathSensitive/ExprEngineBuilders.h new file mode 100644 index 0000000000..965ec3e1c3 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/ExprEngineBuilders.h @@ -0,0 +1,81 @@ +//===-- ExprEngineBuilders.h - "Builder" classes for ExprEngine ---*- 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 smart builder "references" which are used to marshal +// builders between ExprEngine objects and their related components. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_EXPRENGINE_BUILDERS +#define LLVM_CLANG_GR_EXPRENGINE_BUILDERS +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/Analysis/Support/SaveAndRestore.h" + +namespace clang { + +namespace ento { + +class StmtNodeBuilderRef { + ExplodedNodeSet &Dst; + StmtNodeBuilder &B; + ExprEngine& Eng; + ExplodedNode* Pred; + const GRState* state; + const Stmt* stmt; + const unsigned OldSize; + const bool AutoCreateNode; + SaveAndRestore OldSink; + SaveAndRestore OldTag; + SaveOr OldHasGen; + +private: + friend class ExprEngine; + + StmtNodeBuilderRef(); // do not implement + void operator=(const StmtNodeBuilderRef&); // do not implement + + StmtNodeBuilderRef(ExplodedNodeSet &dst, + StmtNodeBuilder &builder, + ExprEngine& eng, + ExplodedNode* pred, + const GRState *st, + const Stmt* s, bool auto_create_node) + : Dst(dst), B(builder), Eng(eng), Pred(pred), + state(st), stmt(s), OldSize(Dst.size()), AutoCreateNode(auto_create_node), + OldSink(B.BuildSinks), OldTag(B.Tag), OldHasGen(B.HasGeneratedNode) {} + +public: + + ~StmtNodeBuilderRef() { + // Handle the case where no nodes where generated. Auto-generate that + // contains the updated state if we aren't generating sinks. + if (!B.BuildSinks && Dst.size() == OldSize && !B.HasGeneratedNode) { + if (AutoCreateNode) + B.MakeNode(Dst, const_cast(stmt), Pred, state); + else + Dst.Add(Pred); + } + } + + const GRState *getState() { return state; } + + GRStateManager& getStateManager() { + return Eng.getStateManager(); + } + + ExplodedNode* MakeNode(const GRState* state) { + return B.MakeNode(Dst, const_cast(stmt), Pred, state); + } +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/GRState.h b/include/clang/StaticAnalyzer/PathSensitive/GRState.h new file mode 100644 index 0000000000..dcdb6e26cb --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/GRState.h @@ -0,0 +1,760 @@ +//== GRState.h - 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 defines SymbolRef, ExprBindKey, and GRState*. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_VALUESTATE_H +#define LLVM_CLANG_GR_VALUESTATE_H + +#include "clang/StaticAnalyzer/PathSensitive/ConstraintManager.h" +#include "clang/StaticAnalyzer/PathSensitive/Environment.h" +#include "clang/StaticAnalyzer/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/PathSensitive/SValBuilder.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/Support/Casting.h" + +namespace llvm { +class APSInt; +class BumpPtrAllocator; +class raw_ostream; +} + +namespace clang { +class ASTContext; + +namespace ento { + +class GRStateManager; +class Checker; + +typedef ConstraintManager* (*ConstraintManagerCreator)(GRStateManager&, + SubEngine&); +typedef StoreManager* (*StoreManagerCreator)(GRStateManager&); + +//===----------------------------------------------------------------------===// +// GRStateTrait - Traits used by the Generic Data Map of a GRState. +//===----------------------------------------------------------------------===// + +template struct GRStatePartialTrait; + +template struct GRStateTrait { + typedef typename T::data_type data_type; + static inline void* GDMIndex() { return &T::TagInt; } + static inline void* MakeVoidPtr(data_type D) { return (void*) D; } + static inline data_type MakeData(void* const* P) { + return P ? (data_type) *P : (data_type) 0; + } +}; + +class GRStateManager; + +/// GRState - This class encapsulates: +/// +/// 1. A mapping from expressions to values (Environment) +/// 2. A mapping from locations to values (Store) +/// 3. Constraints on symbolic values (GenericDataMap) +/// +/// Together these represent the "abstract state" of a program. +/// +/// GRState is intended to be used as a functional object; that is, +/// once it is created and made "persistent" in a FoldingSet, its +/// values will never change. +class GRState : public llvm::FoldingSetNode { +public: + typedef llvm::ImmutableSet IntSetTy; + typedef llvm::ImmutableMap GenericDataMap; + +private: + void operator=(const GRState& R) const; // Do not implement. + + friend class GRStateManager; + + GRStateManager *StateMgr; + Environment Env; // Maps a Stmt to its current SVal. + Store St; // Maps a location to its current value. + GenericDataMap GDM; // Custom data stored by a client of this class. + + /// makeWithStore - Return a GRState with the same values as the current + /// state with the exception of using the specified Store. + const GRState *makeWithStore(Store store) const; + +public: + + /// This ctor is used when creating the first GRState object. + GRState(GRStateManager *mgr, const Environment& env, + Store st, GenericDataMap gdm) + : StateMgr(mgr), + Env(env), + St(st), + GDM(gdm) {} + + /// Copy ctor - We must explicitly define this or else the "Next" ptr + /// in FoldingSetNode will also get copied. + GRState(const GRState& RHS) + : llvm::FoldingSetNode(), + StateMgr(RHS.StateMgr), + Env(RHS.Env), + St(RHS.St), + GDM(RHS.GDM) {} + + /// getStateManager - Return the GRStateManager associated with this state. + GRStateManager &getStateManager() const { + return *StateMgr; + } + + /// getEnvironment - Return the environment associated with this state. + /// The environment is the mapping from expressions to values. + const Environment& getEnvironment() const { return Env; } + + /// getStore - Return the store associated with this state. The store + /// is a mapping from locations to values. + Store getStore() const { return St; } + + void setStore(Store s) { St = s; } + + /// getGDM - Return the generic data map associated with this state. + GenericDataMap getGDM() const { return GDM; } + + void setGDM(GenericDataMap gdm) { GDM = gdm; } + + /// Profile - Profile the contents of a GRState object for use in a + /// FoldingSet. Two GRState objects are considered equal if they + /// have the same Environment, Store, and GenericDataMap. + static void Profile(llvm::FoldingSetNodeID& ID, const GRState* V) { + V->Env.Profile(ID); + ID.AddPointer(V->St); + V->GDM.Profile(ID); + } + + /// Profile - Used to profile the contents of this object for inclusion + /// in a FoldingSet. + void Profile(llvm::FoldingSetNodeID& ID) const { + Profile(ID, this); + } + + BasicValueFactory &getBasicVals() const; + SymbolManager &getSymbolManager() const; + + //==---------------------------------------------------------------------==// + // Constraints on values. + //==---------------------------------------------------------------------==// + // + // Each GRState records constraints on symbolic values. These constraints + // are managed using the ConstraintManager associated with a GRStateManager. + // As constraints gradually accrue on symbolic values, added constraints + // may conflict and indicate that a state is infeasible (as no real values + // could satisfy all the constraints). This is the principal mechanism + // for modeling path-sensitivity in ExprEngine/GRState. + // + // Various "assume" methods form the interface for adding constraints to + // symbolic values. A call to 'assume' indicates an assumption being placed + // on one or symbolic values. 'assume' methods take the following inputs: + // + // (1) A GRState object representing the current state. + // + // (2) The assumed constraint (which is specific to a given "assume" method). + // + // (3) A binary value "Assumption" that indicates whether the constraint is + // assumed to be true or false. + // + // The output of "assume*" is a new GRState object with the added constraints. + // If no new state is feasible, NULL is returned. + // + + const GRState *assume(DefinedOrUnknownSVal cond, bool assumption) const; + + /// This method assumes both "true" and "false" for 'cond', and + /// returns both corresponding states. It's shorthand for doing + /// 'assume' twice. + std::pair + assume(DefinedOrUnknownSVal cond) const; + + const GRState *assumeInBound(DefinedOrUnknownSVal idx, + DefinedOrUnknownSVal upperBound, + bool assumption) const; + + //==---------------------------------------------------------------------==// + // Utility methods for getting regions. + //==---------------------------------------------------------------------==// + + const VarRegion* getRegion(const VarDecl *D, const LocationContext *LC) const; + + //==---------------------------------------------------------------------==// + // Binding and retrieving values to/from the environment and symbolic store. + //==---------------------------------------------------------------------==// + + /// BindCompoundLiteral - Return the state that has the bindings currently + /// in this state plus the bindings for the CompoundLiteral. + const GRState *bindCompoundLiteral(const CompoundLiteralExpr* CL, + const LocationContext *LC, + SVal V) const; + + /// Create a new state by binding the value 'V' to the statement 'S' in the + /// state's environment. + const GRState *BindExpr(const Stmt *S, SVal V, bool Invalidate = true) const; + + /// Create a new state by binding the value 'V' and location 'locaton' to the + /// statement 'S' in the state's environment. + const GRState *bindExprAndLocation(const Stmt *S, SVal location, SVal V) + const; + + const GRState *bindDecl(const VarRegion *VR, SVal V) const; + + const GRState *bindDeclWithNoInit(const VarRegion *VR) const; + + const GRState *bindLoc(Loc location, SVal V) const; + + const GRState *bindLoc(SVal location, SVal V) const; + + const GRState *bindDefault(SVal loc, SVal V) const; + + const GRState *unbindLoc(Loc LV) const; + + /// InvalidateRegion - Returns the state with bindings for the given region + /// cleared from the store. See InvalidateRegions. + const GRState *InvalidateRegion(const MemRegion *R, + const Expr *E, unsigned BlockCount, + StoreManager::InvalidatedSymbols *IS = NULL) + const { + return InvalidateRegions(&R, &R+1, E, BlockCount, IS, false); + } + + /// InvalidateRegions - Returns the state with bindings for the given regions + /// cleared from the store. The regions are provided as a continuous array + /// from Begin to End. Optionally invalidates global regions as well. + const GRState *InvalidateRegions(const MemRegion * const *Begin, + const MemRegion * const *End, + const Expr *E, unsigned BlockCount, + StoreManager::InvalidatedSymbols *IS, + bool invalidateGlobals) const; + + /// EnterStackFrame - Returns the state for entry to the given stack frame, + /// preserving the current state. + const GRState *EnterStackFrame(const StackFrameContext *frame) const; + + /// Get the lvalue for a variable reference. + Loc getLValue(const VarDecl *D, const LocationContext *LC) const; + + /// Get the lvalue for a StringLiteral. + Loc getLValue(const StringLiteral *literal) const; + + Loc getLValue(const CompoundLiteralExpr *literal, + const LocationContext *LC) const; + + /// Get the lvalue for an ivar reference. + SVal getLValue(const ObjCIvarDecl *decl, SVal base) const; + + /// Get the lvalue for a field reference. + SVal getLValue(const FieldDecl *decl, SVal Base) const; + + /// Get the lvalue for an array index. + SVal getLValue(QualType ElementType, SVal Idx, SVal Base) const; + + const llvm::APSInt *getSymVal(SymbolRef sym) const; + + /// Returns the SVal bound to the statement 'S' in the state's environment. + SVal getSVal(const Stmt* S) const; + + SVal getSValAsScalarOrLoc(const Stmt *Ex) const; + + SVal getSVal(Loc LV, QualType T = QualType()) const; + + /// Returns the "raw" SVal bound to LV before any value simplfication. + SVal getRawSVal(Loc LV, QualType T= QualType()) const; + + SVal getSVal(const MemRegion* R) const; + + SVal getSValAsScalarOrLoc(const MemRegion *R) const; + + const llvm::APSInt *getSymVal(SymbolRef sym); + + bool scanReachableSymbols(SVal val, SymbolVisitor& visitor) const; + + bool scanReachableSymbols(const SVal *I, const SVal *E, + SymbolVisitor &visitor) const; + + bool scanReachableSymbols(const MemRegion * const *I, + const MemRegion * const *E, + SymbolVisitor &visitor) const; + + template CB scanReachableSymbols(SVal val) const; + template CB scanReachableSymbols(const SVal *beg, + const SVal *end) const; + + template CB + scanReachableSymbols(const MemRegion * const *beg, + const MemRegion * const *end) const; + + //==---------------------------------------------------------------------==// + // Accessing the Generic Data Map (GDM). + //==---------------------------------------------------------------------==// + + void* const* FindGDM(void* K) const; + + template + const GRState *add(typename GRStateTrait::key_type K) const; + + template + typename GRStateTrait::data_type + get() const { + return GRStateTrait::MakeData(FindGDM(GRStateTrait::GDMIndex())); + } + + template + typename GRStateTrait::lookup_type + get(typename GRStateTrait::key_type key) const { + void* const* d = FindGDM(GRStateTrait::GDMIndex()); + return GRStateTrait::Lookup(GRStateTrait::MakeData(d), key); + } + + template + typename GRStateTrait::context_type get_context() const; + + + template + const GRState *remove(typename GRStateTrait::key_type K) const; + + template + const GRState *remove(typename GRStateTrait::key_type K, + typename GRStateTrait::context_type C) const; + template + const GRState *remove() const; + + template + const GRState *set(typename GRStateTrait::data_type D) const; + + template + const GRState *set(typename GRStateTrait::key_type K, + typename GRStateTrait::value_type E) const; + + template + const GRState *set(typename GRStateTrait::key_type K, + typename GRStateTrait::value_type E, + typename GRStateTrait::context_type C) const; + + template + bool contains(typename GRStateTrait::key_type key) const { + void* const* d = FindGDM(GRStateTrait::GDMIndex()); + return GRStateTrait::Contains(GRStateTrait::MakeData(d), key); + } + + // State pretty-printing. + class Printer { + public: + virtual ~Printer() {} + virtual void Print(llvm::raw_ostream& Out, const GRState* state, + const char* nl, const char* sep) = 0; + }; + + // Pretty-printing. + void print(llvm::raw_ostream& Out, CFG &C, const char *nl = "\n", + const char *sep = "") const; + + void printStdErr(CFG &C) const; + + void printDOT(llvm::raw_ostream& Out, CFG &C) const; +}; + +class GRStateSet { + typedef llvm::SmallPtrSet ImplTy; + ImplTy Impl; +public: + GRStateSet() {} + + inline void Add(const GRState* St) { + Impl.insert(St); + } + + typedef ImplTy::const_iterator iterator; + + inline unsigned size() const { return Impl.size(); } + inline bool empty() const { return Impl.empty(); } + + inline iterator begin() const { return Impl.begin(); } + inline iterator end() const { return Impl.end(); } + + class AutoPopulate { + GRStateSet& S; + unsigned StartSize; + const GRState* St; + public: + AutoPopulate(GRStateSet& s, const GRState* st) + : S(s), StartSize(S.size()), St(st) {} + + ~AutoPopulate() { + if (StartSize == S.size()) + S.Add(St); + } + }; +}; + +//===----------------------------------------------------------------------===// +// GRStateManager - Factory object for GRStates. +//===----------------------------------------------------------------------===// + +class GRStateManager { + friend class GRState; + friend class ExprEngine; // FIXME: Remove. +private: + /// Eng - The SubEngine that owns this state manager. + SubEngine &Eng; + + EnvironmentManager EnvMgr; + llvm::OwningPtr StoreMgr; + llvm::OwningPtr ConstraintMgr; + + GRState::GenericDataMap::Factory GDMFactory; + + typedef llvm::DenseMap > GDMContextsTy; + GDMContextsTy GDMContexts; + + /// Printers - A set of printer objects used for pretty-printing a GRState. + /// GRStateManager owns these objects. + std::vector Printers; + + /// StateSet - FoldingSet containing all the states created for analyzing + /// a particular function. This is used to unique states. + llvm::FoldingSet StateSet; + + /// Object that manages the data for all created SVals. + llvm::OwningPtr svalBuilder; + + /// Alloc - A BumpPtrAllocator to allocate states. + llvm::BumpPtrAllocator &Alloc; + +public: + GRStateManager(ASTContext& Ctx, + StoreManagerCreator CreateStoreManager, + ConstraintManagerCreator CreateConstraintManager, + llvm::BumpPtrAllocator& alloc, + SubEngine &subeng) + : Eng(subeng), + EnvMgr(alloc), + GDMFactory(alloc), + svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)), + Alloc(alloc) { + StoreMgr.reset((*CreateStoreManager)(*this)); + ConstraintMgr.reset((*CreateConstraintManager)(*this, subeng)); + } + + ~GRStateManager(); + + const GRState *getInitialState(const LocationContext *InitLoc); + + ASTContext &getContext() { return svalBuilder->getContext(); } + const ASTContext &getContext() const { return svalBuilder->getContext(); } + + BasicValueFactory &getBasicVals() { + return svalBuilder->getBasicValueFactory(); + } + const BasicValueFactory& getBasicVals() const { + return svalBuilder->getBasicValueFactory(); + } + + SValBuilder &getSValBuilder() { + return *svalBuilder; + } + + SymbolManager &getSymbolManager() { + return svalBuilder->getSymbolManager(); + } + const SymbolManager &getSymbolManager() const { + return svalBuilder->getSymbolManager(); + } + + llvm::BumpPtrAllocator& getAllocator() { return Alloc; } + + MemRegionManager& getRegionManager() { + return svalBuilder->getRegionManager(); + } + const MemRegionManager& getRegionManager() const { + return svalBuilder->getRegionManager(); + } + + StoreManager& getStoreManager() { return *StoreMgr; } + ConstraintManager& getConstraintManager() { return *ConstraintMgr; } + SubEngine& getOwningEngine() { return Eng; } + + const GRState* RemoveDeadBindings(const GRState* St, + const StackFrameContext *LCtx, + SymbolReaper& SymReaper); + + /// Marshal a new state for the callee in another translation unit. + /// 'state' is owned by the caller's engine. + const GRState *MarshalState(const GRState *state, const StackFrameContext *L); + +public: + + SVal ArrayToPointer(Loc Array) { + return StoreMgr->ArrayToPointer(Array); + } + + // Methods that manipulate the GDM. + const GRState* addGDM(const GRState* St, void* Key, void* Data); + const GRState *removeGDM(const GRState *state, void *Key); + + // Methods that query & manipulate the Store. + + void iterBindings(const GRState* state, StoreManager::BindingsHandler& F) { + StoreMgr->iterBindings(state->getStore(), F); + } + + const GRState* getPersistentState(GRState& Impl); + + //==---------------------------------------------------------------------==// + // Generic Data Map methods. + //==---------------------------------------------------------------------==// + // + // GRStateManager and GRState support a "generic data map" that allows + // different clients of GRState objects to embed arbitrary data within a + // GRState object. The generic data map is essentially an immutable map + // from a "tag" (that acts as the "key" for a client) and opaque values. + // Tags/keys and values are simply void* values. The typical way that clients + // generate unique tags are by taking the address of a static variable. + // Clients are responsible for ensuring that data values referred to by a + // the data pointer are immutable (and thus are essentially purely functional + // data). + // + // The templated methods below use the GRStateTrait class + // to resolve keys into the GDM and to return data values to clients. + // + + // Trait based GDM dispatch. + template + const GRState* set(const GRState* st, typename GRStateTrait::data_type D) { + return addGDM(st, GRStateTrait::GDMIndex(), + GRStateTrait::MakeVoidPtr(D)); + } + + template + const GRState* set(const GRState* st, + typename GRStateTrait::key_type K, + typename GRStateTrait::value_type V, + typename GRStateTrait::context_type C) { + + return addGDM(st, GRStateTrait::GDMIndex(), + GRStateTrait::MakeVoidPtr(GRStateTrait::Set(st->get(), K, V, C))); + } + + template + const GRState* add(const GRState* st, + typename GRStateTrait::key_type K, + typename GRStateTrait::context_type C) { + return addGDM(st, GRStateTrait::GDMIndex(), + GRStateTrait::MakeVoidPtr(GRStateTrait::Add(st->get(), K, C))); + } + + template + const GRState* remove(const GRState* st, + typename GRStateTrait::key_type K, + typename GRStateTrait::context_type C) { + + return addGDM(st, GRStateTrait::GDMIndex(), + GRStateTrait::MakeVoidPtr(GRStateTrait::Remove(st->get(), K, C))); + } + + template + const GRState *remove(const GRState *st) { + return removeGDM(st, GRStateTrait::GDMIndex()); + } + + void* FindGDMContext(void* index, + void* (*CreateContext)(llvm::BumpPtrAllocator&), + void (*DeleteContext)(void*)); + + template + typename GRStateTrait::context_type get_context() { + void* p = FindGDMContext(GRStateTrait::GDMIndex(), + GRStateTrait::CreateContext, + GRStateTrait::DeleteContext); + + return GRStateTrait::MakeContext(p); + } + + const llvm::APSInt* getSymVal(const GRState* St, SymbolRef sym) { + return ConstraintMgr->getSymVal(St, sym); + } + + void EndPath(const GRState* St) { + ConstraintMgr->EndPath(St); + } +}; + + +//===----------------------------------------------------------------------===// +// Out-of-line method definitions for GRState. +//===----------------------------------------------------------------------===// + +inline const llvm::APSInt *GRState::getSymVal(SymbolRef sym) { + return getStateManager().getSymVal(this, sym); +} + +inline const VarRegion* GRState::getRegion(const VarDecl *D, + const LocationContext *LC) const { + return getStateManager().getRegionManager().getVarRegion(D, LC); +} + +inline const GRState *GRState::assume(DefinedOrUnknownSVal Cond, + bool Assumption) const { + if (Cond.isUnknown()) + return this; + + return getStateManager().ConstraintMgr->assume(this, cast(Cond), + Assumption); +} + +inline std::pair +GRState::assume(DefinedOrUnknownSVal Cond) const { + if (Cond.isUnknown()) + return std::make_pair(this, this); + + return getStateManager().ConstraintMgr->assumeDual(this, + cast(Cond)); +} + +inline const GRState *GRState::bindLoc(SVal LV, SVal V) const { + return !isa(LV) ? this : bindLoc(cast(LV), V); +} + +inline Loc GRState::getLValue(const VarDecl* VD, + const LocationContext *LC) const { + return getStateManager().StoreMgr->getLValueVar(VD, LC); +} + +inline Loc GRState::getLValue(const StringLiteral *literal) const { + return getStateManager().StoreMgr->getLValueString(literal); +} + +inline Loc GRState::getLValue(const CompoundLiteralExpr *literal, + const LocationContext *LC) const { + return getStateManager().StoreMgr->getLValueCompoundLiteral(literal, LC); +} + +inline SVal GRState::getLValue(const ObjCIvarDecl *D, SVal Base) const { + return getStateManager().StoreMgr->getLValueIvar(D, Base); +} + +inline SVal GRState::getLValue(const FieldDecl* D, SVal Base) const { + return getStateManager().StoreMgr->getLValueField(D, Base); +} + +inline SVal GRState::getLValue(QualType ElementType, SVal Idx, SVal Base) const{ + if (NonLoc *N = dyn_cast(&Idx)) + return getStateManager().StoreMgr->getLValueElement(ElementType, *N, Base); + return UnknownVal(); +} + +inline const llvm::APSInt *GRState::getSymVal(SymbolRef sym) const { + return getStateManager().getSymVal(this, sym); +} + +inline SVal GRState::getSVal(const Stmt* Ex) const { + return Env.getSVal(Ex, *getStateManager().svalBuilder); +} + +inline SVal GRState::getSValAsScalarOrLoc(const Stmt *S) const { + if (const Expr *Ex = dyn_cast(S)) { + QualType T = Ex->getType(); + if (Loc::IsLocType(T) || T->isIntegerType()) + return getSVal(S); + } + + return UnknownVal(); +} + +inline SVal GRState::getRawSVal(Loc LV, QualType T) const { + return getStateManager().StoreMgr->Retrieve(St, LV, T); +} + +inline SVal GRState::getSVal(const MemRegion* R) const { + return getStateManager().StoreMgr->Retrieve(St, loc::MemRegionVal(R)); +} + +inline BasicValueFactory &GRState::getBasicVals() const { + return getStateManager().getBasicVals(); +} + +inline SymbolManager &GRState::getSymbolManager() const { + return getStateManager().getSymbolManager(); +} + +template +const GRState *GRState::add(typename GRStateTrait::key_type K) const { + return getStateManager().add(this, K, get_context()); +} + +template +typename GRStateTrait::context_type GRState::get_context() const { + return getStateManager().get_context(); +} + +template +const GRState *GRState::remove(typename GRStateTrait::key_type K) const { + return getStateManager().remove(this, K, get_context()); +} + +template +const GRState *GRState::remove(typename GRStateTrait::key_type K, + typename GRStateTrait::context_type C) const { + return getStateManager().remove(this, K, C); +} + +template +const GRState *GRState::remove() const { + return getStateManager().remove(this); +} + +template +const GRState *GRState::set(typename GRStateTrait::data_type D) const { + return getStateManager().set(this, D); +} + +template +const GRState *GRState::set(typename GRStateTrait::key_type K, + typename GRStateTrait::value_type E) const { + return getStateManager().set(this, K, E, get_context()); +} + +template +const GRState *GRState::set(typename GRStateTrait::key_type K, + typename GRStateTrait::value_type E, + typename GRStateTrait::context_type C) const { + return getStateManager().set(this, K, E, C); +} + +template +CB GRState::scanReachableSymbols(SVal val) const { + CB cb(this); + scanReachableSymbols(val, cb); + return cb; +} + +template +CB GRState::scanReachableSymbols(const SVal *beg, const SVal *end) const { + CB cb(this); + scanReachableSymbols(beg, end, cb); + return cb; +} + +template +CB GRState::scanReachableSymbols(const MemRegion * const *beg, + const MemRegion * const *end) const { + CB cb(this); + scanReachableSymbols(beg, end, cb); + return cb; +} + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/GRStateTrait.h b/include/clang/StaticAnalyzer/PathSensitive/GRStateTrait.h new file mode 100644 index 0000000000..914625a285 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/GRStateTrait.h @@ -0,0 +1,152 @@ +//==- GRStateTrait.h - Partial implementations of GRStateTrait -----*- 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 partial implementations of template specializations of +// the class GRStateTrait<>. GRStateTrait<> is used by GRState to implement +// set/get methods for mapulating a GRState's generic data map. +// +//===----------------------------------------------------------------------===// + + +#ifndef LLVM_CLANG_GR_GRSTATETRAIT_H +#define LLVM_CLANG_GR_GRSTATETRAIT_H + +namespace llvm { + class BumpPtrAllocator; + template class ImmutableMap; + template class ImmutableSet; + template class ImmutableList; + template class ImmutableListImpl; +} + +namespace clang { + +namespace ento { + template struct GRStatePartialTrait; + + // Partial-specialization for ImmutableMap. + + template + struct GRStatePartialTrait< llvm::ImmutableMap > { + typedef llvm::ImmutableMap data_type; + typedef typename data_type::Factory& context_type; + typedef Key key_type; + typedef Data value_type; + typedef const value_type* lookup_type; + + static inline data_type MakeData(void* const* p) { + return p ? data_type((typename data_type::TreeTy*) *p) : data_type(0); + } + static inline void* MakeVoidPtr(data_type B) { + return B.getRoot(); + } + static lookup_type Lookup(data_type B, key_type K) { + return B.lookup(K); + } + static data_type Set(data_type B, key_type K, value_type E,context_type F){ + return F.add(B, K, E); + } + + static data_type Remove(data_type B, key_type K, context_type F) { + return F.remove(B, K); + } + + static inline context_type MakeContext(void* p) { + return *((typename data_type::Factory*) p); + } + + static void* CreateContext(llvm::BumpPtrAllocator& Alloc) { + return new typename data_type::Factory(Alloc); + } + + static void DeleteContext(void* Ctx) { + delete (typename data_type::Factory*) Ctx; + } + }; + + + // Partial-specialization for ImmutableSet. + + template + struct GRStatePartialTrait< llvm::ImmutableSet > { + typedef llvm::ImmutableSet data_type; + typedef typename data_type::Factory& context_type; + typedef Key key_type; + + static inline data_type MakeData(void* const* p) { + return p ? data_type((typename data_type::TreeTy*) *p) : data_type(0); + } + + static inline void* MakeVoidPtr(data_type B) { + return B.getRoot(); + } + + static data_type Add(data_type B, key_type K, context_type F) { + return F.add(B, K); + } + + static data_type Remove(data_type B, key_type K, context_type F) { + return F.remove(B, K); + } + + static bool Contains(data_type B, key_type K) { + return B.contains(K); + } + + static inline context_type MakeContext(void* p) { + return *((typename data_type::Factory*) p); + } + + static void* CreateContext(llvm::BumpPtrAllocator& Alloc) { + return new typename data_type::Factory(Alloc); + } + + static void DeleteContext(void* Ctx) { + delete (typename data_type::Factory*) Ctx; + } + }; + + // Partial-specialization for ImmutableList. + + template + struct GRStatePartialTrait< llvm::ImmutableList > { + typedef llvm::ImmutableList data_type; + typedef T key_type; + typedef typename data_type::Factory& context_type; + + static data_type Add(data_type L, key_type K, context_type F) { + return F.add(K, L); + } + + static inline data_type MakeData(void* const* p) { + return p ? data_type((const llvm::ImmutableListImpl*) *p) + : data_type(0); + } + + static inline void* MakeVoidPtr(data_type D) { + return (void*) D.getInternalPointer(); + } + + static inline context_type MakeContext(void* p) { + return *((typename data_type::Factory*) p); + } + + static void* CreateContext(llvm::BumpPtrAllocator& Alloc) { + return new typename data_type::Factory(Alloc); + } + + static void DeleteContext(void* Ctx) { + delete (typename data_type::Factory*) Ctx; + } + }; +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/PathSensitive/MemRegion.h new file mode 100644 index 0000000000..33779a9fd5 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/MemRegion.h @@ -0,0 +1,1077 @@ +//== MemRegion.h - Abstract memory regions for static analysis --*- 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 MemRegion and its subclasses. MemRegion defines a +// partially-typed abstraction of memory useful for path-sensitive dataflow +// analyses. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_MEMREGION_H +#define LLVM_CLANG_GR_MEMREGION_H + +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/ErrorHandling.h" +#include "llvm/ADT/FoldingSet.h" +#include + +namespace llvm { +class BumpPtrAllocator; +class raw_ostream; +} + +namespace clang { + +class LocationContext; +class StackFrameContext; + +namespace ento { + +class MemRegionManager; +class MemSpaceRegion; +class SValBuilder; +class VarRegion; +class CodeTextRegion; + +/// Represent a region's offset within the top level base region. +class RegionOffset { + /// The base region. + const MemRegion *R; + + /// The bit offset within the base region. It shouldn't be negative. + int64_t Offset; + +public: + RegionOffset(const MemRegion *r) : R(r), Offset(0) {} + RegionOffset(const MemRegion *r, int64_t off) : R(r), Offset(off) {} + + const MemRegion *getRegion() const { return R; } + int64_t getOffset() const { return Offset; } +}; + +//===----------------------------------------------------------------------===// +// Base region classes. +//===----------------------------------------------------------------------===// + +/// MemRegion - The root abstract class for all memory regions. +class MemRegion : public llvm::FoldingSetNode { + friend class MemRegionManager; +public: + enum Kind { + // Memory spaces. + GenericMemSpaceRegionKind, + StackLocalsSpaceRegionKind, + StackArgumentsSpaceRegionKind, + HeapSpaceRegionKind, + UnknownSpaceRegionKind, + NonStaticGlobalSpaceRegionKind, + StaticGlobalSpaceRegionKind, + BEG_GLOBAL_MEMSPACES = NonStaticGlobalSpaceRegionKind, + END_GLOBAL_MEMSPACES = StaticGlobalSpaceRegionKind, + BEG_MEMSPACES = GenericMemSpaceRegionKind, + END_MEMSPACES = StaticGlobalSpaceRegionKind, + // Untyped regions. + SymbolicRegionKind, + AllocaRegionKind, + // Typed regions. + BEG_TYPED_REGIONS, + FunctionTextRegionKind = BEG_TYPED_REGIONS, + BlockTextRegionKind, + BlockDataRegionKind, + CompoundLiteralRegionKind, + CXXThisRegionKind, + StringRegionKind, + ElementRegionKind, + // Decl Regions. + BEG_DECL_REGIONS, + VarRegionKind = BEG_DECL_REGIONS, + FieldRegionKind, + ObjCIvarRegionKind, + END_DECL_REGIONS = ObjCIvarRegionKind, + CXXTempObjectRegionKind, + CXXBaseObjectRegionKind, + END_TYPED_REGIONS = CXXBaseObjectRegionKind + }; + +private: + const Kind kind; + +protected: + MemRegion(Kind k) : kind(k) {} + virtual ~MemRegion(); + +public: + ASTContext &getContext() const; + + virtual void Profile(llvm::FoldingSetNodeID& ID) const = 0; + + virtual MemRegionManager* getMemRegionManager() const = 0; + + std::string getString() const; + + const MemSpaceRegion *getMemorySpace() const; + + const MemRegion *getBaseRegion() const; + + const MemRegion *StripCasts() const; + + bool hasGlobalsOrParametersStorage() const; + + bool hasStackStorage() const; + + bool hasStackNonParametersStorage() const; + + bool hasStackParametersStorage() const; + + /// Compute the offset within the top level memory object. + RegionOffset getAsOffset() const; + + virtual void dumpToStream(llvm::raw_ostream& os) const; + + void dump() const; + + Kind getKind() const { return kind; } + + template const RegionTy* getAs() const; + + virtual bool isBoundable() const { return false; } + + static bool classof(const MemRegion*) { return true; } +}; + +/// MemSpaceRegion - A memory region that represents and "memory space"; +/// for example, the set of global variables, the stack frame, etc. +class MemSpaceRegion : public MemRegion { +protected: + friend class MemRegionManager; + + MemRegionManager *Mgr; + + MemSpaceRegion(MemRegionManager *mgr, Kind k = GenericMemSpaceRegionKind) + : MemRegion(k), Mgr(mgr) { + assert(classof(this)); + } + + MemRegionManager* getMemRegionManager() const { return Mgr; } + +public: + bool isBoundable() const { return false; } + + void Profile(llvm::FoldingSetNodeID &ID) const; + + static bool classof(const MemRegion *R) { + Kind k = R->getKind(); + return k >= BEG_MEMSPACES && k <= END_MEMSPACES; + } +}; + +class GlobalsSpaceRegion : public MemSpaceRegion { +protected: + GlobalsSpaceRegion(MemRegionManager *mgr, Kind k) + : MemSpaceRegion(mgr, k) {} +public: + static bool classof(const MemRegion *R) { + Kind k = R->getKind(); + return k >= BEG_GLOBAL_MEMSPACES && k <= END_GLOBAL_MEMSPACES; + } +}; + +class StaticGlobalSpaceRegion : public GlobalsSpaceRegion { + friend class MemRegionManager; + + const CodeTextRegion *CR; + + StaticGlobalSpaceRegion(MemRegionManager *mgr, const CodeTextRegion *cr) + : GlobalsSpaceRegion(mgr, StaticGlobalSpaceRegionKind), CR(cr) {} + +public: + void Profile(llvm::FoldingSetNodeID &ID) const; + + void dumpToStream(llvm::raw_ostream& os) const; + + const CodeTextRegion *getCodeRegion() const { return CR; } + + static bool classof(const MemRegion *R) { + return R->getKind() == StaticGlobalSpaceRegionKind; + } +}; + +class NonStaticGlobalSpaceRegion : public GlobalsSpaceRegion { + friend class MemRegionManager; + + NonStaticGlobalSpaceRegion(MemRegionManager *mgr) + : GlobalsSpaceRegion(mgr, NonStaticGlobalSpaceRegionKind) {} + +public: + + void dumpToStream(llvm::raw_ostream& os) const; + + static bool classof(const MemRegion *R) { + return R->getKind() == NonStaticGlobalSpaceRegionKind; + } +}; + +class HeapSpaceRegion : public MemSpaceRegion { + friend class MemRegionManager; + + HeapSpaceRegion(MemRegionManager *mgr) + : MemSpaceRegion(mgr, HeapSpaceRegionKind) {} +public: + static bool classof(const MemRegion *R) { + return R->getKind() == HeapSpaceRegionKind; + } +}; + +class UnknownSpaceRegion : public MemSpaceRegion { + friend class MemRegionManager; + UnknownSpaceRegion(MemRegionManager *mgr) + : MemSpaceRegion(mgr, UnknownSpaceRegionKind) {} +public: + static bool classof(const MemRegion *R) { + return R->getKind() == UnknownSpaceRegionKind; + } +}; + +class StackSpaceRegion : public MemSpaceRegion { +private: + const StackFrameContext *SFC; + +protected: + StackSpaceRegion(MemRegionManager *mgr, Kind k, const StackFrameContext *sfc) + : MemSpaceRegion(mgr, k), SFC(sfc) { + assert(classof(this)); + } + +public: + const StackFrameContext *getStackFrame() const { return SFC; } + + void Profile(llvm::FoldingSetNodeID &ID) const; + + static bool classof(const MemRegion *R) { + Kind k = R->getKind(); + return k >= StackLocalsSpaceRegionKind && + k <= StackArgumentsSpaceRegionKind; + } +}; + +class StackLocalsSpaceRegion : public StackSpaceRegion { +private: + friend class MemRegionManager; + StackLocalsSpaceRegion(MemRegionManager *mgr, const StackFrameContext *sfc) + : StackSpaceRegion(mgr, StackLocalsSpaceRegionKind, sfc) {} +public: + static bool classof(const MemRegion *R) { + return R->getKind() == StackLocalsSpaceRegionKind; + } +}; + +class StackArgumentsSpaceRegion : public StackSpaceRegion { +private: + friend class MemRegionManager; + StackArgumentsSpaceRegion(MemRegionManager *mgr, const StackFrameContext *sfc) + : StackSpaceRegion(mgr, StackArgumentsSpaceRegionKind, sfc) {} +public: + static bool classof(const MemRegion *R) { + return R->getKind() == StackArgumentsSpaceRegionKind; + } +}; + + +/// SubRegion - A region that subsets another larger region. Most regions +/// are subclasses of SubRegion. +class SubRegion : public MemRegion { +protected: + const MemRegion* superRegion; + SubRegion(const MemRegion* sReg, Kind k) : MemRegion(k), superRegion(sReg) {} +public: + const MemRegion* getSuperRegion() const { + return superRegion; + } + + /// getExtent - Returns the size of the region in bytes. + virtual DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const { + return UnknownVal(); + } + + MemRegionManager* getMemRegionManager() const; + + bool isSubRegionOf(const MemRegion* R) const; + + static bool classof(const MemRegion* R) { + return R->getKind() > END_MEMSPACES; + } +}; + +//===----------------------------------------------------------------------===// +// MemRegion subclasses. +//===----------------------------------------------------------------------===// + +/// AllocaRegion - A region that represents an untyped blob of bytes created +/// by a call to 'alloca'. +class AllocaRegion : public SubRegion { + friend class MemRegionManager; +protected: + unsigned Cnt; // Block counter. Used to distinguish different pieces of + // memory allocated by alloca at the same call site. + const Expr* Ex; + + AllocaRegion(const Expr* ex, unsigned cnt, const MemRegion *superRegion) + : SubRegion(superRegion, AllocaRegionKind), Cnt(cnt), Ex(ex) {} + +public: + + const Expr* getExpr() const { return Ex; } + + bool isBoundable() const { return true; } + + DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const; + + void Profile(llvm::FoldingSetNodeID& ID) const; + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const Expr* Ex, + unsigned Cnt, const MemRegion *superRegion); + + void dumpToStream(llvm::raw_ostream& os) const; + + static bool classof(const MemRegion* R) { + return R->getKind() == AllocaRegionKind; + } +}; + +/// TypedRegion - An abstract class representing regions that are typed. +class TypedRegion : public SubRegion { +protected: + TypedRegion(const MemRegion* sReg, Kind k) : SubRegion(sReg, k) {} + +public: + virtual QualType getValueType() const = 0; + + virtual QualType getLocationType() const { + // FIXME: We can possibly optimize this later to cache this value. + return getContext().getPointerType(getValueType()); + } + + QualType getDesugaredValueType(ASTContext &Context) const { + QualType T = getValueType(); + return T.getTypePtrOrNull() ? T.getDesugaredType(Context) : T; + } + + QualType getDesugaredLocationType(ASTContext &Context) const { + return getLocationType().getDesugaredType(Context); + } + + bool isBoundable() const { return true; } + + static bool classof(const MemRegion* R) { + unsigned k = R->getKind(); + return k >= BEG_TYPED_REGIONS && k <= END_TYPED_REGIONS; + } +}; + + +class CodeTextRegion : public TypedRegion { +protected: + CodeTextRegion(const MemRegion *sreg, Kind k) : TypedRegion(sreg, k) {} +public: + QualType getValueType() const { + assert(0 && "Do not get the object type of a CodeTextRegion."); + return QualType(); + } + + bool isBoundable() const { return false; } + + static bool classof(const MemRegion* R) { + Kind k = R->getKind(); + return k >= FunctionTextRegionKind && k <= BlockTextRegionKind; + } +}; + +/// FunctionTextRegion - A region that represents code texts of function. +class FunctionTextRegion : public CodeTextRegion { + const FunctionDecl *FD; +public: + FunctionTextRegion(const FunctionDecl* fd, const MemRegion* sreg) + : CodeTextRegion(sreg, FunctionTextRegionKind), FD(fd) {} + + QualType getLocationType() const { + return getContext().getPointerType(FD->getType()); + } + + const FunctionDecl *getDecl() const { + return FD; + } + + virtual void dumpToStream(llvm::raw_ostream& os) const; + + void Profile(llvm::FoldingSetNodeID& ID) const; + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const FunctionDecl *FD, + const MemRegion*); + + static bool classof(const MemRegion* R) { + return R->getKind() == FunctionTextRegionKind; + } +}; + + +/// BlockTextRegion - A region that represents code texts of blocks (closures). +/// Blocks are represented with two kinds of regions. BlockTextRegions +/// represent the "code", while BlockDataRegions represent instances of blocks, +/// which correspond to "code+data". The distinction is important, because +/// like a closure a block captures the values of externally referenced +/// variables. +class BlockTextRegion : public CodeTextRegion { + friend class MemRegionManager; + + const BlockDecl *BD; + AnalysisContext *AC; + CanQualType locTy; + + BlockTextRegion(const BlockDecl *bd, CanQualType lTy, + AnalysisContext *ac, const MemRegion* sreg) + : CodeTextRegion(sreg, BlockTextRegionKind), BD(bd), AC(ac), locTy(lTy) {} + +public: + QualType getLocationType() const { + return locTy; + } + + const BlockDecl *getDecl() const { + return BD; + } + + AnalysisContext *getAnalysisContext() const { return AC; } + + virtual void dumpToStream(llvm::raw_ostream& os) const; + + void Profile(llvm::FoldingSetNodeID& ID) const; + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const BlockDecl *BD, + CanQualType, const AnalysisContext*, + const MemRegion*); + + static bool classof(const MemRegion* R) { + return R->getKind() == BlockTextRegionKind; + } +}; + +/// BlockDataRegion - A region that represents a block instance. +/// Blocks are represented with two kinds of regions. BlockTextRegions +/// represent the "code", while BlockDataRegions represent instances of blocks, +/// which correspond to "code+data". The distinction is important, because +/// like a closure a block captures the values of externally referenced +/// variables. +class BlockDataRegion : public SubRegion { + friend class MemRegionManager; + const BlockTextRegion *BC; + const LocationContext *LC; // Can be null */ + void *ReferencedVars; + + BlockDataRegion(const BlockTextRegion *bc, const LocationContext *lc, + const MemRegion *sreg) + : SubRegion(sreg, BlockDataRegionKind), BC(bc), LC(lc), ReferencedVars(0) {} + +public: + const BlockTextRegion *getCodeRegion() const { return BC; } + + const BlockDecl *getDecl() const { return BC->getDecl(); } + + class referenced_vars_iterator { + const MemRegion * const *R; + public: + explicit referenced_vars_iterator(const MemRegion * const *r) : R(r) {} + + operator const MemRegion * const *() const { + return R; + } + + const VarRegion* operator*() const { + return cast(*R); + } + + bool operator==(const referenced_vars_iterator &I) const { + return I.R == R; + } + bool operator!=(const referenced_vars_iterator &I) const { + return I.R != R; + } + referenced_vars_iterator& operator++() { + ++R; + return *this; + } + }; + + referenced_vars_iterator referenced_vars_begin() const; + referenced_vars_iterator referenced_vars_end() const; + + virtual void dumpToStream(llvm::raw_ostream& os) const; + + void Profile(llvm::FoldingSetNodeID& ID) const; + + static void ProfileRegion(llvm::FoldingSetNodeID&, const BlockTextRegion *, + const LocationContext *, const MemRegion *); + + static bool classof(const MemRegion* R) { + return R->getKind() == BlockDataRegionKind; + } +private: + void LazyInitializeReferencedVars(); +}; + +/// SymbolicRegion - A special, "non-concrete" region. Unlike other region +/// clases, SymbolicRegion represents a region that serves as an alias for +/// either a real region, a NULL pointer, etc. It essentially is used to +/// map the concept of symbolic values into the domain of regions. Symbolic +/// regions do not need to be typed. +class SymbolicRegion : public SubRegion { +protected: + const SymbolRef sym; + +public: + SymbolicRegion(const SymbolRef s, const MemRegion* sreg) + : SubRegion(sreg, SymbolicRegionKind), sym(s) {} + + SymbolRef getSymbol() const { + return sym; + } + + bool isBoundable() const { return true; } + + DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const; + + void Profile(llvm::FoldingSetNodeID& ID) const; + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, + SymbolRef sym, + const MemRegion* superRegion); + + void dumpToStream(llvm::raw_ostream& os) const; + + static bool classof(const MemRegion* R) { + return R->getKind() == SymbolicRegionKind; + } +}; + +/// StringRegion - Region associated with a StringLiteral. +class StringRegion : public TypedRegion { + friend class MemRegionManager; + const StringLiteral* Str; +protected: + + StringRegion(const StringLiteral* str, const MemRegion* sreg) + : TypedRegion(sreg, StringRegionKind), Str(str) {} + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, + const StringLiteral* Str, + const MemRegion* superRegion); + +public: + + const StringLiteral* getStringLiteral() const { return Str; } + + QualType getValueType() const { + return Str->getType(); + } + + DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const; + + bool isBoundable() const { return false; } + + void Profile(llvm::FoldingSetNodeID& ID) const { + ProfileRegion(ID, Str, superRegion); + } + + void dumpToStream(llvm::raw_ostream& os) const; + + static bool classof(const MemRegion* R) { + return R->getKind() == StringRegionKind; + } +}; + +/// CompoundLiteralRegion - A memory region representing a compound literal. +/// Compound literals are essentially temporaries that are stack allocated +/// or in the global constant pool. +class CompoundLiteralRegion : public TypedRegion { +private: + friend class MemRegionManager; + const CompoundLiteralExpr* CL; + + CompoundLiteralRegion(const CompoundLiteralExpr* cl, const MemRegion* sReg) + : TypedRegion(sReg, CompoundLiteralRegionKind), CL(cl) {} + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, + const CompoundLiteralExpr* CL, + const MemRegion* superRegion); +public: + QualType getValueType() const { + return CL->getType(); + } + + bool isBoundable() const { return !CL->isFileScope(); } + + void Profile(llvm::FoldingSetNodeID& ID) const; + + void dumpToStream(llvm::raw_ostream& os) const; + + const CompoundLiteralExpr* getLiteralExpr() const { return CL; } + + static bool classof(const MemRegion* R) { + return R->getKind() == CompoundLiteralRegionKind; + } +}; + +class DeclRegion : public TypedRegion { +protected: + const Decl* D; + + DeclRegion(const Decl* d, const MemRegion* sReg, Kind k) + : TypedRegion(sReg, k), D(d) {} + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl* D, + const MemRegion* superRegion, Kind k); + +public: + const Decl* getDecl() const { return D; } + void Profile(llvm::FoldingSetNodeID& ID) const; + + DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const; + + static bool classof(const MemRegion* R) { + unsigned k = R->getKind(); + return k >= BEG_DECL_REGIONS && k <= END_DECL_REGIONS; + } +}; + +class VarRegion : public DeclRegion { + friend class MemRegionManager; + + // Constructors and private methods. + VarRegion(const VarDecl* vd, const MemRegion* sReg) + : DeclRegion(vd, sReg, VarRegionKind) {} + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const VarDecl* VD, + const MemRegion *superRegion) { + DeclRegion::ProfileRegion(ID, VD, superRegion, VarRegionKind); + } + + void Profile(llvm::FoldingSetNodeID& ID) const; + +public: + const VarDecl *getDecl() const { return cast(D); } + + const StackFrameContext *getStackFrame() const; + + QualType getValueType() const { + // FIXME: We can cache this if needed. + return getDecl()->getType(); + } + + void dumpToStream(llvm::raw_ostream& os) const; + + static bool classof(const MemRegion* R) { + return R->getKind() == VarRegionKind; + } +}; + +/// CXXThisRegion - Represents the region for the implicit 'this' parameter +/// in a call to a C++ method. This region doesn't represent the object +/// referred to by 'this', but rather 'this' itself. +class CXXThisRegion : public TypedRegion { + friend class MemRegionManager; + CXXThisRegion(const PointerType *thisPointerTy, + const MemRegion *sReg) + : TypedRegion(sReg, CXXThisRegionKind), ThisPointerTy(thisPointerTy) {} + + static void ProfileRegion(llvm::FoldingSetNodeID &ID, + const PointerType *PT, + const MemRegion *sReg); + + void Profile(llvm::FoldingSetNodeID &ID) const; + +public: + QualType getValueType() const { + return QualType(ThisPointerTy, 0); + } + + void dumpToStream(llvm::raw_ostream& os) const; + + static bool classof(const MemRegion* R) { + return R->getKind() == CXXThisRegionKind; + } + +private: + const PointerType *ThisPointerTy; +}; + +class FieldRegion : public DeclRegion { + friend class MemRegionManager; + + FieldRegion(const FieldDecl* fd, const MemRegion* sReg) + : DeclRegion(fd, sReg, FieldRegionKind) {} + +public: + + void dumpToStream(llvm::raw_ostream& os) const; + + const FieldDecl* getDecl() const { return cast(D); } + + QualType getValueType() const { + // FIXME: We can cache this if needed. + return getDecl()->getType(); + } + + DefinedOrUnknownSVal getExtent(SValBuilder &svalBuilder) const; + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const FieldDecl* FD, + const MemRegion* superRegion) { + DeclRegion::ProfileRegion(ID, FD, superRegion, FieldRegionKind); + } + + static bool classof(const MemRegion* R) { + return R->getKind() == FieldRegionKind; + } +}; + +class ObjCIvarRegion : public DeclRegion { + + friend class MemRegionManager; + + ObjCIvarRegion(const ObjCIvarDecl* ivd, const MemRegion* sReg) + : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {} + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, const ObjCIvarDecl* ivd, + const MemRegion* superRegion) { + DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind); + } + +public: + const ObjCIvarDecl* getDecl() const { return cast(D); } + QualType getValueType() const { return getDecl()->getType(); } + + void dumpToStream(llvm::raw_ostream& os) const; + + static bool classof(const MemRegion* R) { + return R->getKind() == ObjCIvarRegionKind; + } +}; +//===----------------------------------------------------------------------===// +// Auxillary data classes for use with MemRegions. +//===----------------------------------------------------------------------===// + +class ElementRegion; + +class RegionRawOffset { +private: + friend class ElementRegion; + + const MemRegion *Region; + int64_t Offset; + + RegionRawOffset(const MemRegion* reg, int64_t offset = 0) + : Region(reg), Offset(offset) {} + +public: + // FIXME: Eventually support symbolic offsets. + int64_t getByteOffset() const { return Offset; } + const MemRegion *getRegion() const { return Region; } + + void dumpToStream(llvm::raw_ostream& os) const; + void dump() const; +}; + +class ElementRegion : public TypedRegion { + friend class MemRegionManager; + + QualType ElementType; + NonLoc Index; + + ElementRegion(QualType elementType, NonLoc Idx, const MemRegion* sReg) + : TypedRegion(sReg, ElementRegionKind), + ElementType(elementType), Index(Idx) { + assert((!isa(&Idx) || + cast(&Idx)->getValue().isSigned()) && + "The index must be signed"); + } + + static void ProfileRegion(llvm::FoldingSetNodeID& ID, QualType elementType, + SVal Idx, const MemRegion* superRegion); + +public: + + NonLoc getIndex() const { return Index; } + + QualType getValueType() const { + return ElementType; + } + + QualType getElementType() const { + return ElementType; + } + /// Compute the offset within the array. The array might also be a subobject. + RegionRawOffset getAsArrayOffset() const; + + void dumpToStream(llvm::raw_ostream& os) const; + + void Profile(llvm::FoldingSetNodeID& ID) const; + + static bool classof(const MemRegion* R) { + return R->getKind() == ElementRegionKind; + } +}; + +// C++ temporary object associated with an expression. +class CXXTempObjectRegion : public TypedRegion { + friend class MemRegionManager; + + Expr const *Ex; + + CXXTempObjectRegion(Expr const *E, MemRegion const *sReg) + : TypedRegion(sReg, CXXTempObjectRegionKind), Ex(E) {} + + static void ProfileRegion(llvm::FoldingSetNodeID &ID, + Expr const *E, const MemRegion *sReg); + +public: + QualType getValueType() const { + return Ex->getType(); + } + + void dumpToStream(llvm::raw_ostream& os) const; + + void Profile(llvm::FoldingSetNodeID &ID) const; + + static bool classof(const MemRegion* R) { + return R->getKind() == CXXTempObjectRegionKind; + } +}; + +// CXXBaseObjectRegion represents a base object within a C++ object. It is +// identified by the base class declaration and the region of its parent object. +class CXXBaseObjectRegion : public TypedRegion { + friend class MemRegionManager; + + const CXXRecordDecl *decl; + + CXXBaseObjectRegion(const CXXRecordDecl *d, const MemRegion *sReg) + : TypedRegion(sReg, CXXBaseObjectRegionKind), decl(d) {} + + static void ProfileRegion(llvm::FoldingSetNodeID &ID, + const CXXRecordDecl *decl, const MemRegion *sReg); + +public: + QualType getValueType() const; + + void dumpToStream(llvm::raw_ostream& os) const; + + void Profile(llvm::FoldingSetNodeID &ID) const; + + static bool classof(const MemRegion *region) { + return region->getKind() == CXXBaseObjectRegionKind; + } +}; + +template +const RegionTy* MemRegion::getAs() const { + if (const RegionTy* RT = dyn_cast(this)) + return RT; + + return NULL; +} + +//===----------------------------------------------------------------------===// +// MemRegionManager - Factory object for creating regions. +//===----------------------------------------------------------------------===// + +class MemRegionManager { + ASTContext &C; + llvm::BumpPtrAllocator& A; + llvm::FoldingSet Regions; + + NonStaticGlobalSpaceRegion *globals; + + llvm::DenseMap + StackLocalsSpaceRegions; + llvm::DenseMap + StackArgumentsSpaceRegions; + llvm::DenseMap + StaticsGlobalSpaceRegions; + + HeapSpaceRegion *heap; + UnknownSpaceRegion *unknown; + MemSpaceRegion *code; + +public: + MemRegionManager(ASTContext &c, llvm::BumpPtrAllocator& a) + : C(c), A(a), globals(0), heap(0), unknown(0), code(0) {} + + ~MemRegionManager(); + + ASTContext &getContext() { return C; } + + llvm::BumpPtrAllocator &getAllocator() { return A; } + + /// getStackLocalsRegion - Retrieve the memory region associated with the + /// specified stack frame. + const StackLocalsSpaceRegion * + getStackLocalsRegion(const StackFrameContext *STC); + + /// getStackArgumentsRegion - Retrieve the memory region associated with + /// function/method arguments of the specified stack frame. + const StackArgumentsSpaceRegion * + getStackArgumentsRegion(const StackFrameContext *STC); + + /// getGlobalsRegion - Retrieve the memory region associated with + /// global variables. + const GlobalsSpaceRegion *getGlobalsRegion(const CodeTextRegion *R = 0); + + /// getHeapRegion - Retrieve the memory region associated with the + /// generic "heap". + const HeapSpaceRegion *getHeapRegion(); + + /// getUnknownRegion - Retrieve the memory region associated with unknown + /// memory space. + const MemSpaceRegion *getUnknownRegion(); + + const MemSpaceRegion *getCodeRegion(); + + /// getAllocaRegion - Retrieve a region associated with a call to alloca(). + const AllocaRegion *getAllocaRegion(const Expr* Ex, unsigned Cnt, + const LocationContext *LC); + + /// getCompoundLiteralRegion - Retrieve the region associated with a + /// given CompoundLiteral. + const CompoundLiteralRegion* + getCompoundLiteralRegion(const CompoundLiteralExpr* CL, + const LocationContext *LC); + + /// getCXXThisRegion - Retrieve the [artifical] region associated with the + /// parameter 'this'. + const CXXThisRegion *getCXXThisRegion(QualType thisPointerTy, + const LocationContext *LC); + + /// getSymbolicRegion - Retrieve or create a "symbolic" memory region. + const SymbolicRegion* getSymbolicRegion(SymbolRef sym); + + const StringRegion* getStringRegion(const StringLiteral* Str); + + /// getVarRegion - Retrieve or create the memory region associated with + /// a specified VarDecl and LocationContext. + const VarRegion* getVarRegion(const VarDecl *D, const LocationContext *LC); + + /// getVarRegion - Retrieve or create the memory region associated with + /// a specified VarDecl and super region. + const VarRegion* getVarRegion(const VarDecl *D, const MemRegion *superR); + + /// getElementRegion - Retrieve the memory region associated with the + /// associated element type, index, and super region. + const ElementRegion *getElementRegion(QualType elementType, NonLoc Idx, + const MemRegion *superRegion, + ASTContext &Ctx); + + const ElementRegion *getElementRegionWithSuper(const ElementRegion *ER, + const MemRegion *superRegion) { + return getElementRegion(ER->getElementType(), ER->getIndex(), + superRegion, ER->getContext()); + } + + /// getFieldRegion - Retrieve or create the memory region associated with + /// a specified FieldDecl. 'superRegion' corresponds to the containing + /// memory region (which typically represents the memory representing + /// a structure or class). + const FieldRegion *getFieldRegion(const FieldDecl* fd, + const MemRegion* superRegion); + + const FieldRegion *getFieldRegionWithSuper(const FieldRegion *FR, + const MemRegion *superRegion) { + return getFieldRegion(FR->getDecl(), superRegion); + } + + /// getObjCIvarRegion - Retrieve or create the memory region associated with + /// a specified Objective-c instance variable. 'superRegion' corresponds + /// to the containing region (which typically represents the Objective-C + /// object). + const ObjCIvarRegion *getObjCIvarRegion(const ObjCIvarDecl* ivd, + const MemRegion* superRegion); + + const CXXTempObjectRegion *getCXXTempObjectRegion(Expr const *Ex, + LocationContext const *LC); + + const CXXBaseObjectRegion *getCXXBaseObjectRegion(const CXXRecordDecl *decl, + const MemRegion *superRegion); + + const FunctionTextRegion *getFunctionTextRegion(const FunctionDecl *FD); + const BlockTextRegion *getBlockTextRegion(const BlockDecl *BD, + CanQualType locTy, + AnalysisContext *AC); + + /// getBlockDataRegion - Get the memory region associated with an instance + /// of a block. Unlike many other MemRegions, the LocationContext* + /// argument is allowed to be NULL for cases where we have no known + /// context. + const BlockDataRegion *getBlockDataRegion(const BlockTextRegion *bc, + const LocationContext *lc = NULL); + + bool isGlobalsRegion(const MemRegion* R) { + assert(R); + return R == globals; + } + +private: + template + RegionTy* getRegion(const A1 a1); + + template + RegionTy* getSubRegion(const A1 a1, const MemRegion* superRegion); + + template + RegionTy* getRegion(const A1 a1, const A2 a2); + + template + RegionTy* getSubRegion(const A1 a1, const A2 a2, + const MemRegion* superRegion); + + template + RegionTy* getSubRegion(const A1 a1, const A2 a2, const A3 a3, + const MemRegion* superRegion); + + template + const REG* LazyAllocate(REG*& region); + + template + const REG* LazyAllocate(REG*& region, ARG a); +}; + +//===----------------------------------------------------------------------===// +// Out-of-line member definitions. +//===----------------------------------------------------------------------===// + +inline ASTContext& MemRegion::getContext() const { + return getMemRegionManager()->getContext(); +} + +} // end GR namespace + +} // end clang namespace + +//===----------------------------------------------------------------------===// +// Pretty-printing regions. +//===----------------------------------------------------------------------===// + +namespace llvm { +static inline raw_ostream& operator<<(raw_ostream& os, + const clang::ento::MemRegion* R) { + R->dumpToStream(os); + return os; +} +} // end llvm namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/PathSensitive/SValBuilder.h new file mode 100644 index 0000000000..0705510ccd --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/SValBuilder.h @@ -0,0 +1,259 @@ +// SValBuilder.h - Construction of SVals from evaluating 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 SValBuilder, a class that defines the interface for +// "symbolical evaluators" which construct an SVal from an expression. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_SVALBUILDER +#define LLVM_CLANG_GR_SVALBUILDER + +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" + +namespace clang { + +namespace ento { + +class GRState; + +class SValBuilder { +protected: + ASTContext &Context; + + /// Manager of APSInt values. + BasicValueFactory BasicVals; + + /// Manages the creation of symbols. + SymbolManager SymMgr; + + /// Manages the creation of memory regions. + MemRegionManager MemMgr; + + GRStateManager &StateMgr; + + /// The scalar type to use for array indices. + const QualType ArrayIndexTy; + + /// The width of the scalar type used for array indices. + const unsigned ArrayIndexWidth; + +public: + // FIXME: Make these protected again one RegionStoreManager correctly + // handles loads from differening bound value types. + virtual SVal evalCastNL(NonLoc val, QualType castTy) = 0; + virtual SVal evalCastL(Loc val, QualType castTy) = 0; + +public: + SValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, + GRStateManager &stateMgr) + : Context(context), BasicVals(context, alloc), + SymMgr(context, BasicVals, alloc), + MemMgr(context, alloc), + StateMgr(stateMgr), + ArrayIndexTy(context.IntTy), + ArrayIndexWidth(context.getTypeSize(ArrayIndexTy)) {} + + virtual ~SValBuilder() {} + + SVal evalCast(SVal V, QualType castTy, QualType originalType); + + virtual SVal evalMinus(NonLoc val) = 0; + + virtual SVal evalComplement(NonLoc val) = 0; + + virtual SVal evalBinOpNN(const GRState *state, BinaryOperator::Opcode Op, + NonLoc lhs, NonLoc rhs, QualType resultTy) = 0; + + virtual SVal evalBinOpLL(const GRState *state, BinaryOperator::Opcode Op, + Loc lhs, Loc rhs, QualType resultTy) = 0; + + virtual SVal evalBinOpLN(const GRState *state, BinaryOperator::Opcode Op, + Loc lhs, NonLoc rhs, QualType resultTy) = 0; + + /// getKnownValue - evaluates a given SVal. If the SVal has only one possible + /// (integer) value, that value is returned. Otherwise, returns NULL. + virtual const llvm::APSInt *getKnownValue(const GRState *state, SVal V) = 0; + + SVal evalBinOp(const GRState *ST, BinaryOperator::Opcode Op, + SVal L, SVal R, QualType T); + + DefinedOrUnknownSVal evalEQ(const GRState *ST, DefinedOrUnknownSVal L, + DefinedOrUnknownSVal R); + + ASTContext &getContext() { return Context; } + const ASTContext &getContext() const { return Context; } + + GRStateManager &getStateManager() { return StateMgr; } + + QualType getConditionType() const { + return getContext().IntTy; + } + + QualType getArrayIndexType() const { + return ArrayIndexTy; + } + + BasicValueFactory &getBasicValueFactory() { return BasicVals; } + const BasicValueFactory &getBasicValueFactory() const { return BasicVals; } + + SymbolManager &getSymbolManager() { return SymMgr; } + const SymbolManager &getSymbolManager() const { return SymMgr; } + + MemRegionManager &getRegionManager() { return MemMgr; } + const MemRegionManager &getRegionManager() const { return MemMgr; } + + // Forwarding methods to SymbolManager. + + const SymbolConjured* getConjuredSymbol(const Stmt* E, QualType T, + unsigned VisitCount, + const void* SymbolTag = 0) { + return SymMgr.getConjuredSymbol(E, T, VisitCount, SymbolTag); + } + + const SymbolConjured* getConjuredSymbol(const Expr* E, unsigned VisitCount, + const void* SymbolTag = 0) { + return SymMgr.getConjuredSymbol(E, VisitCount, SymbolTag); + } + + /// makeZeroVal - Construct an SVal representing '0' for the specified type. + DefinedOrUnknownSVal makeZeroVal(QualType T); + + /// getRegionValueSymbolVal - make a unique symbol for value of R. + DefinedOrUnknownSVal getRegionValueSymbolVal(const TypedRegion *R); + + DefinedOrUnknownSVal getConjuredSymbolVal(const void *SymbolTag, + const Expr *E, unsigned Count); + DefinedOrUnknownSVal getConjuredSymbolVal(const void *SymbolTag, + const Expr *E, QualType T, + unsigned Count); + + DefinedOrUnknownSVal getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, + const TypedRegion *R); + + DefinedSVal getMetadataSymbolVal(const void *SymbolTag, const MemRegion *MR, + const Expr *E, QualType T, unsigned Count); + + DefinedSVal getFunctionPointer(const FunctionDecl *FD); + + DefinedSVal getBlockPointer(const BlockDecl *BD, CanQualType locTy, + const LocationContext *LC); + + NonLoc makeCompoundVal(QualType T, llvm::ImmutableList Vals) { + return nonloc::CompoundVal(BasicVals.getCompoundValData(T, Vals)); + } + + NonLoc makeLazyCompoundVal(const void *store, const TypedRegion *R) { + return nonloc::LazyCompoundVal(BasicVals.getLazyCompoundValData(store, R)); + } + + NonLoc makeZeroArrayIndex() { + return nonloc::ConcreteInt(BasicVals.getValue(0, ArrayIndexTy)); + } + + NonLoc makeArrayIndex(uint64_t idx) { + return nonloc::ConcreteInt(BasicVals.getValue(idx, ArrayIndexTy)); + } + + SVal convertToArrayIndex(SVal V); + + nonloc::ConcreteInt makeIntVal(const IntegerLiteral* I) { + return nonloc::ConcreteInt(BasicVals.getValue(I->getValue(), + I->getType()->isUnsignedIntegerType())); + } + + nonloc::ConcreteInt makeIntVal(const CXXBoolLiteralExpr *E) { + return E->getValue() ? nonloc::ConcreteInt(BasicVals.getValue(1, 1, true)) + : nonloc::ConcreteInt(BasicVals.getValue(0, 1, true)); + } + + nonloc::ConcreteInt makeIntVal(const llvm::APSInt& V) { + return nonloc::ConcreteInt(BasicVals.getValue(V)); + } + + loc::ConcreteInt makeIntLocVal(const llvm::APSInt &v) { + return loc::ConcreteInt(BasicVals.getValue(v)); + } + + NonLoc makeIntVal(const llvm::APInt& V, bool isUnsigned) { + return nonloc::ConcreteInt(BasicVals.getValue(V, isUnsigned)); + } + + DefinedSVal makeIntVal(uint64_t X, QualType T) { + if (Loc::IsLocType(T)) + return loc::ConcreteInt(BasicVals.getValue(X, T)); + + return nonloc::ConcreteInt(BasicVals.getValue(X, T)); + } + + NonLoc makeIntVal(uint64_t X, bool isUnsigned) { + return nonloc::ConcreteInt(BasicVals.getIntValue(X, isUnsigned)); + } + + NonLoc makeIntValWithPtrWidth(uint64_t X, bool isUnsigned) { + return nonloc::ConcreteInt(BasicVals.getIntWithPtrWidth(X, isUnsigned)); + } + + NonLoc makeIntVal(uint64_t X, unsigned BitWidth, bool isUnsigned) { + return nonloc::ConcreteInt(BasicVals.getValue(X, BitWidth, isUnsigned)); + } + + NonLoc makeLocAsInteger(Loc V, unsigned Bits) { + return nonloc::LocAsInteger(BasicVals.getPersistentSValWithData(V, Bits)); + } + + NonLoc makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APSInt& rhs, QualType T); + + NonLoc makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const SymExpr *rhs, QualType T); + + NonLoc makeTruthVal(bool b, QualType T) { + return nonloc::ConcreteInt(BasicVals.getTruthValue(b, T)); + } + + NonLoc makeTruthVal(bool b) { + return nonloc::ConcreteInt(BasicVals.getTruthValue(b)); + } + + Loc makeNull() { + return loc::ConcreteInt(BasicVals.getZeroWithPtrWidth()); + } + + Loc makeLoc(SymbolRef Sym) { + return loc::MemRegionVal(MemMgr.getSymbolicRegion(Sym)); + } + + Loc makeLoc(const MemRegion* R) { + return loc::MemRegionVal(R); + } + + Loc makeLoc(const AddrLabelExpr* E) { + return loc::GotoLabel(E->getLabel()); + } + + Loc makeLoc(const llvm::APSInt& V) { + return loc::ConcreteInt(BasicVals.getValue(V)); + } + +}; + +SValBuilder* createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc, + ASTContext &context, + GRStateManager &stateMgr); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/SVals.h b/include/clang/StaticAnalyzer/PathSensitive/SVals.h new file mode 100644 index 0000000000..d4d846954e --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/SVals.h @@ -0,0 +1,522 @@ +//== SVals.h - Abstract Values for Static Analysis ---------*- 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 SVal, Loc, and NonLoc, classes that represent +// abstract r-values for use with path-sensitive value tracking. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_RVALUE_H +#define LLVM_CLANG_GR_RVALUE_H + +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" +#include "llvm/Support/Casting.h" +#include "llvm/ADT/ImmutableList.h" + +namespace llvm { + class raw_ostream; +} + +//==------------------------------------------------------------------------==// +// Base SVal types. +//==------------------------------------------------------------------------==// + +namespace clang { + +namespace ento { + +class CompoundValData; +class LazyCompoundValData; +class GRState; +class BasicValueFactory; +class MemRegion; +class TypedRegion; +class MemRegionManager; +class GRStateManager; +class SValBuilder; + +/// SVal - This represents a symbolic expression, which can be either +/// an L-value or an R-value. +/// +class SVal { +public: + enum BaseKind { + // The enumerators must be representable using 2 bits. + UndefinedKind = 0, // for subclass UndefinedVal (an uninitialized value) + UnknownKind = 1, // for subclass UnknownVal (a void value) + LocKind = 2, // for subclass Loc (an L-value) + NonLocKind = 3 // for subclass NonLoc (an R-value that's not + // an L-value) + }; + enum { BaseBits = 2, BaseMask = 0x3 }; + +protected: + const void* Data; + + /// The lowest 2 bits are a BaseKind (0 -- 3). + /// The higher bits are an unsigned "kind" value. + unsigned Kind; + +protected: + explicit SVal(const void* d, bool isLoc, unsigned ValKind) + : Data(d), Kind((isLoc ? LocKind : NonLocKind) | (ValKind << BaseBits)) {} + + explicit SVal(BaseKind k, const void* D = NULL) + : Data(D), Kind(k) {} + +public: + explicit SVal() : Data(0), Kind(0) {} + ~SVal() {} + + /// BufferTy - A temporary buffer to hold a set of SVals. + typedef llvm::SmallVector BufferTy; + + inline unsigned getRawKind() const { return Kind; } + inline BaseKind getBaseKind() const { return (BaseKind) (Kind & BaseMask); } + inline unsigned getSubKind() const { return (Kind & ~BaseMask) >> BaseBits; } + + inline void Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddInteger((unsigned) getRawKind()); + ID.AddPointer(Data); + } + + inline bool operator==(const SVal& R) const { + return getRawKind() == R.getRawKind() && Data == R.Data; + } + + inline bool operator!=(const SVal& R) const { + return !(*this == R); + } + + inline bool isUnknown() const { + return getRawKind() == UnknownKind; + } + + inline bool isUndef() const { + return getRawKind() == UndefinedKind; + } + + inline bool isUnknownOrUndef() const { + return getRawKind() <= UnknownKind; + } + + inline bool isValid() const { + return getRawKind() > UnknownKind; + } + + bool isConstant() const; + + bool isConstant(int I) const; + + bool isZeroConstant() const; + + /// hasConjuredSymbol - If this SVal wraps a conjured symbol, return true; + bool hasConjuredSymbol() const; + + /// getAsFunctionDecl - If this SVal is a MemRegionVal and wraps a + /// CodeTextRegion wrapping a FunctionDecl, return that FunctionDecl. + /// Otherwise return 0. + const FunctionDecl* getAsFunctionDecl() const; + + /// getAsLocSymbol - If this SVal is a location (subclasses Loc) and + /// wraps a symbol, return that SymbolRef. Otherwise return NULL. + SymbolRef getAsLocSymbol() const; + + /// Get the symbol in the SVal or its base region. + SymbolRef getLocSymbolInBase() const; + + /// getAsSymbol - If this Sval wraps a symbol return that SymbolRef. + /// Otherwise return a SymbolRef where 'isValid()' returns false. + SymbolRef getAsSymbol() const; + + /// getAsSymbolicExpression - If this Sval wraps a symbolic expression then + /// return that expression. Otherwise return NULL. + const SymExpr *getAsSymbolicExpression() const; + + const MemRegion *getAsRegion() const; + + void dumpToStream(llvm::raw_ostream& OS) const; + void dump() const; + + // Iterators. + class symbol_iterator { + llvm::SmallVector itr; + void expand(); + public: + symbol_iterator() {} + symbol_iterator(const SymExpr* SE); + + symbol_iterator& operator++(); + SymbolRef operator*(); + + bool operator==(const symbol_iterator& X) const; + bool operator!=(const symbol_iterator& X) const; + }; + + symbol_iterator symbol_begin() const { + const SymExpr *SE = getAsSymbolicExpression(); + if (SE) + return symbol_iterator(SE); + else + return symbol_iterator(); + } + + symbol_iterator symbol_end() const { return symbol_iterator(); } + + // Implement isa support. + static inline bool classof(const SVal*) { return true; } +}; + + +class UndefinedVal : public SVal { +public: + UndefinedVal() : SVal(UndefinedKind) {} + UndefinedVal(const void* D) : SVal(UndefinedKind, D) {} + + static inline bool classof(const SVal* V) { + return V->getBaseKind() == UndefinedKind; + } + + const void* getData() const { return Data; } +}; + +class DefinedOrUnknownSVal : public SVal { +private: + // Do not implement. We want calling these methods to be a compiler + // error since they are tautologically false. + bool isUndef() const; + bool isValid() const; + +protected: + explicit DefinedOrUnknownSVal(const void* d, bool isLoc, unsigned ValKind) + : SVal(d, isLoc, ValKind) {} + + explicit DefinedOrUnknownSVal(BaseKind k, void *D = NULL) + : SVal(k, D) {} + +public: + // Implement isa support. + static inline bool classof(const SVal *V) { + return !V->isUndef(); + } +}; + +class UnknownVal : public DefinedOrUnknownSVal { +public: + explicit UnknownVal() : DefinedOrUnknownSVal(UnknownKind) {} + + static inline bool classof(const SVal *V) { + return V->getBaseKind() == UnknownKind; + } +}; + +class DefinedSVal : public DefinedOrUnknownSVal { +private: + // Do not implement. We want calling these methods to be a compiler + // error since they are tautologically true/false. + bool isUnknown() const; + bool isUnknownOrUndef() const; + bool isValid() const; +protected: + explicit DefinedSVal(const void* d, bool isLoc, unsigned ValKind) + : DefinedOrUnknownSVal(d, isLoc, ValKind) {} +public: + // Implement isa support. + static inline bool classof(const SVal *V) { + return !V->isUnknownOrUndef(); + } +}; + +class NonLoc : public DefinedSVal { +protected: + explicit NonLoc(unsigned SubKind, const void* d) + : DefinedSVal(d, false, SubKind) {} + +public: + void dumpToStream(llvm::raw_ostream& Out) const; + + // Implement isa support. + static inline bool classof(const SVal* V) { + return V->getBaseKind() == NonLocKind; + } +}; + +class Loc : public DefinedSVal { +protected: + explicit Loc(unsigned SubKind, const void* D) + : DefinedSVal(const_cast(D), true, SubKind) {} + +public: + void dumpToStream(llvm::raw_ostream& Out) const; + + Loc(const Loc& X) : DefinedSVal(X.Data, true, X.getSubKind()) {} + + // Implement isa support. + static inline bool classof(const SVal* V) { + return V->getBaseKind() == LocKind; + } + + static inline bool IsLocType(QualType T) { + return T->isAnyPointerType() || T->isBlockPointerType() || + T->isReferenceType(); + } +}; + +//==------------------------------------------------------------------------==// +// Subclasses of NonLoc. +//==------------------------------------------------------------------------==// + +namespace nonloc { + +enum Kind { ConcreteIntKind, SymbolValKind, SymExprValKind, + LocAsIntegerKind, CompoundValKind, LazyCompoundValKind }; + +class SymbolVal : public NonLoc { +public: + SymbolVal(SymbolRef sym) : NonLoc(SymbolValKind, sym) {} + + SymbolRef getSymbol() const { + return (const SymbolData*) Data; + } + + static inline bool classof(const SVal* V) { + return V->getBaseKind() == NonLocKind && + V->getSubKind() == SymbolValKind; + } + + static inline bool classof(const NonLoc* V) { + return V->getSubKind() == SymbolValKind; + } +}; + +class SymExprVal : public NonLoc { +public: + explicit SymExprVal(const SymExpr *SE) + : NonLoc(SymExprValKind, reinterpret_cast(SE)) {} + + const SymExpr *getSymbolicExpression() const { + return reinterpret_cast(Data); + } + + static inline bool classof(const SVal* V) { + return V->getBaseKind() == NonLocKind && + V->getSubKind() == SymExprValKind; + } + + static inline bool classof(const NonLoc* V) { + return V->getSubKind() == SymExprValKind; + } +}; + +class ConcreteInt : public NonLoc { +public: + explicit ConcreteInt(const llvm::APSInt& V) : NonLoc(ConcreteIntKind, &V) {} + + const llvm::APSInt& getValue() const { + return *static_cast(Data); + } + + // Transfer functions for binary/unary operations on ConcreteInts. + SVal evalBinOp(SValBuilder &svalBuilder, BinaryOperator::Opcode Op, + const ConcreteInt& R) const; + + ConcreteInt evalComplement(SValBuilder &svalBuilder) const; + + ConcreteInt evalMinus(SValBuilder &svalBuilder) const; + + // Implement isa support. + static inline bool classof(const SVal* V) { + return V->getBaseKind() == NonLocKind && + V->getSubKind() == ConcreteIntKind; + } + + static inline bool classof(const NonLoc* V) { + return V->getSubKind() == ConcreteIntKind; + } +}; + +class LocAsInteger : public NonLoc { + friend class ento::SValBuilder; + + explicit LocAsInteger(const std::pair& data) : + NonLoc(LocAsIntegerKind, &data) { + assert (isa(data.first)); + } + +public: + + Loc getLoc() const { + return cast(((std::pair*) Data)->first); + } + + const Loc& getPersistentLoc() const { + const SVal& V = ((std::pair*) Data)->first; + return cast(V); + } + + unsigned getNumBits() const { + return ((std::pair*) Data)->second; + } + + // Implement isa support. + static inline bool classof(const SVal* V) { + return V->getBaseKind() == NonLocKind && + V->getSubKind() == LocAsIntegerKind; + } + + static inline bool classof(const NonLoc* V) { + return V->getSubKind() == LocAsIntegerKind; + } +}; + +class CompoundVal : public NonLoc { + friend class ento::SValBuilder; + + explicit CompoundVal(const CompoundValData* D) : NonLoc(CompoundValKind, D) {} + +public: + const CompoundValData* getValue() const { + return static_cast(Data); + } + + typedef llvm::ImmutableList::iterator iterator; + iterator begin() const; + iterator end() const; + + static bool classof(const SVal* V) { + return V->getBaseKind() == NonLocKind && V->getSubKind() == CompoundValKind; + } + + static bool classof(const NonLoc* V) { + return V->getSubKind() == CompoundValKind; + } +}; + +class LazyCompoundVal : public NonLoc { + friend class ento::SValBuilder; + + explicit LazyCompoundVal(const LazyCompoundValData *D) + : NonLoc(LazyCompoundValKind, D) {} +public: + const LazyCompoundValData *getCVData() const { + return static_cast(Data); + } + const void *getStore() const; + const TypedRegion *getRegion() const; + + static bool classof(const SVal *V) { + return V->getBaseKind() == NonLocKind && + V->getSubKind() == LazyCompoundValKind; + } + static bool classof(const NonLoc *V) { + return V->getSubKind() == LazyCompoundValKind; + } +}; + +} // end namespace ento::nonloc + +//==------------------------------------------------------------------------==// +// Subclasses of Loc. +//==------------------------------------------------------------------------==// + +namespace loc { + +enum Kind { GotoLabelKind, MemRegionKind, ConcreteIntKind }; + +class GotoLabel : public Loc { +public: + explicit GotoLabel(LabelStmt* Label) : Loc(GotoLabelKind, Label) {} + + const LabelStmt* getLabel() const { + return static_cast(Data); + } + + static inline bool classof(const SVal* V) { + return V->getBaseKind() == LocKind && + V->getSubKind() == GotoLabelKind; + } + + static inline bool classof(const Loc* V) { + return V->getSubKind() == GotoLabelKind; + } +}; + + +class MemRegionVal : public Loc { +public: + explicit MemRegionVal(const MemRegion* r) : Loc(MemRegionKind, r) {} + + const MemRegion* getRegion() const { + return static_cast(Data); + } + + const MemRegion* StripCasts() const; + + template + const REGION* getRegionAs() const { + return llvm::dyn_cast(getRegion()); + } + + inline bool operator==(const MemRegionVal& R) const { + return getRegion() == R.getRegion(); + } + + inline bool operator!=(const MemRegionVal& R) const { + return getRegion() != R.getRegion(); + } + + // Implement isa support. + static inline bool classof(const SVal* V) { + return V->getBaseKind() == LocKind && + V->getSubKind() == MemRegionKind; + } + + static inline bool classof(const Loc* V) { + return V->getSubKind() == MemRegionKind; + } +}; + +class ConcreteInt : public Loc { +public: + explicit ConcreteInt(const llvm::APSInt& V) : Loc(ConcreteIntKind, &V) {} + + const llvm::APSInt& getValue() const { + return *static_cast(Data); + } + + // Transfer functions for binary/unary operations on ConcreteInts. + SVal evalBinOp(BasicValueFactory& BasicVals, BinaryOperator::Opcode Op, + const ConcreteInt& R) const; + + // Implement isa support. + static inline bool classof(const SVal* V) { + return V->getBaseKind() == LocKind && + V->getSubKind() == ConcreteIntKind; + } + + static inline bool classof(const Loc* V) { + return V->getSubKind() == ConcreteIntKind; + } +}; + +} // end ento::loc namespace +} // end GR namespace + +} // end clang namespace + +namespace llvm { +static inline llvm::raw_ostream& operator<<(llvm::raw_ostream& os, + clang::ento::SVal V) { + V.dumpToStream(os); + return os; +} + +} // end llvm namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/Store.h b/include/clang/StaticAnalyzer/PathSensitive/Store.h new file mode 100644 index 0000000000..c48f129feb --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/Store.h @@ -0,0 +1,259 @@ +//== Store.h - Interface for maps from Locations to 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 defined the types Store and StoreManager. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_STORE_H +#define LLVM_CLANG_GR_STORE_H + +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/PathSensitive/SValBuilder.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/Optional.h" + +namespace clang { + +class Stmt; +class Expr; +class ObjCIvarDecl; +class StackFrameContext; + +namespace ento { + +/// Store - This opaque type encapsulates an immutable mapping from +/// locations to values. At a high-level, it represents the symbolic +/// memory model. Different subclasses of StoreManager may choose +/// different types to represent the locations and values. +typedef const void* Store; + +class GRState; +class GRStateManager; +class SubRegionMap; + +class StoreManager { +protected: + SValBuilder &svalBuilder; + GRStateManager &StateMgr; + + /// MRMgr - Manages region objects associated with this StoreManager. + MemRegionManager &MRMgr; + ASTContext &Ctx; + + StoreManager(GRStateManager &stateMgr); + +public: + virtual ~StoreManager() {} + + /// Return the value bound to specified location in a given state. + /// \param[in] state The analysis state. + /// \param[in] loc The symbolic memory location. + /// \param[in] T An optional type that provides a hint indicating the + /// expected type of the returned value. This is used if the value is + /// lazily computed. + /// \return The value bound to the location \c loc. + virtual SVal Retrieve(Store store, Loc loc, QualType T = QualType()) = 0; + + /// Return a state with the specified value bound to the given location. + /// \param[in] state The analysis state. + /// \param[in] loc The symbolic memory location. + /// \param[in] val The value to bind to location \c loc. + /// \return A pointer to a GRState object that contains the same bindings as + /// \c state with the addition of having the value specified by \c val bound + /// to the location given for \c loc. + virtual Store Bind(Store store, Loc loc, SVal val) = 0; + + virtual Store BindDefault(Store store, const MemRegion *R, SVal V) { + return store; + } + + virtual Store Remove(Store St, Loc L) = 0; + + /// BindCompoundLiteral - Return the store that has the bindings currently + /// in 'store' plus the bindings for the CompoundLiteral. 'R' is the region + /// for the compound literal and 'BegInit' and 'EndInit' represent an + /// array of initializer values. + virtual Store BindCompoundLiteral(Store store, + const CompoundLiteralExpr* cl, + const LocationContext *LC, SVal v) = 0; + + /// getInitialStore - Returns the initial "empty" store representing the + /// value bindings upon entry to an analyzed function. + virtual Store getInitialStore(const LocationContext *InitLoc) = 0; + + /// getRegionManager - Returns the internal RegionManager object that is + /// used to query and manipulate MemRegion objects. + MemRegionManager& getRegionManager() { return MRMgr; } + + /// getSubRegionMap - Returns an opaque map object that clients can query + /// to get the subregions of a given MemRegion object. It is the + // caller's responsibility to 'delete' the returned map. + virtual SubRegionMap *getSubRegionMap(Store store) = 0; + + virtual Loc getLValueVar(const VarDecl *VD, const LocationContext *LC) { + return svalBuilder.makeLoc(MRMgr.getVarRegion(VD, LC)); + } + + virtual Loc getLValueString(const StringLiteral* S) { + return svalBuilder.makeLoc(MRMgr.getStringRegion(S)); + } + + Loc getLValueCompoundLiteral(const CompoundLiteralExpr* CL, + const LocationContext *LC) { + return loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)); + } + + virtual SVal getLValueIvar(const ObjCIvarDecl* decl, SVal base) { + return getLValueFieldOrIvar(decl, base); + } + + virtual SVal getLValueField(const FieldDecl* D, SVal Base) { + return getLValueFieldOrIvar(D, Base); + } + + virtual SVal getLValueElement(QualType elementType, NonLoc offset, SVal Base); + + // FIXME: This should soon be eliminated altogether; clients should deal with + // region extents directly. + virtual DefinedOrUnknownSVal getSizeInElements(const GRState *state, + const MemRegion *region, + QualType EleTy) { + return UnknownVal(); + } + + /// ArrayToPointer - Used by ExprEngine::VistCast to handle implicit + /// conversions between arrays and pointers. + virtual SVal ArrayToPointer(Loc Array) = 0; + + /// Evaluates DerivedToBase casts. + virtual SVal evalDerivedToBase(SVal derived, QualType basePtrType) { + return UnknownVal(); + } + + class CastResult { + const GRState *state; + const MemRegion *region; + public: + const GRState *getState() const { return state; } + const MemRegion* getRegion() const { return region; } + CastResult(const GRState *s, const MemRegion* r = 0) : state(s), region(r){} + }; + + const ElementRegion *GetElementZeroRegion(const MemRegion *R, QualType T); + + /// CastRegion - Used by ExprEngine::VisitCast to handle casts from + /// a MemRegion* to a specific location type. 'R' is the region being + /// casted and 'CastToTy' the result type of the cast. + const MemRegion *CastRegion(const MemRegion *region, QualType CastToTy); + + + /// evalBinOp - Perform pointer arithmetic. + virtual SVal evalBinOp(BinaryOperator::Opcode Op, + Loc lhs, NonLoc rhs, QualType resultTy) { + return UnknownVal(); + } + + virtual Store RemoveDeadBindings(Store store, const StackFrameContext *LCtx, + SymbolReaper& SymReaper, + llvm::SmallVectorImpl& RegionRoots) = 0; + + virtual Store BindDecl(Store store, const VarRegion *VR, SVal initVal) = 0; + + virtual Store BindDeclWithNoInit(Store store, const VarRegion *VR) = 0; + + typedef llvm::DenseSet InvalidatedSymbols; + typedef llvm::SmallVector InvalidatedRegions; + + /// InvalidateRegions - Clears out the specified regions from the store, + /// marking their values as unknown. Depending on the store, this may also + /// invalidate additional regions that may have changed based on accessing + /// the given regions. Optionally, invalidates non-static globals as well. + /// \param[in] store The initial store + /// \param[in] Begin A pointer to the first region to invalidate. + /// \param[in] End A pointer just past the last region to invalidate. + /// \param[in] E The current statement being evaluated. Used to conjure + /// symbols to mark the values of invalidated regions. + /// \param[in] Count The current block count. Used to conjure + /// symbols to mark the values of invalidated regions. + /// \param[in,out] IS A set to fill with any symbols that are no longer + /// accessible. Pass \c NULL if this information will not be used. + /// \param[in] invalidateGlobals If \c true, any non-static global regions + /// are invalidated as well. + /// \param[in,out] Regions A vector to fill with any regions being + /// invalidated. This should include any regions explicitly invalidated + /// even if they do not currently have bindings. Pass \c NULL if this + /// information will not be used. + virtual Store InvalidateRegions(Store store, + const MemRegion * const *Begin, + const MemRegion * const *End, + const Expr *E, unsigned Count, + InvalidatedSymbols *IS, + bool invalidateGlobals, + InvalidatedRegions *Regions) = 0; + + /// EnterStackFrame - Let the StoreManager to do something when execution + /// engine is about to execute into a callee. + virtual Store EnterStackFrame(const GRState *state, + const StackFrameContext *frame); + + virtual void print(Store store, llvm::raw_ostream& Out, + const char* nl, const char *sep) = 0; + + class BindingsHandler { + public: + virtual ~BindingsHandler(); + virtual bool HandleBinding(StoreManager& SMgr, Store store, + const MemRegion *region, SVal val) = 0; + }; + + /// iterBindings - Iterate over the bindings in the Store. + virtual void iterBindings(Store store, BindingsHandler& f) = 0; + +protected: + const MemRegion *MakeElementRegion(const MemRegion *baseRegion, + QualType pointeeTy, uint64_t index = 0); + + /// CastRetrievedVal - Used by subclasses of StoreManager to implement + /// implicit casts that arise from loads from regions that are reinterpreted + /// as another region. + SVal CastRetrievedVal(SVal val, const TypedRegion *region, QualType castTy, + bool performTestOnly = true); + +private: + SVal getLValueFieldOrIvar(const Decl* decl, SVal base); +}; + +// FIXME: Do we still need this? +/// SubRegionMap - An abstract interface that represents a queryable map +/// between MemRegion objects and their subregions. +class SubRegionMap { +public: + virtual ~SubRegionMap() {} + + class Visitor { + public: + virtual ~Visitor() {} + virtual bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) = 0; + }; + + virtual bool iterSubRegions(const MemRegion *region, Visitor& V) const = 0; +}; + +// FIXME: Do we need to pass GRStateManager anymore? +StoreManager *CreateBasicStoreManager(GRStateManager& StMgr); +StoreManager *CreateRegionStoreManager(GRStateManager& StMgr); +StoreManager *CreateFieldsOnlyRegionStoreManager(GRStateManager& StMgr); +StoreManager *CreateFlatStoreManager(GRStateManager &StMgr); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/PathSensitive/SubEngine.h new file mode 100644 index 0000000000..8672bd1a02 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/SubEngine.h @@ -0,0 +1,113 @@ +//== SubEngine.h - 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. +// +//===----------------------------------------------------------------------===// +// +// This file defines the interface of a subengine of the CoreEngine. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_CLANG_GR_SUBENGINE_H +#define LLVM_CLANG_GR_SUBENGINE_H + +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" + +namespace clang { + +class CFGBlock; +class CFGElement; +class LocationContext; +class Stmt; + +namespace ento { + +class AnalysisManager; +class ExplodedNode; +class GRState; +class GRStateManager; +class BlockCounter; +class StmtNodeBuilder; +class BranchNodeBuilder; +class IndirectGotoNodeBuilder; +class SwitchNodeBuilder; +class EndPathNodeBuilder; +class CallEnterNodeBuilder; +class CallExitNodeBuilder; +class MemRegion; + +class SubEngine { +public: + virtual ~SubEngine() {} + + virtual const GRState* getInitialState(const LocationContext *InitLoc) = 0; + + virtual AnalysisManager &getAnalysisManager() = 0; + + virtual GRStateManager &getStateManager() = 0; + + /// Called by CoreEngine. Used to generate new successor + /// nodes by processing the 'effects' of a block-level statement. + virtual void ProcessElement(const CFGElement E, StmtNodeBuilder& builder)=0; + + /// Called by CoreEngine when start processing + /// a CFGBlock. This method returns true if the analysis should continue + /// exploring the given path, and false otherwise. + virtual bool ProcessBlockEntrance(const CFGBlock* B, const ExplodedNode *Pred, + BlockCounter BC) = 0; + + /// Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a branch condition. + virtual void ProcessBranch(const Stmt* Condition, const Stmt* Term, + BranchNodeBuilder& builder) = 0; + + /// Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a computed goto jump. + virtual void ProcessIndirectGoto(IndirectGotoNodeBuilder& builder) = 0; + + /// Called by CoreEngine. Used to generate successor + /// nodes by processing the 'effects' of a switch statement. + virtual void ProcessSwitch(SwitchNodeBuilder& builder) = 0; + + /// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path + /// nodes when the control reaches the end of a function. + virtual void ProcessEndPath(EndPathNodeBuilder& builder) = 0; + + // Generate the entry node of the callee. + virtual void ProcessCallEnter(CallEnterNodeBuilder &builder) = 0; + + // Generate the first post callsite node. + virtual void ProcessCallExit(CallExitNodeBuilder &builder) = 0; + + /// Called by ConstraintManager. Used to call checker-specific + /// logic for handling assumptions on symbolic values. + virtual const GRState* ProcessAssume(const GRState *state, + SVal cond, bool assumption) = 0; + + /// WantsRegionChangeUpdate - Called by GRStateManager to determine if a + /// region change should trigger a ProcessRegionChanges update. + virtual bool WantsRegionChangeUpdate(const GRState* state) = 0; + + /// ProcessRegionChanges - Called by GRStateManager whenever a change is made + /// to the store. Used to update checkers that track region values. + virtual const GRState* ProcessRegionChanges(const GRState* state, + const MemRegion* const *Begin, + const MemRegion* const *End) = 0; + + inline const GRState* ProcessRegionChange(const GRState* state, + const MemRegion* MR) { + return ProcessRegionChanges(state, &MR, &MR+1); + } + + /// Called by CoreEngine when the analysis worklist is either empty or the + // maximum number of analysis steps have been reached. + virtual void ProcessEndWorklist(bool hasWorkRemaining) = 0; +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/SummaryManager.h b/include/clang/StaticAnalyzer/PathSensitive/SummaryManager.h new file mode 100644 index 0000000000..ed87851598 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/SummaryManager.h @@ -0,0 +1,61 @@ +//== SummaryManager.h - Generic handling of function summaries --*- 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 SummaryManager and related classes, which provides +// a generic mechanism for managing function summaries. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_SUMMARY +#define LLVM_CLANG_GR_SUMMARY + +#include "llvm/ADT/FoldingSet.h" +#include "llvm/Support/Allocator.h" + +namespace clang { + +namespace ento { + +namespace summMgr { + + +/* Key kinds: + + - C functions + - C++ functions (name + parameter types) + - ObjC methods: + - Class, selector (class method) + - Class, selector (instance method) + - Category, selector (instance method) + - Protocol, selector (instance method) + - C++ methods + - Class, function name + parameter types + const + */ + +class SummaryKey { + +}; + +} // end namespace clang::summMgr + +class SummaryManagerImpl { + +}; + + +template +class SummaryManager : SummaryManagerImpl { + +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/PathSensitive/SymbolManager.h new file mode 100644 index 0000000000..ad173bb435 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/SymbolManager.h @@ -0,0 +1,489 @@ +//== SymbolManager.h - Management of Symbolic 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 defines SymbolManager, a class that manages symbolic values +// created for use by ExprEngine and related classes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_SYMMGR_H +#define LLVM_CLANG_GR_SYMMGR_H + +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/Analysis/AnalysisContext.h" +#include "llvm/Support/DataTypes.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/DenseSet.h" + +namespace llvm { +class BumpPtrAllocator; +class raw_ostream; +} + +namespace clang { + class ASTContext; + class StackFrameContext; + +namespace ento { + class BasicValueFactory; + class MemRegion; + class SubRegion; + class TypedRegion; + class VarRegion; + +class SymExpr : public llvm::FoldingSetNode { +public: + enum Kind { BEGIN_SYMBOLS, + RegionValueKind, ConjuredKind, DerivedKind, ExtentKind, + MetadataKind, + END_SYMBOLS, + SymIntKind, SymSymKind }; +private: + Kind K; + +protected: + SymExpr(Kind k) : K(k) {} + +public: + virtual ~SymExpr() {} + + Kind getKind() const { return K; } + + void dump() const; + + virtual void dumpToStream(llvm::raw_ostream &os) const = 0; + + virtual QualType getType(ASTContext&) const = 0; + virtual void Profile(llvm::FoldingSetNodeID& profile) = 0; + + // Implement isa support. + static inline bool classof(const SymExpr*) { return true; } +}; + +typedef unsigned SymbolID; + +class SymbolData : public SymExpr { +private: + const SymbolID Sym; + +protected: + SymbolData(Kind k, SymbolID sym) : SymExpr(k), Sym(sym) {} + +public: + virtual ~SymbolData() {} + + SymbolID getSymbolID() const { return Sym; } + + // Implement isa support. + static inline bool classof(const SymExpr* SE) { + Kind k = SE->getKind(); + return k > BEGIN_SYMBOLS && k < END_SYMBOLS; + } +}; + +typedef const SymbolData* SymbolRef; + +// A symbol representing the value of a MemRegion. +class SymbolRegionValue : public SymbolData { + const TypedRegion *R; + +public: + SymbolRegionValue(SymbolID sym, const TypedRegion *r) + : SymbolData(RegionValueKind, sym), R(r) {} + + const TypedRegion* getRegion() const { return R; } + + static void Profile(llvm::FoldingSetNodeID& profile, const TypedRegion* R) { + profile.AddInteger((unsigned) RegionValueKind); + profile.AddPointer(R); + } + + virtual void Profile(llvm::FoldingSetNodeID& profile) { + Profile(profile, R); + } + + void dumpToStream(llvm::raw_ostream &os) const; + + QualType getType(ASTContext&) const; + + // Implement isa support. + static inline bool classof(const SymExpr* SE) { + return SE->getKind() == RegionValueKind; + } +}; + +// A symbol representing the result of an expression. +class SymbolConjured : public SymbolData { + const Stmt* S; + QualType T; + unsigned Count; + const void* SymbolTag; + +public: + SymbolConjured(SymbolID sym, const Stmt* s, QualType t, unsigned count, + const void* symbolTag) + : SymbolData(ConjuredKind, sym), S(s), T(t), Count(count), + SymbolTag(symbolTag) {} + + const Stmt* getStmt() const { return S; } + unsigned getCount() const { return Count; } + const void* getTag() const { return SymbolTag; } + + QualType getType(ASTContext&) const; + + void dumpToStream(llvm::raw_ostream &os) const; + + static void Profile(llvm::FoldingSetNodeID& profile, const Stmt* S, + QualType T, unsigned Count, const void* SymbolTag) { + profile.AddInteger((unsigned) ConjuredKind); + profile.AddPointer(S); + profile.Add(T); + profile.AddInteger(Count); + profile.AddPointer(SymbolTag); + } + + virtual void Profile(llvm::FoldingSetNodeID& profile) { + Profile(profile, S, T, Count, SymbolTag); + } + + // Implement isa support. + static inline bool classof(const SymExpr* SE) { + return SE->getKind() == ConjuredKind; + } +}; + +// A symbol representing the value of a MemRegion whose parent region has +// symbolic value. +class SymbolDerived : public SymbolData { + SymbolRef parentSymbol; + const TypedRegion *R; + +public: + SymbolDerived(SymbolID sym, SymbolRef parent, const TypedRegion *r) + : SymbolData(DerivedKind, sym), parentSymbol(parent), R(r) {} + + SymbolRef getParentSymbol() const { return parentSymbol; } + const TypedRegion *getRegion() const { return R; } + + QualType getType(ASTContext&) const; + + void dumpToStream(llvm::raw_ostream &os) const; + + static void Profile(llvm::FoldingSetNodeID& profile, SymbolRef parent, + const TypedRegion *r) { + profile.AddInteger((unsigned) DerivedKind); + profile.AddPointer(r); + profile.AddPointer(parent); + } + + virtual void Profile(llvm::FoldingSetNodeID& profile) { + Profile(profile, parentSymbol, R); + } + + // Implement isa support. + static inline bool classof(const SymExpr* SE) { + return SE->getKind() == DerivedKind; + } +}; + +/// SymbolExtent - Represents the extent (size in bytes) of a bounded region. +/// Clients should not ask the SymbolManager for a region's extent. Always use +/// SubRegion::getExtent instead -- the value returned may not be a symbol. +class SymbolExtent : public SymbolData { + const SubRegion *R; + +public: + SymbolExtent(SymbolID sym, const SubRegion *r) + : SymbolData(ExtentKind, sym), R(r) {} + + const SubRegion *getRegion() const { return R; } + + QualType getType(ASTContext&) const; + + void dumpToStream(llvm::raw_ostream &os) const; + + static void Profile(llvm::FoldingSetNodeID& profile, const SubRegion *R) { + profile.AddInteger((unsigned) ExtentKind); + profile.AddPointer(R); + } + + virtual void Profile(llvm::FoldingSetNodeID& profile) { + Profile(profile, R); + } + + // Implement isa support. + static inline bool classof(const SymExpr* SE) { + return SE->getKind() == ExtentKind; + } +}; + +/// SymbolMetadata - Represents path-dependent metadata about a specific region. +/// Metadata symbols remain live as long as they are marked as in use before +/// dead-symbol sweeping AND their associated regions are still alive. +/// Intended for use by checkers. +class SymbolMetadata : public SymbolData { + const MemRegion* R; + const Stmt* S; + QualType T; + unsigned Count; + const void* Tag; +public: + SymbolMetadata(SymbolID sym, const MemRegion* r, const Stmt* s, QualType t, + unsigned count, const void* tag) + : SymbolData(MetadataKind, sym), R(r), S(s), T(t), Count(count), Tag(tag) {} + + const MemRegion *getRegion() const { return R; } + const Stmt* getStmt() const { return S; } + unsigned getCount() const { return Count; } + const void* getTag() const { return Tag; } + + QualType getType(ASTContext&) const; + + void dumpToStream(llvm::raw_ostream &os) const; + + static void Profile(llvm::FoldingSetNodeID& profile, const MemRegion *R, + const Stmt *S, QualType T, unsigned Count, + const void *Tag) { + profile.AddInteger((unsigned) MetadataKind); + profile.AddPointer(R); + profile.AddPointer(S); + profile.Add(T); + profile.AddInteger(Count); + profile.AddPointer(Tag); + } + + virtual void Profile(llvm::FoldingSetNodeID& profile) { + Profile(profile, R, S, T, Count, Tag); + } + + // Implement isa support. + static inline bool classof(const SymExpr* SE) { + return SE->getKind() == MetadataKind; + } +}; + +// SymIntExpr - Represents symbolic expression like 'x' + 3. +class SymIntExpr : public SymExpr { + const SymExpr *LHS; + BinaryOperator::Opcode Op; + const llvm::APSInt& RHS; + QualType T; + +public: + SymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APSInt& rhs, QualType t) + : SymExpr(SymIntKind), LHS(lhs), Op(op), RHS(rhs), T(t) {} + + // FIXME: We probably need to make this out-of-line to avoid redundant + // generation of virtual functions. + QualType getType(ASTContext& C) const { return T; } + + BinaryOperator::Opcode getOpcode() const { return Op; } + + void dumpToStream(llvm::raw_ostream &os) const; + + const SymExpr *getLHS() const { return LHS; } + const llvm::APSInt &getRHS() const { return RHS; } + + static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, + BinaryOperator::Opcode op, const llvm::APSInt& rhs, + QualType t) { + ID.AddInteger((unsigned) SymIntKind); + ID.AddPointer(lhs); + ID.AddInteger(op); + ID.AddPointer(&rhs); + ID.Add(t); + } + + void Profile(llvm::FoldingSetNodeID& ID) { + Profile(ID, LHS, Op, RHS, T); + } + + // Implement isa support. + static inline bool classof(const SymExpr* SE) { + return SE->getKind() == SymIntKind; + } +}; + +// SymSymExpr - Represents symbolic expression like 'x' + 'y'. +class SymSymExpr : public SymExpr { + const SymExpr *LHS; + BinaryOperator::Opcode Op; + const SymExpr *RHS; + QualType T; + +public: + SymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op, const SymExpr *rhs, + QualType t) + : SymExpr(SymSymKind), LHS(lhs), Op(op), RHS(rhs), T(t) {} + + BinaryOperator::Opcode getOpcode() const { return Op; } + const SymExpr *getLHS() const { return LHS; } + const SymExpr *getRHS() const { return RHS; } + + // FIXME: We probably need to make this out-of-line to avoid redundant + // generation of virtual functions. + QualType getType(ASTContext& C) const { return T; } + + void dumpToStream(llvm::raw_ostream &os) const; + + static void Profile(llvm::FoldingSetNodeID& ID, const SymExpr *lhs, + BinaryOperator::Opcode op, const SymExpr *rhs, QualType t) { + ID.AddInteger((unsigned) SymSymKind); + ID.AddPointer(lhs); + ID.AddInteger(op); + ID.AddPointer(rhs); + ID.Add(t); + } + + void Profile(llvm::FoldingSetNodeID& ID) { + Profile(ID, LHS, Op, RHS, T); + } + + // Implement isa support. + static inline bool classof(const SymExpr* SE) { + return SE->getKind() == SymSymKind; + } +}; + +class SymbolManager { + typedef llvm::FoldingSet DataSetTy; + DataSetTy DataSet; + unsigned SymbolCounter; + llvm::BumpPtrAllocator& BPAlloc; + BasicValueFactory &BV; + ASTContext& Ctx; + +public: + SymbolManager(ASTContext& ctx, BasicValueFactory &bv, + llvm::BumpPtrAllocator& bpalloc) + : SymbolCounter(0), BPAlloc(bpalloc), BV(bv), Ctx(ctx) {} + + ~SymbolManager(); + + static bool canSymbolicate(QualType T); + + /// Make a unique symbol for MemRegion R according to its kind. + const SymbolRegionValue* getRegionValueSymbol(const TypedRegion* R); + + const SymbolConjured* getConjuredSymbol(const Stmt* E, QualType T, + unsigned VisitCount, + const void* SymbolTag = 0); + + const SymbolConjured* getConjuredSymbol(const Expr* E, unsigned VisitCount, + const void* SymbolTag = 0) { + return getConjuredSymbol(E, E->getType(), VisitCount, SymbolTag); + } + + const SymbolDerived *getDerivedSymbol(SymbolRef parentSymbol, + const TypedRegion *R); + + const SymbolExtent *getExtentSymbol(const SubRegion *R); + + const SymbolMetadata* getMetadataSymbol(const MemRegion* R, const Stmt* S, + QualType T, unsigned VisitCount, + const void* SymbolTag = 0); + + const SymIntExpr *getSymIntExpr(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APSInt& rhs, QualType t); + + const SymIntExpr *getSymIntExpr(const SymExpr &lhs, BinaryOperator::Opcode op, + const llvm::APSInt& rhs, QualType t) { + return getSymIntExpr(&lhs, op, rhs, t); + } + + const SymSymExpr *getSymSymExpr(const SymExpr *lhs, BinaryOperator::Opcode op, + const SymExpr *rhs, QualType t); + + QualType getType(const SymExpr *SE) const { + return SE->getType(Ctx); + } + + ASTContext &getContext() { return Ctx; } + BasicValueFactory &getBasicVals() { return BV; } +}; + +class SymbolReaper { + typedef llvm::DenseSet SetTy; + + SetTy TheLiving; + SetTy MetadataInUse; + SetTy TheDead; + const LocationContext *LCtx; + const Stmt *Loc; + SymbolManager& SymMgr; + +public: + SymbolReaper(const LocationContext *ctx, const Stmt *s, SymbolManager& symmgr) + : LCtx(ctx), Loc(s), SymMgr(symmgr) {} + + ~SymbolReaper() {} + + const LocationContext *getLocationContext() const { return LCtx; } + const Stmt *getCurrentStatement() const { return Loc; } + + bool isLive(SymbolRef sym); + bool isLive(const Stmt *ExprVal) const; + bool isLive(const VarRegion *VR) const; + + // markLive - Unconditionally marks a symbol as live. This should never be + // used by checkers, only by the state infrastructure such as the store and + // environment. Checkers should instead use metadata symbols and markInUse. + void markLive(SymbolRef sym); + + // markInUse - Marks a symbol as important to a checker. For metadata symbols, + // this will keep the symbol alive as long as its associated region is also + // live. For other symbols, this has no effect; checkers are not permitted + // to influence the life of other symbols. This should be used before any + // symbol marking has occurred, i.e. in the MarkLiveSymbols callback. + void markInUse(SymbolRef sym); + + // maybeDead - If a symbol is known to be live, marks the symbol as live. + // Otherwise, if the symbol cannot be proven live, it is marked as dead. + // Returns true if the symbol is dead, false if live. + bool maybeDead(SymbolRef sym); + + typedef SetTy::const_iterator dead_iterator; + dead_iterator dead_begin() const { return TheDead.begin(); } + dead_iterator dead_end() const { return TheDead.end(); } + + bool hasDeadSymbols() const { + return !TheDead.empty(); + } + + /// isDead - Returns whether or not a symbol has been confirmed dead. This + /// should only be called once all marking of dead symbols has completed. + /// (For checkers, this means only in the evalDeadSymbols callback.) + bool isDead(SymbolRef sym) const { + return TheDead.count(sym); + } +}; + +class SymbolVisitor { +public: + // VisitSymbol - A visitor method invoked by + // GRStateManager::scanReachableSymbols. The method returns \c true if + // symbols should continue be scanned and \c false otherwise. + virtual bool VisitSymbol(SymbolRef sym) = 0; + virtual ~SymbolVisitor(); +}; + +} // end GR namespace + +} // end clang namespace + +namespace llvm { +static inline llvm::raw_ostream& operator<<(llvm::raw_ostream& os, + const clang::ento::SymExpr *SE) { + SE->dumpToStream(os); + return os; +} +} // end llvm namespace +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/TransferFuncs.h b/include/clang/StaticAnalyzer/PathSensitive/TransferFuncs.h new file mode 100644 index 0000000000..22e3a4d3c0 --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/TransferFuncs.h @@ -0,0 +1,92 @@ +//== TransferFuncs.h - Path-Sens. Transfer Functions Interface ---*- 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 TransferFuncs, which provides a base-class that +// defines an interface for transfer functions used by ExprEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_TRANSFERFUNCS +#define LLVM_CLANG_GR_TRANSFERFUNCS + +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" +#include + +namespace clang { +class ObjCMessageExpr; + +namespace ento { +class ExplodedNode; +class ExplodedNodeSet; +class EndPathNodeBuilder; +class ExprEngine; +class StmtNodeBuilder; +class StmtNodeBuilderRef; + +class TransferFuncs { +public: + TransferFuncs() {} + virtual ~TransferFuncs() {} + + virtual void RegisterPrinters(std::vector& Printers) {} + virtual void RegisterChecks(ExprEngine& Eng) {} + + + // Calls. + + virtual void evalCall(ExplodedNodeSet& Dst, + ExprEngine& Engine, + StmtNodeBuilder& Builder, + const CallExpr* CE, SVal L, + ExplodedNode* Pred) {} + + virtual void evalObjCMessageExpr(ExplodedNodeSet& Dst, + ExprEngine& Engine, + StmtNodeBuilder& Builder, + const ObjCMessageExpr* ME, + ExplodedNode* Pred, + const GRState *state) {} + + // Stores. + + virtual void evalBind(StmtNodeBuilderRef& B, SVal location, SVal val) {} + + // End-of-path and dead symbol notification. + + virtual void evalEndPath(ExprEngine& Engine, + EndPathNodeBuilder& Builder) {} + + + virtual void evalDeadSymbols(ExplodedNodeSet& Dst, + ExprEngine& Engine, + StmtNodeBuilder& Builder, + ExplodedNode* Pred, + const GRState* state, + SymbolReaper& SymReaper) {} + + // Return statements. + virtual void evalReturn(ExplodedNodeSet& Dst, + ExprEngine& Engine, + StmtNodeBuilder& Builder, + const ReturnStmt* S, + ExplodedNode* Pred) {} + + // Assumptions. + virtual const GRState* evalAssume(const GRState *state, + SVal Cond, bool Assumption) { + return state; + } +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/include/clang/StaticAnalyzer/PathSensitive/WorkList.h b/include/clang/StaticAnalyzer/PathSensitive/WorkList.h new file mode 100644 index 0000000000..be909061cd --- /dev/null +++ b/include/clang/StaticAnalyzer/PathSensitive/WorkList.h @@ -0,0 +1,94 @@ +//==- WorkList.h - Worklist class used by CoreEngine ---------------*- 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 WorkList, a pure virtual class that represents an opaque +// worklist used by CoreEngine to explore the reachability state space. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_WORKLIST +#define LLVM_CLANG_GR_WORKLIST + +#include "clang/StaticAnalyzer/PathSensitive/BlockCounter.h" +#include + +namespace clang { + +class CFGBlock; + +namespace ento { + +class ExplodedNode; +class ExplodedNodeImpl; + +class WorkListUnit { + ExplodedNode* Node; + BlockCounter Counter; + const CFGBlock* Block; + unsigned BlockIdx; // This is the index of the next statement. + +public: + WorkListUnit(ExplodedNode* N, BlockCounter C, + const CFGBlock* B, unsigned idx) + : Node(N), + Counter(C), + Block(B), + BlockIdx(idx) {} + + explicit WorkListUnit(ExplodedNode* N, BlockCounter C) + : Node(N), + Counter(C), + Block(NULL), + BlockIdx(0) {} + + ExplodedNode* getNode() const { return Node; } + BlockCounter getBlockCounter() const { return Counter; } + const CFGBlock* getBlock() const { return Block; } + unsigned getIndex() const { return BlockIdx; } +}; + +class WorkList { + BlockCounter CurrentCounter; +public: + virtual ~WorkList(); + virtual bool hasWork() const = 0; + + virtual void Enqueue(const WorkListUnit& U) = 0; + + void Enqueue(ExplodedNode* N, const CFGBlock* B, unsigned idx) { + Enqueue(WorkListUnit(N, CurrentCounter, B, idx)); + } + + void Enqueue(ExplodedNode* N) { + Enqueue(WorkListUnit(N, CurrentCounter)); + } + + virtual WorkListUnit Dequeue() = 0; + + void setBlockCounter(BlockCounter C) { CurrentCounter = C; } + BlockCounter getBlockCounter() const { return CurrentCounter; } + + class Visitor { + public: + Visitor() {} + virtual ~Visitor(); + virtual bool Visit(const WorkListUnit &U) = 0; + }; + virtual bool VisitItemsInWorkList(Visitor &V) = 0; + + static WorkList *MakeDFS(); + static WorkList *MakeBFS(); + static WorkList *MakeBFSBlockDFSContents(); +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 6197dc72d0..a75dd22386 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -12,5 +12,5 @@ add_subdirectory(Serialization) add_subdirectory(Frontend) add_subdirectory(FrontendTool) add_subdirectory(Index) -add_subdirectory(EntoSA) -add_subdirectory(EntoSA/Checkers) +add_subdirectory(StaticAnalyzer) +add_subdirectory(StaticAnalyzer/Checkers) diff --git a/lib/FrontendTool/CMakeLists.txt b/lib/FrontendTool/CMakeLists.txt index 89937e1762..31983df365 100644 --- a/lib/FrontendTool/CMakeLists.txt +++ b/lib/FrontendTool/CMakeLists.txt @@ -1,7 +1,7 @@ set(LLVM_NO_RTTI 1) set(LLVM_USED_LIBS clangDriver clangFrontend clangRewrite clangCodeGen - clangEntoCheckers clangEntoCore) + clangStaticAnalyzerCheckers clangStaticAnalyzerCore) add_clang_library(clangFrontendTool ExecuteCompilerInvocation.cpp diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index e17e8b7156..6278975b75 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/FrontendTool/Utils.h" -#include "clang/EntoSA/FrontendActions.h" +#include "clang/StaticAnalyzer/FrontendActions.h" #include "clang/CodeGen/CodeGenAction.h" #include "clang/Driver/CC1Options.h" #include "clang/Driver/OptTable.h" diff --git a/lib/Makefile b/lib/Makefile index 300b9ea097..bf357fc7d5 100755 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,7 +9,7 @@ CLANG_LEVEL := .. PARALLEL_DIRS = Headers Basic Lex Parse AST Sema CodeGen Analysis \ - EntoSA Rewrite Serialization Frontend FrontendTool Index Driver + StaticAnalyzer Rewrite Serialization Frontend FrontendTool Index Driver include $(CLANG_LEVEL)/Makefile diff --git a/lib/EntoSA/AggExprVisitor.cpp b/lib/StaticAnalyzer/AggExprVisitor.cpp similarity index 97% rename from lib/EntoSA/AggExprVisitor.cpp rename to lib/StaticAnalyzer/AggExprVisitor.cpp index c356186622..0cc23900ed 100644 --- a/lib/EntoSA/AggExprVisitor.cpp +++ b/lib/StaticAnalyzer/AggExprVisitor.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" #include "clang/AST/StmtVisitor.h" using namespace clang; diff --git a/lib/EntoSA/AnalysisManager.cpp b/lib/StaticAnalyzer/AnalysisManager.cpp similarity index 94% rename from lib/EntoSA/AnalysisManager.cpp rename to lib/StaticAnalyzer/AnalysisManager.cpp index fa64f472f7..cc5e271393 100644 --- a/lib/EntoSA/AnalysisManager.cpp +++ b/lib/StaticAnalyzer/AnalysisManager.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/PathSensitive/AnalysisManager.h" #include "clang/Index/Entity.h" #include "clang/Index/Indexer.h" diff --git a/lib/EntoSA/AnalyzerStatsChecker.cpp b/lib/StaticAnalyzer/AnalyzerStatsChecker.cpp similarity index 95% rename from lib/EntoSA/AnalyzerStatsChecker.cpp rename to lib/StaticAnalyzer/AnalyzerStatsChecker.cpp index fb0e74b4b5..8521c18c54 100644 --- a/lib/EntoSA/AnalyzerStatsChecker.cpp +++ b/lib/StaticAnalyzer/AnalyzerStatsChecker.cpp @@ -9,9 +9,9 @@ // This file reports various statistics about analyzer visitation. //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExplodedGraph.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" // FIXME: Restructure checker registration. #include "Checkers/ExprEngineExperimentalChecks.h" diff --git a/lib/EntoSA/BasicConstraintManager.cpp b/lib/StaticAnalyzer/BasicConstraintManager.cpp similarity index 98% rename from lib/EntoSA/BasicConstraintManager.cpp rename to lib/StaticAnalyzer/BasicConstraintManager.cpp index 92837d2ff4..8bdc4fa003 100644 --- a/lib/EntoSA/BasicConstraintManager.cpp +++ b/lib/StaticAnalyzer/BasicConstraintManager.cpp @@ -13,9 +13,9 @@ //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/PathSensitive/GRStateTrait.h" -#include "clang/EntoSA/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" #include "llvm/Support/raw_ostream.h" using namespace clang; diff --git a/lib/EntoSA/BasicStore.cpp b/lib/StaticAnalyzer/BasicStore.cpp similarity index 99% rename from lib/EntoSA/BasicStore.cpp rename to lib/StaticAnalyzer/BasicStore.cpp index 8c4a732aa3..35bcf945fb 100644 --- a/lib/EntoSA/BasicStore.cpp +++ b/lib/StaticAnalyzer/BasicStore.cpp @@ -15,7 +15,7 @@ #include "clang/AST/ExprObjC.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisContext.h" -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" #include "llvm/ADT/ImmutableMap.h" using namespace clang; diff --git a/lib/EntoSA/BasicValueFactory.cpp b/lib/StaticAnalyzer/BasicValueFactory.cpp similarity index 99% rename from lib/EntoSA/BasicValueFactory.cpp rename to lib/StaticAnalyzer/BasicValueFactory.cpp index 0d1cb4a913..d8b501bc7c 100644 --- a/lib/EntoSA/BasicValueFactory.cpp +++ b/lib/StaticAnalyzer/BasicValueFactory.cpp @@ -13,7 +13,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/PathSensitive/BasicValueFactory.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/BlockCounter.cpp b/lib/StaticAnalyzer/BlockCounter.cpp similarity index 97% rename from lib/EntoSA/BlockCounter.cpp rename to lib/StaticAnalyzer/BlockCounter.cpp index b4e6eeee3f..47f6088a47 100644 --- a/lib/EntoSA/BlockCounter.cpp +++ b/lib/StaticAnalyzer/BlockCounter.cpp @@ -13,7 +13,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/BlockCounter.h" +#include "clang/StaticAnalyzer/PathSensitive/BlockCounter.h" #include "llvm/ADT/ImmutableMap.h" using namespace clang; diff --git a/lib/EntoSA/BugReporter.cpp b/lib/StaticAnalyzer/BugReporter.cpp similarity index 99% rename from lib/EntoSA/BugReporter.cpp rename to lib/StaticAnalyzer/BugReporter.cpp index ff35024499..8edbe14878 100644 --- a/lib/EntoSA/BugReporter.cpp +++ b/lib/StaticAnalyzer/BugReporter.cpp @@ -12,9 +12,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/BugReporter/BugReporter.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" #include "clang/AST/ASTContext.h" #include "clang/Analysis/CFG.h" #include "clang/AST/Expr.h" @@ -22,7 +22,7 @@ #include "clang/AST/StmtObjC.h" #include "clang/Basic/SourceManager.h" #include "clang/Analysis/ProgramPoint.h" -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" #include "llvm/Support/raw_ostream.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/STLExtras.h" diff --git a/lib/EntoSA/BugReporterVisitors.cpp b/lib/StaticAnalyzer/BugReporterVisitors.cpp similarity index 98% rename from lib/EntoSA/BugReporterVisitors.cpp rename to lib/StaticAnalyzer/BugReporterVisitors.cpp index d264505b23..91d727592c 100644 --- a/lib/EntoSA/BugReporterVisitors.cpp +++ b/lib/StaticAnalyzer/BugReporterVisitors.cpp @@ -14,10 +14,10 @@ #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" -#include "clang/EntoSA/PathSensitive/ExplodedGraph.h" -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/CFRefCount.cpp b/lib/StaticAnalyzer/CFRefCount.cpp similarity index 99% rename from lib/EntoSA/CFRefCount.cpp rename to lib/StaticAnalyzer/CFRefCount.cpp index 9c9637eb82..5fd223c471 100644 --- a/lib/EntoSA/CFRefCount.cpp +++ b/lib/StaticAnalyzer/CFRefCount.cpp @@ -16,15 +16,15 @@ #include "clang/AST/StmtVisitor.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceManager.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" -#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" #include "clang/Analysis/DomainSpecific/CocoaConventions.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExprEngineBuilders.h" -#include "clang/EntoSA/PathSensitive/GRStateTrait.h" -#include "clang/EntoSA/PathSensitive/TransferFuncs.h" -#include "clang/EntoSA/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngineBuilders.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableList.h" diff --git a/lib/EntoSA/CMakeLists.txt b/lib/StaticAnalyzer/CMakeLists.txt similarity index 85% rename from lib/EntoSA/CMakeLists.txt rename to lib/StaticAnalyzer/CMakeLists.txt index 09a6c2f364..cf81e1d5ad 100644 --- a/lib/EntoSA/CMakeLists.txt +++ b/lib/StaticAnalyzer/CMakeLists.txt @@ -2,7 +2,7 @@ set(LLVM_NO_RTTI 1) set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite) -add_clang_library(clangEntoCore +add_clang_library(clangStaticAnalyzerCore AggExprVisitor.cpp AnalysisManager.cpp AnalyzerStatsChecker.cpp @@ -37,5 +37,5 @@ add_clang_library(clangEntoCore TextPathDiagnostics.cpp ) -add_dependencies(clangEntoCore ClangAttrClasses ClangAttrList ClangDeclNodes +add_dependencies(clangStaticAnalyzerCore ClangAttrClasses ClangAttrList ClangDeclNodes ClangStmtNodes) diff --git a/lib/EntoSA/CXXExprEngine.cpp b/lib/StaticAnalyzer/CXXExprEngine.cpp similarity index 99% rename from lib/EntoSA/CXXExprEngine.cpp rename to lib/StaticAnalyzer/CXXExprEngine.cpp index 3ecd439d0a..47b3a0bd06 100644 --- a/lib/EntoSA/CXXExprEngine.cpp +++ b/lib/StaticAnalyzer/CXXExprEngine.cpp @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/AnalysisManager.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" #include "clang/AST/DeclCXX.h" using namespace clang; diff --git a/lib/EntoSA/Checker.cpp b/lib/StaticAnalyzer/Checker.cpp similarity index 95% rename from lib/EntoSA/Checker.cpp rename to lib/StaticAnalyzer/Checker.cpp index 943be6b931..6867ff5b64 100644 --- a/lib/EntoSA/Checker.cpp +++ b/lib/StaticAnalyzer/Checker.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/Checker.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/CheckerHelpers.cpp b/lib/StaticAnalyzer/CheckerHelpers.cpp similarity index 97% rename from lib/EntoSA/CheckerHelpers.cpp rename to lib/StaticAnalyzer/CheckerHelpers.cpp index 7c7f707afd..be9fee548f 100644 --- a/lib/EntoSA/CheckerHelpers.cpp +++ b/lib/StaticAnalyzer/CheckerHelpers.cpp @@ -11,7 +11,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerHelpers.h" #include "clang/AST/Expr.h" // Recursively find any substatements containing macros diff --git a/lib/EntoSA/Checkers/AdjustedReturnValueChecker.cpp b/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp similarity index 94% rename from lib/EntoSA/Checkers/AdjustedReturnValueChecker.cpp rename to lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp index 914b1e4fbb..46e2de5c26 100644 --- a/lib/EntoSA/Checkers/AdjustedReturnValueChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AdjustedReturnValueChecker.cpp @@ -14,9 +14,9 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/AnalysisConsumer.cpp b/lib/StaticAnalyzer/Checkers/AnalysisConsumer.cpp similarity index 97% rename from lib/EntoSA/Checkers/AnalysisConsumer.cpp rename to lib/StaticAnalyzer/Checkers/AnalysisConsumer.cpp index 043a24a2ef..ee4eae2ebf 100644 --- a/lib/EntoSA/Checkers/AnalysisConsumer.cpp +++ b/lib/StaticAnalyzer/Checkers/AnalysisConsumer.cpp @@ -11,7 +11,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/AnalysisConsumer.h" +#include "clang/StaticAnalyzer/AnalysisConsumer.h" #include "clang/AST/ASTConsumer.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" @@ -20,14 +20,14 @@ #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/Analyses/UninitializedValues.h" #include "clang/Analysis/CFG.h" -#include "clang/EntoSA/Checkers/LocalCheckers.h" -#include "clang/EntoSA/ManagerRegistry.h" -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" -#include "clang/EntoSA/PathSensitive/AnalysisManager.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" -#include "clang/EntoSA/PathSensitive/TransferFuncs.h" -#include "clang/EntoSA/PathDiagnosticClients.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/ManagerRegistry.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/PathDiagnosticClients.h" // FIXME: Restructure checker registration. #include "ExprEngineExperimentalChecks.h" diff --git a/lib/EntoSA/Checkers/ArrayBoundChecker.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp similarity index 94% rename from lib/EntoSA/Checkers/ArrayBoundChecker.cpp rename to lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp index c72a4668ab..13e628bcb1 100644 --- a/lib/EntoSA/Checkers/ArrayBoundChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp @@ -13,9 +13,9 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp similarity index 98% rename from lib/EntoSA/Checkers/ArrayBoundCheckerV2.cpp rename to lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp index 33683bac32..b55fadebf8 100644 --- a/lib/EntoSA/Checkers/ArrayBoundCheckerV2.cpp +++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp @@ -13,9 +13,9 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" #include "clang/AST/CharUnits.h" using namespace clang; diff --git a/lib/EntoSA/Checkers/AttrNonNullChecker.cpp b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp similarity index 97% rename from lib/EntoSA/Checkers/AttrNonNullChecker.cpp rename to lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp index f887e24d16..646b30bc31 100644 --- a/lib/EntoSA/Checkers/AttrNonNullChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/AttrNonNullChecker.cpp @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp similarity index 97% rename from lib/EntoSA/Checkers/BasicObjCFoundationChecks.cpp rename to lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp index 8e83160a20..b4e2959150 100644 --- a/lib/EntoSA/Checkers/BasicObjCFoundationChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp @@ -15,14 +15,14 @@ #include "BasicObjCFoundationChecks.h" -#include "clang/EntoSA/PathSensitive/ExplodedGraph.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/MemRegion.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/ExprObjC.h" diff --git a/lib/EntoSA/Checkers/BasicObjCFoundationChecks.h b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.h similarity index 100% rename from lib/EntoSA/Checkers/BasicObjCFoundationChecks.h rename to lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.h diff --git a/lib/EntoSA/Checkers/BuiltinFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp similarity index 97% rename from lib/EntoSA/Checkers/BuiltinFunctionChecker.cpp rename to lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp index 1db8aac93f..2e6f1b90e1 100644 --- a/lib/EntoSA/Checkers/BuiltinFunctionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/PathSensitive/Checker.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" #include "clang/Basic/Builtins.h" using namespace clang; diff --git a/lib/EntoSA/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/Checkers/CMakeLists.txt similarity index 90% rename from lib/EntoSA/Checkers/CMakeLists.txt rename to lib/StaticAnalyzer/Checkers/CMakeLists.txt index 224a515d9a..e4b9541c23 100644 --- a/lib/EntoSA/Checkers/CMakeLists.txt +++ b/lib/StaticAnalyzer/Checkers/CMakeLists.txt @@ -1,4 +1,4 @@ -add_clang_library(clangEntoCheckers +add_clang_library(clangStaticAnalyzerCheckers AdjustedReturnValueChecker.cpp AnalysisConsumer.cpp ArrayBoundChecker.cpp @@ -49,5 +49,5 @@ add_clang_library(clangEntoCheckers VLASizeChecker.cpp ) -add_dependencies(clangEntoCore ClangAttrClasses ClangAttrList ClangDeclNodes +add_dependencies(clangStaticAnalyzerCore ClangAttrClasses ClangAttrList ClangDeclNodes ClangStmtNodes) diff --git a/lib/EntoSA/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp similarity index 99% rename from lib/EntoSA/Checkers/CStringChecker.cpp rename to lib/StaticAnalyzer/Checkers/CStringChecker.cpp index 199d6b2f5e..b7513c39c1 100644 --- a/lib/EntoSA/Checkers/CStringChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -13,9 +13,9 @@ //===----------------------------------------------------------------------===// #include "ExprEngineExperimentalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" #include "llvm/ADT/StringSwitch.h" using namespace clang; diff --git a/lib/EntoSA/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp similarity index 99% rename from lib/EntoSA/Checkers/CallAndMessageChecker.cpp rename to lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp index 2fa37e3fa5..2998406da0 100644 --- a/lib/EntoSA/Checkers/CallAndMessageChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp @@ -15,8 +15,8 @@ #include "ExprEngineInternalChecks.h" #include "clang/AST/ParentMap.h" #include "clang/Basic/TargetInfo.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp similarity index 95% rename from lib/EntoSA/Checkers/CastSizeChecker.cpp rename to lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp index 41a72ef32d..9329ea39d4 100644 --- a/lib/EntoSA/Checkers/CastSizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// #include "clang/AST/CharUnits.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" #include "ExprEngineInternalChecks.h" using namespace clang; diff --git a/lib/EntoSA/Checkers/CastToStructChecker.cpp b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp similarity index 95% rename from lib/EntoSA/Checkers/CastToStructChecker.cpp rename to lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp index 83393b9268..dd6ac7ea84 100644 --- a/lib/EntoSA/Checkers/CastToStructChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp @@ -13,8 +13,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" #include "ExprEngineInternalChecks.h" using namespace clang; diff --git a/lib/EntoSA/Checkers/CheckDeadStores.cpp b/lib/StaticAnalyzer/Checkers/CheckDeadStores.cpp similarity index 98% rename from lib/EntoSA/Checkers/CheckDeadStores.cpp rename to lib/StaticAnalyzer/Checkers/CheckDeadStores.cpp index 047c83abc6..1fe40c56e8 100644 --- a/lib/EntoSA/Checkers/CheckDeadStores.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckDeadStores.cpp @@ -12,11 +12,11 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/Visitors/CFGRecStmtVisitor.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" #include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" #include "clang/Basic/Diagnostic.h" #include "clang/AST/ASTContext.h" diff --git a/lib/EntoSA/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp similarity index 98% rename from lib/EntoSA/Checkers/CheckObjCDealloc.cpp rename to lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp index 391e30f61e..932a6f5945 100644 --- a/lib/EntoSA/Checkers/CheckObjCDealloc.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp @@ -13,9 +13,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/Checkers/LocalCheckers.h" -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/DeclObjC.h" diff --git a/lib/EntoSA/Checkers/CheckObjCInstMethSignature.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp similarity index 95% rename from lib/EntoSA/Checkers/CheckObjCInstMethSignature.cpp rename to lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp index f908e69cc7..57e388f44d 100644 --- a/lib/EntoSA/Checkers/CheckObjCInstMethSignature.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp @@ -13,9 +13,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/Checkers/LocalCheckers.h" -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Type.h" #include "clang/AST/ASTContext.h" diff --git a/lib/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp similarity index 99% rename from lib/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp rename to lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp index 98da69231c..b30e985f38 100644 --- a/lib/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp @@ -12,8 +12,8 @@ //===----------------------------------------------------------------------===// #include "clang/Basic/TargetInfo.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" -#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" #include "clang/AST/StmtVisitor.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/EntoSA/Checkers/CheckSizeofPointer.cpp b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp similarity index 94% rename from lib/EntoSA/Checkers/CheckSizeofPointer.cpp rename to lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp index f7ab9514e9..ed060b27fb 100644 --- a/lib/EntoSA/Checkers/CheckSizeofPointer.cpp +++ b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp @@ -12,9 +12,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" #include "clang/AST/StmtVisitor.h" -#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp similarity index 94% rename from lib/EntoSA/Checkers/ChrootChecker.cpp rename to lib/StaticAnalyzer/Checkers/ChrootChecker.cpp index e951c29d1a..a3f4daa3b0 100644 --- a/lib/EntoSA/Checkers/ChrootChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp @@ -12,11 +12,11 @@ //===----------------------------------------------------------------------===// #include "ExprEngineExperimentalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/PathSensitive/GRStateTrait.h" -#include "clang/EntoSA/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" #include "llvm/ADT/ImmutableMap.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp similarity index 96% rename from lib/EntoSA/Checkers/DereferenceChecker.cpp rename to lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp index 35fae40935..7dc8ec84df 100644 --- a/lib/EntoSA/Checkers/DereferenceChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/Checkers/DereferenceChecker.h" -#include "clang/EntoSA/PathSensitive/Checker.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp similarity index 95% rename from lib/EntoSA/Checkers/DivZeroChecker.cpp rename to lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp index 24c743a8d0..8332af8f5d 100644 --- a/lib/EntoSA/Checkers/DivZeroChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/ExprEngine.cpp b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp similarity index 99% rename from lib/EntoSA/Checkers/ExprEngine.cpp rename to lib/StaticAnalyzer/Checkers/ExprEngine.cpp index afd6318acc..17bc339f75 100644 --- a/lib/EntoSA/Checkers/ExprEngine.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprEngine.cpp @@ -16,11 +16,11 @@ // FIXME: Restructure checker registration. #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/AnalysisManager.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" -#include "clang/EntoSA/PathSensitive/ExprEngineBuilders.h" -#include "clang/EntoSA/PathSensitive/Checker.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngineBuilders.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" #include "clang/AST/CharUnits.h" #include "clang/AST/ParentMap.h" #include "clang/AST/StmtObjC.h" diff --git a/lib/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp b/lib/StaticAnalyzer/Checkers/ExprEngineExperimentalChecks.cpp similarity index 96% rename from lib/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp rename to lib/StaticAnalyzer/Checkers/ExprEngineExperimentalChecks.cpp index 6ab5a4d5da..52a3aced2d 100644 --- a/lib/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp +++ b/lib/StaticAnalyzer/Checkers/ExprEngineExperimentalChecks.cpp @@ -14,7 +14,7 @@ #include "ExprEngineInternalChecks.h" #include "ExprEngineExperimentalChecks.h" -#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/ExprEngineExperimentalChecks.h b/lib/StaticAnalyzer/Checkers/ExprEngineExperimentalChecks.h similarity index 100% rename from lib/EntoSA/Checkers/ExprEngineExperimentalChecks.h rename to lib/StaticAnalyzer/Checkers/ExprEngineExperimentalChecks.h diff --git a/lib/EntoSA/Checkers/ExprEngineInternalChecks.h b/lib/StaticAnalyzer/Checkers/ExprEngineInternalChecks.h similarity index 100% rename from lib/EntoSA/Checkers/ExprEngineInternalChecks.h rename to lib/StaticAnalyzer/Checkers/ExprEngineInternalChecks.h diff --git a/lib/EntoSA/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp similarity index 94% rename from lib/EntoSA/Checkers/FixedAddressChecker.cpp rename to lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp index ea736435d6..77e9876d47 100644 --- a/lib/EntoSA/Checkers/FixedAddressChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp @@ -14,8 +14,8 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/FrontendActions.cpp b/lib/StaticAnalyzer/Checkers/FrontendActions.cpp similarity index 88% rename from lib/EntoSA/Checkers/FrontendActions.cpp rename to lib/StaticAnalyzer/Checkers/FrontendActions.cpp index b901afc5e1..b0e8e5d9bb 100644 --- a/lib/EntoSA/Checkers/FrontendActions.cpp +++ b/lib/StaticAnalyzer/Checkers/FrontendActions.cpp @@ -7,9 +7,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/FrontendActions.h" +#include "clang/StaticAnalyzer/FrontendActions.h" #include "clang/Frontend/CompilerInstance.h" -#include "clang/EntoSA/AnalysisConsumer.h" +#include "clang/StaticAnalyzer/AnalysisConsumer.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/IdempotentOperationChecker.cpp b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp similarity index 98% rename from lib/EntoSA/Checkers/IdempotentOperationChecker.cpp rename to lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp index 04f652cdcb..435d3d4a48 100644 --- a/lib/EntoSA/Checkers/IdempotentOperationChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/IdempotentOperationChecker.cpp @@ -45,12 +45,12 @@ #include "ExprEngineExperimentalChecks.h" #include "clang/Analysis/CFGStmtMap.h" #include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerHelpers.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/CoreEngine.h" -#include "clang/EntoSA/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" #include "clang/AST/Stmt.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallSet.h" diff --git a/lib/EntoSA/Checkers/LLVMConventionsChecker.cpp b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp similarity index 98% rename from lib/EntoSA/Checkers/LLVMConventionsChecker.cpp rename to lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp index e88997f79c..eefad95f21 100644 --- a/lib/EntoSA/Checkers/LLVMConventionsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp @@ -14,8 +14,8 @@ #include "clang/AST/DeclTemplate.h" #include "clang/AST/StmtVisitor.h" -#include "clang/EntoSA/Checkers/LocalCheckers.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" #include #include "llvm/ADT/StringRef.h" diff --git a/lib/EntoSA/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp similarity index 96% rename from lib/EntoSA/Checkers/MacOSXAPIChecker.cpp rename to lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp index c2f5f4b83c..44887fa769 100644 --- a/lib/EntoSA/Checkers/MacOSXAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp @@ -17,9 +17,9 @@ #include "ExprEngineInternalChecks.h" #include "clang/Basic/TargetInfo.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/raw_ostream.h" diff --git a/lib/EntoSA/Checkers/Makefile b/lib/StaticAnalyzer/Checkers/Makefile similarity index 92% rename from lib/EntoSA/Checkers/Makefile rename to lib/StaticAnalyzer/Checkers/Makefile index 9ba4e282d2..d4de35c1f5 100644 --- a/lib/EntoSA/Checkers/Makefile +++ b/lib/StaticAnalyzer/Checkers/Makefile @@ -12,6 +12,6 @@ ##===----------------------------------------------------------------------===## CLANG_LEVEL := ../../.. -LIBRARYNAME := clangEntoCheckers +LIBRARYNAME := clangStaticAnalyzerCheckers include $(CLANG_LEVEL)/Makefile diff --git a/lib/EntoSA/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp similarity index 98% rename from lib/EntoSA/Checkers/MallocChecker.cpp rename to lib/StaticAnalyzer/Checkers/MallocChecker.cpp index 2b0ae82ccd..42243cb525 100644 --- a/lib/EntoSA/Checkers/MallocChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp @@ -13,11 +13,11 @@ //===----------------------------------------------------------------------===// #include "ExprEngineExperimentalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/PathSensitive/GRStateTrait.h" -#include "clang/EntoSA/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" #include "llvm/ADT/ImmutableMap.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp similarity index 93% rename from lib/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp rename to lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp index acad46c352..6ef242ba42 100644 --- a/lib/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp @@ -15,9 +15,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/BugReporter/BugReporter.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" #include "BasicObjCFoundationChecks.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Decl.h" diff --git a/lib/EntoSA/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp similarity index 96% rename from lib/EntoSA/Checkers/NSErrorChecker.cpp rename to lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp index d5c395f0a1..54e6118893 100644 --- a/lib/EntoSA/Checkers/NSErrorChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp @@ -15,10 +15,10 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/Checkers/LocalCheckers.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" -#include "clang/EntoSA/Checkers/DereferenceChecker.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" #include "BasicObjCFoundationChecks.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/Decl.h" diff --git a/lib/EntoSA/Checkers/NoReturnFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp similarity index 97% rename from lib/EntoSA/Checkers/NoReturnFunctionChecker.cpp rename to lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp index 4b96ddba39..8cdc04f9ff 100644 --- a/lib/EntoSA/Checkers/NoReturnFunctionChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" #include "llvm/ADT/StringSwitch.h" using namespace clang; diff --git a/lib/EntoSA/Checkers/OSAtomicChecker.cpp b/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp similarity index 99% rename from lib/EntoSA/Checkers/OSAtomicChecker.cpp rename to lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp index cc67c3df7a..9931734842 100644 --- a/lib/EntoSA/Checkers/OSAtomicChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/OSAtomicChecker.cpp @@ -12,7 +12,7 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/PathSensitive/Checker.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" #include "clang/Basic/Builtins.h" using namespace clang; diff --git a/lib/EntoSA/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp similarity index 92% rename from lib/EntoSA/Checkers/ObjCAtSyncChecker.cpp rename to lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp index 3e901b6007..2d94e8ce91 100644 --- a/lib/EntoSA/Checkers/ObjCAtSyncChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/Checkers/DereferenceChecker.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp similarity index 96% rename from lib/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp rename to lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp index a2e2475f28..799da332b9 100644 --- a/lib/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp @@ -13,9 +13,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/Checkers/LocalCheckers.h" -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" #include "clang/AST/ExprObjC.h" #include "clang/AST/Expr.h" #include "clang/AST/DeclObjC.h" diff --git a/lib/EntoSA/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp similarity index 95% rename from lib/EntoSA/Checkers/PointerArithChecker.cpp rename to lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp index 30009a2560..2b03dbcd9c 100644 --- a/lib/EntoSA/Checkers/PointerArithChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp similarity index 95% rename from lib/EntoSA/Checkers/PointerSubChecker.cpp rename to lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp index 923d0a86d8..1ba60f7541 100644 --- a/lib/EntoSA/Checkers/PointerSubChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp @@ -14,8 +14,8 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp similarity index 95% rename from lib/EntoSA/Checkers/PthreadLockChecker.cpp rename to lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp index 56c9adb509..7a91696c92 100644 --- a/lib/EntoSA/Checkers/PthreadLockChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp @@ -12,9 +12,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" -#include "clang/EntoSA/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" #include "ExprEngineExperimentalChecks.h" #include "llvm/ADT/ImmutableSet.h" diff --git a/lib/EntoSA/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp similarity index 94% rename from lib/EntoSA/Checkers/ReturnPointerRangeChecker.cpp rename to lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp index 8afe9847aa..a56c1b6584 100644 --- a/lib/EntoSA/Checkers/ReturnPointerRangeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp @@ -13,9 +13,9 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp similarity index 91% rename from lib/EntoSA/Checkers/ReturnUndefChecker.cpp rename to lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp index 8e46519b21..50ffd322db 100644 --- a/lib/EntoSA/Checkers/ReturnUndefChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp @@ -14,9 +14,9 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/StackAddrLeakChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrLeakChecker.cpp similarity index 97% rename from lib/EntoSA/Checkers/StackAddrLeakChecker.cpp rename to lib/StaticAnalyzer/Checkers/StackAddrLeakChecker.cpp index c9aebadca1..1ec5c32aff 100644 --- a/lib/EntoSA/Checkers/StackAddrLeakChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StackAddrLeakChecker.cpp @@ -13,9 +13,9 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" #include "clang/Basic/SourceManager.h" #include "llvm/ADT/SmallString.h" using namespace clang; diff --git a/lib/EntoSA/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp similarity index 97% rename from lib/EntoSA/Checkers/StreamChecker.cpp rename to lib/StaticAnalyzer/Checkers/StreamChecker.cpp index cb7899fbb2..a6d1e079c4 100644 --- a/lib/EntoSA/Checkers/StreamChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp @@ -12,11 +12,11 @@ //===----------------------------------------------------------------------===// #include "ExprEngineExperimentalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/PathSensitive/GRStateTrait.h" -#include "clang/EntoSA/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" #include "llvm/ADT/ImmutableMap.h" using namespace clang; diff --git a/lib/EntoSA/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp similarity index 97% rename from lib/EntoSA/Checkers/UndefBranchChecker.cpp rename to lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp index 30d0728530..c0b3b7afec 100644 --- a/lib/EntoSA/Checkers/UndefBranchChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/Checker.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp similarity index 94% rename from lib/EntoSA/Checkers/UndefCapturedBlockVarChecker.cpp rename to lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp index dcdcf1f741..57e698bb7c 100644 --- a/lib/EntoSA/Checkers/UndefCapturedBlockVarChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp @@ -12,9 +12,9 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" -#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" #include "llvm/Support/raw_ostream.h" using namespace clang; diff --git a/lib/EntoSA/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp similarity index 93% rename from lib/EntoSA/Checkers/UndefResultChecker.cpp rename to lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp index c0427402b1..91e489bd2a 100644 --- a/lib/EntoSA/Checkers/UndefResultChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp @@ -13,9 +13,9 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp similarity index 93% rename from lib/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp rename to lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp index aea78c048b..2cd293b2f4 100644 --- a/lib/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp similarity index 95% rename from lib/EntoSA/Checkers/UndefinedAssignmentChecker.cpp rename to lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp index 5060cdff96..3146bbda41 100644 --- a/lib/EntoSA/Checkers/UndefinedAssignmentChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp @@ -13,8 +13,8 @@ //===----------------------------------------------------------------------===// #include "ExprEngineInternalChecks.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp similarity index 98% rename from lib/EntoSA/Checkers/UnixAPIChecker.cpp rename to lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp index c45659c011..9092f93ba7 100644 --- a/lib/EntoSA/Checkers/UnixAPIChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp @@ -14,8 +14,8 @@ #include "ExprEngineInternalChecks.h" #include "clang/Basic/TargetInfo.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" #include "llvm/ADT/Optional.h" #include "llvm/ADT/StringSwitch.h" #include diff --git a/lib/EntoSA/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp similarity index 96% rename from lib/EntoSA/Checkers/UnreachableCodeChecker.cpp rename to lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index e3ea72e133..98834485eb 100644 --- a/lib/EntoSA/Checkers/UnreachableCodeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -16,11 +16,11 @@ #include "clang/AST/ParentMap.h" #include "clang/Basic/Builtins.h" #include "clang/Basic/SourceManager.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExplodedGraph.h" -#include "clang/EntoSA/PathSensitive/SVals.h" -#include "clang/EntoSA/PathSensitive/CheckerHelpers.h" -#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" #include "ExprEngineExperimentalChecks.h" #include "llvm/ADT/SmallPtrSet.h" diff --git a/lib/EntoSA/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp similarity index 96% rename from lib/EntoSA/Checkers/VLASizeChecker.cpp rename to lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp index 0d17238ee7..e24521af53 100644 --- a/lib/EntoSA/Checkers/VLASizeChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp @@ -16,9 +16,9 @@ #include "ExprEngineInternalChecks.h" #include "clang/AST/CharUnits.h" -#include "clang/EntoSA/BugReporter/BugType.h" -#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/CoreEngine.cpp b/lib/StaticAnalyzer/CoreEngine.cpp similarity index 99% rename from lib/EntoSA/CoreEngine.cpp rename to lib/StaticAnalyzer/CoreEngine.cpp index 478e77b4af..a23c7f9d2d 100644 --- a/lib/EntoSA/CoreEngine.cpp +++ b/lib/StaticAnalyzer/CoreEngine.cpp @@ -12,9 +12,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/AnalysisManager.h" -#include "clang/EntoSA/PathSensitive/CoreEngine.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" #include "clang/Index/TranslationUnit.h" #include "clang/AST/Expr.h" #include "llvm/Support/Casting.h" diff --git a/lib/StaticAnalyzer/EntoSA/AggExprVisitor.cpp b/lib/StaticAnalyzer/EntoSA/AggExprVisitor.cpp new file mode 100644 index 0000000000..0cc23900ed --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/AggExprVisitor.cpp @@ -0,0 +1,63 @@ +//=-- AggExprVisitor.cpp - evaluating expressions of C++ class type -*- 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 AggExprVisitor class, which contains lots of boiler +// plate code for evaluating expressions of C++ class type. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/AST/StmtVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +/// AggExprVisitor is designed after AggExprEmitter of the CodeGen module. It +/// is used for evaluating exprs of C++ object type. Evaluating such exprs +/// requires a destination pointer pointing to the object being evaluated +/// into. Passing such a pointer around would pollute the Visit* interface of +/// ExprEngine. AggExprVisitor encapsulates code that goes through various +/// cast and construct exprs (and others), and at the final point, dispatches +/// back to the ExprEngine to let the real evaluation logic happen. +class AggExprVisitor : public StmtVisitor { + const MemRegion *Dest; + ExplodedNode *Pred; + ExplodedNodeSet &DstSet; + ExprEngine &Eng; + +public: + AggExprVisitor(const MemRegion *dest, ExplodedNode *N, ExplodedNodeSet &dst, + ExprEngine &eng) + : Dest(dest), Pred(N), DstSet(dst), Eng(eng) {} + + void VisitCastExpr(CastExpr *E); + void VisitCXXConstructExpr(CXXConstructExpr *E); +}; +} + +void AggExprVisitor::VisitCastExpr(CastExpr *E) { + switch (E->getCastKind()) { + default: + assert(0 && "Unhandled cast kind"); + case CK_NoOp: + case CK_ConstructorConversion: + Visit(E->getSubExpr()); + break; + } +} + +void AggExprVisitor::VisitCXXConstructExpr(CXXConstructExpr *E) { + Eng.VisitCXXConstructExpr(E, Dest, Pred, DstSet); +} + +void ExprEngine::VisitAggExpr(const Expr *E, const MemRegion *Dest, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + AggExprVisitor(Dest, Pred, Dst, *this).Visit(const_cast(E)); +} diff --git a/lib/StaticAnalyzer/EntoSA/AnalysisManager.cpp b/lib/StaticAnalyzer/EntoSA/AnalysisManager.cpp new file mode 100644 index 0000000000..cc5e271393 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/AnalysisManager.cpp @@ -0,0 +1,32 @@ +//===-- 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/PathSensitive/AnalysisManager.h" +#include "clang/Index/Entity.h" +#include "clang/Index/Indexer.h" + +using namespace clang; +using namespace ento; + +AnalysisContext * +AnalysisManager::getAnalysisContextInAnotherTU(const Decl *D) { + idx::Entity Ent = idx::Entity::get(const_cast(D), + Idxer->getProgram()); + FunctionDecl *FuncDef; + idx::TranslationUnit *TU; + llvm::tie(FuncDef, TU) = Idxer->getDefinitionFor(Ent); + + if (FuncDef == 0) + return 0; + + // This AnalysisContext wraps function definition in another translation unit. + // But it is still owned by the AnalysisManager associated with the current + // translation unit. + return AnaCtxMgr.getContext(FuncDef, TU); +} diff --git a/lib/StaticAnalyzer/EntoSA/AnalyzerStatsChecker.cpp b/lib/StaticAnalyzer/EntoSA/AnalyzerStatsChecker.cpp new file mode 100644 index 0000000000..8521c18c54 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/AnalyzerStatsChecker.cpp @@ -0,0 +1,123 @@ +//==--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/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" + +// FIXME: Restructure checker registration. +#include "Checkers/ExprEngineExperimentalChecks.h" + +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallPtrSet.h" + +using namespace clang; +using namespace ento; + +namespace { +class AnalyzerStatsChecker : public CheckerVisitor { +public: + static void *getTag(); + void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, ExprEngine &Eng); + +private: + llvm::SmallPtrSet reachable; +}; +} + +void *AnalyzerStatsChecker::getTag() { + static int x = 0; + return &x; +} + +void ento::RegisterAnalyzerStatsChecker(ExprEngine &Eng) { + Eng.registerCheck(new AnalyzerStatsChecker()); +} + +void AnalyzerStatsChecker::VisitEndAnalysis(ExplodedGraph &G, + BugReporter &B, + ExprEngine &Eng) { + const CFG *C = 0; + const Decl *D = 0; + const LocationContext *LC = 0; + const SourceManager &SM = B.getSourceManager(); + + // Iterate over explodedgraph + for (ExplodedGraph::node_iterator I = G.nodes_begin(); + I != G.nodes_end(); ++I) { + const ProgramPoint &P = I->getLocation(); + // Save the LocationContext if we don't have it already + if (!LC) + LC = P.getLocationContext(); + + if (const BlockEntrance *BE = dyn_cast(&P)) { + const CFGBlock *CB = BE->getBlock(); + reachable.insert(CB); + } + } + + // Get the CFG and the Decl of this block + C = LC->getCFG(); + D = LC->getAnalysisContext()->getDecl(); + + 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--; + + // Generate the warning string + llvm::SmallString<128> buf; + llvm::raw_svector_ostream output(buf); + PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); + if (Loc.isValid()) { + output << Loc.getFilename() << " : "; + + if (isa(D) || isa(D)) { + const NamedDecl *ND = cast(D); + output << ND; + } + else if (isa(D)) { + output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn(); + } + } + + output << " -> Total CFGBlocks: " << total << " | Unreachable CFGBlocks: " + << unreachable << " | Aborted Block: " + << (Eng.wasBlockAborted() ? "yes" : "no") + << " | Empty WorkList: " + << (Eng.hasEmptyWorkList() ? "yes" : "no"); + + B.EmitBasicReport("Analyzer Statistics", "Internal Statistics", output.str(), + D->getLocation()); + + // Emit warning for each block we bailed out on + typedef CoreEngine::BlocksAborted::const_iterator AbortedIterator; + const CoreEngine &CE = Eng.getCoreEngine(); + for (AbortedIterator I = CE.blocks_aborted_begin(), + E = CE.blocks_aborted_end(); I != E; ++I) { + const BlockEdge &BE = I->first; + const CFGBlock *Exit = BE.getDst(); + const CFGElement &CE = Exit->front(); + if (const CFGStmt *CS = dyn_cast(&CE)) + B.EmitBasicReport("Bailout Point", "Internal Statistics", "The analyzer " + "stopped analyzing at this point", CS->getStmt()->getLocStart()); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/BasicConstraintManager.cpp b/lib/StaticAnalyzer/EntoSA/BasicConstraintManager.cpp new file mode 100644 index 0000000000..8bdc4fa003 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/BasicConstraintManager.cpp @@ -0,0 +1,338 @@ +//== BasicConstraintManager.cpp - Manage basic 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 BasicConstraintManager, a class that tracks simple +// equality and inequality constraints on symbolic values of GRState. +// +//===----------------------------------------------------------------------===// + +#include "SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + + +namespace { class ConstNotEq {}; } +namespace { class ConstEq {}; } + +typedef llvm::ImmutableMap ConstNotEqTy; +typedef llvm::ImmutableMap ConstEqTy; + +static int ConstEqIndex = 0; +static int ConstNotEqIndex = 0; + +namespace clang { +namespace ento { +template<> +struct GRStateTrait : public GRStatePartialTrait { + static inline void* GDMIndex() { return &ConstNotEqIndex; } +}; + +template<> +struct GRStateTrait : public GRStatePartialTrait { + static inline void* GDMIndex() { return &ConstEqIndex; } +}; +} +} + +namespace { +// BasicConstraintManager only tracks equality and inequality constraints of +// constants and integer variables. +class BasicConstraintManager + : public SimpleConstraintManager { + GRState::IntSetTy::Factory ISetFactory; +public: + BasicConstraintManager(GRStateManager &statemgr, SubEngine &subengine) + : SimpleConstraintManager(subengine), + ISetFactory(statemgr.getAllocator()) {} + + const GRState *assumeSymNE(const GRState* state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const GRState *assumeSymEQ(const GRState* state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const GRState *assumeSymLT(const GRState* state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const GRState *assumeSymGT(const GRState* state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const GRState *assumeSymGE(const GRState* state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const GRState *assumeSymLE(const GRState* state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment); + + const GRState* AddEQ(const GRState* state, SymbolRef sym, const llvm::APSInt& V); + + const GRState* AddNE(const GRState* state, SymbolRef sym, const llvm::APSInt& V); + + const llvm::APSInt* getSymVal(const GRState* state, SymbolRef sym) const; + bool isNotEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) + const; + bool isEqual(const GRState* state, SymbolRef sym, const llvm::APSInt& V) + const; + + const GRState* RemoveDeadBindings(const GRState* state, SymbolReaper& SymReaper); + + void print(const GRState* state, llvm::raw_ostream& Out, + const char* nl, const char *sep); +}; + +} // end anonymous namespace + +ConstraintManager* ento::CreateBasicConstraintManager(GRStateManager& statemgr, + SubEngine &subengine) { + return new BasicConstraintManager(statemgr, subengine); +} + + +const GRState* +BasicConstraintManager::assumeSymNE(const GRState *state, SymbolRef sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) { + // First, determine if sym == X, where X+Adjustment != V. + llvm::APSInt Adjusted = V-Adjustment; + if (const llvm::APSInt* X = getSymVal(state, sym)) { + bool isFeasible = (*X != Adjusted); + return isFeasible ? state : NULL; + } + + // Second, determine if sym+Adjustment != V. + if (isNotEqual(state, sym, Adjusted)) + return state; + + // If we reach here, sym is not a constant and we don't know if it is != V. + // Make that assumption. + return AddNE(state, sym, Adjusted); +} + +const GRState* +BasicConstraintManager::assumeSymEQ(const GRState *state, SymbolRef sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) { + // First, determine if sym == X, where X+Adjustment != V. + llvm::APSInt Adjusted = V-Adjustment; + if (const llvm::APSInt* X = getSymVal(state, sym)) { + bool isFeasible = (*X == Adjusted); + return isFeasible ? state : NULL; + } + + // Second, determine if sym+Adjustment != V. + if (isNotEqual(state, sym, Adjusted)) + return NULL; + + // If we reach here, sym is not a constant and we don't know if it is == V. + // Make that assumption. + return AddEQ(state, sym, Adjusted); +} + +// The logic for these will be handled in another ConstraintManager. +const GRState* +BasicConstraintManager::assumeSymLT(const GRState *state, SymbolRef sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) { + // Is 'V' the smallest possible value? + if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { + // sym cannot be any value less than 'V'. This path is infeasible. + return NULL; + } + + // FIXME: For now have assuming x < y be the same as assuming sym != V; + return assumeSymNE(state, sym, V, Adjustment); +} + +const GRState* +BasicConstraintManager::assumeSymGT(const GRState *state, SymbolRef sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) { + // Is 'V' the largest possible value? + if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { + // sym cannot be any value greater than 'V'. This path is infeasible. + return NULL; + } + + // FIXME: For now have assuming x > y be the same as assuming sym != V; + return assumeSymNE(state, sym, V, Adjustment); +} + +const GRState* +BasicConstraintManager::assumeSymGE(const GRState *state, SymbolRef sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) { + // Reject a path if the value of sym is a constant X and !(X+Adj >= V). + if (const llvm::APSInt *X = getSymVal(state, sym)) { + bool isFeasible = (*X >= V-Adjustment); + return isFeasible ? state : NULL; + } + + // Sym is not a constant, but it is worth looking to see if V is the + // maximum integer value. + if (V == llvm::APSInt::getMaxValue(V.getBitWidth(), V.isUnsigned())) { + llvm::APSInt Adjusted = V-Adjustment; + + // If we know that sym != V (after adjustment), then this condition + // is infeasible since there is no other value greater than V. + bool isFeasible = !isNotEqual(state, sym, Adjusted); + + // If the path is still feasible then as a consequence we know that + // 'sym+Adjustment == V' because there are no larger values. + // Add this constraint. + return isFeasible ? AddEQ(state, sym, Adjusted) : NULL; + } + + return state; +} + +const GRState* +BasicConstraintManager::assumeSymLE(const GRState *state, SymbolRef sym, + const llvm::APSInt &V, + const llvm::APSInt &Adjustment) { + // Reject a path if the value of sym is a constant X and !(X+Adj <= V). + if (const llvm::APSInt* X = getSymVal(state, sym)) { + bool isFeasible = (*X <= V-Adjustment); + return isFeasible ? state : NULL; + } + + // Sym is not a constant, but it is worth looking to see if V is the + // minimum integer value. + if (V == llvm::APSInt::getMinValue(V.getBitWidth(), V.isUnsigned())) { + llvm::APSInt Adjusted = V-Adjustment; + + // If we know that sym != V (after adjustment), then this condition + // is infeasible since there is no other value less than V. + bool isFeasible = !isNotEqual(state, sym, Adjusted); + + // If the path is still feasible then as a consequence we know that + // 'sym+Adjustment == V' because there are no smaller values. + // Add this constraint. + return isFeasible ? AddEQ(state, sym, Adjusted) : NULL; + } + + return state; +} + +const GRState* BasicConstraintManager::AddEQ(const GRState* state, SymbolRef sym, + const llvm::APSInt& V) { + // Create a new state with the old binding replaced. + return state->set(sym, &state->getBasicVals().getValue(V)); +} + +const GRState* BasicConstraintManager::AddNE(const GRState* state, SymbolRef sym, + const llvm::APSInt& V) { + + // First, retrieve the NE-set associated with the given symbol. + ConstNotEqTy::data_type* T = state->get(sym); + GRState::IntSetTy S = T ? *T : ISetFactory.getEmptySet(); + + // Now add V to the NE set. + S = ISetFactory.add(S, &state->getBasicVals().getValue(V)); + + // Create a new state with the old binding replaced. + return state->set(sym, S); +} + +const llvm::APSInt* BasicConstraintManager::getSymVal(const GRState* state, + SymbolRef sym) const { + const ConstEqTy::data_type* T = state->get(sym); + return T ? *T : NULL; +} + +bool BasicConstraintManager::isNotEqual(const GRState* state, SymbolRef sym, + const llvm::APSInt& V) const { + + // Retrieve the NE-set associated with the given symbol. + const ConstNotEqTy::data_type* T = state->get(sym); + + // See if V is present in the NE-set. + return T ? T->contains(&state->getBasicVals().getValue(V)) : false; +} + +bool BasicConstraintManager::isEqual(const GRState* state, SymbolRef sym, + const llvm::APSInt& V) const { + // Retrieve the EQ-set associated with the given symbol. + const ConstEqTy::data_type* T = state->get(sym); + // See if V is present in the EQ-set. + return T ? **T == V : false; +} + +/// Scan all symbols referenced by the constraints. If the symbol is not alive +/// as marked in LSymbols, mark it as dead in DSymbols. +const GRState* +BasicConstraintManager::RemoveDeadBindings(const GRState* state, + SymbolReaper& SymReaper) { + + ConstEqTy CE = state->get(); + ConstEqTy::Factory& CEFactory = state->get_context(); + + for (ConstEqTy::iterator I = CE.begin(), E = CE.end(); I!=E; ++I) { + SymbolRef sym = I.getKey(); + if (SymReaper.maybeDead(sym)) + CE = CEFactory.remove(CE, sym); + } + state = state->set(CE); + + ConstNotEqTy CNE = state->get(); + ConstNotEqTy::Factory& CNEFactory = state->get_context(); + + for (ConstNotEqTy::iterator I = CNE.begin(), E = CNE.end(); I != E; ++I) { + SymbolRef sym = I.getKey(); + if (SymReaper.maybeDead(sym)) + CNE = CNEFactory.remove(CNE, sym); + } + + return state->set(CNE); +} + +void BasicConstraintManager::print(const GRState* state, llvm::raw_ostream& Out, + const char* nl, const char *sep) { + // Print equality constraints. + + ConstEqTy CE = state->get(); + + if (!CE.isEmpty()) { + Out << nl << sep << "'==' constraints:"; + for (ConstEqTy::iterator I = CE.begin(), E = CE.end(); I!=E; ++I) + Out << nl << " $" << I.getKey() << " : " << *I.getData(); + } + + // Print != constraints. + + ConstNotEqTy CNE = state->get(); + + if (!CNE.isEmpty()) { + Out << nl << sep << "'!=' constraints:"; + + for (ConstNotEqTy::iterator I = CNE.begin(), EI = CNE.end(); I!=EI; ++I) { + Out << nl << " $" << I.getKey() << " : "; + bool isFirst = true; + + GRState::IntSetTy::iterator J = I.getData().begin(), + EJ = I.getData().end(); + + for ( ; J != EJ; ++J) { + if (isFirst) isFirst = false; + else Out << ", "; + + Out << (*J)->getSExtValue(); // Hack: should print to raw_ostream. + } + } + } +} diff --git a/lib/StaticAnalyzer/EntoSA/BasicStore.cpp b/lib/StaticAnalyzer/EntoSA/BasicStore.cpp new file mode 100644 index 0000000000..35bcf945fb --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/BasicStore.cpp @@ -0,0 +1,594 @@ +//== BasicStore.cpp - Basic map from Locations to 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 defined the BasicStore and BasicStoreManager classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "llvm/ADT/ImmutableMap.h" + +using namespace clang; +using namespace ento; + +typedef llvm::ImmutableMap BindingsTy; + +namespace { + +class BasicStoreSubRegionMap : public SubRegionMap { +public: + BasicStoreSubRegionMap() {} + + bool iterSubRegions(const MemRegion* R, Visitor& V) const { + return true; // Do nothing. No subregions. + } +}; + +class BasicStoreManager : public StoreManager { + BindingsTy::Factory VBFactory; +public: + BasicStoreManager(GRStateManager& mgr) + : StoreManager(mgr), VBFactory(mgr.getAllocator()) {} + + ~BasicStoreManager() {} + + SubRegionMap *getSubRegionMap(Store store) { + return new BasicStoreSubRegionMap(); + } + + SVal Retrieve(Store store, Loc loc, QualType T = QualType()); + + Store InvalidateRegion(Store store, const MemRegion *R, const Expr *E, + unsigned Count, InvalidatedSymbols *IS); + + Store InvalidateRegions(Store store, const MemRegion * const *Begin, + const MemRegion * const *End, const Expr *E, + unsigned Count, InvalidatedSymbols *IS, + bool invalidateGlobals, InvalidatedRegions *Regions); + + Store scanForIvars(Stmt *B, const Decl* SelfDecl, + const MemRegion *SelfRegion, Store St); + + Store Bind(Store St, Loc loc, SVal V); + Store Remove(Store St, Loc loc); + Store getInitialStore(const LocationContext *InitLoc); + + Store BindCompoundLiteral(Store store, const CompoundLiteralExpr*, + const LocationContext*, SVal val) { + return store; + } + + /// ArrayToPointer - Used by ExprEngine::VistCast to handle implicit + /// conversions between arrays and pointers. + SVal ArrayToPointer(Loc Array) { return Array; } + + /// RemoveDeadBindings - Scans a BasicStore of 'state' for dead values. + /// It updatees the GRState object in place with the values removed. + Store RemoveDeadBindings(Store store, const StackFrameContext *LCtx, + SymbolReaper& SymReaper, + llvm::SmallVectorImpl& RegionRoots); + + void iterBindings(Store store, BindingsHandler& f); + + Store BindDecl(Store store, const VarRegion *VR, SVal InitVal) { + return BindDeclInternal(store, VR, &InitVal); + } + + Store BindDeclWithNoInit(Store store, const VarRegion *VR) { + return BindDeclInternal(store, VR, 0); + } + + Store BindDeclInternal(Store store, const VarRegion *VR, SVal *InitVal); + + static inline BindingsTy GetBindings(Store store) { + return BindingsTy(static_cast(store)); + } + + void print(Store store, llvm::raw_ostream& Out, const char* nl, + const char *sep); + +private: + SVal LazyRetrieve(Store store, const TypedRegion *R); +}; + +} // end anonymous namespace + + +StoreManager* ento::CreateBasicStoreManager(GRStateManager& StMgr) { + return new BasicStoreManager(StMgr); +} + +static bool isHigherOrderRawPtr(QualType T, ASTContext &C) { + bool foundPointer = false; + while (1) { + const PointerType *PT = T->getAs(); + if (!PT) { + if (!foundPointer) + return false; + + // intptr_t* or intptr_t**, etc? + if (T->isIntegerType() && C.getTypeSize(T) == C.getTypeSize(C.VoidPtrTy)) + return true; + + QualType X = C.getCanonicalType(T).getUnqualifiedType(); + return X == C.VoidTy; + } + + foundPointer = true; + T = PT->getPointeeType(); + } +} + +SVal BasicStoreManager::LazyRetrieve(Store store, const TypedRegion *R) { + const VarRegion *VR = dyn_cast(R); + if (!VR) + return UnknownVal(); + + const VarDecl *VD = VR->getDecl(); + QualType T = VD->getType(); + + // Only handle simple types that we can symbolicate. + if (!SymbolManager::canSymbolicate(T) || !T->isScalarType()) + return UnknownVal(); + + // Globals and parameters start with symbolic values. + // Local variables initially are undefined. + + // Non-static globals may have had their values reset by InvalidateRegions. + const MemSpaceRegion *MS = VR->getMemorySpace(); + if (isa(MS)) { + BindingsTy B = GetBindings(store); + // FIXME: Copy-and-pasted from RegionStore.cpp. + if (BindingsTy::data_type *Val = B.lookup(MS)) { + if (SymbolRef parentSym = Val->getAsSymbol()) + return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); + + if (Val->isZeroConstant()) + return svalBuilder.makeZeroVal(T); + + if (Val->isUnknownOrUndef()) + return *Val; + + assert(0 && "Unknown default value."); + } + } + + if (VR->hasGlobalsOrParametersStorage() || + isa(VR->getMemorySpace())) + return svalBuilder.getRegionValueSymbolVal(R); + + return UndefinedVal(); +} + +SVal BasicStoreManager::Retrieve(Store store, Loc loc, QualType T) { + if (isa(loc)) + return UnknownVal(); + + assert(!isa(loc)); + + switch (loc.getSubKind()) { + + case loc::MemRegionKind: { + const MemRegion* R = cast(loc).getRegion(); + + if (!(isa(R) || isa(R) || + isa(R))) + return UnknownVal(); + + BindingsTy B = GetBindings(store); + BindingsTy::data_type *Val = B.lookup(R); + const TypedRegion *TR = cast(R); + + if (Val) + return CastRetrievedVal(*Val, TR, T); + + SVal V = LazyRetrieve(store, TR); + return V.isUnknownOrUndef() ? V : CastRetrievedVal(V, TR, T); + } + + case loc::ConcreteIntKind: + // Support direct accesses to memory. It's up to individual checkers + // to flag an error. + return UnknownVal(); + + default: + assert (false && "Invalid Loc."); + break; + } + + return UnknownVal(); +} + +Store BasicStoreManager::Bind(Store store, Loc loc, SVal V) { + if (isa(loc)) + return store; + + const MemRegion* R = cast(loc).getRegion(); + + // Special case: a default symbol assigned to the NonStaticGlobalsSpaceRegion + // that is used to derive other symbols. + if (isa(R)) { + BindingsTy B = GetBindings(store); + return VBFactory.add(B, R, V).getRoot(); + } + + // Special case: handle store of pointer values (Loc) to pointers via + // a cast to intXX_t*, void*, etc. This is needed to handle + // OSCompareAndSwap32Barrier/OSCompareAndSwap64Barrier. + if (isa(V) || isa(V)) + if (const ElementRegion *ER = dyn_cast(R)) { + // FIXME: Should check for index 0. + QualType T = ER->getLocationType(); + + if (isHigherOrderRawPtr(T, Ctx)) + R = ER->getSuperRegion(); + } + + if (!(isa(R) || isa(R) || isa(R))) + return store; + + const TypedRegion *TyR = cast(R); + + // Do not bind to arrays. We need to explicitly check for this so that + // we do not encounter any weirdness of trying to load/store from arrays. + if (TyR->isBoundable() && TyR->getValueType()->isArrayType()) + return store; + + if (nonloc::LocAsInteger *X = dyn_cast(&V)) { + // Only convert 'V' to a location iff the underlying region type + // is a location as well. + // FIXME: We are allowing a store of an arbitrary location to + // a pointer. We may wish to flag a type error here if the types + // are incompatible. This may also cause lots of breakage + // elsewhere. Food for thought. + if (TyR->isBoundable() && Loc::IsLocType(TyR->getValueType())) + V = X->getLoc(); + } + + BindingsTy B = GetBindings(store); + return V.isUnknown() + ? VBFactory.remove(B, R).getRoot() + : VBFactory.add(B, R, V).getRoot(); +} + +Store BasicStoreManager::Remove(Store store, Loc loc) { + switch (loc.getSubKind()) { + case loc::MemRegionKind: { + const MemRegion* R = cast(loc).getRegion(); + + if (!(isa(R) || isa(R) || + isa(R))) + return store; + + return VBFactory.remove(GetBindings(store), R).getRoot(); + } + default: + assert ("Remove for given Loc type not yet implemented."); + return store; + } +} + +Store BasicStoreManager::RemoveDeadBindings(Store store, + const StackFrameContext *LCtx, + SymbolReaper& SymReaper, + llvm::SmallVectorImpl& RegionRoots) +{ + BindingsTy B = GetBindings(store); + typedef SVal::symbol_iterator symbol_iterator; + + // Iterate over the variable bindings. + for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { + if (const VarRegion *VR = dyn_cast(I.getKey())) { + if (SymReaper.isLive(VR)) + RegionRoots.push_back(VR); + else + continue; + } + else if (isa(I.getKey()) || + isa(I.getKey()) || + isa(I.getKey())) + RegionRoots.push_back(I.getKey()); + else + continue; + + // Mark the bindings in the data as live. + SVal X = I.getData(); + for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) + SymReaper.markLive(*SI); + } + + // Scan for live variables and live symbols. + llvm::SmallPtrSet Marked; + + while (!RegionRoots.empty()) { + const MemRegion* MR = RegionRoots.back(); + RegionRoots.pop_back(); + + while (MR) { + if (const SymbolicRegion* SymR = dyn_cast(MR)) { + SymReaper.markLive(SymR->getSymbol()); + break; + } + else if (isa(MR) || isa(MR) || + isa(MR) || isa(MR)) { + if (Marked.count(MR)) + break; + + Marked.insert(MR); + SVal X = Retrieve(store, loc::MemRegionVal(MR)); + + // FIXME: We need to handle symbols nested in region definitions. + for (symbol_iterator SI=X.symbol_begin(),SE=X.symbol_end();SI!=SE;++SI) + SymReaper.markLive(*SI); + + if (!isa(X)) + break; + + const loc::MemRegionVal& LVD = cast(X); + RegionRoots.push_back(LVD.getRegion()); + break; + } + else if (const SubRegion* R = dyn_cast(MR)) + MR = R->getSuperRegion(); + else + break; + } + } + + // Remove dead variable bindings. + for (BindingsTy::iterator I=B.begin(), E=B.end(); I!=E ; ++I) { + const MemRegion* R = I.getKey(); + + if (!Marked.count(R)) { + store = Remove(store, svalBuilder.makeLoc(R)); + SVal X = I.getData(); + + for (symbol_iterator SI=X.symbol_begin(), SE=X.symbol_end(); SI!=SE; ++SI) + SymReaper.maybeDead(*SI); + } + } + + return store; +} + +Store BasicStoreManager::scanForIvars(Stmt *B, const Decl* SelfDecl, + const MemRegion *SelfRegion, Store St) { + for (Stmt::child_iterator CI=B->child_begin(), CE=B->child_end(); + CI != CE; ++CI) { + + if (!*CI) + continue; + + // Check if the statement is an ivar reference. We only + // care about self.ivar. + if (ObjCIvarRefExpr *IV = dyn_cast(*CI)) { + const Expr *Base = IV->getBase()->IgnoreParenCasts(); + if (const DeclRefExpr *DR = dyn_cast(Base)) { + if (DR->getDecl() == SelfDecl) { + const ObjCIvarRegion *IVR = MRMgr.getObjCIvarRegion(IV->getDecl(), + SelfRegion); + SVal X = svalBuilder.getRegionValueSymbolVal(IVR); + St = Bind(St, svalBuilder.makeLoc(IVR), X); + } + } + } + else + St = scanForIvars(*CI, SelfDecl, SelfRegion, St); + } + + return St; +} + +Store BasicStoreManager::getInitialStore(const LocationContext *InitLoc) { + // The LiveVariables information already has a compilation of all VarDecls + // used in the function. Iterate through this set, and "symbolicate" + // any VarDecl whose value originally comes from outside the function. + typedef LiveVariables::AnalysisDataTy LVDataTy; + LVDataTy& D = InitLoc->getLiveVariables()->getAnalysisData(); + Store St = VBFactory.getEmptyMap().getRoot(); + + for (LVDataTy::decl_iterator I=D.begin_decl(), E=D.end_decl(); I != E; ++I) { + const NamedDecl* ND = I->first; + + // Handle implicit parameters. + if (const ImplicitParamDecl* PD = dyn_cast(ND)) { + const Decl& CD = *InitLoc->getDecl(); + if (const ObjCMethodDecl* MD = dyn_cast(&CD)) { + if (MD->getSelfDecl() == PD) { + // FIXME: Add type constraints (when they become available) to + // SelfRegion? (i.e., it implements MD->getClassInterface()). + const VarRegion *VR = MRMgr.getVarRegion(PD, InitLoc); + const MemRegion *SelfRegion = + svalBuilder.getRegionValueSymbolVal(VR).getAsRegion(); + assert(SelfRegion); + St = Bind(St, svalBuilder.makeLoc(VR), loc::MemRegionVal(SelfRegion)); + // Scan the method for ivar references. While this requires an + // entire AST scan, the cost should not be high in practice. + St = scanForIvars(MD->getBody(), PD, SelfRegion, St); + } + } + } + } + + if (const CXXMethodDecl *MD = dyn_cast(InitLoc->getDecl())) { + // For C++ methods add symbolic region for 'this' in initial stack frame. + QualType ThisT = MD->getThisType(StateMgr.getContext()); + MemRegionManager &RegMgr = svalBuilder.getRegionManager(); + const CXXThisRegion *ThisR = RegMgr.getCXXThisRegion(ThisT, InitLoc); + SVal ThisV = svalBuilder.getRegionValueSymbolVal(ThisR); + St = Bind(St, svalBuilder.makeLoc(ThisR), ThisV); + } + + return St; +} + +Store BasicStoreManager::BindDeclInternal(Store store, const VarRegion* VR, + SVal* InitVal) { + + BasicValueFactory& BasicVals = StateMgr.getBasicVals(); + const VarDecl *VD = VR->getDecl(); + + // BasicStore does not model arrays and structs. + if (VD->getType()->isArrayType() || VD->getType()->isStructureOrClassType()) + return store; + + if (VD->hasGlobalStorage()) { + // Handle variables with global storage: extern, static, PrivateExtern. + + // FIXME:: static variables may have an initializer, but the second time a + // function is called those values may not be current. Currently, a function + // will not be called more than once. + + // Static global variables should not be visited here. + assert(!(VD->getStorageClass() == SC_Static && + VD->isFileVarDecl())); + + // Process static variables. + if (VD->getStorageClass() == SC_Static) { + // C99: 6.7.8 Initialization + // If an object that has static storage duration is not initialized + // explicitly, then: + // —if it has pointer type, it is initialized to a null pointer; + // —if it has arithmetic type, it is initialized to (positive or + // unsigned) zero; + if (!InitVal) { + QualType T = VD->getType(); + if (Loc::IsLocType(T)) + store = Bind(store, loc::MemRegionVal(VR), + loc::ConcreteInt(BasicVals.getValue(0, T))); + else if (T->isIntegerType() && T->isScalarType()) + store = Bind(store, loc::MemRegionVal(VR), + nonloc::ConcreteInt(BasicVals.getValue(0, T))); + } else { + store = Bind(store, loc::MemRegionVal(VR), *InitVal); + } + } + } else { + // Process local scalar variables. + QualType T = VD->getType(); + // BasicStore only supports scalars. + if ((T->isScalarType() || T->isReferenceType()) && + svalBuilder.getSymbolManager().canSymbolicate(T)) { + SVal V = InitVal ? *InitVal : UndefinedVal(); + store = Bind(store, loc::MemRegionVal(VR), V); + } + } + + return store; +} + +void BasicStoreManager::print(Store store, llvm::raw_ostream& Out, + const char* nl, const char *sep) { + + BindingsTy B = GetBindings(store); + Out << "Variables:" << nl; + + bool isFirst = true; + + for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) { + if (isFirst) + isFirst = false; + else + Out << nl; + + Out << ' ' << I.getKey() << " : " << I.getData(); + } +} + + +void BasicStoreManager::iterBindings(Store store, BindingsHandler& f) { + BindingsTy B = GetBindings(store); + + for (BindingsTy::iterator I=B.begin(), E=B.end(); I != E; ++I) + if (!f.HandleBinding(*this, store, I.getKey(), I.getData())) + return; + +} + +StoreManager::BindingsHandler::~BindingsHandler() {} + +//===----------------------------------------------------------------------===// +// Binding invalidation. +//===----------------------------------------------------------------------===// + + +Store BasicStoreManager::InvalidateRegions(Store store, + const MemRegion * const *I, + const MemRegion * const *End, + const Expr *E, unsigned Count, + InvalidatedSymbols *IS, + bool invalidateGlobals, + InvalidatedRegions *Regions) { + if (invalidateGlobals) { + BindingsTy B = GetBindings(store); + for (BindingsTy::iterator I=B.begin(), End=B.end(); I != End; ++I) { + const MemRegion *R = I.getKey(); + if (isa(R->getMemorySpace())) + store = InvalidateRegion(store, R, E, Count, IS); + } + } + + for ( ; I != End ; ++I) { + const MemRegion *R = *I; + // Don't invalidate globals twice. + if (invalidateGlobals) { + if (isa(R->getMemorySpace())) + continue; + } + store = InvalidateRegion(store, *I, E, Count, IS); + if (Regions) + Regions->push_back(R); + } + + // FIXME: This is copy-and-paste from RegionStore.cpp. + if (invalidateGlobals) { + // Bind the non-static globals memory space to a new symbol that we will + // use to derive the bindings for all non-static globals. + const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(); + SVal V = + svalBuilder.getConjuredSymbolVal(/* SymbolTag = */ (void*) GS, E, + /* symbol type, doesn't matter */ Ctx.IntTy, + Count); + + store = Bind(store, loc::MemRegionVal(GS), V); + if (Regions) + Regions->push_back(GS); + } + + return store; +} + + +Store BasicStoreManager::InvalidateRegion(Store store, + const MemRegion *R, + const Expr *E, + unsigned Count, + InvalidatedSymbols *IS) { + R = R->StripCasts(); + + if (!(isa(R) || isa(R))) + return store; + + if (IS) { + BindingsTy B = GetBindings(store); + if (BindingsTy::data_type *Val = B.lookup(R)) { + if (SymbolRef Sym = Val->getAsSymbol()) + IS->insert(Sym); + } + } + + QualType T = cast(R)->getValueType(); + SVal V = svalBuilder.getConjuredSymbolVal(R, E, T, Count); + return Bind(store, loc::MemRegionVal(R), V); +} + diff --git a/lib/StaticAnalyzer/EntoSA/BasicValueFactory.cpp b/lib/StaticAnalyzer/EntoSA/BasicValueFactory.cpp new file mode 100644 index 0000000000..d8b501bc7c --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/BasicValueFactory.cpp @@ -0,0 +1,290 @@ +//=== BasicValueFactory.cpp - Basic values for Path Sens analysis --*- 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 BasicValueFactory, a class that manages the lifetime +// of APSInt objects and symbolic constraints used by ExprEngine +// and related classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathSensitive/BasicValueFactory.h" + +using namespace clang; +using namespace ento; + +void CompoundValData::Profile(llvm::FoldingSetNodeID& ID, QualType T, + llvm::ImmutableList L) { + T.Profile(ID); + ID.AddPointer(L.getInternalPointer()); +} + +void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID, + const void *store,const TypedRegion *region) { + ID.AddPointer(store); + ID.AddPointer(region); +} + +typedef std::pair SValData; +typedef std::pair SValPair; + +namespace llvm { +template<> struct FoldingSetTrait { + static inline void Profile(const SValData& X, llvm::FoldingSetNodeID& ID) { + X.first.Profile(ID); + ID.AddPointer( (void*) X.second); + } +}; + +template<> struct FoldingSetTrait { + static inline void Profile(const SValPair& X, llvm::FoldingSetNodeID& ID) { + X.first.Profile(ID); + X.second.Profile(ID); + } +}; +} + +typedef llvm::FoldingSet > + PersistentSValsTy; + +typedef llvm::FoldingSet > + PersistentSValPairsTy; + +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 (APSIntSetTy::iterator I=APSIntSet.begin(), E=APSIntSet.end(); I!=E; ++I) + I->getValue().~APSInt(); + + delete (PersistentSValsTy*) PersistentSVals; + delete (PersistentSValPairsTy*) PersistentSValPairs; +} + +const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) { + llvm::FoldingSetNodeID ID; + void* InsertPos; + typedef llvm::FoldingSetNodeWrapper FoldNodeTy; + + X.Profile(ID); + FoldNodeTy* P = APSIntSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!P) { + P = (FoldNodeTy*) BPAlloc.Allocate(); + 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) { + + unsigned bits = Ctx.getTypeSize(T); + llvm::APSInt V(bits, T->isUnsignedIntegerType() || Loc::IsLocType(T)); + V = X; + return getValue(V); +} + +const CompoundValData* +BasicValueFactory::getCompoundValData(QualType T, + llvm::ImmutableList Vals) { + + llvm::FoldingSetNodeID ID; + CompoundValData::Profile(ID, T, Vals); + void* InsertPos; + + CompoundValData* D = CompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!D) { + D = (CompoundValData*) BPAlloc.Allocate(); + new (D) CompoundValData(T, Vals); + CompoundValDataSet.InsertNode(D, InsertPos); + } + + return D; +} + +const LazyCompoundValData* +BasicValueFactory::getLazyCompoundValData(const void *store, + const TypedRegion *region) { + llvm::FoldingSetNodeID ID; + LazyCompoundValData::Profile(ID, store, region); + void* InsertPos; + + LazyCompoundValData *D = + LazyCompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos); + + if (!D) { + D = (LazyCompoundValData*) BPAlloc.Allocate(); + new (D) LazyCompoundValData(store, region); + LazyCompoundValDataSet.InsertNode(D, InsertPos); + } + + return D; +} + +const llvm::APSInt* +BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op, + const llvm::APSInt& V1, const llvm::APSInt& V2) { + + switch (Op) { + default: + assert (false && "Invalid Opcode."); + + case BO_Mul: + return &getValue( V1 * V2 ); + + case BO_Div: + return &getValue( V1 / V2 ); + + case BO_Rem: + 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. + + // FIXME: Expand these checks to include all undefined behavior. + + if (V2.isSigned() && V2.isNegative()) + return NULL; + + uint64_t Amt = V2.getZExtValue(); + + if (Amt > V1.getBitWidth()) + return NULL; + + return &getValue( V1.operator<<( (unsigned) Amt )); + } + + case BO_Shr: { + + // FIXME: This logic should probably go higher up, where we can + // test these conditions symbolically. + + // FIXME: Expand these checks to include all undefined behavior. + + if (V2.isSigned() && V2.isNegative()) + return NULL; + + uint64_t Amt = V2.getZExtValue(); + + if (Amt > V1.getBitWidth()) + return NULL; + + 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& +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); + + typedef llvm::FoldingSetNodeWrapper FoldNodeTy; + FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); + + if (!P) { + P = (FoldNodeTy*) BPAlloc.Allocate(); + new (P) FoldNodeTy(std::make_pair(V, Data)); + Map.InsertNode(P, InsertPos); + } + + return P->getValue(); +} + +const std::pair& +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); + + typedef llvm::FoldingSetNodeWrapper FoldNodeTy; + FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos); + + if (!P) { + P = (FoldNodeTy*) BPAlloc.Allocate(); + 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/lib/StaticAnalyzer/EntoSA/BlockCounter.cpp b/lib/StaticAnalyzer/EntoSA/BlockCounter.cpp new file mode 100644 index 0000000000..47f6088a47 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/BlockCounter.cpp @@ -0,0 +1,86 @@ +//==- 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/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 (CallSite == RHS.CallSite) ? (BlockID < RHS.BlockID) + : (CallSite < RHS.CallSite); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(CallSite); + ID.AddInteger(BlockID); + } +}; + +} + +typedef llvm::ImmutableMap CountMap; + +static inline CountMap GetMap(void* D) { + return CountMap(static_cast(D)); +} + +static inline CountMap::Factory& GetFactory(void* F) { + return *static_cast(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(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/lib/StaticAnalyzer/EntoSA/BugReporter.cpp b/lib/StaticAnalyzer/EntoSA/BugReporter.cpp new file mode 100644 index 0000000000..8edbe14878 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/BugReporter.cpp @@ -0,0 +1,1892 @@ +// BugReporter.cpp - Generate PathDiagnostics for Bugs ------------*- 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 BugReporter, a utility class for generating +// PathDiagnostics. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/AST/ASTContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/StmtObjC.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Analysis/ProgramPoint.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/OwningPtr.h" +#include + +using namespace clang; +using namespace ento; + +BugReporterVisitor::~BugReporterVisitor() {} +BugReporterContext::~BugReporterContext() { + for (visitor_iterator I = visitor_begin(), E = visitor_end(); I != E; ++I) + if ((*I)->isOwnedByReporterContext()) delete *I; +} + +void BugReporterContext::addVisitor(BugReporterVisitor* visitor) { + if (!visitor) + return; + + llvm::FoldingSetNodeID ID; + visitor->Profile(ID); + void *InsertPos; + + if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) { + delete visitor; + return; + } + + CallbacksSet.InsertNode(visitor, InsertPos); + Callbacks = F.add(visitor, Callbacks); +} + +//===----------------------------------------------------------------------===// +// Helper routines for walking the ExplodedGraph and fetching statements. +//===----------------------------------------------------------------------===// + +static inline const Stmt* GetStmt(const ProgramPoint &P) { + if (const StmtPoint* SP = dyn_cast(&P)) + return SP->getStmt(); + else if (const BlockEdge* BE = dyn_cast(&P)) + return BE->getSrc()->getTerminator(); + + return 0; +} + +static inline const ExplodedNode* +GetPredecessorNode(const ExplodedNode* N) { + return N->pred_empty() ? NULL : *(N->pred_begin()); +} + +static inline const ExplodedNode* +GetSuccessorNode(const ExplodedNode* N) { + return N->succ_empty() ? NULL : *(N->succ_begin()); +} + +static const Stmt* GetPreviousStmt(const ExplodedNode* N) { + for (N = GetPredecessorNode(N); N; N = GetPredecessorNode(N)) + if (const Stmt *S = GetStmt(N->getLocation())) + return S; + + return 0; +} + +static const Stmt* GetNextStmt(const ExplodedNode* N) { + for (N = GetSuccessorNode(N); N; N = GetSuccessorNode(N)) + if (const Stmt *S = GetStmt(N->getLocation())) { + // Check if the statement is '?' or '&&'/'||'. These are "merges", + // not actual statement points. + switch (S->getStmtClass()) { + case Stmt::ChooseExprClass: + case Stmt::ConditionalOperatorClass: continue; + case Stmt::BinaryOperatorClass: { + BinaryOperatorKind Op = cast(S)->getOpcode(); + if (Op == BO_LAnd || Op == BO_LOr) + continue; + break; + } + default: + break; + } + + // Some expressions don't have locations. + if (S->getLocStart().isInvalid()) + continue; + + return S; + } + + return 0; +} + +static inline const Stmt* +GetCurrentOrPreviousStmt(const ExplodedNode* N) { + if (const Stmt *S = GetStmt(N->getLocation())) + return S; + + return GetPreviousStmt(N); +} + +static inline const Stmt* +GetCurrentOrNextStmt(const ExplodedNode* N) { + if (const Stmt *S = GetStmt(N->getLocation())) + return S; + + return GetNextStmt(N); +} + +//===----------------------------------------------------------------------===// +// PathDiagnosticBuilder and its associated routines and helper objects. +//===----------------------------------------------------------------------===// + +typedef llvm::DenseMap NodeBackMap; + +namespace { +class NodeMapClosure : public BugReport::NodeResolver { + NodeBackMap& M; +public: + NodeMapClosure(NodeBackMap *m) : M(*m) {} + ~NodeMapClosure() {} + + const ExplodedNode* getOriginalNode(const ExplodedNode* N) { + NodeBackMap::iterator I = M.find(N); + return I == M.end() ? 0 : I->second; + } +}; + +class PathDiagnosticBuilder : public BugReporterContext { + BugReport *R; + PathDiagnosticClient *PDC; + llvm::OwningPtr PM; + NodeMapClosure NMC; +public: + PathDiagnosticBuilder(GRBugReporter &br, + BugReport *r, NodeBackMap *Backmap, + PathDiagnosticClient *pdc) + : BugReporterContext(br), + R(r), PDC(pdc), NMC(Backmap) { + addVisitor(R); + } + + PathDiagnosticLocation ExecutionContinues(const ExplodedNode* N); + + PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream& os, + const ExplodedNode* N); + + Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); } + + ParentMap& getParentMap() { return R->getErrorNode()->getParentMap(); } + + const Stmt *getParent(const Stmt *S) { + return getParentMap().getParent(S); + } + + virtual NodeMapClosure& getNodeResolver() { return NMC; } + + PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S); + + PathDiagnosticClient::PathGenerationScheme getGenerationScheme() const { + return PDC ? PDC->getGenerationScheme() : PathDiagnosticClient::Extensive; + } + + bool supportsLogicalOpControlFlow() const { + return PDC ? PDC->supportsLogicalOpControlFlow() : true; + } +}; +} // end anonymous namespace + +PathDiagnosticLocation +PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode* N) { + if (const Stmt *S = GetNextStmt(N)) + return PathDiagnosticLocation(S, getSourceManager()); + + return FullSourceLoc(N->getLocationContext()->getDecl()->getBodyRBrace(), + 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().getInstantiationLineNumber(Loc.asLocation()) + << '.'; + else { + os << "Execution jumps to the end of the "; + const Decl *D = N->getLocationContext()->getDecl(); + if (isa(D)) + os << "method"; + else if (isa(D)) + os << "function"; + else { + assert(isa(D)); + os << "anonymous block"; + } + os << '.'; + } + + return Loc; +} + +static bool IsNested(const Stmt *S, ParentMap &PM) { + if (isa(S) && PM.isConsumedExpr(cast(S))) + return true; + + const Stmt *Parent = PM.getParentIgnoreParens(S); + + if (Parent) + switch (Parent->getStmtClass()) { + case Stmt::ForStmtClass: + case Stmt::DoStmtClass: + case Stmt::WhileStmtClass: + return true; + default: + break; + } + + return false; +} + +PathDiagnosticLocation +PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) { + assert(S && "Null Stmt* passed to getEnclosingStmtLocation"); + ParentMap &P = getParentMap(); + SourceManager &SMgr = getSourceManager(); + + while (IsNested(S, P)) { + const Stmt *Parent = P.getParentIgnoreParens(S); + + if (!Parent) + break; + + switch (Parent->getStmtClass()) { + case Stmt::BinaryOperatorClass: { + const BinaryOperator *B = cast(Parent); + if (B->isLogicalOp()) + return PathDiagnosticLocation(S, SMgr); + break; + } + case Stmt::CompoundStmtClass: + case Stmt::StmtExprClass: + return PathDiagnosticLocation(S, SMgr); + case Stmt::ChooseExprClass: + // Similar to '?' if we are referring to condition, just have the edge + // point to the entire choose expression. + if (cast(Parent)->getCond() == S) + return PathDiagnosticLocation(Parent, SMgr); + else + return PathDiagnosticLocation(S, SMgr); + case Stmt::ConditionalOperatorClass: + // For '?', if we are referring to condition, just have the edge point + // to the entire '?' expression. + if (cast(Parent)->getCond() == S) + return PathDiagnosticLocation(Parent, SMgr); + else + return PathDiagnosticLocation(S, SMgr); + case Stmt::DoStmtClass: + return PathDiagnosticLocation(S, SMgr); + case Stmt::ForStmtClass: + if (cast(Parent)->getBody() == S) + return PathDiagnosticLocation(S, SMgr); + break; + case Stmt::IfStmtClass: + if (cast(Parent)->getCond() != S) + return PathDiagnosticLocation(S, SMgr); + break; + case Stmt::ObjCForCollectionStmtClass: + if (cast(Parent)->getBody() == S) + return PathDiagnosticLocation(S, SMgr); + break; + case Stmt::WhileStmtClass: + if (cast(Parent)->getCond() != S) + return PathDiagnosticLocation(S, SMgr); + break; + default: + break; + } + + S = Parent; + } + + assert(S && "Cannot have null Stmt for PathDiagnosticLocation"); + + // Special case: DeclStmts can appear in for statement declarations, in which + // case the ForStmt is the context. + if (isa(S)) { + if (const Stmt *Parent = P.getParent(S)) { + switch (Parent->getStmtClass()) { + case Stmt::ForStmtClass: + case Stmt::ObjCForCollectionStmtClass: + return PathDiagnosticLocation(Parent, SMgr); + default: + break; + } + } + } + else if (isa(S)) { + // Special case: the binary operator represents the initialization + // code in a for statement (this can happen when the variable being + // initialized is an old variable. + if (const ForStmt *FS = + dyn_cast_or_null(P.getParentIgnoreParens(S))) { + if (FS->getInit() == S) + return PathDiagnosticLocation(FS, SMgr); + } + } + + return PathDiagnosticLocation(S, SMgr); +} + +//===----------------------------------------------------------------------===// +// ScanNotableSymbols: closure-like callback for scanning Store bindings. +//===----------------------------------------------------------------------===// + +static const VarDecl* +GetMostRecentVarDeclBinding(const ExplodedNode* N, + GRStateManager& VMgr, SVal X) { + + for ( ; N ; N = N->pred_empty() ? 0 : *N->pred_begin()) { + + ProgramPoint P = N->getLocation(); + + if (!isa(P)) + continue; + + const DeclRefExpr* DR = dyn_cast(cast(P).getStmt()); + + if (!DR) + continue; + + SVal Y = N->getState()->getSVal(DR); + + if (X != Y) + continue; + + const VarDecl* VD = dyn_cast(DR->getDecl()); + + if (!VD) + continue; + + return VD; + } + + return 0; +} + +namespace { +class NotableSymbolHandler +: public StoreManager::BindingsHandler { + + SymbolRef Sym; + const GRState* PrevSt; + const Stmt* S; + GRStateManager& VMgr; + const ExplodedNode* Pred; + PathDiagnostic& PD; + BugReporter& BR; + +public: + + NotableSymbolHandler(SymbolRef sym, const GRState* prevst, const Stmt* s, + GRStateManager& vmgr, const ExplodedNode* pred, + PathDiagnostic& pd, BugReporter& br) + : Sym(sym), PrevSt(prevst), S(s), VMgr(vmgr), Pred(pred), PD(pd), BR(br) {} + + bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, + SVal V) { + + SymbolRef ScanSym = V.getAsSymbol(); + + if (ScanSym != Sym) + return true; + + // Check if the previous state has this binding. + SVal X = PrevSt->getSVal(loc::MemRegionVal(R)); + + if (X == V) // Same binding? + return true; + + // Different binding. Only handle assignments for now. We don't pull + // this check out of the loop because we will eventually handle other + // cases. + + VarDecl *VD = 0; + + if (const BinaryOperator* B = dyn_cast(S)) { + if (!B->isAssignmentOp()) + return true; + + // What variable did we assign to? + DeclRefExpr* DR = dyn_cast(B->getLHS()->IgnoreParenCasts()); + + if (!DR) + return true; + + VD = dyn_cast(DR->getDecl()); + } + else if (const DeclStmt* DS = dyn_cast(S)) { + // FIXME: Eventually CFGs won't have DeclStmts. Right now we + // assume that each DeclStmt has a single Decl. This invariant + // holds by contruction in the CFG. + VD = dyn_cast(*DS->decl_begin()); + } + + if (!VD) + return true; + + // What is the most recently referenced variable with this binding? + const VarDecl* MostRecent = GetMostRecentVarDeclBinding(Pred, VMgr, V); + + if (!MostRecent) + return true; + + // Create the diagnostic. + FullSourceLoc L(S->getLocStart(), BR.getSourceManager()); + + if (Loc::IsLocType(VD->getType())) { + std::string msg = "'" + std::string(VD->getNameAsString()) + + "' now aliases '" + MostRecent->getNameAsString() + "'"; + + PD.push_front(new PathDiagnosticEventPiece(L, msg)); + } + + return true; + } +}; +} + +static void HandleNotableSymbol(const ExplodedNode* N, + const Stmt* S, + SymbolRef Sym, BugReporter& BR, + PathDiagnostic& PD) { + + const ExplodedNode* Pred = N->pred_empty() ? 0 : *N->pred_begin(); + const GRState* PrevSt = Pred ? Pred->getState() : 0; + + if (!PrevSt) + return; + + // Look at the region bindings of the current state that map to the + // specified symbol. Are any of them not in the previous state? + GRStateManager& VMgr = cast(BR).getStateManager(); + NotableSymbolHandler H(Sym, PrevSt, S, VMgr, Pred, PD, BR); + cast(BR).getStateManager().iterBindings(N->getState(), H); +} + +namespace { +class ScanNotableSymbols +: public StoreManager::BindingsHandler { + + llvm::SmallSet AlreadyProcessed; + const ExplodedNode* N; + const Stmt* S; + GRBugReporter& BR; + PathDiagnostic& PD; + +public: + ScanNotableSymbols(const ExplodedNode* n, const Stmt* s, + GRBugReporter& br, PathDiagnostic& pd) + : N(n), S(s), BR(br), PD(pd) {} + + bool HandleBinding(StoreManager& SMgr, Store store, + const MemRegion* R, SVal V) { + + SymbolRef ScanSym = V.getAsSymbol(); + + if (!ScanSym) + return true; + + if (!BR.isNotable(ScanSym)) + return true; + + if (AlreadyProcessed.count(ScanSym)) + return true; + + AlreadyProcessed.insert(ScanSym); + + HandleNotableSymbol(N, S, ScanSym, BR, PD); + return true; + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// "Minimal" path diagnostic generation algorithm. +//===----------------------------------------------------------------------===// + +static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM); + +static void GenerateMinimalPathDiagnostic(PathDiagnostic& PD, + PathDiagnosticBuilder &PDB, + const ExplodedNode *N) { + + SourceManager& SMgr = PDB.getSourceManager(); + const ExplodedNode* NextNode = N->pred_empty() + ? NULL : *(N->pred_begin()); + while (NextNode) { + N = NextNode; + NextNode = GetPredecessorNode(N); + + ProgramPoint P = N->getLocation(); + + if (const BlockEdge* BE = dyn_cast(&P)) { + const CFGBlock* Src = BE->getSrc(); + const CFGBlock* Dst = BE->getDst(); + const Stmt* T = Src->getTerminator(); + + if (!T) + continue; + + FullSourceLoc Start(T->getLocStart(), SMgr); + + switch (T->getStmtClass()) { + default: + break; + + case Stmt::GotoStmtClass: + case Stmt::IndirectGotoStmtClass: { + const Stmt* S = GetNextStmt(N); + + if (!S) + continue; + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S); + + os << "Control jumps to line " + << End.asLocation().getInstantiationLineNumber(); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + break; + } + + case Stmt::SwitchStmtClass: { + // Figure out what case arm we took. + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + if (const Stmt* S = Dst->getLabel()) { + PathDiagnosticLocation End(S, SMgr); + + switch (S->getStmtClass()) { + default: + os << "No cases match in the switch statement. " + "Control jumps to line " + << End.asLocation().getInstantiationLineNumber(); + break; + case Stmt::DefaultStmtClass: + os << "Control jumps to the 'default' case at line " + << End.asLocation().getInstantiationLineNumber(); + break; + + case Stmt::CaseStmtClass: { + os << "Control jumps to 'case "; + const CaseStmt* Case = cast(S); + const Expr* LHS = Case->getLHS()->IgnoreParenCasts(); + + // Determine if it is an enum. + bool GetRawInt = true; + + if (const DeclRefExpr* DR = dyn_cast(LHS)) { + // FIXME: Maybe this should be an assertion. Are there cases + // were it is not an EnumConstantDecl? + const EnumConstantDecl* D = + dyn_cast(DR->getDecl()); + + if (D) { + GetRawInt = false; + os << D; + } + } + + if (GetRawInt) + os << LHS->EvaluateAsInt(PDB.getASTContext()); + + os << ":' at line " + << End.asLocation().getInstantiationLineNumber(); + break; + } + } + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + else { + os << "'Default' branch taken. "; + const PathDiagnosticLocation &End = PDB.ExecutionContinues(os, N); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + + break; + } + + case Stmt::BreakStmtClass: + case Stmt::ContinueStmtClass: { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + PathDiagnosticLocation End = PDB.ExecutionContinues(os, N); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + break; + } + + // Determine control-flow for ternary '?'. + 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.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + break; + } + + // Determine control-flow for short-circuited '&&' and '||'. + case Stmt::BinaryOperatorClass: { + if (!PDB.supportsLogicalOpControlFlow()) + break; + + const BinaryOperator *B = cast(T); + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Left side of '"; + + if (B->getOpcode() == BO_LAnd) { + os << "&&" << "' is "; + + if (*(Src->succ_begin()+1) == Dst) { + os << "false"; + PathDiagnosticLocation End(B->getLHS(), SMgr); + PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + else { + os << "true"; + PathDiagnosticLocation Start(B->getLHS(), SMgr); + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + } + else { + assert(B->getOpcode() == BO_LOr); + os << "||" << "' is "; + + if (*(Src->succ_begin()+1) == Dst) { + os << "false"; + PathDiagnosticLocation Start(B->getLHS(), SMgr); + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + else { + os << "true"; + PathDiagnosticLocation End(B->getLHS(), SMgr); + PathDiagnosticLocation Start(B->getOperatorLoc(), SMgr); + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + } + + 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.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + else { + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.push_front(new 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.push_front(new PathDiagnosticControlFlowPiece(Start, End, + os.str())); + } + else { + PathDiagnosticLocation End = PDB.ExecutionContinues(N); + if (const Stmt *S = End.asStmt()) + End = PDB.getEnclosingStmtLocation(S); + + PD.push_front(new 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.push_front(new PathDiagnosticControlFlowPiece(Start, End, + "Taking false branch")); + else + PD.push_front(new PathDiagnosticControlFlowPiece(Start, End, + "Taking true branch")); + + break; + } + } + } + + if (NextNode) { + for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), + E = PDB.visitor_end(); I!=E; ++I) { + if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) + PD.push_front(p); + } + } + + if (const PostStmt* PS = dyn_cast(&P)) { + // Scan the region bindings, and see if a "notable" symbol has a new + // lval binding. + ScanNotableSymbols SNS(N, PS->getStmt(), PDB.getBugReporter(), PD); + PDB.getStateManager().iterBindings(N->getState(), SNS); + } + } + + // After constructing the full PathDiagnostic, do a pass over it to compact + // PathDiagnosticPieces that occur within a macro. + CompactPathDiagnostic(PD, PDB.getSourceManager()); +} + +//===----------------------------------------------------------------------===// +// "Extensive" PathDiagnostic generation. +//===----------------------------------------------------------------------===// + +static bool IsControlFlowExpr(const Stmt *S) { + const Expr *E = dyn_cast(S); + + if (!E) + return false; + + E = E->IgnoreParenCasts(); + + if (isa(E)) + return true; + + if (const BinaryOperator *B = dyn_cast(E)) + if (B->isLogicalOp()) + return true; + + return false; +} + +namespace { +class ContextLocation : public PathDiagnosticLocation { + bool IsDead; +public: + ContextLocation(const PathDiagnosticLocation &L, bool isdead = false) + : PathDiagnosticLocation(L), IsDead(isdead) {} + + void markDead() { IsDead = true; } + bool isDead() const { return IsDead; } +}; + +class EdgeBuilder { + std::vector CLocs; + typedef std::vector::iterator iterator; + PathDiagnostic &PD; + PathDiagnosticBuilder &PDB; + PathDiagnosticLocation PrevLoc; + + bool IsConsumedExpr(const PathDiagnosticLocation &L); + + bool containsLocation(const PathDiagnosticLocation &Container, + const PathDiagnosticLocation &Containee); + + PathDiagnosticLocation getContextLocation(const PathDiagnosticLocation &L); + + PathDiagnosticLocation cleanUpLocation(PathDiagnosticLocation L, + bool firstCharOnly = false) { + if (const Stmt *S = L.asStmt()) { + const Stmt *Original = S; + while (1) { + // Adjust the location for some expressions that are best referenced + // by one of their subexpressions. + switch (S->getStmtClass()) { + default: + break; + case Stmt::ParenExprClass: + S = cast(S)->IgnoreParens(); + firstCharOnly = true; + continue; + case Stmt::ConditionalOperatorClass: + S = cast(S)->getCond(); + firstCharOnly = true; + continue; + case Stmt::ChooseExprClass: + S = cast(S)->getCond(); + firstCharOnly = true; + continue; + case Stmt::BinaryOperatorClass: + S = cast(S)->getLHS(); + firstCharOnly = true; + continue; + } + + break; + } + + if (S != Original) + L = PathDiagnosticLocation(S, L.getManager()); + } + + if (firstCharOnly) + L = PathDiagnosticLocation(L.asLocation()); + + return L; + } + + void popLocation() { + if (!CLocs.back().isDead() && CLocs.back().asLocation().isFileID()) { + // For contexts, we only one the first character as the range. + rawAddEdge(cleanUpLocation(CLocs.back(), true)); + } + CLocs.pop_back(); + } + +public: + EdgeBuilder(PathDiagnostic &pd, PathDiagnosticBuilder &pdb) + : PD(pd), PDB(pdb) { + + // If the PathDiagnostic already has pieces, add the enclosing statement + // of the first piece as a context as well. + if (!PD.empty()) { + PrevLoc = PD.begin()->getLocation(); + + if (const Stmt *S = PrevLoc.asStmt()) + addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); + } + } + + ~EdgeBuilder() { + while (!CLocs.empty()) popLocation(); + + // Finally, add an initial edge from the start location of the first + // statement (if it doesn't already exist). + // FIXME: Should handle CXXTryStmt if analyser starts supporting C++. + if (const CompoundStmt *CS = + dyn_cast_or_null(PDB.getCodeDecl().getBody())) + if (!CS->body_empty()) { + SourceLocation Loc = (*CS->body_begin())->getLocStart(); + rawAddEdge(PathDiagnosticLocation(Loc, PDB.getSourceManager())); + } + + } + + void addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd = false); + + void rawAddEdge(PathDiagnosticLocation NewLoc); + + void addContext(const Stmt *S); + void addExtendedContext(const Stmt *S); +}; +} // end anonymous namespace + + +PathDiagnosticLocation +EdgeBuilder::getContextLocation(const PathDiagnosticLocation &L) { + if (const Stmt *S = L.asStmt()) { + if (IsControlFlowExpr(S)) + return L; + + return PDB.getEnclosingStmtLocation(S); + } + + return L; +} + +bool EdgeBuilder::containsLocation(const PathDiagnosticLocation &Container, + const PathDiagnosticLocation &Containee) { + + if (Container == Containee) + return true; + + if (Container.asDecl()) + return true; + + if (const Stmt *S = Containee.asStmt()) + if (const Stmt *ContainerS = Container.asStmt()) { + while (S) { + if (S == ContainerS) + return true; + S = PDB.getParent(S); + } + return false; + } + + // Less accurate: compare using source ranges. + SourceRange ContainerR = Container.asRange(); + SourceRange ContaineeR = Containee.asRange(); + + SourceManager &SM = PDB.getSourceManager(); + SourceLocation ContainerRBeg = SM.getInstantiationLoc(ContainerR.getBegin()); + SourceLocation ContainerREnd = SM.getInstantiationLoc(ContainerR.getEnd()); + SourceLocation ContaineeRBeg = SM.getInstantiationLoc(ContaineeR.getBegin()); + SourceLocation ContaineeREnd = SM.getInstantiationLoc(ContaineeR.getEnd()); + + unsigned ContainerBegLine = SM.getInstantiationLineNumber(ContainerRBeg); + unsigned ContainerEndLine = SM.getInstantiationLineNumber(ContainerREnd); + unsigned ContaineeBegLine = SM.getInstantiationLineNumber(ContaineeRBeg); + unsigned ContaineeEndLine = SM.getInstantiationLineNumber(ContaineeREnd); + + assert(ContainerBegLine <= ContainerEndLine); + assert(ContaineeBegLine <= ContaineeEndLine); + + return (ContainerBegLine <= ContaineeBegLine && + ContainerEndLine >= ContaineeEndLine && + (ContainerBegLine != ContaineeBegLine || + SM.getInstantiationColumnNumber(ContainerRBeg) <= + SM.getInstantiationColumnNumber(ContaineeRBeg)) && + (ContainerEndLine != ContaineeEndLine || + SM.getInstantiationColumnNumber(ContainerREnd) >= + SM.getInstantiationColumnNumber(ContainerREnd))); +} + +void EdgeBuilder::rawAddEdge(PathDiagnosticLocation NewLoc) { + if (!PrevLoc.isValid()) { + PrevLoc = NewLoc; + return; + } + + const PathDiagnosticLocation &NewLocClean = cleanUpLocation(NewLoc); + const PathDiagnosticLocation &PrevLocClean = cleanUpLocation(PrevLoc); + + if (NewLocClean.asLocation() == PrevLocClean.asLocation()) + return; + + // FIXME: Ignore intra-macro edges for now. + if (NewLocClean.asLocation().getInstantiationLoc() == + PrevLocClean.asLocation().getInstantiationLoc()) + return; + + PD.push_front(new PathDiagnosticControlFlowPiece(NewLocClean, PrevLocClean)); + PrevLoc = NewLoc; +} + +void EdgeBuilder::addEdge(PathDiagnosticLocation NewLoc, bool alwaysAdd) { + + if (!alwaysAdd && NewLoc.asLocation().isMacroID()) + return; + + const PathDiagnosticLocation &CLoc = getContextLocation(NewLoc); + + while (!CLocs.empty()) { + ContextLocation &TopContextLoc = CLocs.back(); + + // Is the top location context the same as the one for the new location? + if (TopContextLoc == CLoc) { + if (alwaysAdd) { + if (IsConsumedExpr(TopContextLoc) && + !IsControlFlowExpr(TopContextLoc.asStmt())) + TopContextLoc.markDead(); + + rawAddEdge(NewLoc); + } + + return; + } + + if (containsLocation(TopContextLoc, CLoc)) { + if (alwaysAdd) { + rawAddEdge(NewLoc); + + if (IsConsumedExpr(CLoc) && !IsControlFlowExpr(CLoc.asStmt())) { + CLocs.push_back(ContextLocation(CLoc, true)); + return; + } + } + + CLocs.push_back(CLoc); + return; + } + + // Context does not contain the location. Flush it. + popLocation(); + } + + // If we reach here, there is no enclosing context. Just add the edge. + rawAddEdge(NewLoc); +} + +bool EdgeBuilder::IsConsumedExpr(const PathDiagnosticLocation &L) { + if (const Expr *X = dyn_cast_or_null(L.asStmt())) + return PDB.getParentMap().isConsumedExpr(X) && !IsControlFlowExpr(X); + + return false; +} + +void EdgeBuilder::addExtendedContext(const Stmt *S) { + if (!S) + return; + + const Stmt *Parent = PDB.getParent(S); + while (Parent) { + if (isa(Parent)) + Parent = PDB.getParent(Parent); + else + break; + } + + if (Parent) { + switch (Parent->getStmtClass()) { + case Stmt::DoStmtClass: + case Stmt::ObjCAtSynchronizedStmtClass: + addContext(Parent); + default: + break; + } + } + + addContext(S); +} + +void EdgeBuilder::addContext(const Stmt *S) { + if (!S) + return; + + PathDiagnosticLocation L(S, PDB.getSourceManager()); + + while (!CLocs.empty()) { + const PathDiagnosticLocation &TopContextLoc = CLocs.back(); + + // Is the top location context the same as the one for the new location? + if (TopContextLoc == L) + return; + + if (containsLocation(TopContextLoc, L)) { + CLocs.push_back(L); + return; + } + + // Context does not contain the location. Flush it. + popLocation(); + } + + CLocs.push_back(L); +} + +static void GenerateExtensivePathDiagnostic(PathDiagnostic& PD, + PathDiagnosticBuilder &PDB, + const ExplodedNode *N) { + EdgeBuilder EB(PD, PDB); + + const ExplodedNode* NextNode = N->pred_empty() ? NULL : *(N->pred_begin()); + while (NextNode) { + N = NextNode; + NextNode = GetPredecessorNode(N); + ProgramPoint P = N->getLocation(); + + do { + // Block edges. + if (const BlockEdge *BE = dyn_cast(&P)) { + const CFGBlock &Blk = *BE->getSrc(); + const Stmt *Term = Blk.getTerminator(); + + // Are we jumping to the head of a loop? Add a special diagnostic. + if (const Stmt *Loop = BE->getDst()->getLoopTarget()) { + PathDiagnosticLocation L(Loop, PDB.getSourceManager()); + const CompoundStmt *CS = NULL; + + if (!Term) { + if (const ForStmt *FS = dyn_cast(Loop)) + CS = dyn_cast(FS->getBody()); + else if (const WhileStmt *WS = dyn_cast(Loop)) + CS = dyn_cast(WS->getBody()); + } + + PathDiagnosticEventPiece *p = + new PathDiagnosticEventPiece(L, + "Looping back to the head of the loop"); + + EB.addEdge(p->getLocation(), true); + PD.push_front(p); + + if (CS) { + PathDiagnosticLocation BL(CS->getRBracLoc(), + PDB.getSourceManager()); + BL = PathDiagnosticLocation(BL.asLocation()); + EB.addEdge(BL); + } + } + + if (Term) + EB.addContext(Term); + + break; + } + + if (const BlockEntrance *BE = dyn_cast(&P)) { + if (CFGStmt S = BE->getFirstElement().getAs()) { + if (IsControlFlowExpr(S)) { + // Add the proper context for '&&', '||', and '?'. + EB.addContext(S); + } + else + EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); + } + + break; + } + } while (0); + + if (!NextNode) + continue; + + for (BugReporterContext::visitor_iterator I = PDB.visitor_begin(), + E = PDB.visitor_end(); I!=E; ++I) { + if (PathDiagnosticPiece* p = (*I)->VisitNode(N, NextNode, PDB)) { + const PathDiagnosticLocation &Loc = p->getLocation(); + EB.addEdge(Loc, true); + PD.push_front(p); + if (const Stmt *S = Loc.asStmt()) + EB.addExtendedContext(PDB.getEnclosingStmtLocation(S).asStmt()); + } + } + } +} + +//===----------------------------------------------------------------------===// +// Methods for BugType and subclasses. +//===----------------------------------------------------------------------===// +BugType::~BugType() { + // Free up the equivalence class objects. Observe that we get a pointer to + // the object first before incrementing the iterator, as destroying the + // node before doing so means we will read from freed memory. + for (iterator I = begin(), E = end(); I !=E; ) { + BugReportEquivClass *EQ = &*I; + ++I; + delete EQ; + } +} +void BugType::FlushReports(BugReporter &BR) {} + +//===----------------------------------------------------------------------===// +// Methods for BugReport and subclasses. +//===----------------------------------------------------------------------===// +BugReport::~BugReport() {} +RangedBugReport::~RangedBugReport() {} + +const Stmt* BugReport::getStmt() const { + ProgramPoint ProgP = ErrorNode->getLocation(); + const Stmt *S = NULL; + + if (BlockEntrance* BE = dyn_cast(&ProgP)) { + CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit(); + if (BE->getBlock() == &Exit) + S = GetPreviousStmt(ErrorNode); + } + if (!S) + S = GetStmt(ProgP); + + return S; +} + +PathDiagnosticPiece* +BugReport::getEndPath(BugReporterContext& BRC, + const ExplodedNode* EndPathNode) { + + const Stmt* S = getStmt(); + + if (!S) + return NULL; + + BugReport::ranges_iterator Beg, End; + llvm::tie(Beg, End) = getRanges(); + PathDiagnosticLocation L(S, BRC.getSourceManager()); + + // Only add the statement itself as a range if we didn't specify any + // special ranges for this report. + PathDiagnosticPiece* P = new PathDiagnosticEventPiece(L, getDescription(), + Beg == End); + + for (; Beg != End; ++Beg) + P->addRange(*Beg); + + return P; +} + +std::pair +BugReport::getRanges() const { + if (const Expr* E = dyn_cast_or_null(getStmt())) { + R = E->getSourceRange(); + assert(R.isValid()); + return std::make_pair(&R, &R+1); + } + else + return std::make_pair(ranges_iterator(), ranges_iterator()); +} + +SourceLocation BugReport::getLocation() const { + if (ErrorNode) + if (const Stmt* S = GetCurrentOrPreviousStmt(ErrorNode)) { + // For member expressions, return the location of the '.' or '->'. + if (const MemberExpr *ME = dyn_cast(S)) + return ME->getMemberLoc(); + // For binary operators, return the location of the operator. + if (const BinaryOperator *B = dyn_cast(S)) + return B->getOperatorLoc(); + + return S->getLocStart(); + } + + return FullSourceLoc(); +} + +PathDiagnosticPiece* BugReport::VisitNode(const ExplodedNode* N, + const ExplodedNode* PrevN, + BugReporterContext &BRC) { + return NULL; +} + +//===----------------------------------------------------------------------===// +// Methods for BugReporter and subclasses. +//===----------------------------------------------------------------------===// + +BugReportEquivClass::~BugReportEquivClass() { + for (iterator I=begin(), E=end(); I!=E; ++I) delete *I; +} + +GRBugReporter::~GRBugReporter() { } +BugReporterData::~BugReporterData() {} + +ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); } + +GRStateManager& +GRBugReporter::getStateManager() { return Eng.getStateManager(); } + +BugReporter::~BugReporter() { FlushReports(); } + +void BugReporter::FlushReports() { + if (BugTypes.isEmpty()) + return; + + // First flush the warnings for each BugType. This may end up creating new + // warnings and new BugTypes. Because ImmutableSet is a functional data + // structure, we do not need to worry about the iterators being invalidated. + for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) + const_cast(*I)->FlushReports(*this); + + // Iterate through BugTypes a second time. BugTypes may have been updated + // with new BugType objects and new warnings. + for (BugTypesTy::iterator I=BugTypes.begin(), E=BugTypes.end(); I!=E; ++I) { + BugType *BT = const_cast(*I); + + typedef llvm::FoldingSet SetTy; + SetTy& EQClasses = BT->EQClasses; + + for (SetTy::iterator EI=EQClasses.begin(), EE=EQClasses.end(); EI!=EE;++EI){ + BugReportEquivClass& EQ = *EI; + FlushReport(EQ); + } + + // Delete the BugType object. + delete BT; + } + + // Remove all references to the BugType objects. + BugTypes = F.getEmptySet(); +} + +//===----------------------------------------------------------------------===// +// PathDiagnostics generation. +//===----------------------------------------------------------------------===// + +static std::pair, + std::pair > +MakeReportGraph(const ExplodedGraph* G, + llvm::SmallVectorImpl &nodes) { + + // Create the trimmed graph. It will contain the shortest paths from the + // error nodes to the root. In the new graph we should only have one + // error node unless there are two or more error nodes with the same minimum + // path length. + ExplodedGraph* GTrim; + InterExplodedGraphMap* NMap; + + llvm::DenseMap InverseMap; + llvm::tie(GTrim, NMap) = G->Trim(nodes.data(), nodes.data() + nodes.size(), + &InverseMap); + + // Create owning pointers for GTrim and NMap just to ensure that they are + // released when this function exists. + llvm::OwningPtr AutoReleaseGTrim(GTrim); + llvm::OwningPtr AutoReleaseNMap(NMap); + + // Find the (first) error node in the trimmed graph. We just need to consult + // the node map (NMap) which maps from nodes in the original graph to nodes + // in the new graph. + + std::queue WS; + typedef llvm::DenseMap IndexMapTy; + IndexMapTy IndexMap; + + for (unsigned nodeIndex = 0 ; nodeIndex < nodes.size(); ++nodeIndex) { + const ExplodedNode *originalNode = nodes[nodeIndex]; + if (const ExplodedNode *N = NMap->getMappedNode(originalNode)) { + WS.push(N); + IndexMap[originalNode] = nodeIndex; + } + } + + assert(!WS.empty() && "No error node found in the trimmed graph."); + + // Create a new (third!) graph with a single path. This is the graph + // that will be returned to the caller. + ExplodedGraph *GNew = new ExplodedGraph(); + + // Sometimes the trimmed graph can contain a cycle. Perform a reverse BFS + // to the root node, and then construct a new graph that contains only + // a single path. + llvm::DenseMap Visited; + + unsigned cnt = 0; + const ExplodedNode* Root = 0; + + while (!WS.empty()) { + const ExplodedNode* Node = WS.front(); + WS.pop(); + + if (Visited.find(Node) != Visited.end()) + continue; + + Visited[Node] = cnt++; + + if (Node->pred_empty()) { + Root = Node; + break; + } + + for (ExplodedNode::const_pred_iterator I=Node->pred_begin(), + E=Node->pred_end(); I!=E; ++I) + WS.push(*I); + } + + assert(Root); + + // Now walk from the root down the BFS path, always taking the successor + // with the lowest number. + ExplodedNode *Last = 0, *First = 0; + NodeBackMap *BM = new NodeBackMap(); + unsigned NodeIndex = 0; + + for ( const ExplodedNode *N = Root ;;) { + // Lookup the number associated with the current node. + llvm::DenseMap::iterator I = Visited.find(N); + assert(I != Visited.end()); + + // Create the equivalent node in the new graph with the same state + // and location. + ExplodedNode* NewN = GNew->getNode(N->getLocation(), N->getState()); + + // Store the mapping to the original node. + llvm::DenseMap::iterator IMitr=InverseMap.find(N); + assert(IMitr != InverseMap.end() && "No mapping to original node."); + (*BM)[NewN] = (const ExplodedNode*) IMitr->second; + + // Link up the new node with the previous node. + if (Last) + NewN->addPredecessor(Last, *GNew); + + Last = NewN; + + // Are we at the final node? + IndexMapTy::iterator IMI = + IndexMap.find((const ExplodedNode*)(IMitr->second)); + if (IMI != IndexMap.end()) { + First = NewN; + NodeIndex = IMI->second; + break; + } + + // Find the next successor node. We choose the node that is marked + // with the lowest DFS number. + ExplodedNode::const_succ_iterator SI = N->succ_begin(); + ExplodedNode::const_succ_iterator SE = N->succ_end(); + N = 0; + + for (unsigned MinVal = 0; SI != SE; ++SI) { + + I = Visited.find(*SI); + + if (I == Visited.end()) + continue; + + if (!N || I->second < MinVal) { + N = *SI; + MinVal = I->second; + } + } + + assert(N); + } + + assert(First); + + return std::make_pair(std::make_pair(GNew, BM), + std::make_pair(First, NodeIndex)); +} + +/// CompactPathDiagnostic - This function postprocesses a PathDiagnostic object +/// and collapses PathDiagosticPieces that are expanded by macros. +static void CompactPathDiagnostic(PathDiagnostic &PD, const SourceManager& SM) { + typedef std::vector > + MacroStackTy; + + typedef std::vector + PiecesTy; + + MacroStackTy MacroStack; + PiecesTy Pieces; + + for (PathDiagnostic::iterator I = PD.begin(), E = PD.end(); I!=E; ++I) { + // Get the location of the PathDiagnosticPiece. + const FullSourceLoc Loc = I->getLocation().asLocation(); + + // Determine the instantiation location, which is the location we group + // related PathDiagnosticPieces. + SourceLocation InstantiationLoc = Loc.isMacroID() ? + SM.getInstantiationLoc(Loc) : + SourceLocation(); + + if (Loc.isFileID()) { + MacroStack.clear(); + Pieces.push_back(&*I); + continue; + } + + assert(Loc.isMacroID()); + + // Is the PathDiagnosticPiece within the same macro group? + if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) { + MacroStack.back().first->push_back(&*I); + continue; + } + + // We aren't in the same group. Are we descending into a new macro + // or are part of an old one? + PathDiagnosticMacroPiece *MacroGroup = 0; + + SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ? + SM.getInstantiationLoc(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. + PathDiagnosticMacroPiece *NewGroup = new PathDiagnosticMacroPiece(Loc); + + if (MacroGroup) + MacroGroup->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->push_back(&*I); + } + + // Now take the pieces and construct a new PathDiagnostic. + PD.resetPath(false); + + for (PiecesTy::iterator I=Pieces.begin(), E=Pieces.end(); I!=E; ++I) { + if (PathDiagnosticMacroPiece *MP=dyn_cast(*I)) + if (!MP->containsEvent()) { + delete MP; + continue; + } + + PD.push_back(*I); + } +} + +void GRBugReporter::GeneratePathDiagnostic(PathDiagnostic& PD, + llvm::SmallVectorImpl &bugReports) { + + assert(!bugReports.empty()); + llvm::SmallVector errorNodes; + for (llvm::SmallVectorImpl::iterator I = bugReports.begin(), + E = bugReports.end(); I != E; ++I) { + errorNodes.push_back((*I)->getErrorNode()); + } + + // Construct a new graph that contains only a single path from the error + // node to a root. + const std::pair, + std::pair >& + GPair = MakeReportGraph(&getGraph(), errorNodes); + + // Find the BugReport with the original location. + assert(GPair.second.second < bugReports.size()); + BugReport *R = bugReports[GPair.second.second]; + assert(R && "No original report found for sliced graph."); + + llvm::OwningPtr ReportGraph(GPair.first.first); + llvm::OwningPtr BackMap(GPair.first.second); + const ExplodedNode *N = GPair.second.first; + + // Start building the path diagnostic... + PathDiagnosticBuilder PDB(*this, R, BackMap.get(), getPathDiagnosticClient()); + + if (PathDiagnosticPiece* Piece = R->getEndPath(PDB, N)) + PD.push_back(Piece); + else + return; + + // Register node visitors. + R->registerInitialVisitors(PDB, N); + bugreporter::registerNilReceiverVisitor(PDB); + + switch (PDB.getGenerationScheme()) { + case PathDiagnosticClient::Extensive: + GenerateExtensivePathDiagnostic(PD, PDB, N); + break; + case PathDiagnosticClient::Minimal: + GenerateMinimalPathDiagnostic(PD, PDB, N); + break; + } +} + +void BugReporter::Register(BugType *BT) { + BugTypes = F.add(BugTypes, BT); +} + +void BugReporter::EmitReport(BugReport* R) { + // 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 = BT.EQClasses.FindNodeOrInsertPos(ID, InsertPos); + + if (!EQ) { + EQ = new BugReportEquivClass(R); + BT.EQClasses.InsertNode(EQ, InsertPos); + } + else + EQ->AddReport(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()) {} +}; +} + +static BugReport * +FindReportInEquivalenceClass(BugReportEquivClass& EQ, + llvm::SmallVectorImpl &bugReports) { + + BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end(); + assert(I != E); + BugReport *R = *I; + BugType& BT = R->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()) { + for (BugReportEquivClass::iterator I=EQ.begin(), E=EQ.end(); I!=E; ++I) { + 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 = 0; + + for (; I != E; ++I) { + R = *I; + const ExplodedNode *errorNode = R->getErrorNode(); + + if (!errorNode) + continue; + if (errorNode->isSink()) { + assert(false && + "BugType::isSuppressSink() should not be 'true' for sink end nodes"); + return 0; + } + // No successors? By definition this nodes isn't post-dominated by a sink. + if (errorNode->succ_empty()) { + bugReports.push_back(R); + if (!exampleReport) + exampleReport = R; + 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. + typedef FRIEC_WLItem WLItem; + typedef llvm::SmallVector DFSWorkList; + llvm::DenseMap 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(R); + if (!exampleReport) + exampleReport = R; + 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; +} + +//===----------------------------------------------------------------------===// +// DiagnosticCache. This is a hack to cache analyzer diagnostics. It +// uses global state, which eventually should go elsewhere. +//===----------------------------------------------------------------------===// +namespace { +class DiagCacheItem : public llvm::FoldingSetNode { + llvm::FoldingSetNodeID ID; +public: + DiagCacheItem(BugReport *R, PathDiagnostic *PD) { + ID.AddString(R->getBugType().getName()); + ID.AddString(R->getBugType().getCategory()); + ID.AddString(R->getDescription()); + ID.AddInteger(R->getLocation().getRawEncoding()); + PD->Profile(ID); + } + + void Profile(llvm::FoldingSetNodeID &id) { + id = ID; + } + + llvm::FoldingSetNodeID &getID() { return ID; } +}; +} + +static bool IsCachedDiagnostic(BugReport *R, PathDiagnostic *PD) { + // FIXME: Eventually this diagnostic cache should reside in something + // like AnalysisManager instead of being a static variable. This is + // really unsafe in the long term. + typedef llvm::FoldingSet DiagnosticCache; + static DiagnosticCache DC; + + void *InsertPos; + DiagCacheItem *Item = new DiagCacheItem(R, PD); + + if (DC.FindNodeOrInsertPos(Item->getID(), InsertPos)) { + delete Item; + return true; + } + + DC.InsertNode(Item, InsertPos); + return false; +} + +void BugReporter::FlushReport(BugReportEquivClass& EQ) { + llvm::SmallVector bugReports; + BugReport *exampleReport = FindReportInEquivalenceClass(EQ, bugReports); + if (!exampleReport) + return; + + PathDiagnosticClient* PD = getPathDiagnosticClient(); + + // FIXME: Make sure we use the 'R' for the path that was actually used. + // Probably doesn't make a difference in practice. + BugType& BT = exampleReport->getBugType(); + + llvm::OwningPtr + D(new PathDiagnostic(exampleReport->getBugType().getName(), + !PD || PD->useVerboseDescription() + ? exampleReport->getDescription() + : exampleReport->getShortDescription(), + BT.getCategory())); + + if (!bugReports.empty()) + GeneratePathDiagnostic(*D.get(), bugReports); + + if (IsCachedDiagnostic(exampleReport, D.get())) + return; + + // Get the meta data. + std::pair Meta = + exampleReport->getExtraDescriptiveText(); + for (const char** s = Meta.first; s != Meta.second; ++s) + D->addMeta(*s); + + // Emit a summary diagnostic to the regular Diagnostics engine. + BugReport::ranges_iterator Beg, End; + llvm::tie(Beg, End) = exampleReport->getRanges(); + Diagnostic &Diag = getDiagnostic(); + FullSourceLoc L(exampleReport->getLocation(), getSourceManager()); + + // Search the description for '%', as that will be interpretted as a + // format character by FormatDiagnostics. + llvm::StringRef desc = exampleReport->getShortDescription(); + unsigned ErrorDiag; + { + llvm::SmallString<512> TmpStr; + llvm::raw_svector_ostream Out(TmpStr); + for (llvm::StringRef::iterator I=desc.begin(), E=desc.end(); I!=E; ++I) + if (*I == '%') + Out << "%%"; + else + Out << *I; + + Out.flush(); + ErrorDiag = Diag.getCustomDiagID(Diagnostic::Warning, TmpStr); + } + + { + DiagnosticBuilder diagBuilder = Diag.Report(L, ErrorDiag); + for (BugReport::ranges_iterator I = Beg; I != End; ++I) + diagBuilder << *I; + } + + // Emit a full diagnostic for the path if we have a PathDiagnosticClient. + if (!PD) + return; + + if (D->empty()) { + PathDiagnosticPiece* piece = + new PathDiagnosticEventPiece(L, exampleReport->getDescription()); + + for ( ; Beg != End; ++Beg) piece->addRange(*Beg); + D->push_back(piece); + } + + PD->HandlePathDiagnostic(D.take()); +} + +void BugReporter::EmitBasicReport(llvm::StringRef name, llvm::StringRef str, + SourceLocation Loc, + SourceRange* RBeg, unsigned NumRanges) { + EmitBasicReport(name, "", str, Loc, RBeg, NumRanges); +} + +void BugReporter::EmitBasicReport(llvm::StringRef name, + llvm::StringRef category, + llvm::StringRef str, SourceLocation Loc, + SourceRange* RBeg, unsigned NumRanges) { + + // 'BT' will be owned by BugReporter as soon as we call 'EmitReport'. + BugType *BT = new BugType(name, category); + FullSourceLoc L = getContext().getFullLoc(Loc); + RangedBugReport *R = new DiagBugReport(*BT, str, L); + for ( ; NumRanges > 0 ; --NumRanges, ++RBeg) R->addRange(*RBeg); + EmitReport(R); +} diff --git a/lib/StaticAnalyzer/EntoSA/BugReporterVisitors.cpp b/lib/StaticAnalyzer/EntoSA/BugReporterVisitors.cpp new file mode 100644 index 0000000000..91d727592c --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/BugReporterVisitors.cpp @@ -0,0 +1,457 @@ +// BugReporterVisitors.cpp - Helpers for reporting bugs -----------*- 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 BugReporter "visitors" which can be used to +// enhance the diagnostics reported for a bug. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +const Stmt *bugreporter::GetDerefExpr(const ExplodedNode *N) { + // Pattern match for a few useful cases (do something smarter later): + // a[0], p->f, *p + const Stmt *S = N->getLocationAs()->getStmt(); + + if (const UnaryOperator *U = dyn_cast(S)) { + if (U->getOpcode() == UO_Deref) + return U->getSubExpr()->IgnoreParenCasts(); + } + else if (const MemberExpr *ME = dyn_cast(S)) { + return ME->getBase()->IgnoreParenCasts(); + } + else if (const ArraySubscriptExpr *AE = dyn_cast(S)) { + // Retrieve the base for arrays since BasicStoreManager doesn't know how + // to reason about them. + return AE->getBase(); + } + + return NULL; +} + +const Stmt *bugreporter::GetDenomExpr(const ExplodedNode *N) { + const Stmt *S = N->getLocationAs()->getStmt(); + if (const BinaryOperator *BE = dyn_cast(S)) + return BE->getRHS(); + return NULL; +} + +const Stmt *bugreporter::GetCalleeExpr(const ExplodedNode *N) { + // Callee is checked as a PreVisit to the CallExpr. + const Stmt *S = N->getLocationAs()->getStmt(); + if (const CallExpr *CE = dyn_cast(S)) + return CE->getCallee(); + return NULL; +} + +const Stmt *bugreporter::GetRetValExpr(const ExplodedNode *N) { + const Stmt *S = N->getLocationAs()->getStmt(); + if (const ReturnStmt *RS = dyn_cast(S)) + return RS->getRetValue(); + return NULL; +} + +//===----------------------------------------------------------------------===// +// Definitions for bug reporter visitors. +//===----------------------------------------------------------------------===// + +namespace { +class FindLastStoreBRVisitor : public BugReporterVisitor { + const MemRegion *R; + SVal V; + bool satisfied; + const ExplodedNode *StoreSite; +public: + FindLastStoreBRVisitor(SVal v, const MemRegion *r) + : R(r), V(v), satisfied(false), StoreSite(0) {} + + virtual void Profile(llvm::FoldingSetNodeID &ID) const { + static int tag = 0; + ID.AddPointer(&tag); + ID.AddPointer(R); + ID.Add(V); + } + + PathDiagnosticPiece* VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext& BRC) { + + if (satisfied) + return NULL; + + if (!StoreSite) { + const ExplodedNode *Node = N, *Last = NULL; + + for ( ; Node ; Last = Node, Node = Node->getFirstPred()) { + + if (const VarRegion *VR = dyn_cast(R)) { + if (const PostStmt *P = Node->getLocationAs()) + if (const DeclStmt *DS = P->getStmtAs()) + if (DS->getSingleDecl() == VR->getDecl()) { + Last = Node; + break; + } + } + + if (Node->getState()->getSVal(R) != V) + break; + } + + if (!Node || !Last) { + satisfied = true; + return NULL; + } + + StoreSite = Last; + } + + if (StoreSite != N) + return NULL; + + satisfied = true; + llvm::SmallString<256> sbuf; + llvm::raw_svector_ostream os(sbuf); + + if (const PostStmt *PS = N->getLocationAs()) { + if (const DeclStmt *DS = PS->getStmtAs()) { + + if (const VarRegion *VR = dyn_cast(R)) { + os << "Variable '" << VR->getDecl() << "' "; + } + else + return NULL; + + if (isa(V)) { + bool b = false; + if (R->isBoundable()) { + if (const TypedRegion *TR = dyn_cast(R)) { + if (TR->getValueType()->isObjCObjectPointerType()) { + os << "initialized to nil"; + b = true; + } + } + } + + if (!b) + os << "initialized to a null pointer value"; + } + else if (isa(V)) { + os << "initialized to " << cast(V).getValue(); + } + else if (V.isUndef()) { + if (isa(R)) { + const VarDecl *VD = cast(DS->getSingleDecl()); + if (VD->getInit()) + os << "initialized to a garbage value"; + else + os << "declared without an initial value"; + } + } + } + } + + if (os.str().empty()) { + if (isa(V)) { + bool b = false; + if (R->isBoundable()) { + if (const TypedRegion *TR = dyn_cast(R)) { + if (TR->getValueType()->isObjCObjectPointerType()) { + os << "nil object reference stored to "; + b = true; + } + } + } + + if (!b) + os << "Null pointer value stored to "; + } + else if (V.isUndef()) { + os << "Uninitialized value stored to "; + } + else if (isa(V)) { + os << "The value " << cast(V).getValue() + << " is assigned to "; + } + else + return NULL; + + if (const VarRegion *VR = dyn_cast(R)) { + os << '\'' << VR->getDecl() << '\''; + } + else + return NULL; + } + + // FIXME: Refactor this into BugReporterContext. + const Stmt *S = 0; + ProgramPoint P = N->getLocation(); + + if (BlockEdge *BE = dyn_cast(&P)) { + const CFGBlock *BSrc = BE->getSrc(); + S = BSrc->getTerminatorCondition(); + } + else if (PostStmt *PS = dyn_cast(&P)) { + S = PS->getStmt(); + } + + if (!S) + return NULL; + + // Construct a new PathDiagnosticPiece. + PathDiagnosticLocation L(S, BRC.getSourceManager()); + return new PathDiagnosticEventPiece(L, os.str()); + } +}; + + +static void registerFindLastStore(BugReporterContext& BRC, const MemRegion *R, + SVal V) { + BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); +} + +class TrackConstraintBRVisitor : public BugReporterVisitor { + DefinedSVal Constraint; + const bool Assumption; + bool isSatisfied; +public: + TrackConstraintBRVisitor(DefinedSVal constraint, bool assumption) + : Constraint(constraint), Assumption(assumption), isSatisfied(false) {} + + void Profile(llvm::FoldingSetNodeID &ID) const { + static int tag = 0; + ID.AddPointer(&tag); + ID.AddBoolean(Assumption); + ID.Add(Constraint); + } + + PathDiagnosticPiece* VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext& BRC) { + if (isSatisfied) + return NULL; + + // Check if in the previous state it was feasible for this constraint + // to *not* be true. + if (PrevN->getState()->assume(Constraint, !Assumption)) { + + 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. + if (N->getState()->assume(Constraint, !Assumption)) + return NULL; + + // We found the transition point for the constraint. We now need to + // pretty-print the constraint. (work-in-progress) + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + if (isa(Constraint)) { + os << "Assuming pointer value is "; + os << (Assumption ? "non-null" : "null"); + } + + if (os.str().empty()) + return NULL; + + // FIXME: Refactor this into BugReporterContext. + const Stmt *S = 0; + ProgramPoint P = N->getLocation(); + + if (BlockEdge *BE = dyn_cast(&P)) { + const CFGBlock *BSrc = BE->getSrc(); + S = BSrc->getTerminatorCondition(); + } + else if (PostStmt *PS = dyn_cast(&P)) { + S = PS->getStmt(); + } + + if (!S) + return NULL; + + // Construct a new PathDiagnosticPiece. + PathDiagnosticLocation L(S, BRC.getSourceManager()); + return new PathDiagnosticEventPiece(L, os.str()); + } + + return NULL; + } +}; +} // end anonymous namespace + +static void registerTrackConstraint(BugReporterContext& BRC, + DefinedSVal Constraint, + bool Assumption) { + BRC.addVisitor(new TrackConstraintBRVisitor(Constraint, Assumption)); +} + +void bugreporter::registerTrackNullOrUndefValue(BugReporterContext& BRC, + const void *data, + const ExplodedNode* N) { + + const Stmt *S = static_cast(data); + + if (!S) + return; + + GRStateManager &StateMgr = BRC.getStateManager(); + const GRState *state = N->getState(); + + // Walk through lvalue-to-rvalue conversions. + if (const DeclRefExpr *DR = dyn_cast(S)) { + if (const VarDecl *VD = dyn_cast(DR->getDecl())) { + const VarRegion *R = + StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); + + // What did we load? + SVal V = state->getSVal(loc::MemRegionVal(R)); + + if (isa(V) || isa(V) + || V.isUndef()) { + ::registerFindLastStore(BRC, R, V); + } + } + } + + SVal V = state->getSValAsScalarOrLoc(S); + + // Uncomment this to find cases where we aren't properly getting the + // base value that was dereferenced. + // assert(!V.isUnknownOrUndef()); + + // Is it a symbolic value? + if (loc::MemRegionVal *L = dyn_cast(&V)) { + const SubRegion *R = cast(L->getRegion()); + while (R && !isa(R)) { + R = dyn_cast(R->getSuperRegion()); + } + + if (R) { + assert(isa(R)); + registerTrackConstraint(BRC, loc::MemRegionVal(R), false); + } + } +} + +void bugreporter::registerFindLastStore(BugReporterContext& BRC, + const void *data, + const ExplodedNode* N) { + + const MemRegion *R = static_cast(data); + + if (!R) + return; + + const GRState *state = N->getState(); + SVal V = state->getSVal(R); + + if (V.isUnknown()) + return; + + BRC.addVisitor(new FindLastStoreBRVisitor(V, R)); +} + + +namespace { +class NilReceiverVisitor : public BugReporterVisitor { +public: + NilReceiverVisitor() {} + + void Profile(llvm::FoldingSetNodeID &ID) const { + static int x = 0; + ID.AddPointer(&x); + } + + PathDiagnosticPiece* VisitNode(const ExplodedNode *N, + const ExplodedNode *PrevN, + BugReporterContext& BRC) { + + const PostStmt *P = N->getLocationAs(); + if (!P) + return 0; + const ObjCMessageExpr *ME = P->getStmtAs(); + if (!ME) + return 0; + const Expr *Receiver = ME->getInstanceReceiver(); + if (!Receiver) + return 0; + const GRState *state = N->getState(); + const SVal &V = state->getSVal(Receiver); + const DefinedOrUnknownSVal *DV = dyn_cast(&V); + if (!DV) + return 0; + state = state->assume(*DV, true); + if (state) + return 0; + + // 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::registerTrackNullOrUndefValue(BRC, Receiver, N); + // Issue a message saying that the method was skipped. + PathDiagnosticLocation L(Receiver, BRC.getSourceManager()); + return new PathDiagnosticEventPiece(L, "No method actually called " + "because the receiver is nil"); + } +}; +} // end anonymous namespace + +void bugreporter::registerNilReceiverVisitor(BugReporterContext &BRC) { + BRC.addVisitor(new NilReceiverVisitor()); +} + +// Registers every VarDecl inside a Stmt with a last store vistor. +void bugreporter::registerVarDeclsLastStore(BugReporterContext &BRC, + const void *stmt, + const ExplodedNode *N) { + const Stmt *S = static_cast(stmt); + + std::deque WorkList; + + WorkList.push_back(S); + + while (!WorkList.empty()) { + const Stmt *Head = WorkList.front(); + WorkList.pop_front(); + + GRStateManager &StateMgr = BRC.getStateManager(); + const GRState *state = N->getState(); + + if (const DeclRefExpr *DR = dyn_cast(Head)) { + if (const VarDecl *VD = dyn_cast(DR->getDecl())) { + const VarRegion *R = + StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext()); + + // What did we load? + SVal V = state->getSVal(S); + + if (isa(V) || isa(V)) { + ::registerFindLastStore(BRC, R, V); + } + } + } + + for (Stmt::const_child_iterator I = Head->child_begin(); + I != Head->child_end(); ++I) + WorkList.push_back(*I); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/CFRefCount.cpp b/lib/StaticAnalyzer/EntoSA/CFRefCount.cpp new file mode 100644 index 0000000000..5fd223c471 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/CFRefCount.cpp @@ -0,0 +1,3500 @@ +// CFRefCount.cpp - Transfer functions for tracking simple 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 defines the methods for CFRefCount, which implements +// a reference count checker for Core Foundation (Mac OS X). +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclObjC.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngineBuilders.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.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/StringExtras.h" +#include + +using namespace clang; +using namespace ento; +using llvm::StringRef; +using llvm::StrInStrNoCase; + +namespace { +class InstanceReceiver { + const ObjCMessageExpr *ME; + const LocationContext *LC; +public: + InstanceReceiver(const ObjCMessageExpr *me = 0, + const LocationContext *lc = 0) : ME(me), LC(lc) {} + + bool isValid() const { + return ME && ME->isInstanceMessage(); + } + operator bool() const { + return isValid(); + } + + SVal getSValAsScalarOrLoc(const GRState *state) { + assert(isValid()); + // We have an expression for the receiver? Fetch the value + // of that expression. + if (const Expr *Ex = ME->getInstanceReceiver()) + return state->getSValAsScalarOrLoc(Ex); + + // Otherwise we are sending a message to super. In this case the + // object reference is the same as 'self'. + if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl()) + return state->getSVal(state->getRegion(SelfDecl, LC)); + + return UnknownVal(); + } + + SourceRange getSourceRange() const { + assert(isValid()); + if (const Expr *Ex = ME->getInstanceReceiver()) + return Ex->getSourceRange(); + + // Otherwise we are sending a message to super. + SourceLocation L = ME->getSuperLoc(); + assert(L.isValid()); + return SourceRange(L, L); + } +}; +} + +static const ObjCMethodDecl* +ResolveToInterfaceMethodDecl(const ObjCMethodDecl *MD) { + const ObjCInterfaceDecl *ID = MD->getClassInterface(); + + return MD->isInstanceMethod() + ? ID->lookupInstanceMethod(MD->getSelector()) + : ID->lookupClassMethod(MD->getSelector()); +} + +namespace { +class GenericNodeBuilder { + StmtNodeBuilder *SNB; + const Stmt *S; + const void *tag; + EndPathNodeBuilder *ENB; +public: + GenericNodeBuilder(StmtNodeBuilder &snb, const Stmt *s, + const void *t) + : SNB(&snb), S(s), tag(t), ENB(0) {} + + GenericNodeBuilder(EndPathNodeBuilder &enb) + : SNB(0), S(0), tag(0), ENB(&enb) {} + + ExplodedNode *MakeNode(const GRState *state, ExplodedNode *Pred) { + if (SNB) + return SNB->generateNode(PostStmt(S, Pred->getLocationContext(), tag), + state, Pred); + + assert(ENB); + return ENB->generateNode(state, Pred); + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Primitives used for constructing summaries for function/method calls. +//===----------------------------------------------------------------------===// + +/// ArgEffect is used to summarize a function/method call's effect on a +/// particular argument. +enum ArgEffect { Autorelease, Dealloc, DecRef, DecRefMsg, DoNothing, + DoNothingByRef, IncRefMsg, IncRef, MakeCollectable, MayEscape, + NewAutoreleasePool, SelfOwn, StopTracking }; + +namespace llvm { +template <> struct FoldingSetTrait { +static inline void Profile(const ArgEffect X, FoldingSetNodeID& ID) { + ID.AddInteger((unsigned) X); +} +}; +} // end llvm namespace + +/// ArgEffects summarizes the effects of a function/method call on all of +/// its arguments. +typedef llvm::ImmutableMap ArgEffects; + +namespace { + +/// RetEffect is used to summarize a function/method call's behavior with +/// respect to its return value. +class RetEffect { +public: + enum Kind { NoRet, Alias, OwnedSymbol, OwnedAllocatedSymbol, + NotOwnedSymbol, GCNotOwnedSymbol, ReceiverAlias, + OwnedWhenTrackedReceiver }; + + enum ObjKind { CF, ObjC, AnyObj }; + +private: + Kind K; + ObjKind O; + unsigned index; + + RetEffect(Kind k, unsigned idx = 0) : K(k), O(AnyObj), index(idx) {} + RetEffect(Kind k, ObjKind o) : K(k), O(o), index(0) {} + +public: + Kind getKind() const { return K; } + + ObjKind getObjKind() const { return O; } + + unsigned getIndex() const { + assert(getKind() == Alias); + return index; + } + + bool isOwned() const { + return K == OwnedSymbol || K == OwnedAllocatedSymbol || + K == OwnedWhenTrackedReceiver; + } + + static RetEffect MakeOwnedWhenTrackedReceiver() { + return RetEffect(OwnedWhenTrackedReceiver, ObjC); + } + + static RetEffect MakeAlias(unsigned Idx) { + return RetEffect(Alias, Idx); + } + static RetEffect MakeReceiverAlias() { + return RetEffect(ReceiverAlias); + } + static RetEffect MakeOwned(ObjKind o, bool isAllocated = false) { + return RetEffect(isAllocated ? OwnedAllocatedSymbol : OwnedSymbol, o); + } + static RetEffect MakeNotOwned(ObjKind o) { + return RetEffect(NotOwnedSymbol, o); + } + static RetEffect MakeGCNotOwned() { + return RetEffect(GCNotOwnedSymbol, ObjC); + } + + static RetEffect MakeNoRet() { + return RetEffect(NoRet); + } +}; + +//===----------------------------------------------------------------------===// +// Reference-counting logic (typestate + counts). +//===----------------------------------------------------------------------===// + +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. + ErrorDeallocGC, // Calling -dealloc with GC enabled. + 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. + ErrorGCLeakReturned, + ErrorOverAutorelease, + ErrorReturnedNotOwned + }; + +private: + Kind kind; + RetEffect::ObjKind okind; + unsigned Cnt; + unsigned ACnt; + QualType T; + + RefVal(Kind k, RetEffect::ObjKind o, unsigned cnt, unsigned acnt, QualType t) + : kind(k), okind(o), Cnt(cnt), ACnt(acnt), T(t) {} + +public: + Kind getKind() const { return kind; } + + RetEffect::ObjKind getObjKind() const { return okind; } + + 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; } + + bool isOwned() const { + return getKind() == Owned; + } + + bool isNotOwned() const { + return getKind() == NotOwned; + } + + bool isReturnedOwned() const { + return getKind() == ReturnedOwned; + } + + bool isReturnedNotOwned() const { + return getKind() == ReturnedNotOwned; + } + + static RefVal makeOwned(RetEffect::ObjKind o, QualType t, + unsigned Count = 1) { + return RefVal(Owned, o, Count, 0, t); + } + + static RefVal makeNotOwned(RetEffect::ObjKind o, QualType t, + unsigned Count = 0) { + return RefVal(NotOwned, o, Count, 0, t); + } + + // Comparison, profiling, and pretty-printing. + + bool operator==(const RefVal& X) const { + return kind == X.kind && Cnt == X.Cnt && T == X.T && ACnt == X.ACnt; + } + + RefVal operator-(size_t i) const { + return RefVal(getKind(), getObjKind(), getCount() - i, + getAutoreleaseCount(), getType()); + } + + RefVal operator+(size_t i) const { + return RefVal(getKind(), getObjKind(), getCount() + i, + getAutoreleaseCount(), getType()); + } + + RefVal operator^(Kind k) const { + return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(), + getType()); + } + + RefVal autorelease() const { + return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1, + getType()); + } + + void Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddInteger((unsigned) kind); + ID.AddInteger(Cnt); + ID.AddInteger(ACnt); + ID.Add(T); + } + + void print(llvm::raw_ostream& Out) const; +}; + +void RefVal::print(llvm::raw_ostream& Out) const { + if (!T.isNull()) + Out << "Tracked Type:" << T.getAsString() << '\n'; + + switch (getKind()) { + default: assert(false); + 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 ErrorDeallocGC: + Out << "-dealloc (GC)"; + break; + + case ErrorDeallocNotOwned: + Out << "-dealloc (not-owned)"; + break; + + case ErrorLeak: + Out << "Leaked"; + break; + + case ErrorLeakReturned: + Out << "Leaked (Bad naming)"; + break; + + case ErrorGCLeakReturned: + Out << "Leaked (GC-ed at return)"; + 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; + } + + if (ACnt) { + Out << " [ARC +" << ACnt << ']'; + } +} +} //end anonymous namespace + +//===----------------------------------------------------------------------===// +// RefBindings - State used to track object reference counts. +//===----------------------------------------------------------------------===// + +typedef llvm::ImmutableMap RefBindings; + +namespace clang { +namespace ento { + template<> + struct GRStateTrait : public GRStatePartialTrait { + static void* GDMIndex() { + static int RefBIndex = 0; + return &RefBIndex; + } + }; +} +} + +//===----------------------------------------------------------------------===// +// Summaries +//===----------------------------------------------------------------------===// + +namespace { +class RetainSummary { + /// Args - an ordered vector of (index, ArgEffect) pairs, where index + /// specifies the argument (starting from 0). This can be sparsely + /// populated; arguments with no entry in Args use 'DefaultArgEffect'. + ArgEffects Args; + + /// DefaultArgEffect - The default ArgEffect to apply to arguments that + /// do not have an entry in Args. + ArgEffect DefaultArgEffect; + + /// Receiver - If this summary applies to an Objective-C message expression, + /// this is the effect applied to the state of the receiver. + ArgEffect Receiver; + + /// Ret - The effect on the return value. Used to indicate if the + /// function/method call returns a new tracked symbol, returns an + /// alias of one of the arguments in the call, and so on. + RetEffect Ret; + + /// EndPath - Indicates that execution of this method/function should + /// terminate the simulation of a path. + bool EndPath; + +public: + RetainSummary(ArgEffects A, RetEffect R, ArgEffect defaultEff, + ArgEffect ReceiverEff, bool endpath = false) + : Args(A), DefaultArgEffect(defaultEff), Receiver(ReceiverEff), Ret(R), + EndPath(endpath) {} + + /// getArg - Return the argument effect on the argument specified by + /// idx (starting from 0). + ArgEffect getArg(unsigned idx) const { + if (const ArgEffect *AE = Args.lookup(idx)) + return *AE; + + return DefaultArgEffect; + } + + /// setDefaultArgEffect - Set the default argument effect. + void setDefaultArgEffect(ArgEffect E) { + DefaultArgEffect = E; + } + + /// getRetEffect - Returns the effect on the return value of the call. + RetEffect getRetEffect() const { return Ret; } + + /// setRetEffect - Set the effect of the return value of the call. + void setRetEffect(RetEffect E) { Ret = E; } + + /// isEndPath - Returns true if executing the given method/function should + /// terminate the path. + bool isEndPath() const { return EndPath; } + + /// getReceiverEffect - Returns the effect on the receiver of the call. + /// This is only meaningful if the summary applies to an ObjCMessageExpr*. + ArgEffect getReceiverEffect() const { return Receiver; } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Data structures for constructing summaries. +//===----------------------------------------------------------------------===// + +namespace { +class ObjCSummaryKey { + IdentifierInfo* II; + Selector S; +public: + ObjCSummaryKey(IdentifierInfo* ii, Selector s) + : II(ii), S(s) {} + + ObjCSummaryKey(const ObjCInterfaceDecl* d, Selector s) + : II(d ? d->getIdentifier() : 0), S(s) {} + + ObjCSummaryKey(const ObjCInterfaceDecl* d, IdentifierInfo *ii, Selector s) + : II(d ? d->getIdentifier() : ii), S(s) {} + + ObjCSummaryKey(Selector s) + : II(0), S(s) {} + + IdentifierInfo* getIdentifier() const { return II; } + Selector getSelector() const { return S; } +}; +} + +namespace llvm { +template <> struct DenseMapInfo { + static inline ObjCSummaryKey getEmptyKey() { + return ObjCSummaryKey(DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey()); + } + + static inline ObjCSummaryKey getTombstoneKey() { + return ObjCSummaryKey(DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getTombstoneKey()); + } + + static unsigned getHashValue(const ObjCSummaryKey &V) { + return (DenseMapInfo::getHashValue(V.getIdentifier()) + & 0x88888888) + | (DenseMapInfo::getHashValue(V.getSelector()) + & 0x55555555); + } + + static bool isEqual(const ObjCSummaryKey& LHS, const ObjCSummaryKey& RHS) { + return DenseMapInfo::isEqual(LHS.getIdentifier(), + RHS.getIdentifier()) && + DenseMapInfo::isEqual(LHS.getSelector(), + RHS.getSelector()); + } + +}; +template <> +struct isPodLike { static const bool value = true; }; +} // end llvm namespace + +namespace { +class ObjCSummaryCache { + typedef llvm::DenseMap MapTy; + MapTy M; +public: + ObjCSummaryCache() {} + + RetainSummary* find(const ObjCInterfaceDecl* D, IdentifierInfo *ClsName, + Selector S) { + // Lookup the method using the decl for the class @interface. If we + // have no decl, lookup using the class name. + return D ? find(D, S) : find(ClsName, S); + } + + RetainSummary* find(const ObjCInterfaceDecl* D, Selector S) { + // Do a lookup with the (D,S) pair. If we find a match return + // the iterator. + ObjCSummaryKey K(D, S); + MapTy::iterator I = M.find(K); + + if (I != M.end() || !D) + return I->second; + + // Walk the super chain. If we find a hit with a parent, we'll end + // up returning that summary. We actually allow that key (null,S), as + // we cache summaries for the null ObjCInterfaceDecl* to allow us to + // generate initial summaries without having to worry about NSObject + // being declared. + // FIXME: We may change this at some point. + for (ObjCInterfaceDecl* C=D->getSuperClass() ;; C=C->getSuperClass()) { + if ((I = M.find(ObjCSummaryKey(C, S))) != M.end()) + break; + + if (!C) + return NULL; + } + + // Cache the summary with original key to make the next lookup faster + // and return the iterator. + RetainSummary *Summ = I->second; + M[K] = Summ; + return Summ; + } + + RetainSummary* find(IdentifierInfo* II, Selector S) { + // FIXME: Class method lookup. Right now we dont' have a good way + // of going between IdentifierInfo* and the class hierarchy. + MapTy::iterator I = M.find(ObjCSummaryKey(II, S)); + + if (I == M.end()) + I = M.find(ObjCSummaryKey(S)); + + return I == M.end() ? NULL : I->second; + } + + RetainSummary*& operator[](ObjCSummaryKey K) { + return M[K]; + } + + RetainSummary*& operator[](Selector S) { + return M[ ObjCSummaryKey(S) ]; + } +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Data structures for managing collections of summaries. +//===----------------------------------------------------------------------===// + +namespace { +class RetainSummaryManager { + + //==-----------------------------------------------------------------==// + // Typedefs. + //==-----------------------------------------------------------------==// + + typedef llvm::DenseMap + FuncSummariesTy; + + typedef ObjCSummaryCache ObjCMethodSummariesTy; + + //==-----------------------------------------------------------------==// + // Data. + //==-----------------------------------------------------------------==// + + /// Ctx - The ASTContext object for the analyzed ASTs. + ASTContext& Ctx; + + /// CFDictionaryCreateII - An IdentifierInfo* representing the indentifier + /// "CFDictionaryCreate". + IdentifierInfo* CFDictionaryCreateII; + + /// GCEnabled - Records whether or not the analyzed code runs in GC mode. + const bool GCEnabled; + + /// FuncSummaries - A map from FunctionDecls to summaries. + FuncSummariesTy FuncSummaries; + + /// ObjCClassMethodSummaries - A map from selectors (for instance methods) + /// to summaries. + ObjCMethodSummariesTy ObjCClassMethodSummaries; + + /// ObjCMethodSummaries - A map from selectors to summaries. + ObjCMethodSummariesTy ObjCMethodSummaries; + + /// BPAlloc - A BumpPtrAllocator used for allocating summaries, ArgEffects, + /// and all other data used by the checker. + llvm::BumpPtrAllocator BPAlloc; + + /// AF - A factory for ArgEffects objects. + ArgEffects::Factory AF; + + /// ScratchArgs - A holding buffer for construct ArgEffects. + ArgEffects ScratchArgs; + + /// ObjCAllocRetE - Default return effect for methods returning Objective-C + /// objects. + RetEffect ObjCAllocRetE; + + /// ObjCInitRetE - Default return effect for init methods returning + /// Objective-C objects. + RetEffect ObjCInitRetE; + + RetainSummary DefaultSummary; + RetainSummary* StopSummary; + + //==-----------------------------------------------------------------==// + // Methods. + //==-----------------------------------------------------------------==// + + /// getArgEffects - Returns a persistent ArgEffects object based on the + /// data in ScratchArgs. + ArgEffects getArgEffects(); + + enum UnaryFuncKind { cfretain, cfrelease, cfmakecollectable }; + +public: + RetEffect getObjAllocRetEffect() const { return ObjCAllocRetE; } + + RetainSummary *getDefaultSummary() { + RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate(); + return new (Summ) RetainSummary(DefaultSummary); + } + + RetainSummary* getUnarySummary(const FunctionType* FT, UnaryFuncKind func); + + RetainSummary* getCFSummaryCreateRule(const FunctionDecl* FD); + RetainSummary* getCFSummaryGetRule(const FunctionDecl* FD); + RetainSummary* getCFCreateGetRuleSummary(const FunctionDecl* FD, + StringRef FName); + + RetainSummary* getPersistentSummary(ArgEffects AE, RetEffect RetEff, + ArgEffect ReceiverEff = DoNothing, + ArgEffect DefaultEff = MayEscape, + bool isEndPath = false); + + RetainSummary* getPersistentSummary(RetEffect RE, + ArgEffect ReceiverEff = DoNothing, + ArgEffect DefaultEff = MayEscape) { + return getPersistentSummary(getArgEffects(), RE, ReceiverEff, DefaultEff); + } + + RetainSummary *getPersistentStopSummary() { + if (StopSummary) + return StopSummary; + + StopSummary = getPersistentSummary(RetEffect::MakeNoRet(), + StopTracking, StopTracking); + + return StopSummary; + } + + RetainSummary *getInitMethodSummary(QualType RetTy); + + void InitializeClassMethodSummaries(); + void InitializeMethodSummaries(); +private: + void addNSObjectClsMethSummary(Selector S, RetainSummary *Summ) { + ObjCClassMethodSummaries[S] = Summ; + } + + void addNSObjectMethSummary(Selector S, RetainSummary *Summ) { + ObjCMethodSummaries[S] = Summ; + } + + void addClassMethSummary(const char* Cls, const char* nullaryName, + RetainSummary *Summ) { + IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); + Selector S = GetNullarySelector(nullaryName, Ctx); + ObjCClassMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; + } + + void addInstMethSummary(const char* Cls, const char* nullaryName, + RetainSummary *Summ) { + IdentifierInfo* ClsII = &Ctx.Idents.get(Cls); + Selector S = GetNullarySelector(nullaryName, Ctx); + ObjCMethodSummaries[ObjCSummaryKey(ClsII, S)] = Summ; + } + + Selector generateSelector(va_list argp) { + llvm::SmallVector II; + + while (const char* s = va_arg(argp, const char*)) + II.push_back(&Ctx.Idents.get(s)); + + return Ctx.Selectors.getSelector(II.size(), &II[0]); + } + + void addMethodSummary(IdentifierInfo *ClsII, ObjCMethodSummariesTy& Summaries, + RetainSummary* Summ, va_list argp) { + Selector S = generateSelector(argp); + Summaries[ObjCSummaryKey(ClsII, S)] = Summ; + } + + void addInstMethSummary(const char* Cls, RetainSummary* Summ, ...) { + va_list argp; + va_start(argp, Summ); + addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp); + va_end(argp); + } + + void addClsMethSummary(const char* Cls, RetainSummary* Summ, ...) { + va_list argp; + va_start(argp, Summ); + addMethodSummary(&Ctx.Idents.get(Cls),ObjCClassMethodSummaries, Summ, argp); + va_end(argp); + } + + void addClsMethSummary(IdentifierInfo *II, RetainSummary* Summ, ...) { + va_list argp; + va_start(argp, Summ); + addMethodSummary(II, ObjCClassMethodSummaries, Summ, argp); + va_end(argp); + } + + void addPanicSummary(const char* Cls, ...) { + RetainSummary* Summ = getPersistentSummary(AF.getEmptyMap(), + RetEffect::MakeNoRet(), + DoNothing, DoNothing, true); + va_list argp; + va_start (argp, Cls); + addMethodSummary(&Ctx.Idents.get(Cls), ObjCMethodSummaries, Summ, argp); + va_end(argp); + } + +public: + + RetainSummaryManager(ASTContext& ctx, bool gcenabled) + : Ctx(ctx), + CFDictionaryCreateII(&ctx.Idents.get("CFDictionaryCreate")), + GCEnabled(gcenabled), AF(BPAlloc), ScratchArgs(AF.getEmptyMap()), + ObjCAllocRetE(gcenabled ? RetEffect::MakeGCNotOwned() + : RetEffect::MakeOwned(RetEffect::ObjC, true)), + ObjCInitRetE(gcenabled ? RetEffect::MakeGCNotOwned() + : RetEffect::MakeOwnedWhenTrackedReceiver()), + DefaultSummary(AF.getEmptyMap() /* per-argument effects (none) */, + RetEffect::MakeNoRet() /* return effect */, + MayEscape, /* default argument effect */ + DoNothing /* receiver effect */), + StopSummary(0) { + + InitializeClassMethodSummaries(); + InitializeMethodSummaries(); + } + + ~RetainSummaryManager(); + + RetainSummary* getSummary(const FunctionDecl* FD); + + RetainSummary *getInstanceMethodSummary(const ObjCMessageExpr *ME, + const GRState *state, + const LocationContext *LC); + + RetainSummary* getInstanceMethodSummary(const ObjCMessageExpr* ME, + const ObjCInterfaceDecl* ID) { + return getInstanceMethodSummary(ME->getSelector(), 0, + ID, ME->getMethodDecl(), ME->getType()); + } + + RetainSummary* getInstanceMethodSummary(Selector S, IdentifierInfo *ClsName, + const ObjCInterfaceDecl* ID, + const ObjCMethodDecl *MD, + QualType RetTy); + + RetainSummary *getClassMethodSummary(Selector S, IdentifierInfo *ClsName, + const ObjCInterfaceDecl *ID, + const ObjCMethodDecl *MD, + QualType RetTy); + + RetainSummary *getClassMethodSummary(const ObjCMessageExpr *ME) { + ObjCInterfaceDecl *Class = 0; + switch (ME->getReceiverKind()) { + case ObjCMessageExpr::Class: + case ObjCMessageExpr::SuperClass: + Class = ME->getReceiverInterface(); + break; + + case ObjCMessageExpr::Instance: + case ObjCMessageExpr::SuperInstance: + break; + } + + return getClassMethodSummary(ME->getSelector(), + Class? Class->getIdentifier() : 0, + Class, + ME->getMethodDecl(), ME->getType()); + } + + /// getMethodSummary - This version of getMethodSummary is used to query + /// the summary for the current method being analyzed. + RetainSummary *getMethodSummary(const ObjCMethodDecl *MD) { + // FIXME: Eventually this should be unneeded. + const ObjCInterfaceDecl *ID = MD->getClassInterface(); + Selector S = MD->getSelector(); + IdentifierInfo *ClsName = ID->getIdentifier(); + QualType ResultTy = MD->getResultType(); + + // Resolve the method decl last. + if (const ObjCMethodDecl *InterfaceMD = ResolveToInterfaceMethodDecl(MD)) + MD = InterfaceMD; + + if (MD->isInstanceMethod()) + return getInstanceMethodSummary(S, ClsName, ID, MD, ResultTy); + else + return getClassMethodSummary(S, ClsName, ID, MD, ResultTy); + } + + RetainSummary* getCommonMethodSummary(const ObjCMethodDecl* MD, + Selector S, QualType RetTy); + + void updateSummaryFromAnnotations(RetainSummary &Summ, + const ObjCMethodDecl *MD); + + void updateSummaryFromAnnotations(RetainSummary &Summ, + const FunctionDecl *FD); + + bool isGCEnabled() const { return GCEnabled; } + + RetainSummary *copySummary(RetainSummary *OldSumm) { + RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate(); + new (Summ) RetainSummary(*OldSumm); + return Summ; + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Implementation of checker data structures. +//===----------------------------------------------------------------------===// + +RetainSummaryManager::~RetainSummaryManager() {} + +ArgEffects RetainSummaryManager::getArgEffects() { + ArgEffects AE = ScratchArgs; + ScratchArgs = AF.getEmptyMap(); + return AE; +} + +RetainSummary* +RetainSummaryManager::getPersistentSummary(ArgEffects AE, RetEffect RetEff, + ArgEffect ReceiverEff, + ArgEffect DefaultEff, + bool isEndPath) { + // Create the summary and return it. + RetainSummary *Summ = (RetainSummary*) BPAlloc.Allocate(); + new (Summ) RetainSummary(AE, RetEff, DefaultEff, ReceiverEff, isEndPath); + return Summ; +} + +//===----------------------------------------------------------------------===// +// Summary creation for functions (largely uses of Core Foundation). +//===----------------------------------------------------------------------===// + +static bool isRetain(const FunctionDecl* FD, StringRef FName) { + return FName.endswith("Retain"); +} + +static bool isRelease(const FunctionDecl* FD, StringRef FName) { + return FName.endswith("Release"); +} + +RetainSummary* RetainSummaryManager::getSummary(const FunctionDecl* FD) { + // 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. + RetainSummary *S = 0; + + do { + // We generate "stop" summaries for implicitly defined functions. + if (FD->isImplicit()) { + S = getPersistentStopSummary(); + break; + } + + // [PR 3337] Use 'getAs' to strip away any typedefs on the + // function's type. + const FunctionType* FT = FD->getType()->getAs(); + const IdentifierInfo *II = FD->getIdentifier(); + if (!II) + break; + + StringRef FName = 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. + QualType RetTy = FT->getResultType(); + + // FIXME: This should all be refactored into a chain of "summary lookup" + // filters. + assert(ScratchArgs.isEmpty()); + + if (FName == "pthread_create") { + // Part of: . This will be addressed + // better with IPA. + S = getPersistentStopSummary(); + } else if (FName == "NSMakeCollectable") { + // Handle: id NSMakeCollectable(CFTypeRef) + S = (RetTy->isObjCIdType()) + ? getUnarySummary(FT, cfmakecollectable) + : getPersistentStopSummary(); + } else if (FName == "IOBSDNameMatching" || + FName == "IOServiceMatching" || + FName == "IOServiceNameMatching" || + FName == "IORegistryEntryIDMatching" || + FName == "IOOpenFirmwarePathMatching") { + // Part of . (IOKit) + // This should be addressed using a API table. + S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), + DoNothing, DoNothing); + } else if (FName == "IOServiceGetMatchingService" || + FName == "IOServiceGetMatchingServices") { + // FIXES: + // 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, DecRef); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } else if (FName == "IOServiceAddNotification" || + FName == "IOServiceAddMatchingNotification") { + // Part of . (IOKit) + // This should be addressed using a API table. + ScratchArgs = AF.add(ScratchArgs, 2, DecRef); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } else if (FName == "CVPixelBufferCreateWithBytes") { + // FIXES: + // 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, StopTracking); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } else if (FName == "CGBitmapContextCreateWithData") { + // FIXES: + // 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, StopTracking); + S = getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true), + DoNothing, DoNothing); + } else if (FName == "CVPixelBufferCreateWithPlanarBytes") { + // FIXES: + // 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, StopTracking); + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, DoNothing); + } + + // Did we get a summary? + if (S) + break; + + // Enable this code once the semantics of NSDeallocateObject are resolved + // for GC. +#if 0 + // Handle: NSDeallocateObject(id anObject); + // This method does allow 'nil' (although we don't check it now). + if (strcmp(FName, "NSDeallocateObject") == 0) { + return RetTy == Ctx.VoidTy + ? getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, Dealloc) + : getPersistentStopSummary(); + } +#endif + + if (RetTy->isPointerType()) { + // For CoreFoundation ('CF') types. + if (cocoa::isRefType(RetTy, "CF", FName)) { + if (isRetain(FD, FName)) + S = getUnarySummary(FT, cfretain); + else if (FName.find("MakeCollectable") != StringRef::npos) + S = getUnarySummary(FT, cfmakecollectable); + else + S = getCFCreateGetRuleSummary(FD, FName); + + break; + } + + // For CoreGraphics ('CG') types. + if (cocoa::isRefType(RetTy, "CG", FName)) { + if (isRetain(FD, FName)) + S = getUnarySummary(FT, cfretain); + else + S = getCFCreateGetRuleSummary(FD, FName); + + break; + } + + // For the Disk Arbitration API (DiskArbitration/DADisk.h) + if (cocoa::isRefType(RetTy, "DADisk") || + cocoa::isRefType(RetTy, "DADissenter") || + cocoa::isRefType(RetTy, "DASessionRef")) { + S = getCFCreateGetRuleSummary(FD, FName); + break; + } + + break; + } + + // Check for release functions, the only kind of functions that we care + // about that don't return a pointer type. + if (FName[0] == 'C' && (FName[1] == 'F' || FName[1] == 'G')) { + // Test for 'CGCF'. + FName = FName.substr(FName.startswith("CGCF") ? 4 : 2); + + if (isRelease(FD, FName)) + S = getUnarySummary(FT, cfrelease); + 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. + ArgEffect 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; + + S = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, E); + } + } + } + while (0); + + if (!S) + S = getDefaultSummary(); + + // Annotations override defaults. + assert(S); + updateSummaryFromAnnotations(*S, FD); + + FuncSummaries[FD] = S; + return S; +} + +RetainSummary* +RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl* FD, + StringRef FName) { + + if (FName.find("Create") != StringRef::npos || + FName.find("Copy") != StringRef::npos) + return getCFSummaryCreateRule(FD); + + if (FName.find("Get") != StringRef::npos) + return getCFSummaryGetRule(FD); + + return getDefaultSummary(); +} + +RetainSummary* +RetainSummaryManager::getUnarySummary(const FunctionType* FT, + UnaryFuncKind func) { + + // Sanity check that this is *really* a unary function. This can + // happen if people do weird things. + const FunctionProtoType* FTP = dyn_cast(FT); + if (!FTP || FTP->getNumArgs() != 1) + return getPersistentStopSummary(); + + assert (ScratchArgs.isEmpty()); + + switch (func) { + case cfretain: { + ScratchArgs = AF.add(ScratchArgs, 0, IncRef); + return getPersistentSummary(RetEffect::MakeAlias(0), + DoNothing, DoNothing); + } + + case cfrelease: { + ScratchArgs = AF.add(ScratchArgs, 0, DecRef); + return getPersistentSummary(RetEffect::MakeNoRet(), + DoNothing, DoNothing); + } + + case cfmakecollectable: { + ScratchArgs = AF.add(ScratchArgs, 0, MakeCollectable); + return getPersistentSummary(RetEffect::MakeAlias(0),DoNothing, DoNothing); + } + + default: + assert (false && "Not a supported unary function."); + return getDefaultSummary(); + } +} + +RetainSummary* +RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl* FD) { + assert (ScratchArgs.isEmpty()); + + if (FD->getIdentifier() == CFDictionaryCreateII) { + ScratchArgs = AF.add(ScratchArgs, 1, DoNothingByRef); + ScratchArgs = AF.add(ScratchArgs, 2, DoNothingByRef); + } + + return getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); +} + +RetainSummary* +RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl* FD) { + assert (ScratchArgs.isEmpty()); + return getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::CF), + DoNothing, DoNothing); +} + +//===----------------------------------------------------------------------===// +// Summary creation for Selectors. +//===----------------------------------------------------------------------===// + +RetainSummary* +RetainSummaryManager::getInitMethodSummary(QualType RetTy) { + assert(ScratchArgs.isEmpty()); + // 'init' methods conceptually return a newly allocated object and claim + // the receiver. + if (cocoa::isCocoaObjectRef(RetTy) || cocoa::isCFObjectRef(RetTy)) + return getPersistentSummary(ObjCInitRetE, DecRefMsg); + + return getDefaultSummary(); +} + +void +RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, + const FunctionDecl *FD) { + if (!FD) + return; + + QualType RetTy = FD->getResultType(); + + // Determine if there is a special return effect for this method. + if (cocoa::isCocoaObjectRef(RetTy)) { + if (FD->getAttr()) { + Summ.setRetEffect(ObjCAllocRetE); + } + else if (FD->getAttr()) { + Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); + } + else if (FD->getAttr()) { + Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); + } + else if (FD->getAttr()) { + Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); + } + } + else if (RetTy->getAs()) { + if (FD->getAttr()) { + Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); + } + } +} + +void +RetainSummaryManager::updateSummaryFromAnnotations(RetainSummary &Summ, + const ObjCMethodDecl *MD) { + if (!MD) + return; + + bool isTrackedLoc = false; + + // Determine if there is a special return effect for this method. + if (cocoa::isCocoaObjectRef(MD->getResultType())) { + if (MD->getAttr()) { + Summ.setRetEffect(ObjCAllocRetE); + return; + } + if (MD->getAttr()) { + Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::ObjC)); + return; + } + + isTrackedLoc = true; + } + + if (!isTrackedLoc) + isTrackedLoc = MD->getResultType()->getAs() != NULL; + + if (isTrackedLoc) { + if (MD->getAttr()) + Summ.setRetEffect(RetEffect::MakeOwned(RetEffect::CF, true)); + else if (MD->getAttr()) + Summ.setRetEffect(RetEffect::MakeNotOwned(RetEffect::CF)); + } +} + +RetainSummary* +RetainSummaryManager::getCommonMethodSummary(const ObjCMethodDecl* MD, + Selector S, QualType RetTy) { + + if (MD) { + // Scan the method decl for 'void*' arguments. These should be treated + // as 'StopTracking' because they are often used with delegates. + // Delegates are a frequent form of false positives with the retain + // count checker. + unsigned i = 0; + for (ObjCMethodDecl::param_iterator I = MD->param_begin(), + E = MD->param_end(); I != E; ++I, ++i) + if (ParmVarDecl *PD = *I) { + QualType Ty = Ctx.getCanonicalType(PD->getType()); + if (Ty.getLocalUnqualifiedType() == Ctx.VoidPtrTy) + ScratchArgs = AF.add(ScratchArgs, i, StopTracking); + } + } + + // Any special effect for the receiver? + ArgEffect ReceiverEff = DoNothing; + + // 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()) { + const std::string &str = S.getAsString(); + assert(!str.empty()); + if (StrInStrNoCase(str, "delegate:") != StringRef::npos) + ReceiverEff = StopTracking; + } + + // Look for methods that return an owned object. + if (cocoa::isCocoaObjectRef(RetTy)) { + // EXPERIMENTAL: assume the Cocoa conventions for all objects returned + // by instance methods. + RetEffect E = cocoa::followsFundamentalRule(S) + ? ObjCAllocRetE : RetEffect::MakeNotOwned(RetEffect::ObjC); + + return getPersistentSummary(E, ReceiverEff, MayEscape); + } + + // Look for methods that return an owned core foundation object. + if (cocoa::isCFObjectRef(RetTy)) { + RetEffect E = cocoa::followsFundamentalRule(S) + ? RetEffect::MakeOwned(RetEffect::CF, true) + : RetEffect::MakeNotOwned(RetEffect::CF); + + return getPersistentSummary(E, ReceiverEff, MayEscape); + } + + if (ScratchArgs.isEmpty() && ReceiverEff == DoNothing) + return getDefaultSummary(); + + return getPersistentSummary(RetEffect::MakeNoRet(), ReceiverEff, MayEscape); +} + +RetainSummary* +RetainSummaryManager::getInstanceMethodSummary(const ObjCMessageExpr *ME, + const GRState *state, + const LocationContext *LC) { + + // We need the type-information of the tracked receiver object + // Retrieve it from the state. + const Expr *Receiver = ME->getInstanceReceiver(); + const ObjCInterfaceDecl* ID = 0; + + // FIXME: Is this really working as expected? There are cases where + // we just use the 'ID' from the message expression. + SVal receiverV; + + if (Receiver) { + receiverV = state->getSValAsScalarOrLoc(Receiver); + + // FIXME: Eventually replace the use of state->get with + // a generic API for reasoning about the Objective-C types of symbolic + // objects. + if (SymbolRef Sym = receiverV.getAsLocSymbol()) + if (const RefVal *T = state->get(Sym)) + if (const ObjCObjectPointerType* PT = + T->getType()->getAs()) + ID = PT->getInterfaceDecl(); + + // FIXME: this is a hack. This may or may not be the actual method + // that is called. + if (!ID) { + if (const ObjCObjectPointerType *PT = + Receiver->getType()->getAs()) + ID = PT->getInterfaceDecl(); + } + } else { + // FIXME: Hack for 'super'. + ID = ME->getReceiverInterface(); + } + + // FIXME: The receiver could be a reference to a class, meaning that + // we should use the class method. + RetainSummary *Summ = getInstanceMethodSummary(ME, ID); + + // Special-case: are we sending a mesage to "self"? + // This is a hack. When we have full-IP this should be removed. + if (isa(LC->getDecl()) && Receiver) { + if (const loc::MemRegionVal *L = dyn_cast(&receiverV)) { + // Get the region associated with 'self'. + if (const ImplicitParamDecl *SelfDecl = LC->getSelfDecl()) { + SVal SelfVal = state->getSVal(state->getRegion(SelfDecl, LC)); + if (L->StripCasts() == SelfVal.getAsRegion()) { + // Update the summary to make the default argument effect + // 'StopTracking'. + Summ = copySummary(Summ); + Summ->setDefaultArgEffect(StopTracking); + } + } + } + } + + return Summ ? Summ : getDefaultSummary(); +} + +RetainSummary* +RetainSummaryManager::getInstanceMethodSummary(Selector S, + IdentifierInfo *ClsName, + const ObjCInterfaceDecl* ID, + const ObjCMethodDecl *MD, + QualType RetTy) { + + // Look up a summary in our summary cache. + RetainSummary *Summ = ObjCMethodSummaries.find(ID, ClsName, S); + + if (!Summ) { + assert(ScratchArgs.isEmpty()); + + // "initXXX": pass-through for receiver. + if (cocoa::deriveNamingConvention(S) == cocoa::InitRule) + Summ = getInitMethodSummary(RetTy); + else + Summ = getCommonMethodSummary(MD, S, RetTy); + + // Annotations override defaults. + updateSummaryFromAnnotations(*Summ, MD); + + // Memoize the summary. + ObjCMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; + } + + return Summ; +} + +RetainSummary* +RetainSummaryManager::getClassMethodSummary(Selector S, IdentifierInfo *ClsName, + const ObjCInterfaceDecl *ID, + const ObjCMethodDecl *MD, + QualType RetTy) { + + assert(ClsName && "Class name must be specified."); + RetainSummary *Summ = ObjCClassMethodSummaries.find(ID, ClsName, S); + + if (!Summ) { + Summ = getCommonMethodSummary(MD, S, RetTy); + // Annotations override defaults. + updateSummaryFromAnnotations(*Summ, MD); + // Memoize the summary. + ObjCClassMethodSummaries[ObjCSummaryKey(ID, ClsName, S)] = Summ; + } + + return Summ; +} + +void RetainSummaryManager::InitializeClassMethodSummaries() { + assert(ScratchArgs.isEmpty()); + RetainSummary* Summ = getPersistentSummary(ObjCAllocRetE); + + // Create the [NSAssertionHandler currentHander] summary. + addClassMethSummary("NSAssertionHandler", "currentHandler", + getPersistentSummary(RetEffect::MakeNotOwned(RetEffect::ObjC))); + + // Create the [NSAutoreleasePool addObject:] summary. + ScratchArgs = AF.add(ScratchArgs, 0, Autorelease); + addClassMethSummary("NSAutoreleasePool", "addObject", + getPersistentSummary(RetEffect::MakeNoRet(), + DoNothing, Autorelease)); + + // Create the summaries for [NSObject performSelector...]. We treat + // these as 'stop tracking' for the arguments because they are often + // used for delegates that can release the object. When we have better + // inter-procedural analysis we can potentially do something better. This + // workaround is to remove false positives. + Summ = getPersistentSummary(RetEffect::MakeNoRet(), DoNothing, StopTracking); + IdentifierInfo *NSObjectII = &Ctx.Idents.get("NSObject"); + addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", + "afterDelay", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelector", "withObject", + "afterDelay", "inModes", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", + "withObject", "waitUntilDone", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelectorOnMainThread", + "withObject", "waitUntilDone", "modes", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", + "withObject", "waitUntilDone", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelector", "onThread", + "withObject", "waitUntilDone", "modes", NULL); + addClsMethSummary(NSObjectII, Summ, "performSelectorInBackground", + "withObject", NULL); +} + +void RetainSummaryManager::InitializeMethodSummaries() { + + assert (ScratchArgs.isEmpty()); + + // Create the "init" selector. It just acts as a pass-through for the + // receiver. + RetainSummary *InitSumm = getPersistentSummary(ObjCInitRetE, DecRefMsg); + 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. + RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE); + RetainSummary *CFAllocSumm = + getPersistentSummary(RetEffect::MakeOwned(RetEffect::CF, true)); + + // Create the "retain" selector. + RetEffect E = RetEffect::MakeReceiverAlias(); + RetainSummary *Summ = getPersistentSummary(E, IncRefMsg); + addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ); + + // Create the "release" selector. + Summ = getPersistentSummary(E, DecRefMsg); + addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ); + + // Create the "drain" selector. + Summ = getPersistentSummary(E, isGCEnabled() ? DoNothing : DecRef); + addNSObjectMethSummary(GetNullarySelector("drain", Ctx), Summ); + + // Create the -dealloc summary. + Summ = getPersistentSummary(RetEffect::MakeNoRet(), Dealloc); + addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ); + + // Create the "autorelease" selector. + Summ = getPersistentSummary(E, Autorelease); + addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ); + + // Specially handle NSAutoreleasePool. + addInstMethSummary("NSAutoreleasePool", "init", + getPersistentSummary(RetEffect::MakeReceiverAlias(), + NewAutoreleasePool)); + + // 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 . + // See also http://llvm.org/bugs/show_bug.cgi?id=3714. + RetainSummary *NoTrackYet = getPersistentSummary(RetEffect::MakeNoRet(), + StopTracking, + StopTracking); + + addClassMethSummary("NSWindow", "alloc", NoTrackYet); + +#if 0 + addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", + "styleMask", "backing", "defer", NULL); + + addInstMethSummary("NSWindow", NoTrackYet, "initWithContentRect", + "styleMask", "backing", "defer", "screen", NULL); +#endif + + // 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); + +#if 0 + addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", + "styleMask", "backing", "defer", NULL); + + addInstMethSummary("NSPanel", NoTrackYet, "initWithContentRect", + "styleMask", "backing", "defer", "screen", NULL); +#endif + + // Don't track allocated autorelease pools yet, as it is okay to prematurely + // exit a method. + addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet); + + // Create NSAssertionHandler summaries. + addPanicSummary("NSAssertionHandler", "handleFailureInFunction", "file", + "lineNumber", "description", NULL); + + addPanicSummary("NSAssertionHandler", "handleFailureInMethod", "object", + "file", "lineNumber", "description", NULL); + + // Create summaries QCRenderer/QCView -createSnapShotImageOfType: + addInstMethSummary("QCRenderer", AllocSumm, + "createSnapshotImageOfType", NULL); + addInstMethSummary("QCView", AllocSumm, + "createSnapshotImageOfType", NULL); + + // Create summaries for CIContext, 'createCGImage' and + // 'createCGLayerWithSize'. These objects are CF objects, and are not + // automatically garbage collected. + addInstMethSummary("CIContext", CFAllocSumm, + "createCGImage", "fromRect", NULL); + addInstMethSummary("CIContext", CFAllocSumm, + "createCGImage", "fromRect", "format", "colorSpace", NULL); + addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", + "info", NULL); +} + +//===----------------------------------------------------------------------===// +// AutoreleaseBindings - State used to track objects in autorelease pools. +//===----------------------------------------------------------------------===// + +typedef llvm::ImmutableMap ARCounts; +typedef llvm::ImmutableMap ARPoolContents; +typedef llvm::ImmutableList ARStack; + +static int AutoRCIndex = 0; +static int AutoRBIndex = 0; + +namespace { class AutoreleasePoolContents {}; } +namespace { class AutoreleaseStack {}; } + +namespace clang { +namespace ento { +template<> struct GRStateTrait + : public GRStatePartialTrait { + static inline void* GDMIndex() { return &AutoRBIndex; } +}; + +template<> struct GRStateTrait + : public GRStatePartialTrait { + static inline void* GDMIndex() { return &AutoRCIndex; } +}; +} // end GR namespace +} // end clang namespace + +static SymbolRef GetCurrentAutoreleasePool(const GRState* state) { + ARStack stack = state->get(); + return stack.isEmpty() ? SymbolRef() : stack.getHead(); +} + +static const GRState * SendAutorelease(const GRState *state, + ARCounts::Factory &F, SymbolRef sym) { + + SymbolRef pool = GetCurrentAutoreleasePool(state); + const ARCounts *cnts = state->get(pool); + ARCounts newCnts(0); + + if (cnts) { + const unsigned *cnt = (*cnts).lookup(sym); + newCnts = F.add(*cnts, sym, cnt ? *cnt + 1 : 1); + } + else + newCnts = F.add(F.getEmptyMap(), sym, 1); + + return state->set(pool, newCnts); +} + +//===----------------------------------------------------------------------===// +// Transfer functions. +//===----------------------------------------------------------------------===// + +namespace { + +class CFRefCount : public TransferFuncs { +public: + class BindingsPrinter : public GRState::Printer { + public: + virtual void Print(llvm::raw_ostream& Out, const GRState* state, + const char* nl, const char* sep); + }; + +private: + typedef llvm::DenseMap + SummaryLogTy; + + RetainSummaryManager Summaries; + SummaryLogTy SummaryLog; + const LangOptions& LOpts; + ARCounts::Factory ARCountFactory; + + BugType *useAfterRelease, *releaseNotOwned; + BugType *deallocGC, *deallocNotOwned; + BugType *leakWithinFunction, *leakAtReturn; + BugType *overAutorelease; + BugType *returnNotOwnedForOwned; + BugReporter *BR; + + const GRState * Update(const GRState * state, SymbolRef sym, RefVal V, ArgEffect E, + RefVal::Kind& hasErr); + + void ProcessNonLeakError(ExplodedNodeSet& Dst, + StmtNodeBuilder& Builder, + const Expr* NodeExpr, SourceRange ErrorRange, + ExplodedNode* Pred, + const GRState* St, + RefVal::Kind hasErr, SymbolRef Sym); + + const GRState * HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V, + llvm::SmallVectorImpl &Leaked); + + ExplodedNode* ProcessLeaks(const GRState * state, + llvm::SmallVectorImpl &Leaked, + GenericNodeBuilder &Builder, + ExprEngine &Eng, + ExplodedNode *Pred = 0); + +public: + CFRefCount(ASTContext& Ctx, bool gcenabled, const LangOptions& lopts) + : Summaries(Ctx, gcenabled), + LOpts(lopts), useAfterRelease(0), releaseNotOwned(0), + deallocGC(0), deallocNotOwned(0), + leakWithinFunction(0), leakAtReturn(0), overAutorelease(0), + returnNotOwnedForOwned(0), BR(0) {} + + virtual ~CFRefCount() {} + + void RegisterChecks(ExprEngine &Eng); + + virtual void RegisterPrinters(std::vector& Printers) { + Printers.push_back(new BindingsPrinter()); + } + + bool isGCEnabled() const { return Summaries.isGCEnabled(); } + const LangOptions& getLangOptions() const { return LOpts; } + + const RetainSummary *getSummaryOfNode(const ExplodedNode *N) const { + SummaryLogTy::const_iterator I = SummaryLog.find(N); + return I == SummaryLog.end() ? 0 : I->second; + } + + // Calls. + + void evalSummary(ExplodedNodeSet& Dst, + ExprEngine& Eng, + StmtNodeBuilder& Builder, + const Expr* Ex, + InstanceReceiver Receiver, + const RetainSummary& Summ, + const MemRegion *Callee, + ConstExprIterator arg_beg, ConstExprIterator arg_end, + ExplodedNode* Pred, const GRState *state); + + virtual void evalCall(ExplodedNodeSet& Dst, + ExprEngine& Eng, + StmtNodeBuilder& Builder, + const CallExpr* CE, SVal L, + ExplodedNode* Pred); + + + virtual void evalObjCMessageExpr(ExplodedNodeSet& Dst, + ExprEngine& Engine, + StmtNodeBuilder& Builder, + const ObjCMessageExpr* ME, + ExplodedNode* Pred, + const GRState *state); + // Stores. + virtual void evalBind(StmtNodeBuilderRef& B, SVal location, SVal val); + + // End-of-path. + + virtual void evalEndPath(ExprEngine& Engine, + EndPathNodeBuilder& Builder); + + virtual void evalDeadSymbols(ExplodedNodeSet& Dst, + ExprEngine& Engine, + StmtNodeBuilder& Builder, + ExplodedNode* Pred, + const GRState* state, + SymbolReaper& SymReaper); + + std::pair + HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilder Bd, + ExplodedNode* Pred, ExprEngine &Eng, + SymbolRef Sym, RefVal V, bool &stop); + // Return statements. + + virtual void evalReturn(ExplodedNodeSet& Dst, + ExprEngine& Engine, + StmtNodeBuilder& Builder, + const ReturnStmt* S, + ExplodedNode* Pred); + + // Assumptions. + + virtual const GRState *evalAssume(const GRState* state, SVal condition, + bool assumption); +}; + +} // end anonymous namespace + +static void PrintPool(llvm::raw_ostream &Out, SymbolRef Sym, + const GRState *state) { + Out << ' '; + if (Sym) + Out << Sym->getSymbolID(); + else + Out << ""; + Out << ":{"; + + // Get the contents of the pool. + if (const ARCounts *cnts = state->get(Sym)) + for (ARCounts::iterator J=cnts->begin(), EJ=cnts->end(); J != EJ; ++J) + Out << '(' << J.getKey() << ',' << J.getData() << ')'; + + Out << '}'; +} + +void CFRefCount::BindingsPrinter::Print(llvm::raw_ostream& Out, + const GRState* state, + const char* nl, const char* sep) { + + RefBindings B = state->get(); + + if (!B.isEmpty()) + Out << sep << nl; + + for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { + Out << (*I).first << " : "; + (*I).second.print(Out); + Out << nl; + } + + // Print the autorelease stack. + Out << sep << nl << "AR pool stack:"; + ARStack stack = state->get(); + + PrintPool(Out, SymbolRef(), state); // Print the caller's pool. + for (ARStack::iterator I=stack.begin(), E=stack.end(); I!=E; ++I) + PrintPool(Out, *I, state); + + Out << nl; +} + +//===----------------------------------------------------------------------===// +// Error reporting. +//===----------------------------------------------------------------------===// + +namespace { + + //===-------------===// + // Bug Descriptions. // + //===-------------===// + + class CFRefBug : public BugType { + protected: + CFRefCount& TF; + + CFRefBug(CFRefCount* tf, llvm::StringRef name) + : BugType(name, "Memory (Core Foundation/Objective-C)"), TF(*tf) {} + public: + + CFRefCount& getTF() { return TF; } + + // FIXME: Eventually remove. + virtual const char* getDescription() const = 0; + + virtual bool isLeak() const { return false; } + }; + + class UseAfterRelease : public CFRefBug { + public: + UseAfterRelease(CFRefCount* tf) + : CFRefBug(tf, "Use-after-release") {} + + const char* getDescription() const { + return "Reference-counted object is used after it is released"; + } + }; + + class BadRelease : public CFRefBug { + public: + BadRelease(CFRefCount* tf) : CFRefBug(tf, "Bad release") {} + + const char* getDescription() const { + return "Incorrect decrement of the reference count of an object that is " + "not owned at this point by the caller"; + } + }; + + class DeallocGC : public CFRefBug { + public: + DeallocGC(CFRefCount *tf) + : CFRefBug(tf, "-dealloc called while using garbage collection") {} + + const char *getDescription() const { + return "-dealloc called while using garbage collection"; + } + }; + + class DeallocNotOwned : public CFRefBug { + public: + DeallocNotOwned(CFRefCount *tf) + : CFRefBug(tf, "-dealloc sent to non-exclusively owned object") {} + + const char *getDescription() const { + return "-dealloc sent to object that may be referenced elsewhere"; + } + }; + + class OverAutorelease : public CFRefBug { + public: + OverAutorelease(CFRefCount *tf) : + CFRefBug(tf, "Object sent -autorelease too many times") {} + + const char *getDescription() const { + return "Object sent -autorelease too many times"; + } + }; + + class ReturnedNotOwnedForOwned : public CFRefBug { + public: + ReturnedNotOwnedForOwned(CFRefCount *tf) : + CFRefBug(tf, "Method should return an owned object") {} + + const char *getDescription() const { + return "Object with +0 retain counts returned to caller where a +1 " + "(owning) retain count is expected"; + } + }; + + class Leak : public CFRefBug { + const bool isReturn; + protected: + Leak(CFRefCount* tf, llvm::StringRef name, bool isRet) + : CFRefBug(tf, name), isReturn(isRet) {} + public: + + const char* getDescription() const { return ""; } + + bool isLeak() const { return true; } + }; + + class LeakAtReturn : public Leak { + public: + LeakAtReturn(CFRefCount* tf, llvm::StringRef name) + : Leak(tf, name, true) {} + }; + + class LeakWithinFunction : public Leak { + public: + LeakWithinFunction(CFRefCount* tf, llvm::StringRef name) + : Leak(tf, name, false) {} + }; + + //===---------===// + // Bug Reports. // + //===---------===// + + class CFRefReport : public RangedBugReport { + protected: + SymbolRef Sym; + const CFRefCount &TF; + public: + CFRefReport(CFRefBug& D, const CFRefCount &tf, + ExplodedNode *n, SymbolRef sym) + : RangedBugReport(D, D.getDescription(), n), Sym(sym), TF(tf) {} + + CFRefReport(CFRefBug& D, const CFRefCount &tf, + ExplodedNode *n, SymbolRef sym, llvm::StringRef endText) + : RangedBugReport(D, D.getDescription(), endText, n), Sym(sym), TF(tf) {} + + virtual ~CFRefReport() {} + + CFRefBug& getBugType() const { + return (CFRefBug&) RangedBugReport::getBugType(); + } + + virtual std::pair getRanges() const { + if (!getBugType().isLeak()) + return RangedBugReport::getRanges(); + else + return std::make_pair(ranges_iterator(), ranges_iterator()); + } + + SymbolRef getSymbol() const { return Sym; } + + PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, + const ExplodedNode* N); + + std::pair getExtraDescriptiveText(); + + PathDiagnosticPiece* VisitNode(const ExplodedNode* N, + const ExplodedNode* PrevN, + BugReporterContext& BRC); + }; + + class CFRefLeakReport : public CFRefReport { + SourceLocation AllocSite; + const MemRegion* AllocBinding; + public: + CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, + ExplodedNode *n, SymbolRef sym, + ExprEngine& Eng); + + PathDiagnosticPiece* getEndPath(BugReporterContext& BRC, + const ExplodedNode* N); + + SourceLocation getLocation() const { return AllocSite; } + }; +} // end anonymous namespace + + + +static const char* Msgs[] = { + // GC only + "Code is compiled to only use garbage collection", + // No GC. + "Code is compiled to use reference counts", + // Hybrid, with GC. + "Code is compiled to use either garbage collection (GC) or reference counts" + " (non-GC). The bug occurs with GC enabled", + // Hybrid, without GC + "Code is compiled to use either garbage collection (GC) or reference counts" + " (non-GC). The bug occurs in non-GC mode" +}; + +std::pair CFRefReport::getExtraDescriptiveText() { + CFRefCount& TF = static_cast(getBugType()).getTF(); + + switch (TF.getLangOptions().getGCMode()) { + default: + assert(false); + + case LangOptions::GCOnly: + assert (TF.isGCEnabled()); + return std::make_pair(&Msgs[0], &Msgs[0]+1); + + case LangOptions::NonGC: + assert (!TF.isGCEnabled()); + return std::make_pair(&Msgs[1], &Msgs[1]+1); + + case LangOptions::HybridGC: + if (TF.isGCEnabled()) + return std::make_pair(&Msgs[2], &Msgs[2]+1); + else + return std::make_pair(&Msgs[3], &Msgs[3]+1); + } +} + +static inline bool contains(const llvm::SmallVectorImpl& V, + ArgEffect X) { + for (llvm::SmallVectorImpl::const_iterator I=V.begin(), E=V.end(); + I!=E; ++I) + if (*I == X) return true; + + return false; +} + +PathDiagnosticPiece* CFRefReport::VisitNode(const ExplodedNode* N, + const ExplodedNode* PrevN, + BugReporterContext& BRC) { + + if (!isa(N->getLocation())) + return NULL; + + // Check if the type state has changed. + const GRState *PrevSt = PrevN->getState(); + const GRState *CurrSt = N->getState(); + + const RefVal* CurrT = CurrSt->get(Sym); + if (!CurrT) return NULL; + + const RefVal &CurrV = *CurrT; + const RefVal *PrevT = PrevSt->get(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 = cast(N->getLocation()).getStmt(); + + if (const CallExpr *CE = dyn_cast(S)) { + // Get the name of the callee (if it is available). + SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee()); + if (const FunctionDecl* FD = X.getAsFunctionDecl()) + os << "Call to function '" << FD << '\''; + else + os << "function call"; + } + else { + assert (isa(S)); + os << "Method"; + } + + if (CurrV.getObjKind() == RetEffect::CF) { + os << " returns a Core Foundation object with a "; + } + else { + assert (CurrV.getObjKind() == RetEffect::ObjC); + os << " returns an Objective-C object with a "; + } + + if (CurrV.isOwned()) { + os << "+1 retain count (owning reference)."; + + if (static_cast(getBugType()).getTF().isGCEnabled()) { + assert(CurrV.getObjKind() == RetEffect::CF); + os << " " + "Core Foundation objects are not automatically garbage collected."; + } + } + else { + assert (CurrV.isNotOwned()); + os << "+0 retain count (non-owning reference)."; + } + + PathDiagnosticLocation Pos(S, BRC.getSourceManager()); + return new PathDiagnosticEventPiece(Pos, os.str()); + } + + // Gather up the effects that were performed on the object at this + // program point + llvm::SmallVector AEffects; + + if (const RetainSummary *Summ = + TF.getSummaryOfNode(BRC.getNodeResolver().getOriginalNode(N))) { + // We only have summaries attached to nodes after evaluating CallExpr and + // ObjCMessageExprs. + const Stmt* S = cast(N->getLocation()).getStmt(); + + if (const CallExpr *CE = dyn_cast(S)) { + // Iterate through the parameter expressions and see if the symbol + // was ever passed as an argument. + unsigned i = 0; + + for (CallExpr::const_arg_iterator 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).getAsLocSymbol() != Sym) + continue; + + // We have an argument. Get the effect! + AEffects.push_back(Summ->getArg(i)); + } + } + else if (const ObjCMessageExpr *ME = dyn_cast(S)) { + if (const Expr *receiver = ME->getInstanceReceiver()) + if (CurrSt->getSValAsScalarOrLoc(receiver).getAsLocSymbol() == Sym) { + // The symbol we are tracking is the receiver. + AEffects.push_back(Summ->getReceiverEffect()); + } + } + } + + do { + // Get the previous type state. + RefVal PrevV = *PrevT; + + // Specially handle -dealloc. + if (!TF.isGCEnabled() && contains(AEffects, Dealloc)) { + // Determine if the object's reference count was pushed to zero. + assert(!(PrevV == CurrV) && "The typestate *must* 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"; + break; + } + } + + // Specially handle CFMakeCollectable and friends. + if (contains(AEffects, MakeCollectable)) { + // Get the name of the function. + const Stmt* S = cast(N->getLocation()).getStmt(); + SVal X = CurrSt->getSValAsScalarOrLoc(cast(S)->getCallee()); + const FunctionDecl* FD = X.getAsFunctionDecl(); + const std::string& FName = FD->getNameAsString(); + + if (TF.isGCEnabled()) { + // Determine if the object's reference count was pushed to zero. + assert(!(PrevV == CurrV) && "The typestate *must* have changed."); + + os << "In GC mode a call to '" << FName + << "' decrements an object's retain count and registers the " + "object with the garbage collector. "; + + if (CurrV.getKind() == RefVal::Released) { + assert(CurrV.getCount() == 0); + os << "Since it now has a 0 retain count the object can be " + "automatically collected by the garbage collector."; + } + else + os << "An object must have a 0 retain count to be garbage collected. " + "After this call its retain count is +" << CurrV.getCount() + << '.'; + } + else + os << "When GC is not enabled a call to '" << FName + << "' has no effect on its argument."; + + // Nothing more to say. + break; + } + + // Determine if the typestate has changed. + if (!(PrevV == 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 0; + + assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount()); + os << "Object sent -autorelease message"; + break; + } + + 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."; + + if (PrevV.getKind() == RefVal::Released) { + assert(TF.isGCEnabled() && CurrV.getCount() > 0); + os << " The object is not eligible for garbage collection until the " + "retain count reaches 0 again."; + } + + break; + + case RefVal::Released: + os << "Object released."; + break; + + case RefVal::ReturnedOwned: + os << "Object returned to caller as an owning reference (single retain " + "count transferred to caller)."; + break; + + case RefVal::ReturnedNotOwned: + os << "Object returned to caller with a +0 (non-owning) retain count."; + break; + + default: + return NULL; + } + + // Emit any remaining diagnostics for the argument effects (if any). + for (llvm::SmallVectorImpl::iterator I=AEffects.begin(), + E=AEffects.end(); I != E; ++I) { + + // A bunch of things have alternate behavior under GC. + if (TF.isGCEnabled()) + switch (*I) { + default: break; + case Autorelease: + os << "In GC mode an 'autorelease' has no effect."; + continue; + case IncRefMsg: + os << "In GC mode the 'retain' message has no effect."; + continue; + case DecRefMsg: + os << "In GC mode the 'release' message has no effect."; + continue; + } + } + } while (0); + + if (os.str().empty()) + return 0; // We have nothing to say! + + const Stmt* S = cast(N->getLocation()).getStmt(); + PathDiagnosticLocation Pos(S, BRC.getSourceManager()); + PathDiagnosticPiece* P = new PathDiagnosticEventPiece(Pos, os.str()); + + // Add the range by scanning the children of the statement for any bindings + // to Sym. + for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); + I!=E; ++I) + if (const Expr* Exp = dyn_cast_or_null(*I)) + if (CurrSt->getSValAsScalarOrLoc(Exp).getAsLocSymbol() == Sym) { + P->addRange(Exp->getSourceRange()); + break; + } + + return P; +} + +namespace { + class FindUniqueBinding : + public StoreManager::BindingsHandler { + SymbolRef Sym; + const MemRegion* Binding; + bool First; + + public: + FindUniqueBinding(SymbolRef sym) : Sym(sym), Binding(0), First(true) {} + + bool HandleBinding(StoreManager& SMgr, Store store, const MemRegion* R, + SVal val) { + + SymbolRef SymV = val.getAsSymbol(); + if (!SymV || SymV != Sym) + return true; + + if (Binding) { + First = false; + return false; + } + else + Binding = R; + + return true; + } + + operator bool() { return First && Binding; } + const MemRegion* getRegion() { return Binding; } + }; +} + +static std::pair +GetAllocationSite(GRStateManager& StateMgr, const ExplodedNode* N, + SymbolRef Sym) { + + // Find both first node that referred to the tracked symbol and the + // memory location that value was store to. + const ExplodedNode* Last = N; + const MemRegion* FirstBinding = 0; + + while (N) { + const GRState* St = N->getState(); + RefBindings B = St->get(); + + if (!B.lookup(Sym)) + break; + + FindUniqueBinding FB(Sym); + StateMgr.iterBindings(St, FB); + if (FB) FirstBinding = FB.getRegion(); + + Last = N; + N = N->pred_empty() ? NULL : *(N->pred_begin()); + } + + return std::make_pair(Last, FirstBinding); +} + +PathDiagnosticPiece* +CFRefReport::getEndPath(BugReporterContext& BRC, + const ExplodedNode* EndN) { + // Tell the BugReporterContext to report cases when the tracked symbol is + // assigned to different variables, etc. + BRC.addNotableSymbol(Sym); + return RangedBugReport::getEndPath(BRC, EndN); +} + +PathDiagnosticPiece* +CFRefLeakReport::getEndPath(BugReporterContext& BRC, + const ExplodedNode* EndN){ + + // Tell the BugReporterContext to report cases when the tracked symbol is + // assigned to different variables, etc. + BRC.addNotableSymbol(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. + const ExplodedNode* AllocNode = 0; + const MemRegion* FirstBinding = 0; + + llvm::tie(AllocNode, FirstBinding) = + GetAllocationSite(BRC.getStateManager(), EndN, Sym); + + // Get the allocate site. + assert(AllocNode); + const Stmt* FirstStmt = cast(AllocNode->getLocation()).getStmt(); + + SourceManager& SMgr = BRC.getSourceManager(); + unsigned AllocLine =SMgr.getInstantiationLineNumber(FirstStmt->getLocStart()); + + // 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; + + while (LeakN) { + ProgramPoint P = LeakN->getLocation(); + + if (const PostStmt *PS = dyn_cast(&P)) { + L = PathDiagnosticLocation(PS->getStmt()->getLocStart(), SMgr); + break; + } + else if (const BlockEdge *BE = dyn_cast(&P)) { + if (const Stmt* Term = BE->getSrc()->getTerminator()) { + L = PathDiagnosticLocation(Term->getLocStart(), SMgr); + break; + } + } + + LeakN = LeakN->succ_empty() ? 0 : *(LeakN->succ_begin()); + } + + if (!L.isValid()) { + const Decl &D = EndN->getCodeDecl(); + L = PathDiagnosticLocation(D.getBodyRBrace(), SMgr); + } + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "Object allocated on line " << AllocLine; + + if (FirstBinding) + os << " and stored into '" << FirstBinding->getString() << '\''; + + // Get the retain count. + const RefVal* RV = EndN->getState()->get(Sym); + + if (RV->getKind() == RefVal::ErrorLeakReturned) { + // FIXME: Per comments in rdar://6320065, "create" only applies to CF + // ojbects. Only "copy", "alloc", "retain" and "new" transfer ownership + // to the caller for NS objects. + ObjCMethodDecl& MD = cast(EndN->getCodeDecl()); + os << " is returned from a method whose name ('" + << MD.getSelector().getAsString() + << "') does not contain 'copy' or otherwise starts with" + " 'new' or 'alloc'. This violates the naming convention rules given" + " in the Memory Management Guide for Cocoa (object leaked)"; + } + else if (RV->getKind() == RefVal::ErrorGCLeakReturned) { + ObjCMethodDecl& MD = cast(EndN->getCodeDecl()); + os << " and returned from method '" << MD.getSelector().getAsString() + << "' is potentially leaked when using garbage collection. Callers " + "of this method do not expect a returned object with a +1 retain " + "count since they expect the object to be managed by the garbage " + "collector"; + } + else + os << " is not referenced later in this execution path and has a retain " + "count of +" << RV->getCount() << " (object leaked)"; + + return new PathDiagnosticEventPiece(L, os.str()); +} + +CFRefLeakReport::CFRefLeakReport(CFRefBug& D, const CFRefCount &tf, + ExplodedNode *n, + SymbolRef sym, ExprEngine& Eng) +: CFRefReport(D, tf, n, sym) { + + // Most bug reports are cached at the location where they occured. + // 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 = 0; + + llvm::tie(AllocNode, AllocBinding) = // Set AllocBinding. + GetAllocationSite(Eng.getStateManager(), getErrorNode(), getSymbol()); + + // Get the SourceLocation for the allocation site. + ProgramPoint P = AllocNode->getLocation(); + AllocSite = cast(P).getStmt()->getLocStart(); + + // Fill in the description of the bug. + Description.clear(); + llvm::raw_string_ostream os(Description); + SourceManager& SMgr = Eng.getContext().getSourceManager(); + unsigned AllocLine = SMgr.getInstantiationLineNumber(AllocSite); + os << "Potential leak "; + if (tf.isGCEnabled()) { + os << "(when using garbage collection) "; + } + os << "of an object allocated on line " << AllocLine; + + // FIXME: AllocBinding doesn't get populated for RegionStore yet. + if (AllocBinding) + os << " and stored into '" << AllocBinding->getString() << '\''; +} + +//===----------------------------------------------------------------------===// +// Main checker logic. +//===----------------------------------------------------------------------===// + +/// 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 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). +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(RetE)) + if (const ObjCObjectPointerType *PT = RetTy->getAs()) + 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; +} + +void CFRefCount::evalSummary(ExplodedNodeSet& Dst, + ExprEngine& Eng, + StmtNodeBuilder& Builder, + const Expr* Ex, + InstanceReceiver Receiver, + const RetainSummary& Summ, + const MemRegion *Callee, + ConstExprIterator arg_beg, + ConstExprIterator arg_end, + ExplodedNode* Pred, const GRState *state) { + + // Evaluate the effect of the arguments. + RefVal::Kind hasErr = (RefVal::Kind) 0; + unsigned idx = 0; + SourceRange ErrorRange; + SymbolRef ErrorSym = 0; + + llvm::SmallVector RegionsToInvalidate; + + // HACK: Symbols that have ref-count state that are referenced directly + // (not as structure or array elements, or via bindings) by an argument + // should not have their ref-count state stripped after we have + // done an invalidation pass. + llvm::DenseSet WhitelistedSymbols; + + for (ConstExprIterator I = arg_beg; I != arg_end; ++I, ++idx) { + SVal V = state->getSValAsScalarOrLoc(*I); + SymbolRef Sym = V.getAsLocSymbol(); + + if (Sym) + if (RefBindings::data_type* T = state->get(Sym)) { + WhitelistedSymbols.insert(Sym); + state = Update(state, Sym, *T, Summ.getArg(idx), hasErr); + if (hasErr) { + ErrorRange = (*I)->getSourceRange(); + ErrorSym = Sym; + break; + } + } + + tryAgain: + if (isa(V)) { + if (loc::MemRegionVal* MR = dyn_cast(&V)) { + if (Summ.getArg(idx) == DoNothingByRef) + continue; + + // Invalidate the value of the variable passed by reference. + const MemRegion *R = MR->getRegion(); + + // Are we dealing with an ElementRegion? If the element type is + // a basic integer type (e.g., char, int) and the underying region + // is a variable region then strip off the ElementRegion. + // FIXME: We really need to think about this for the general case + // as sometimes we are reasoning about arrays and other times + // about (char*), etc., is just a form of passing raw bytes. + // e.g., void *p = alloca(); foo((char*)p); + if (const ElementRegion *ER = dyn_cast(R)) { + // Checking for 'integral type' is probably too promiscuous, but + // we'll leave it in for now until we have a systematic way of + // handling all of these cases. Eventually we need to come up + // with an interface to StoreManager so that this logic can be + // approriately delegated to the respective StoreManagers while + // still allowing us to do checker-specific logic (e.g., + // invalidating reference counts), probably via callbacks. + if (ER->getElementType()->isIntegralOrEnumerationType()) { + const MemRegion *superReg = ER->getSuperRegion(); + if (isa(superReg) || isa(superReg) || + isa(superReg)) + R = cast(superReg); + } + // FIXME: What about layers of ElementRegions? + } + + // Mark this region for invalidation. We batch invalidate regions + // below for efficiency. + RegionsToInvalidate.push_back(R); + continue; + } + else { + // Nuke all other arguments passed by reference. + // FIXME: is this necessary or correct? This handles the non-Region + // cases. Is it ever valid to store to these? + state = state->unbindLoc(cast(V)); + } + } + else if (isa(V)) { + // If we are passing a location wrapped as an integer, unwrap it and + // invalidate the values referred by the location. + V = cast(V).getLoc(); + goto tryAgain; + } + } + + // Block calls result in all captured values passed-via-reference to be + // invalidated. + if (const BlockDataRegion *BR = dyn_cast_or_null(Callee)) { + RegionsToInvalidate.push_back(BR); + } + + // Invalidate regions we designed for invalidation use the batch invalidation + // API. + + // FIXME: We can have collisions on the conjured symbol if the + // expression *I also creates conjured symbols. We probably want + // to identify conjured symbols by an expression pair: the enclosing + // expression (the context) and the expression itself. This should + // disambiguate conjured symbols. + unsigned Count = Builder.getCurrentBlockCount(); + StoreManager::InvalidatedSymbols IS; + + // NOTE: Even if RegionsToInvalidate is empty, we must still invalidate + // global variables. + state = state->InvalidateRegions(RegionsToInvalidate.data(), + RegionsToInvalidate.data() + + RegionsToInvalidate.size(), + Ex, Count, &IS, + /* invalidateGlobals = */ true); + + for (StoreManager::InvalidatedSymbols::iterator I = IS.begin(), + E = IS.end(); I!=E; ++I) { + SymbolRef sym = *I; + if (WhitelistedSymbols.count(sym)) + continue; + // Remove any existing reference-count binding. + state = state->remove(*I); + } + + // Evaluate the effect on the message receiver. + if (!ErrorRange.isValid() && Receiver) { + SymbolRef Sym = Receiver.getSValAsScalarOrLoc(state).getAsLocSymbol(); + if (Sym) { + if (const RefVal* T = state->get(Sym)) { + state = Update(state, Sym, *T, Summ.getReceiverEffect(), hasErr); + if (hasErr) { + ErrorRange = Receiver.getSourceRange(); + ErrorSym = Sym; + } + } + } + } + + // Process any errors. + if (hasErr) { + ProcessNonLeakError(Dst, Builder, Ex, ErrorRange, Pred, state, + hasErr, ErrorSym); + return; + } + + // Consult the summary for the return value. + RetEffect RE = Summ.getRetEffect(); + + if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) { + bool found = false; + if (Receiver) { + SVal V = Receiver.getSValAsScalarOrLoc(state); + if (SymbolRef Sym = V.getAsLocSymbol()) + if (state->get(Sym)) { + found = true; + RE = Summaries.getObjAllocRetEffect(); + } + } // FIXME: Otherwise, this is a send-to-super instance message. + if (!found) + RE = RetEffect::MakeNoRet(); + } + + switch (RE.getKind()) { + default: + assert (false && "Unhandled RetEffect."); break; + + case RetEffect::NoRet: { + // Make up a symbol for the return value (not reference counted). + // FIXME: Most of this logic is not specific to the retain/release + // checker. + + // FIXME: We eventually should handle structs and other compound types + // that are returned by value. + + QualType T = Ex->getType(); + + // For CallExpr, use the result type to know if it returns a reference. + if (const CallExpr *CE = dyn_cast(Ex)) { + const Expr *Callee = CE->getCallee(); + if (const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl()) + T = FD->getResultType(); + } + else if (const ObjCMessageExpr *ME = dyn_cast(Ex)) { + if (const ObjCMethodDecl *MD = ME->getMethodDecl()) + T = MD->getResultType(); + } + + if (Loc::IsLocType(T) || (T->isIntegerType() && T->isScalarType())) { + unsigned Count = Builder.getCurrentBlockCount(); + SValBuilder &svalBuilder = Eng.getSValBuilder(); + SVal X = svalBuilder.getConjuredSymbolVal(NULL, Ex, T, Count); + state = state->BindExpr(Ex, X, false); + } + + break; + } + + case RetEffect::Alias: { + unsigned idx = RE.getIndex(); + assert (arg_end >= arg_beg); + assert (idx < (unsigned) (arg_end - arg_beg)); + SVal V = state->getSValAsScalarOrLoc(*(arg_beg+idx)); + state = state->BindExpr(Ex, V, false); + break; + } + + case RetEffect::ReceiverAlias: { + assert(Receiver); + SVal V = Receiver.getSValAsScalarOrLoc(state); + state = state->BindExpr(Ex, V, false); + break; + } + + case RetEffect::OwnedAllocatedSymbol: + case RetEffect::OwnedSymbol: { + unsigned Count = Builder.getCurrentBlockCount(); + SValBuilder &svalBuilder = Eng.getSValBuilder(); + SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count); + QualType RetT = GetReturnType(Ex, svalBuilder.getContext()); + state = state->set(Sym, RefVal::makeOwned(RE.getObjKind(), + RetT)); + state = state->BindExpr(Ex, svalBuilder.makeLoc(Sym), false); + + // FIXME: Add a flag to the checker where allocations are assumed to + // *not fail. +#if 0 + if (RE.getKind() == RetEffect::OwnedAllocatedSymbol) { + bool isFeasible; + state = state.assume(loc::SymbolVal(Sym), true, isFeasible); + assert(isFeasible && "Cannot assume fresh symbol is non-null."); + } +#endif + + break; + } + + case RetEffect::GCNotOwnedSymbol: + case RetEffect::NotOwnedSymbol: { + unsigned Count = Builder.getCurrentBlockCount(); + SValBuilder &svalBuilder = Eng.getSValBuilder(); + SymbolRef Sym = svalBuilder.getConjuredSymbol(Ex, Count); + QualType RetT = GetReturnType(Ex, svalBuilder.getContext()); + state = state->set(Sym, RefVal::makeNotOwned(RE.getObjKind(), + RetT)); + state = state->BindExpr(Ex, svalBuilder.makeLoc(Sym), false); + break; + } + } + + // Generate a sink node if we are at the end of a path. + ExplodedNode *NewNode = + Summ.isEndPath() ? Builder.MakeSinkNode(Dst, Ex, Pred, state) + : Builder.MakeNode(Dst, Ex, Pred, state); + + // Annotate the edge with summary we used. + if (NewNode) SummaryLog[NewNode] = &Summ; +} + + +void CFRefCount::evalCall(ExplodedNodeSet& Dst, + ExprEngine& Eng, + StmtNodeBuilder& Builder, + const CallExpr* CE, SVal L, + ExplodedNode* Pred) { + + RetainSummary *Summ = 0; + + // FIXME: Better support for blocks. For now we stop tracking anything + // that is passed to blocks. + // FIXME: Need to handle variables that are "captured" by the block. + if (dyn_cast_or_null(L.getAsRegion())) { + Summ = Summaries.getPersistentStopSummary(); + } + else { + const FunctionDecl* FD = L.getAsFunctionDecl(); + Summ = !FD ? Summaries.getDefaultSummary() : + Summaries.getSummary(FD); + } + + assert(Summ); + evalSummary(Dst, Eng, Builder, CE, 0, *Summ, L.getAsRegion(), + CE->arg_begin(), CE->arg_end(), Pred, Builder.GetState(Pred)); +} + +void CFRefCount::evalObjCMessageExpr(ExplodedNodeSet& Dst, + ExprEngine& Eng, + StmtNodeBuilder& Builder, + const ObjCMessageExpr* ME, + ExplodedNode* Pred, + const GRState *state) { + RetainSummary *Summ = + ME->isInstanceMessage() + ? Summaries.getInstanceMethodSummary(ME, state,Pred->getLocationContext()) + : Summaries.getClassMethodSummary(ME); + + assert(Summ && "RetainSummary is null"); + evalSummary(Dst, Eng, Builder, ME, + InstanceReceiver(ME, Pred->getLocationContext()), *Summ, NULL, + ME->arg_begin(), ME->arg_end(), Pred, state); +} + +namespace { +class StopTrackingCallback : public SymbolVisitor { + const GRState *state; +public: + StopTrackingCallback(const GRState *st) : state(st) {} + const GRState *getState() const { return state; } + + bool VisitSymbol(SymbolRef sym) { + state = state->remove(sym); + return true; + } +}; +} // end anonymous namespace + + +void CFRefCount::evalBind(StmtNodeBuilderRef& B, SVal location, SVal val) { + // Are we storing to something that causes the value to "escape"? + bool escapes = false; + + // 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 + // (3) we are binding to a memregion with stack storage that the store + // does not understand. + const GRState *state = B.getState(); + + if (!isa(location)) + escapes = true; + else { + const MemRegion* R = cast(location).getRegion(); + escapes = !R->hasStackStorage(); + + if (!escapes) { + // To test (3), generate a new state with the binding removed. If it is + // the same state, then it escapes (since the store cannot represent + // the binding). + escapes = (state == (state->bindLoc(cast(location), UnknownVal()))); + } + } + + // 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. + B.MakeNode(state->scanReachableSymbols(val).getState()); +} + + // Return statements. + +void CFRefCount::evalReturn(ExplodedNodeSet& Dst, + ExprEngine& Eng, + StmtNodeBuilder& Builder, + const ReturnStmt* S, + ExplodedNode* Pred) { + + const Expr* RetE = S->getRetValue(); + if (!RetE) + return; + + const GRState *state = Builder.GetState(Pred); + SymbolRef Sym = state->getSValAsScalarOrLoc(RetE).getAsLocSymbol(); + + if (!Sym) + return; + + // Get the reference count binding (if any). + const RefVal* T = state->get(Sym); + + if (!T) + return; + + // 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; + } + + // Update the binding. + state = state->set(Sym, X); + Pred = Builder.MakeNode(Dst, S, Pred, state); + + // Did we cache out? + if (!Pred) + return; + + // Update the autorelease counts. + static unsigned autoreleasetag = 0; + GenericNodeBuilder Bd(Builder, S, &autoreleasetag); + bool stop = false; + llvm::tie(Pred, state) = HandleAutoreleaseCounts(state , Bd, Pred, Eng, Sym, + X, stop); + + // Did we cache out? + if (!Pred || stop) + return; + + // Get the updated binding. + T = state->get(Sym); + assert(T); + X = *T; + + // Any leaks or other errors? + if (X.isReturnedOwned() && X.getCount() == 0) { + Decl const *CD = &Pred->getCodeDecl(); + if (const ObjCMethodDecl* MD = dyn_cast(CD)) { + const RetainSummary &Summ = *Summaries.getMethodSummary(MD); + RetEffect RE = Summ.getRetEffect(); + bool hasError = false; + + if (RE.getKind() != RetEffect::NoRet) { + if (isGCEnabled() && RE.getObjKind() == RetEffect::ObjC) { + // Things are more complicated with garbage collection. If the + // returned object is suppose to be an Objective-C object, we have + // a leak (as the caller expects a GC'ed object) because no + // method should return ownership unless it returns a CF object. + hasError = true; + X = X ^ RefVal::ErrorGCLeakReturned; + } + else if (!RE.isOwned()) { + // Either we are using GC and the returned object is a CF type + // or we aren't using GC. In either case, we expect that the + // enclosing method is expected to return ownership. + hasError = true; + X = X ^ RefVal::ErrorLeakReturned; + } + } + + if (hasError) { + // Generate an error node. + static int ReturnOwnLeakTag = 0; + state = state->set(Sym, X); + ExplodedNode *N = + Builder.generateNode(PostStmt(S, Pred->getLocationContext(), + &ReturnOwnLeakTag), state, Pred); + if (N) { + CFRefReport *report = + new CFRefLeakReport(*static_cast(leakAtReturn), *this, + N, Sym, Eng); + BR->EmitReport(report); + } + } + } + } + else if (X.isReturnedNotOwned()) { + Decl const *CD = &Pred->getCodeDecl(); + if (const ObjCMethodDecl* MD = dyn_cast(CD)) { + const RetainSummary &Summ = *Summaries.getMethodSummary(MD); + if (Summ.getRetEffect().isOwned()) { + // Trying to return a not owned object to a caller expecting an + // owned object. + + static int ReturnNotOwnedForOwnedTag = 0; + state = state->set(Sym, X ^ RefVal::ErrorReturnedNotOwned); + if (ExplodedNode *N = + Builder.generateNode(PostStmt(S, Pred->getLocationContext(), + &ReturnNotOwnedForOwnedTag), + state, Pred)) { + CFRefReport *report = + new CFRefReport(*static_cast(returnNotOwnedForOwned), + *this, N, Sym); + BR->EmitReport(report); + } + } + } + } +} + +// Assumptions. + +const GRState* CFRefCount::evalAssume(const GRState *state, + SVal Cond, bool Assumption) { + + // 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. + RefBindings B = state->get(); + + if (B.isEmpty()) + return state; + + bool changed = false; + RefBindings::Factory& RefBFactory = state->get_context(); + + for (RefBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { + // Check if the symbol is null (or equal to any constant). + // If this is the case, stop tracking the symbol. + if (state->getSymVal(I.getKey())) { + changed = true; + B = RefBFactory.remove(B, I.getKey()); + } + } + + if (changed) + state = state->set(B); + + return state; +} + +const GRState * CFRefCount::Update(const GRState * state, SymbolRef sym, + RefVal V, ArgEffect E, + RefVal::Kind& hasErr) { + + // In GC mode [... release] and [... retain] do nothing. + switch (E) { + default: break; + case IncRefMsg: E = isGCEnabled() ? DoNothing : IncRef; break; + case DecRefMsg: E = isGCEnabled() ? DoNothing : DecRef; break; + case MakeCollectable: E = isGCEnabled() ? DecRef : DoNothing; break; + case NewAutoreleasePool: E = isGCEnabled() ? DoNothing : + NewAutoreleasePool; break; + } + + // Handle all use-after-releases. + if (!isGCEnabled() && V.getKind() == RefVal::Released) { + V = V ^ RefVal::ErrorUseAfterRelease; + hasErr = V.getKind(); + return state->set(sym, V); + } + + switch (E) { + default: + assert (false && "Unhandled CFRef transition."); + + case Dealloc: + // Any use of -dealloc in GC is *bad*. + if (isGCEnabled()) { + V = V ^ RefVal::ErrorDeallocGC; + hasErr = V.getKind(); + break; + } + + switch (V.getKind()) { + default: + assert(false && "Invalid case."); + case RefVal::Owned: + // The object immediately transitions to the released state. + V = V ^ RefVal::Released; + V.clearCounts(); + return state->set(sym, V); + case RefVal::NotOwned: + V = V ^ RefVal::ErrorDeallocNotOwned; + hasErr = V.getKind(); + break; + } + break; + + case NewAutoreleasePool: + assert(!isGCEnabled()); + return state->add(sym); + + case MayEscape: + if (V.getKind() == RefVal::Owned) { + V = V ^ RefVal::NotOwned; + break; + } + + // Fall-through. + + case DoNothingByRef: + case DoNothing: + return state; + + case Autorelease: + if (isGCEnabled()) + return state; + + // Update the autorelease counts. + state = SendAutorelease(state, ARCountFactory, sym); + V = V.autorelease(); + break; + + case StopTracking: + return state->remove(sym); + + case IncRef: + switch (V.getKind()) { + default: + assert(false); + + case RefVal::Owned: + case RefVal::NotOwned: + V = V + 1; + break; + case RefVal::Released: + // Non-GC cases are handled above. + assert(isGCEnabled()); + V = (V ^ RefVal::Owned) + 1; + break; + } + break; + + case SelfOwn: + V = V ^ RefVal::NotOwned; + // Fall-through. + case DecRef: + switch (V.getKind()) { + default: + // case 'RefVal::Released' handled above. + assert (false); + + case RefVal::Owned: + assert(V.getCount() > 0); + if (V.getCount() == 1) V = V ^ RefVal::Released; + V = V - 1; + break; + + case RefVal::NotOwned: + if (V.getCount() > 0) + V = V - 1; + else { + V = V ^ RefVal::ErrorReleaseNotOwned; + hasErr = V.getKind(); + } + break; + + case RefVal::Released: + // Non-GC cases are handled above. + assert(isGCEnabled()); + V = V ^ RefVal::ErrorUseAfterRelease; + hasErr = V.getKind(); + break; + } + break; + } + return state->set(sym, V); +} + +//===----------------------------------------------------------------------===// +// Handle dead symbols and end-of-path. +//===----------------------------------------------------------------------===// + +std::pair +CFRefCount::HandleAutoreleaseCounts(const GRState * state, GenericNodeBuilder Bd, + ExplodedNode* Pred, + ExprEngine &Eng, + SymbolRef Sym, RefVal V, bool &stop) { + + unsigned ACnt = V.getAutoreleaseCount(); + stop = false; + + // No autorelease counts? Nothing to be done. + if (!ACnt) + return std::make_pair(Pred, state); + + assert(!isGCEnabled() && "Autorelease counts in GC mode?"); + unsigned Cnt = V.getCount(); + + // FIXME: Handle sending 'autorelease' to already released object. + + if (V.getKind() == RefVal::ReturnedOwned) + ++Cnt; + + 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(Cnt - ACnt); + V.setAutoreleaseCount(0); + } + state = state->set(Sym, V); + ExplodedNode *N = Bd.MakeNode(state, Pred); + stop = (N == 0); + return std::make_pair(N, state); + } + + // Woah! More autorelease counts then retain counts left. + // Emit hard error. + stop = true; + V = V ^ RefVal::ErrorOverAutorelease; + state = state->set(Sym, V); + + if (ExplodedNode *N = Bd.MakeNode(state, Pred)) { + N->markAsSink(); + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Object over-autoreleased: object was sent -autorelease"; + if (V.getAutoreleaseCount() > 1) + os << V.getAutoreleaseCount() << " times"; + os << " but the object has "; + if (V.getCount() == 0) + os << "zero (locally visible)"; + else + os << "+" << V.getCount(); + os << " retain counts"; + + CFRefReport *report = + new CFRefReport(*static_cast(overAutorelease), + *this, N, Sym, os.str()); + BR->EmitReport(report); + } + + return std::make_pair((ExplodedNode*)0, state); +} + +const GRState * +CFRefCount::HandleSymbolDeath(const GRState * state, SymbolRef sid, RefVal V, + llvm::SmallVectorImpl &Leaked) { + + bool hasLeak = V.isOwned() || + ((V.isNotOwned() || V.isReturnedOwned()) && V.getCount() > 0); + + if (!hasLeak) + return state->remove(sid); + + Leaked.push_back(sid); + return state->set(sid, V ^ RefVal::ErrorLeak); +} + +ExplodedNode* +CFRefCount::ProcessLeaks(const GRState * state, + llvm::SmallVectorImpl &Leaked, + GenericNodeBuilder &Builder, + ExprEngine& Eng, + ExplodedNode *Pred) { + + if (Leaked.empty()) + return Pred; + + // Generate an intermediate node representing the leak point. + ExplodedNode *N = Builder.MakeNode(state, Pred); + + if (N) { + for (llvm::SmallVectorImpl::iterator + I = Leaked.begin(), E = Leaked.end(); I != E; ++I) { + + CFRefBug *BT = static_cast(Pred ? leakWithinFunction + : leakAtReturn); + assert(BT && "BugType not initialized."); + CFRefLeakReport* report = new CFRefLeakReport(*BT, *this, N, *I, Eng); + BR->EmitReport(report); + } + } + + return N; +} + +void CFRefCount::evalEndPath(ExprEngine& Eng, + EndPathNodeBuilder& Builder) { + + const GRState *state = Builder.getState(); + GenericNodeBuilder Bd(Builder); + RefBindings B = state->get(); + ExplodedNode *Pred = 0; + + for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + bool stop = false; + llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng, + (*I).first, + (*I).second, stop); + + if (stop) + return; + } + + B = state->get(); + llvm::SmallVector Leaked; + + for (RefBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) + state = HandleSymbolDeath(state, (*I).first, (*I).second, Leaked); + + ProcessLeaks(state, Leaked, Bd, Eng, Pred); +} + +void CFRefCount::evalDeadSymbols(ExplodedNodeSet& Dst, + ExprEngine& Eng, + StmtNodeBuilder& Builder, + ExplodedNode* Pred, + const GRState* state, + SymbolReaper& SymReaper) { + const Stmt *S = Builder.getStmt(); + RefBindings B = state->get(); + + // Update counts from autorelease pools + for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), + E = SymReaper.dead_end(); I != E; ++I) { + SymbolRef Sym = *I; + if (const RefVal* T = B.lookup(Sym)){ + // Use the symbol as the tag. + // FIXME: This might not be as unique as we would like. + GenericNodeBuilder Bd(Builder, S, Sym); + bool stop = false; + llvm::tie(Pred, state) = HandleAutoreleaseCounts(state, Bd, Pred, Eng, + Sym, *T, stop); + if (stop) + return; + } + } + + B = state->get(); + llvm::SmallVector Leaked; + + for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), + E = SymReaper.dead_end(); I != E; ++I) { + if (const RefVal* T = B.lookup(*I)) + state = HandleSymbolDeath(state, *I, *T, Leaked); + } + + static unsigned LeakPPTag = 0; + { + GenericNodeBuilder Bd(Builder, S, &LeakPPTag); + Pred = ProcessLeaks(state, Leaked, Bd, Eng, Pred); + } + + // Did we cache out? + if (!Pred) + return; + + // Now generate a new node that nukes the old bindings. + RefBindings::Factory& F = state->get_context(); + + for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), + E = SymReaper.dead_end(); I!=E; ++I) B = F.remove(B, *I); + + state = state->set(B); + Builder.MakeNode(Dst, S, Pred, state); +} + +void CFRefCount::ProcessNonLeakError(ExplodedNodeSet& Dst, + StmtNodeBuilder& Builder, + const Expr* NodeExpr, + SourceRange ErrorRange, + ExplodedNode* Pred, + const GRState* St, + RefVal::Kind hasErr, SymbolRef Sym) { + Builder.BuildSinks = true; + ExplodedNode *N = Builder.MakeNode(Dst, NodeExpr, Pred, St); + + if (!N) + return; + + CFRefBug *BT = 0; + + switch (hasErr) { + default: + assert(false && "Unhandled error."); + return; + case RefVal::ErrorUseAfterRelease: + BT = static_cast(useAfterRelease); + break; + case RefVal::ErrorReleaseNotOwned: + BT = static_cast(releaseNotOwned); + break; + case RefVal::ErrorDeallocGC: + BT = static_cast(deallocGC); + break; + case RefVal::ErrorDeallocNotOwned: + BT = static_cast(deallocNotOwned); + break; + } + + CFRefReport *report = new CFRefReport(*BT, *this, N, Sym); + report->addRange(ErrorRange); + BR->EmitReport(report); +} + +//===----------------------------------------------------------------------===// +// Pieces of the retain/release checker implemented using a CheckerVisitor. +// More pieces of the retain/release checker will be migrated to this interface +// (ideally, all of it some day). +//===----------------------------------------------------------------------===// + +namespace { +class RetainReleaseChecker + : public CheckerVisitor { + CFRefCount *TF; +public: + RetainReleaseChecker(CFRefCount *tf) : TF(tf) {} + static void* getTag() { static int x = 0; return &x; } + + void PostVisitBlockExpr(CheckerContext &C, const BlockExpr *BE); +}; +} // end anonymous namespace + + +void RetainReleaseChecker::PostVisitBlockExpr(CheckerContext &C, + const BlockExpr *BE) { + + // Scan the BlockDecRefExprs for any object the retain/release checker + // may be tracking. + if (!BE->hasBlockDeclRefExprs()) + return; + + const GRState *state = C.getState(); + const BlockDataRegion *R = + cast(state->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. + llvm::SmallVector Regions; + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager(); + + for ( ; I != E; ++I) { + const VarRegion *VR = *I; + if (VR->getSuperRegion() == R) { + VR = MemMgr.getVarRegion(VR->getDecl(), LC); + } + Regions.push_back(VR); + } + + state = + state->scanReachableSymbols(Regions.data(), + Regions.data() + Regions.size()).getState(); + C.addTransition(state); +} + +//===----------------------------------------------------------------------===// +// Transfer function creation for external clients. +//===----------------------------------------------------------------------===// + +void CFRefCount::RegisterChecks(ExprEngine& Eng) { + BugReporter &BR = Eng.getBugReporter(); + + useAfterRelease = new UseAfterRelease(this); + BR.Register(useAfterRelease); + + releaseNotOwned = new BadRelease(this); + BR.Register(releaseNotOwned); + + deallocGC = new DeallocGC(this); + BR.Register(deallocGC); + + deallocNotOwned = new DeallocNotOwned(this); + BR.Register(deallocNotOwned); + + overAutorelease = new OverAutorelease(this); + BR.Register(overAutorelease); + + returnNotOwnedForOwned = new ReturnedNotOwnedForOwned(this); + BR.Register(returnNotOwnedForOwned); + + // First register "return" leaks. + const char* name = 0; + + if (isGCEnabled()) + name = "Leak of returned object when using garbage collection"; + else if (getLangOptions().getGCMode() == LangOptions::HybridGC) + name = "Leak of returned object when not using garbage collection (GC) in " + "dual GC/non-GC code"; + else { + assert(getLangOptions().getGCMode() == LangOptions::NonGC); + name = "Leak of returned object"; + } + + // Leaks should not be reported if they are post-dominated by a sink. + leakAtReturn = new LeakAtReturn(this, name); + leakAtReturn->setSuppressOnSink(true); + BR.Register(leakAtReturn); + + // Second, register leaks within a function/method. + if (isGCEnabled()) + name = "Leak of object when using garbage collection"; + else if (getLangOptions().getGCMode() == LangOptions::HybridGC) + name = "Leak of object when not using garbage collection (GC) in " + "dual GC/non-GC code"; + else { + assert(getLangOptions().getGCMode() == LangOptions::NonGC); + name = "Leak"; + } + + // Leaks should not be reported if they are post-dominated by sinks. + leakWithinFunction = new LeakWithinFunction(this, name); + leakWithinFunction->setSuppressOnSink(true); + BR.Register(leakWithinFunction); + + // Save the reference to the BugReporter. + this->BR = &BR; + + // Register the RetainReleaseChecker with the ExprEngine object. + // Functionality in CFRefCount will be migrated to RetainReleaseChecker + // over time. + Eng.registerCheck(new RetainReleaseChecker(this)); +} + +TransferFuncs* ento::MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, + const LangOptions& lopts) { + return new CFRefCount(Ctx, GCEnabled, lopts); +} diff --git a/lib/StaticAnalyzer/EntoSA/CMakeLists.txt b/lib/StaticAnalyzer/EntoSA/CMakeLists.txt new file mode 100644 index 0000000000..cf81e1d5ad --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/CMakeLists.txt @@ -0,0 +1,41 @@ +set(LLVM_NO_RTTI 1) + +set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite) + +add_clang_library(clangStaticAnalyzerCore + AggExprVisitor.cpp + AnalysisManager.cpp + AnalyzerStatsChecker.cpp + BasicConstraintManager.cpp + BasicStore.cpp + BasicValueFactory.cpp + BugReporter.cpp + BugReporterVisitors.cpp + CFRefCount.cpp + Checker.cpp + CheckerHelpers.cpp + Environment.cpp + ExplodedGraph.cpp + FlatStore.cpp + BlockCounter.cpp + CXXExprEngine.cpp + CoreEngine.cpp + GRState.cpp + HTMLDiagnostics.cpp + ManagerRegistry.cpp + MemRegion.cpp + PathDiagnostic.cpp + PlistDiagnostics.cpp + RangeConstraintManager.cpp + RegionStore.cpp + SimpleConstraintManager.cpp + SimpleSValBuilder.cpp + Store.cpp + SValBuilder.cpp + SVals.cpp + SymbolManager.cpp + TextPathDiagnostics.cpp + ) + +add_dependencies(clangStaticAnalyzerCore ClangAttrClasses ClangAttrList ClangDeclNodes + ClangStmtNodes) diff --git a/lib/StaticAnalyzer/EntoSA/CXXExprEngine.cpp b/lib/StaticAnalyzer/EntoSA/CXXExprEngine.cpp new file mode 100644 index 0000000000..47b3a0bd06 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/CXXExprEngine.cpp @@ -0,0 +1,328 @@ +//===- GRCXXExprEngine.cpp - C++ expr evaluation engine ---------*- 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/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/AST/DeclCXX.h" + +using namespace clang; +using namespace ento; + +namespace { +class CallExprWLItem { +public: + CallExpr::const_arg_iterator I; + ExplodedNode *N; + + CallExprWLItem(const CallExpr::const_arg_iterator &i, ExplodedNode *n) + : I(i), N(n) {} +}; +} + +void ExprEngine::evalArguments(ConstExprIterator AI, ConstExprIterator AE, + const FunctionProtoType *FnType, + ExplodedNode *Pred, ExplodedNodeSet &Dst, + bool FstArgAsLValue) { + + + llvm::SmallVector WorkList; + WorkList.reserve(AE - AI); + WorkList.push_back(CallExprWLItem(AI, Pred)); + + while (!WorkList.empty()) { + CallExprWLItem Item = WorkList.back(); + WorkList.pop_back(); + + if (Item.I == AE) { + Dst.insert(Item.N); + continue; + } + + // Evaluate the argument. + ExplodedNodeSet Tmp; + bool VisitAsLvalue = FstArgAsLValue; + if (FstArgAsLValue) { + FstArgAsLValue = false; + } else { + const unsigned ParamIdx = Item.I - AI; + VisitAsLvalue = FnType && ParamIdx < FnType->getNumArgs() + ? FnType->getArgType(ParamIdx)->isReferenceType() + : false; + } + + Visit(*Item.I, Item.N, Tmp); + ++(Item.I); + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI != NE; ++NI) + WorkList.push_back(CallExprWLItem(Item.I, *NI)); + } +} + +const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXRecordDecl *D, + const StackFrameContext *SFC) { + Type *T = D->getTypeForDecl(); + QualType PT = getContext().getPointerType(QualType(T, 0)); + return svalBuilder.getRegionManager().getCXXThisRegion(PT, SFC); +} + +const CXXThisRegion *ExprEngine::getCXXThisRegion(const CXXMethodDecl *decl, + const StackFrameContext *frameCtx) { + return svalBuilder.getRegionManager(). + getCXXThisRegion(decl->getThisType(getContext()), frameCtx); +} + +void ExprEngine::CreateCXXTemporaryObject(const Expr *Ex, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E; ++I) { + const GRState *state = GetState(*I); + + // Bind the temporary object to the value of the expression. Then bind + // the expression to the location of the object. + SVal V = state->getSVal(Ex); + + const MemRegion *R = + svalBuilder.getRegionManager().getCXXTempObjectRegion(Ex, + Pred->getLocationContext()); + + state = state->bindLoc(loc::MemRegionVal(R), V); + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, loc::MemRegionVal(R))); + } +} + +void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *E, + const MemRegion *Dest, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + if (!Dest) + Dest = svalBuilder.getRegionManager().getCXXTempObjectRegion(E, + Pred->getLocationContext()); + + if (E->isElidable()) { + VisitAggExpr(E->getArg(0), Dest, Pred, Dst); + return; + } + + const CXXConstructorDecl *CD = E->getConstructor(); + assert(CD); + + if (!(CD->isThisDeclarationADefinition() && AMgr.shouldInlineCall())) + // FIXME: invalidate the object. + return; + + + // Evaluate other arguments. + ExplodedNodeSet argsEvaluated; + const FunctionProtoType *FnType = CD->getType()->getAs(); + evalArguments(E->arg_begin(), E->arg_end(), FnType, Pred, argsEvaluated); + // The callee stack frame context used to create the 'this' parameter region. + const StackFrameContext *SFC = AMgr.getStackFrame(CD, + Pred->getLocationContext(), + E, Builder->getBlock(), + Builder->getIndex()); + + const CXXThisRegion *ThisR =getCXXThisRegion(E->getConstructor()->getParent(), + SFC); + + CallEnter Loc(E, SFC, Pred->getLocationContext()); + for (ExplodedNodeSet::iterator NI = argsEvaluated.begin(), + NE = argsEvaluated.end(); NI != NE; ++NI) { + const GRState *state = GetState(*NI); + // Setup 'this' region, so that the ctor is evaluated on the object pointed + // by 'Dest'. + state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); + ExplodedNode *N = Builder->generateNode(Loc, state, Pred); + if (N) + Dst.Add(N); + } +} + +void ExprEngine::VisitCXXDestructor(const CXXDestructorDecl *DD, + const MemRegion *Dest, + const Stmt *S, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + if (!(DD->isThisDeclarationADefinition() && AMgr.shouldInlineCall())) + return; + // Create the context for 'this' region. + const StackFrameContext *SFC = AMgr.getStackFrame(DD, + Pred->getLocationContext(), + S, Builder->getBlock(), + Builder->getIndex()); + + const CXXThisRegion *ThisR = getCXXThisRegion(DD->getParent(), SFC); + + CallEnter PP(S, SFC, Pred->getLocationContext()); + + const GRState *state = Pred->getState(); + state = state->bindLoc(loc::MemRegionVal(ThisR), loc::MemRegionVal(Dest)); + ExplodedNode *N = Builder->generateNode(PP, state, Pred); + if (N) + Dst.Add(N); +} + +void ExprEngine::VisitCXXMemberCallExpr(const CXXMemberCallExpr *MCE, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + // Get the method type. + const FunctionProtoType *FnType = + MCE->getCallee()->getType()->getAs(); + assert(FnType && "Method type not available"); + + // Evaluate explicit arguments with a worklist. + ExplodedNodeSet argsEvaluated; + evalArguments(MCE->arg_begin(), MCE->arg_end(), FnType, Pred, argsEvaluated); + + // Evaluate the implicit object argument. + ExplodedNodeSet AllargsEvaluated; + const MemberExpr *ME = dyn_cast(MCE->getCallee()->IgnoreParens()); + if (!ME) + return; + Expr *ObjArgExpr = ME->getBase(); + for (ExplodedNodeSet::iterator I = argsEvaluated.begin(), + E = argsEvaluated.end(); I != E; ++I) { + Visit(ObjArgExpr, *I, AllargsEvaluated); + } + + // Now evaluate the call itself. + const CXXMethodDecl *MD = cast(ME->getMemberDecl()); + assert(MD && "not a CXXMethodDecl?"); + evalMethodCall(MCE, MD, ObjArgExpr, Pred, AllargsEvaluated, Dst); +} + +void ExprEngine::VisitCXXOperatorCallExpr(const CXXOperatorCallExpr *C, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const CXXMethodDecl *MD = dyn_cast_or_null(C->getCalleeDecl()); + if (!MD) { + // If the operator doesn't represent a method call treat as regural call. + VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst); + return; + } + + // Determine the type of function we're calling (if available). + const FunctionProtoType *Proto = NULL; + QualType FnType = C->getCallee()->IgnoreParens()->getType(); + if (const PointerType *FnTypePtr = FnType->getAs()) + Proto = FnTypePtr->getPointeeType()->getAs(); + + // Evaluate arguments treating the first one (object method is called on) + // as alvalue. + ExplodedNodeSet argsEvaluated; + evalArguments(C->arg_begin(), C->arg_end(), Proto, Pred, argsEvaluated, true); + + // Now evaluate the call itself. + evalMethodCall(C, MD, C->getArg(0), Pred, argsEvaluated, Dst); +} + +void ExprEngine::evalMethodCall(const CallExpr *MCE, const CXXMethodDecl *MD, + const Expr *ThisExpr, ExplodedNode *Pred, + ExplodedNodeSet &Src, ExplodedNodeSet &Dst) { + // Allow checkers to pre-visit the member call. + ExplodedNodeSet PreVisitChecks; + CheckerVisit(MCE, PreVisitChecks, Src, PreVisitStmtCallback); + + if (!(MD->isThisDeclarationADefinition() && AMgr.shouldInlineCall())) { + // FIXME: conservative method call evaluation. + CheckerVisit(MCE, Dst, PreVisitChecks, PostVisitStmtCallback); + return; + } + + const StackFrameContext *SFC = AMgr.getStackFrame(MD, + Pred->getLocationContext(), + MCE, + Builder->getBlock(), + Builder->getIndex()); + const CXXThisRegion *ThisR = getCXXThisRegion(MD, SFC); + CallEnter Loc(MCE, SFC, Pred->getLocationContext()); + for (ExplodedNodeSet::iterator I = PreVisitChecks.begin(), + E = PreVisitChecks.end(); I != E; ++I) { + // Set up 'this' region. + const GRState *state = GetState(*I); + state = state->bindLoc(loc::MemRegionVal(ThisR), state->getSVal(ThisExpr)); + Dst.Add(Builder->generateNode(Loc, state, *I)); + } +} + +void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + if (CNE->isArray()) { + // FIXME: allocating an array has not been handled. + return; + } + + unsigned Count = Builder->getCurrentBlockCount(); + DefinedOrUnknownSVal symVal = + svalBuilder.getConjuredSymbolVal(NULL, CNE, CNE->getType(), Count); + const MemRegion *NewReg = cast(symVal).getRegion(); + + QualType ObjTy = CNE->getType()->getAs()->getPointeeType(); + + const ElementRegion *EleReg = + getStoreManager().GetElementZeroRegion(NewReg, ObjTy); + + // Evaluate constructor arguments. + const FunctionProtoType *FnType = NULL; + const CXXConstructorDecl *CD = CNE->getConstructor(); + if (CD) + FnType = CD->getType()->getAs(); + ExplodedNodeSet argsEvaluated; + evalArguments(CNE->constructor_arg_begin(), CNE->constructor_arg_end(), + FnType, Pred, argsEvaluated); + + // Initialize the object region and bind the 'new' expression. + for (ExplodedNodeSet::iterator I = argsEvaluated.begin(), + E = argsEvaluated.end(); I != E; ++I) { + const GRState *state = GetState(*I); + + if (ObjTy->isRecordType()) { + state = state->InvalidateRegion(EleReg, CNE, Count); + } else { + if (CNE->hasInitializer()) { + SVal V = state->getSVal(*CNE->constructor_arg_begin()); + state = state->bindLoc(loc::MemRegionVal(EleReg), V); + } else { + // Explicitly set to undefined, because currently we retrieve symbolic + // value from symbolic region. + state = state->bindLoc(loc::MemRegionVal(EleReg), UndefinedVal()); + } + } + state = state->BindExpr(CNE, loc::MemRegionVal(EleReg)); + MakeNode(Dst, CNE, *I, state); + } +} + +void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE, + ExplodedNode *Pred,ExplodedNodeSet &Dst) { + // Should do more checking. + ExplodedNodeSet Argevaluated; + Visit(CDE->getArgument(), Pred, Argevaluated); + for (ExplodedNodeSet::iterator I = Argevaluated.begin(), + E = Argevaluated.end(); I != E; ++I) { + const GRState *state = GetState(*I); + MakeNode(Dst, CDE, *I, state); + } +} + +void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + // Get the this object region from StoreManager. + const MemRegion *R = + svalBuilder.getRegionManager().getCXXThisRegion( + getContext().getCanonicalType(TE->getType()), + Pred->getLocationContext()); + + const GRState *state = GetState(Pred); + SVal V = state->getSVal(loc::MemRegionVal(R)); + MakeNode(Dst, TE, Pred, state->BindExpr(TE, V)); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checker.cpp b/lib/StaticAnalyzer/EntoSA/Checker.cpp new file mode 100644 index 0000000000..6867ff5b64 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checker.cpp @@ -0,0 +1,36 @@ +//== Checker.h - Abstract interface 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 and CheckerVisitor, classes used for creating +// domain-specific checks. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" +using namespace clang; +using namespace ento; + +Checker::~Checker() {} + +CheckerContext::~CheckerContext() { + // Do we need to autotransition? 'Dst' can get populated in a variety of + // ways, including 'addTransition()' adding the predecessor node to Dst + // without actually generated a new node. We also shouldn't autotransition + // if we are building sinks or we generated a node and decided to not + // add it as a transition. + if (Dst.size() == size && !B.BuildSinks && !B.HasGeneratedNode) { + if (ST && ST != B.GetState(Pred)) { + static int autoTransitionTag = 0; + B.Tag = &autoTransitionTag; + addTransition(ST); + } + else + Dst.Add(Pred); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/CheckerHelpers.cpp b/lib/StaticAnalyzer/EntoSA/CheckerHelpers.cpp new file mode 100644 index 0000000000..be9fee548f --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/CheckerHelpers.cpp @@ -0,0 +1,80 @@ +//===---- 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/PathSensitive/CheckerHelpers.h" +#include "clang/AST/Expr.h" + +// Recursively find any substatements containing macros +bool clang::ento::containsMacro(const Stmt *S) { + if (S->getLocStart().isMacroID()) + return true; + + if (S->getLocEnd().isMacroID()) + return true; + + for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); + ++I) + if (const Stmt *child = *I) + if (containsMacro(child)) + return true; + + return false; +} + +// Recursively find any substatements containing enum constants +bool clang::ento::containsEnum(const Stmt *S) { + const DeclRefExpr *DR = dyn_cast(S); + + if (DR && isa(DR->getDecl())) + return true; + + for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); + ++I) + if (const Stmt *child = *I) + if (containsEnum(child)) + return true; + + return false; +} + +// Recursively find any substatements containing static vars +bool clang::ento::containsStaticLocal(const Stmt *S) { + const DeclRefExpr *DR = dyn_cast(S); + + if (DR) + if (const VarDecl *VD = dyn_cast(DR->getDecl())) + if (VD->isStaticLocal()) + return true; + + for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); + ++I) + if (const Stmt *child = *I) + if (containsStaticLocal(child)) + return true; + + return false; +} + +// Recursively find any substatements containing __builtin_offsetof +bool clang::ento::containsBuiltinOffsetOf(const Stmt *S) { + if (isa(S)) + return true; + + for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); + ++I) + if (const Stmt *child = *I) + if (containsBuiltinOffsetOf(child)) + return true; + + return false; +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/AdjustedReturnValueChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/AdjustedReturnValueChecker.cpp new file mode 100644 index 0000000000..46e2de5c26 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/AdjustedReturnValueChecker.cpp @@ -0,0 +1,96 @@ +//== AdjustedReturnValueChecker.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 AdjustedReturnValueChecker, a simple check to see if the +// return value of a function call is different than the one the caller thinks +// it is. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class AdjustedReturnValueChecker : + public CheckerVisitor { +public: + AdjustedReturnValueChecker() {} + + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + + static void *getTag() { + static int x = 0; return &x; + } +}; +} + +void ento::RegisterAdjustedReturnValueChecker(ExprEngine &Eng) { + Eng.registerCheck(new AdjustedReturnValueChecker()); +} + +void AdjustedReturnValueChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + + // Get the result type of the call. + QualType expectedResultTy = CE->getType(); + + // Fetch the signature of the called function. + const GRState *state = C.getState(); + + SVal V = state->getSVal(CE); + + if (V.isUnknown()) + return; + + // Casting to void? Discard the value. + if (expectedResultTy->isVoidType()) { + C.generateNode(state->BindExpr(CE, UnknownVal())); + return; + } + + const MemRegion *callee = state->getSVal(CE->getCallee()).getAsRegion(); + if (!callee) + return; + + QualType actualResultTy; + + if (const FunctionTextRegion *FT = dyn_cast(callee)) { + const FunctionDecl *FD = FT->getDecl(); + actualResultTy = FD->getResultType(); + } + else if (const BlockDataRegion *BD = dyn_cast(callee)) { + const BlockTextRegion *BR = BD->getCodeRegion(); + const BlockPointerType *BT=BR->getLocationType()->getAs(); + const FunctionType *FT = BT->getPointeeType()->getAs(); + actualResultTy = FT->getResultType(); + } + + // Can this happen? + if (actualResultTy.isNull()) + return; + + // For now, ignore references. + if (actualResultTy->getAs()) + return; + + + // Are they the same? + if (expectedResultTy != actualResultTy) { + // FIXME: Do more checking and actual emit an error. At least performing + // the cast avoids some assertion failures elsewhere. + SValBuilder &svalBuilder = C.getSValBuilder(); + V = svalBuilder.evalCast(V, expectedResultTy, actualResultTy); + C.generateNode(state->BindExpr(CE, V)); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/AnalysisConsumer.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/AnalysisConsumer.cpp new file mode 100644 index 0000000000..ee4eae2ebf --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/AnalysisConsumer.cpp @@ -0,0 +1,610 @@ +//===--- 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/AnalysisConsumer.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/ParentMap.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/Analyses/UninitializedValues.h" +#include "clang/Analysis/CFG.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/ManagerRegistry.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/PathDiagnosticClients.h" + +// FIXME: Restructure checker registration. +#include "ExprEngineExperimentalChecks.h" +#include "ExprEngineInternalChecks.h" + +#include "clang/Basic/FileManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Frontend/AnalyzerOptions.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/Program.h" +#include "llvm/ADT/OwningPtr.h" + +using namespace clang; +using namespace ento; + +static ExplodedNode::Auditor* CreateUbiViz(); + +//===----------------------------------------------------------------------===// +// Special PathDiagnosticClients. +//===----------------------------------------------------------------------===// + +static PathDiagnosticClient* +createPlistHTMLDiagnosticClient(const std::string& prefix, + const Preprocessor &PP) { + PathDiagnosticClient *PD = + createHTMLDiagnosticClient(llvm::sys::path::parent_path(prefix), PP); + return createPlistDiagnosticClient(prefix, PP, PD); +} + +//===----------------------------------------------------------------------===// +// AnalysisConsumer declaration. +//===----------------------------------------------------------------------===// + +namespace { + +class AnalysisConsumer : public ASTConsumer { +public: + typedef void (*CodeAction)(AnalysisConsumer &C, AnalysisManager &M, Decl *D); + typedef void (*TUAction)(AnalysisConsumer &C, AnalysisManager &M, + TranslationUnitDecl &TU); + +private: + typedef std::vector Actions; + typedef std::vector TUActions; + + Actions FunctionActions; + Actions ObjCMethodActions; + Actions ObjCImplementationActions; + Actions CXXMethodActions; + TUActions TranslationUnitActions; // Remove this. + +public: + ASTContext* Ctx; + const Preprocessor &PP; + const std::string OutDir; + AnalyzerOptions Opts; + + // PD is owned by AnalysisManager. + PathDiagnosticClient *PD; + + StoreManagerCreator CreateStoreMgr; + ConstraintManagerCreator CreateConstraintMgr; + + llvm::OwningPtr Mgr; + + AnalysisConsumer(const Preprocessor& pp, + const std::string& outdir, + const AnalyzerOptions& opts) + : Ctx(0), PP(pp), OutDir(outdir), + Opts(opts), PD(0) { + DigestAnalyzerOptions(); + } + + void DigestAnalyzerOptions() { + // Create the PathDiagnosticClient. + if (!OutDir.empty()) { + switch (Opts.AnalysisDiagOpt) { + default: +#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN, AUTOCREATE) \ + case PD_##NAME: PD = CREATEFN(OutDir, PP); break; +#include "clang/Frontend/Analyses.def" + } + } else if (Opts.AnalysisDiagOpt == PD_TEXT) { + // Create the text client even without a specified output file since + // it just uses diagnostic notes. + PD = createTextPathDiagnosticClient("", PP); + } + + // Create the analyzer component creators. + if (ManagerRegistry::StoreMgrCreator != 0) { + CreateStoreMgr = ManagerRegistry::StoreMgrCreator; + } + else { + switch (Opts.AnalysisStoreOpt) { + default: + assert(0 && "Unknown store manager."); +#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \ + case NAME##Model: CreateStoreMgr = CREATEFN; break; +#include "clang/Frontend/Analyses.def" + } + } + + if (ManagerRegistry::ConstraintMgrCreator != 0) + CreateConstraintMgr = ManagerRegistry::ConstraintMgrCreator; + else { + switch (Opts.AnalysisConstraintsOpt) { + default: + assert(0 && "Unknown store manager."); +#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \ + case NAME##Model: CreateConstraintMgr = CREATEFN; break; +#include "clang/Frontend/Analyses.def" + } + } + } + + void DisplayFunction(const Decl *D) { + if (!Opts.AnalyzerDisplayProgress) + return; + + SourceManager &SM = Mgr->getASTContext().getSourceManager(); + PresumedLoc Loc = SM.getPresumedLoc(D->getLocation()); + if (Loc.isValid()) { + llvm::errs() << "ANALYZE: " << Loc.getFilename(); + + if (isa(D) || isa(D)) { + const NamedDecl *ND = cast(D); + llvm::errs() << ' ' << ND << '\n'; + } + else if (isa(D)) { + llvm::errs() << ' ' << "block(line:" << Loc.getLine() << ",col:" + << Loc.getColumn() << '\n'; + } + else if (const ObjCMethodDecl *MD = dyn_cast(D)) { + Selector S = MD->getSelector(); + llvm::errs() << ' ' << S.getAsString(); + } + } + } + + void addCodeAction(CodeAction action) { + FunctionActions.push_back(action); + ObjCMethodActions.push_back(action); + CXXMethodActions.push_back(action); + } + + void addTranslationUnitAction(TUAction action) { + TranslationUnitActions.push_back(action); + } + + void addObjCImplementationAction(CodeAction action) { + ObjCImplementationActions.push_back(action); + } + + virtual void Initialize(ASTContext &Context) { + Ctx = &Context; + Mgr.reset(new AnalysisManager(*Ctx, PP.getDiagnostics(), + PP.getLangOptions(), PD, + CreateStoreMgr, CreateConstraintMgr, + /* Indexer */ 0, + Opts.MaxNodes, Opts.MaxLoop, + Opts.VisualizeEGDot, Opts.VisualizeEGUbi, + Opts.PurgeDead, Opts.EagerlyAssume, + Opts.TrimGraph, Opts.InlineCall, + Opts.UnoptimizedCFG, Opts.CFGAddImplicitDtors, + Opts.CFGAddInitializers)); + } + + virtual void HandleTranslationUnit(ASTContext &C); + void HandleCode(Decl *D, Actions& actions); +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// AnalysisConsumer implementation. +//===----------------------------------------------------------------------===// + +void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) { + + TranslationUnitDecl *TU = C.getTranslationUnitDecl(); + + for (DeclContext::decl_iterator I = TU->decls_begin(), E = TU->decls_end(); + I != E; ++I) { + Decl *D = *I; + + switch (D->getKind()) { + case Decl::CXXConstructor: + case Decl::CXXDestructor: + case Decl::CXXConversion: + case Decl::CXXMethod: + case Decl::Function: { + FunctionDecl* FD = cast(D); + // We skip function template definitions, as their semantics is + // only determined when they are instantiated. + if (FD->isThisDeclarationADefinition() && + !FD->isDependentContext()) { + if (!Opts.AnalyzeSpecificFunction.empty() && + FD->getDeclName().getAsString() != Opts.AnalyzeSpecificFunction) + break; + DisplayFunction(FD); + HandleCode(FD, FunctionActions); + } + break; + } + + case Decl::ObjCImplementation: { + ObjCImplementationDecl* ID = cast(*I); + HandleCode(ID, ObjCImplementationActions); + + for (ObjCImplementationDecl::method_iterator MI = ID->meth_begin(), + ME = ID->meth_end(); MI != ME; ++MI) { + if ((*MI)->isThisDeclarationADefinition()) { + if (!Opts.AnalyzeSpecificFunction.empty() && + Opts.AnalyzeSpecificFunction != (*MI)->getSelector().getAsString()) + break; + DisplayFunction(*MI); + HandleCode(*MI, ObjCMethodActions); + } + } + break; + } + + default: + break; + } + } + + for (TUActions::iterator I = TranslationUnitActions.begin(), + E = TranslationUnitActions.end(); I != E; ++I) { + (*I)(*this, *Mgr, *TU); + } + + // Explicitly destroy the PathDiagnosticClient. This will flush its output. + // FIXME: This should be replaced with something that doesn't rely on + // side-effects in PathDiagnosticClient's destructor. This is required when + // used with option -disable-free. + Mgr.reset(NULL); +} + +static void FindBlocks(DeclContext *D, llvm::SmallVectorImpl &WL) { + if (BlockDecl *BD = dyn_cast(D)) + WL.push_back(BD); + + for (DeclContext::decl_iterator I = D->decls_begin(), E = D->decls_end(); + I!=E; ++I) + if (DeclContext *DC = dyn_cast(*I)) + FindBlocks(DC, WL); +} + +void AnalysisConsumer::HandleCode(Decl *D, Actions& actions) { + + // Don't run the actions if an error has occured with parsing the file. + Diagnostic &Diags = PP.getDiagnostics(); + if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred()) + return; + + // Don't run the actions on declarations in header files unless + // otherwise specified. + SourceManager &SM = Ctx->getSourceManager(); + SourceLocation SL = SM.getInstantiationLoc(D->getLocation()); + if (!Opts.AnalyzeAll && !SM.isFromMainFile(SL)) + return; + + // Clear the AnalysisManager of old AnalysisContexts. + Mgr->ClearContexts(); + + // Dispatch on the actions. + llvm::SmallVector WL; + WL.push_back(D); + + if (D->hasBody() && Opts.AnalyzeNestedBlocks) + FindBlocks(cast(D), WL); + + for (Actions::iterator I = actions.begin(), E = actions.end(); I != E; ++I) + for (llvm::SmallVectorImpl::iterator WI=WL.begin(), WE=WL.end(); + WI != WE; ++WI) + (*I)(*this, *Mgr, *WI); +} + +//===----------------------------------------------------------------------===// +// Analyses +//===----------------------------------------------------------------------===// + +static void ActionWarnDeadStores(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D) { + if (LiveVariables *L = mgr.getLiveVariables(D)) { + BugReporter BR(mgr); + CheckDeadStores(*mgr.getCFG(D), *L, mgr.getParentMap(D), BR); + } +} + +static void ActionWarnUninitVals(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D) { + if (CFG* c = mgr.getCFG(D)) { + CheckUninitializedValues(*c, mgr.getASTContext(), mgr.getDiagnostic()); + } +} + + +static void ActionExprEngine(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D, + TransferFuncs* tf) { + + llvm::OwningPtr TF(tf); + + // Construct the analysis engine. We first query for the LiveVariables + // information to see if the CFG is valid. + // FIXME: Inter-procedural analysis will need to handle invalid CFGs. + if (!mgr.getLiveVariables(D)) + return; + ExprEngine Eng(mgr, TF.take()); + + if (C.Opts.EnableExperimentalInternalChecks) + RegisterExperimentalInternalChecks(Eng); + + RegisterAppleChecks(Eng, *D); + + if (C.Opts.EnableExperimentalChecks) + RegisterExperimentalChecks(Eng); + + // Enable idempotent operation checking if it was explicitly turned on, or if + // we are running experimental checks (i.e. everything) + if (C.Opts.IdempotentOps || C.Opts.EnableExperimentalChecks + || C.Opts.EnableExperimentalInternalChecks) + RegisterIdempotentOperationChecker(Eng); + + if (C.Opts.BufferOverflows) + RegisterArrayBoundCheckerV2(Eng); + + // Enable AnalyzerStatsChecker if it was given as an argument + if (C.Opts.AnalyzerStats) + RegisterAnalyzerStatsChecker(Eng); + + // Set the graph auditor. + llvm::OwningPtr Auditor; + if (mgr.shouldVisualizeUbigraph()) { + Auditor.reset(CreateUbiViz()); + ExplodedNode::SetAuditor(Auditor.get()); + } + + // Execute the worklist algorithm. + Eng.ExecuteWorkList(mgr.getStackFrame(D, 0), mgr.getMaxNodes()); + + // Release the auditor (if any) so that it doesn't monitor the graph + // created BugReporter. + ExplodedNode::SetAuditor(0); + + // Visualize the exploded graph. + if (mgr.shouldVisualizeGraphviz()) + Eng.ViewGraph(mgr.shouldTrimGraph()); + + // Display warnings. + Eng.getBugReporter().FlushReports(); +} + +static void ActionObjCMemCheckerAux(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D, bool GCEnabled) { + + TransferFuncs* TF = MakeCFRefCountTF(mgr.getASTContext(), + GCEnabled, + mgr.getLangOptions()); + + ActionExprEngine(C, mgr, D, TF); +} + +static void ActionObjCMemChecker(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D) { + + switch (mgr.getLangOptions().getGCMode()) { + default: + assert (false && "Invalid GC mode."); + case LangOptions::NonGC: + ActionObjCMemCheckerAux(C, mgr, D, false); + break; + + case LangOptions::GCOnly: + ActionObjCMemCheckerAux(C, mgr, D, true); + break; + + case LangOptions::HybridGC: + ActionObjCMemCheckerAux(C, mgr, D, false); + ActionObjCMemCheckerAux(C, mgr, D, true); + break; + } +} + +static void ActionDisplayLiveVariables(AnalysisConsumer &C, + AnalysisManager& mgr, Decl *D) { + if (LiveVariables* L = mgr.getLiveVariables(D)) { + L->dumpBlockLiveness(mgr.getSourceManager()); + } +} + +static void ActionCFGDump(AnalysisConsumer &C, AnalysisManager& mgr, Decl *D) { + if (CFG *cfg = mgr.getCFG(D)) { + cfg->dump(mgr.getLangOptions()); + } +} + +static void ActionCFGView(AnalysisConsumer &C, AnalysisManager& mgr, Decl *D) { + if (CFG *cfg = mgr.getCFG(D)) { + cfg->viewCFG(mgr.getLangOptions()); + } +} + +static void ActionSecuritySyntacticChecks(AnalysisConsumer &C, + AnalysisManager &mgr, Decl *D) { + BugReporter BR(mgr); + CheckSecuritySyntaxOnly(D, BR); +} + +static void ActionLLVMConventionChecker(AnalysisConsumer &C, + AnalysisManager &mgr, + TranslationUnitDecl &TU) { + BugReporter BR(mgr); + CheckLLVMConventions(TU, BR); +} + +static void ActionWarnObjCDealloc(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D) { + if (mgr.getLangOptions().getGCMode() == LangOptions::GCOnly) + return; + BugReporter BR(mgr); + CheckObjCDealloc(cast(D), mgr.getLangOptions(), BR); +} + +static void ActionWarnObjCUnusedIvars(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D) { + BugReporter BR(mgr); + CheckObjCUnusedIvar(cast(D), BR); +} + +static void ActionWarnObjCMethSigs(AnalysisConsumer &C, AnalysisManager& mgr, + Decl *D) { + BugReporter BR(mgr); + CheckObjCInstMethSignature(cast(D), BR); +} + +static void ActionWarnSizeofPointer(AnalysisConsumer &C, AnalysisManager &mgr, + Decl *D) { + BugReporter BR(mgr); + CheckSizeofPointer(D, BR); +} + +//===----------------------------------------------------------------------===// +// AnalysisConsumer creation. +//===----------------------------------------------------------------------===// + +ASTConsumer* ento::CreateAnalysisConsumer(const Preprocessor& pp, + const std::string& OutDir, + const AnalyzerOptions& Opts) { + llvm::OwningPtr C(new AnalysisConsumer(pp, OutDir, Opts)); + + for (unsigned i = 0; i < Opts.AnalysisList.size(); ++i) + switch (Opts.AnalysisList[i]) { +#define ANALYSIS(NAME, CMD, DESC, SCOPE)\ + case NAME:\ + C->add ## SCOPE ## Action(&Action ## NAME);\ + break; +#include "clang/Frontend/Analyses.def" + default: break; + } + + // Last, disable the effects of '-Werror' when using the AnalysisConsumer. + pp.getDiagnostics().setWarningsAsErrors(false); + + return C.take(); +} + +//===----------------------------------------------------------------------===// +// Ubigraph Visualization. FIXME: Move to separate file. +//===----------------------------------------------------------------------===// + +namespace { + +class UbigraphViz : public ExplodedNode::Auditor { + llvm::OwningPtr Out; + llvm::sys::Path Dir, Filename; + unsigned Cntr; + + typedef llvm::DenseMap VMap; + VMap M; + +public: + UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir, + llvm::sys::Path& filename); + + ~UbigraphViz(); + + virtual void AddEdge(ExplodedNode* Src, ExplodedNode* Dst); +}; + +} // end anonymous namespace + +static ExplodedNode::Auditor* CreateUbiViz() { + std::string ErrMsg; + + llvm::sys::Path Dir = llvm::sys::Path::GetTemporaryDirectory(&ErrMsg); + if (!ErrMsg.empty()) + return 0; + + llvm::sys::Path Filename = Dir; + Filename.appendComponent("llvm_ubi"); + Filename.makeUnique(true,&ErrMsg); + + if (!ErrMsg.empty()) + return 0; + + llvm::errs() << "Writing '" << Filename.str() << "'.\n"; + + llvm::OwningPtr Stream; + Stream.reset(new llvm::raw_fd_ostream(Filename.c_str(), ErrMsg)); + + if (!ErrMsg.empty()) + return 0; + + return new UbigraphViz(Stream.take(), Dir, Filename); +} + +void UbigraphViz::AddEdge(ExplodedNode* Src, ExplodedNode* Dst) { + + assert (Src != Dst && "Self-edges are not allowed."); + + // Lookup the Src. If it is a new node, it's a root. + VMap::iterator SrcI= M.find(Src); + unsigned SrcID; + + if (SrcI == M.end()) { + M[Src] = SrcID = Cntr++; + *Out << "('vertex', " << SrcID << ", ('color','#00ff00'))\n"; + } + else + SrcID = SrcI->second; + + // Lookup the Dst. + VMap::iterator DstI= M.find(Dst); + unsigned DstID; + + if (DstI == M.end()) { + M[Dst] = DstID = Cntr++; + *Out << "('vertex', " << DstID << ")\n"; + } + else { + // We have hit DstID before. Change its style to reflect a cache hit. + DstID = DstI->second; + *Out << "('change_vertex_style', " << DstID << ", 1)\n"; + } + + // Add the edge. + *Out << "('edge', " << SrcID << ", " << DstID + << ", ('arrow','true'), ('oriented', 'true'))\n"; +} + +UbigraphViz::UbigraphViz(llvm::raw_ostream* out, llvm::sys::Path& dir, + llvm::sys::Path& filename) + : Out(out), Dir(dir), Filename(filename), Cntr(0) { + + *Out << "('vertex_style_attribute', 0, ('shape', 'icosahedron'))\n"; + *Out << "('vertex_style', 1, 0, ('shape', 'sphere'), ('color', '#ffcc66')," + " ('size', '1.5'))\n"; +} + +UbigraphViz::~UbigraphViz() { + Out.reset(0); + llvm::errs() << "Running 'ubiviz' program... "; + std::string ErrMsg; + llvm::sys::Path Ubiviz = llvm::sys::Program::FindProgramByName("ubiviz"); + std::vector args; + args.push_back(Ubiviz.c_str()); + args.push_back(Filename.c_str()); + args.push_back(0); + + if (llvm::sys::Program::ExecuteAndWait(Ubiviz, &args[0],0,0,0,0,&ErrMsg)) { + llvm::errs() << "Error viewing graph: " << ErrMsg << "\n"; + } + + // Delete the directory. + Dir.eraseFromDisk(true); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/ArrayBoundChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/ArrayBoundChecker.cpp new file mode 100644 index 0000000000..13e628bcb1 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/ArrayBoundChecker.cpp @@ -0,0 +1,91 @@ +//== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class ArrayBoundChecker : + public CheckerVisitor { + BuiltinBug *BT; +public: + ArrayBoundChecker() : BT(0) {} + static void *getTag() { static int x = 0; return &x; } + void visitLocation(CheckerContext &C, const Stmt *S, SVal l); +}; +} + +void ento::RegisterArrayBoundChecker(ExprEngine &Eng) { + Eng.registerCheck(new ArrayBoundChecker()); +} + +void ArrayBoundChecker::visitLocation(CheckerContext &C, const Stmt *S, SVal l){ + // Check for out of bound array element access. + const MemRegion *R = l.getAsRegion(); + if (!R) + return; + + const ElementRegion *ER = dyn_cast(R); + if (!ER) + return; + + // Get the index of the accessed element. + DefinedOrUnknownSVal Idx = cast(ER->getIndex()); + + // Zero index is always in bound, this also passes ElementRegions created for + // pointer casts. + if (Idx.isZeroConstant()) + return; + + const GRState *state = C.getState(); + + // Get the size of the array. + DefinedOrUnknownSVal NumElements + = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(), + ER->getValueType()); + + const GRState *StInBound = state->assumeInBound(Idx, NumElements, true); + const GRState *StOutBound = state->assumeInBound(Idx, NumElements, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.generateSink(StOutBound); + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("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. + RangedBugReport *report = + new RangedBugReport(*BT, BT->getDescription(), N); + + report->addRange(S->getSourceRange()); + C.EmitReport(report); + return; + } + + // Array bound check succeeded. From this point forward the array bound + // should always succeed. + assert(StInBound); + C.addTransition(StInBound); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/ArrayBoundCheckerV2.cpp new file mode 100644 index 0000000000..b55fadebf8 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/ArrayBoundCheckerV2.cpp @@ -0,0 +1,277 @@ +//== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/AST/CharUnits.h" + +using namespace clang; +using namespace ento; + +namespace { +class ArrayBoundCheckerV2 : + public CheckerVisitor { + BuiltinBug *BT; + + enum OOB_Kind { OOB_Precedes, OOB_Excedes }; + + void reportOOB(CheckerContext &C, const GRState *errorState, + OOB_Kind kind); + +public: + ArrayBoundCheckerV2() : BT(0) {} + static void *getTag() { static int x = 0; return &x; } + void visitLocation(CheckerContext &C, const Stmt *S, SVal l); +}; + +// FIXME: Eventually replace RegionRawOffset with this class. +class RegionRawOffsetV2 { +private: + const SubRegion *baseRegion; + SVal byteOffset; + + RegionRawOffsetV2() + : baseRegion(0), byteOffset(UnknownVal()) {} + +public: + RegionRawOffsetV2(const SubRegion* base, SVal offset) + : baseRegion(base), byteOffset(offset) {} + + NonLoc getByteOffset() const { return cast(byteOffset); } + const SubRegion *getRegion() const { return baseRegion; } + + static RegionRawOffsetV2 computeOffset(const GRState *state, + SValBuilder &svalBuilder, + SVal location); + + void dump() const; + void dumpToStream(llvm::raw_ostream& os) const; +}; +} + +void ento::RegisterArrayBoundCheckerV2(ExprEngine &Eng) { + Eng.registerCheck(new ArrayBoundCheckerV2()); +} + +void ArrayBoundCheckerV2::visitLocation(CheckerContext &checkerContext, + const Stmt *S, + SVal location) { + + // NOTE: Instead of using GRState::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. + const GRState *state = checkerContext.getState(); + const GRState *originalState = state; + + SValBuilder &svalBuilder = checkerContext.getSValBuilder(); + const RegionRawOffsetV2 &rawOffset = + RegionRawOffsetV2::computeOffset(state, svalBuilder, location); + + if (!rawOffset.getRegion()) + return; + + // CHECK LOWER BOUND: Is byteOffset < 0? If so, we are doing a load/store + // before the first valid offset in the memory region. + + SVal lowerBound + = svalBuilder.evalBinOpNN(state, BO_LT, rawOffset.getByteOffset(), + svalBuilder.makeZeroArrayIndex(), + svalBuilder.getConditionType()); + + NonLoc *lowerBoundToCheck = dyn_cast(&lowerBound); + if (!lowerBoundToCheck) + return; + + const GRState *state_precedesLowerBound, *state_withinLowerBound; + llvm::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 (!isa(extentVal)) + break; + + SVal upperbound + = svalBuilder.evalBinOpNN(state, BO_GE, rawOffset.getByteOffset(), + cast(extentVal), + svalBuilder.getConditionType()); + + NonLoc *upperboundToCheck = dyn_cast(&upperbound); + if (!upperboundToCheck) + break; + + const GRState *state_exceedsUpperBound, *state_withinUpperBound; + llvm::tie(state_exceedsUpperBound, state_withinUpperBound) = + state->assume(*upperboundToCheck); + + // Are we constrained enough to definitely exceed the upper bound? + if (state_exceedsUpperBound && !state_withinUpperBound) { + reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes); + return; + } + + assert(state_withinUpperBound); + state = state_withinUpperBound; + } + while (false); + + if (state != originalState) + checkerContext.generateNode(state); +} + +void ArrayBoundCheckerV2::reportOOB(CheckerContext &checkerContext, + const GRState *errorState, + OOB_Kind kind) { + + ExplodedNode *errorNode = checkerContext.generateSink(errorState); + if (!errorNode) + return; + + if (!BT) + BT = new BuiltinBug("Out-of-bound access"); + + // FIXME: This diagnostics are preliminary. We should get far better + // diagnostics for explaining buffer overruns. + + llvm::SmallString<256> buf; + llvm::raw_svector_ostream os(buf); + os << "Out of bound memory access " + << (kind == OOB_Precedes ? "(accessed memory precedes memory block)" + : "(access exceeds upper limit of memory block)"); + + checkerContext.EmitReport(new RangedBugReport(*BT, os.str(), errorNode)); +} + +void RegionRawOffsetV2::dump() const { + dumpToStream(llvm::errs()); +} + +void RegionRawOffsetV2::dumpToStream(llvm::raw_ostream& os) const { + os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}'; +} + +// FIXME: Merge with the implementation of the same method in Store.cpp +static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { + if (const RecordType *RT = Ty->getAs()) { + const RecordDecl *D = RT->getDecl(); + if (!D->getDefinition()) + return false; + } + + return true; +} + + +// Lazily computes a value to be used by 'computeOffset'. If 'val' +// is unknown or undefined, we lazily substitute '0'. Otherwise, +// return 'val'. +static inline SVal getValue(SVal val, SValBuilder &svalBuilder) { + return isa(val) ? 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(const GRState *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(const GRState *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, + cast(x), cast(y), + svalBuilder.getArrayIndexType()); +} + +/// Compute a raw byte offset from a base region. Used for array bounds +/// checking. +RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(const GRState *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(region)) + if (!offset.isUnknownOrUndef()) + return RegionRawOffsetV2(subReg, offset); + return RegionRawOffsetV2(); + } + case MemRegion::ElementRegionKind: { + const ElementRegion *elemReg = cast(region); + SVal index = elemReg->getIndex(); + if (!isa(index)) + return RegionRawOffsetV2(); + QualType elemType = elemReg->getElementType(); + // If the element is an incomplete type, go no further. + ASTContext &astContext = svalBuilder.getContext(); + if (!IsCompleteType(astContext, elemType)) + return RegionRawOffsetV2(); + + // Update the offset. + offset = addValue(state, + getValue(offset, svalBuilder), + scaleValue(state, + cast(index), + astContext.getTypeSizeInChars(elemType), + svalBuilder), + svalBuilder); + + if (offset.isUnknownOrUndef()) + return RegionRawOffsetV2(); + + region = elemReg->getSuperRegion(); + continue; + } + } + } + return RegionRawOffsetV2(); +} + + + diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/AttrNonNullChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/AttrNonNullChecker.cpp new file mode 100644 index 0000000000..646b30bc31 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/AttrNonNullChecker.cpp @@ -0,0 +1,136 @@ +//===--- AttrNonNullChecker.h - 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 AttrNonNullChecker, a builtin check in ExprEngine that +// performs checks for arguments declared to have nonnull attribute. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class AttrNonNullChecker + : public CheckerVisitor { + BugType *BT; +public: + AttrNonNullChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; +} // end anonymous namespace + +void ento::RegisterAttrNonNullChecker(ExprEngine &Eng) { + Eng.registerCheck(new AttrNonNullChecker()); +} + +void AttrNonNullChecker::PreVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + + // Check if the callee has a 'nonnull' attribute. + SVal X = state->getSVal(CE->getCallee()); + + const FunctionDecl* FD = X.getAsFunctionDecl(); + if (!FD) + return; + + const NonNullAttr* Att = FD->getAttr(); + if (!Att) + return; + + // Iterate through the arguments of CE and check them for null. + unsigned idx = 0; + + for (CallExpr::const_arg_iterator I=CE->arg_begin(), E=CE->arg_end(); I!=E; + ++I, ++idx) { + + if (!Att->isNonNull(idx)) + continue; + + SVal V = state->getSVal(*I); + DefinedSVal *DV = dyn_cast(&V); + + // If the value is unknown or undefined, we can't perform this check. + if (!DV) + continue; + + if (!isa(*DV)) { + // If the argument is a union type, we want to handle a potential + // transparent_unoin GCC extension. + QualType T = (*I)->getType(); + const RecordType *UT = T->getAsUnionType(); + if (!UT || !UT->getDecl()->hasAttr()) + continue; + if (nonloc::CompoundVal *CSV = dyn_cast(DV)) { + nonloc::CompoundVal::iterator CSV_I = CSV->begin(); + assert(CSV_I != CSV->end()); + V = *CSV_I; + DV = dyn_cast(&V); + assert(++CSV_I == CSV->end()); + if (!DV) + continue; + } + else { + // FIXME: Handle LazyCompoundVals? + continue; + } + } + + ConstraintManager &CM = C.getConstraintManager(); + const GRState *stateNotNull, *stateNull; + llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); + + if (stateNull && !stateNotNull) { + // Generate an error node. Check for a null node in case + // we cache out. + if (ExplodedNode *errorNode = C.generateSink(stateNull)) { + + // 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 (!BT) + BT = new BugType("Argument with 'nonnull' attribute passed null", + "API"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, + "Null pointer passed as an argument to a " + "'nonnull' parameter", errorNode); + + // Highlight the range of the argument that was null. + const Expr *arg = *I; + R->addRange(arg->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, arg); + + // Emit the bug report. + C.EmitReport(R); + } + + // Always return. Either we cached out or we just emitted an error. + return; + } + + // If a pointer value passed the check we should assume that it is + // indeed not null from this point forward. + assert(stateNotNull); + 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); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/BasicObjCFoundationChecks.cpp new file mode 100644 index 0000000000..b4e2959150 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/BasicObjCFoundationChecks.cpp @@ -0,0 +1,521 @@ +//== 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 "BasicObjCFoundationChecks.h" + +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/ASTContext.h" + +using namespace clang; +using namespace ento; + +namespace { +class APIMisuse : public BugType { +public: + APIMisuse(const char* name) : BugType(name, "API Misuse (Apple)") {} +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static const ObjCInterfaceType* GetReceiverType(const ObjCMessageExpr* ME) { + QualType T; + switch (ME->getReceiverKind()) { + case ObjCMessageExpr::Instance: + T = ME->getInstanceReceiver()->getType(); + break; + + case ObjCMessageExpr::SuperInstance: + T = ME->getSuperType(); + break; + + case ObjCMessageExpr::Class: + case ObjCMessageExpr::SuperClass: + return 0; + } + + if (const ObjCObjectPointerType *PT = T->getAs()) + return PT->getInterfaceType(); + + return NULL; +} + +static const char* GetReceiverNameType(const ObjCMessageExpr* ME) { + if (const ObjCInterfaceType *ReceiverType = GetReceiverType(ME)) + return ReceiverType->getDecl()->getIdentifier()->getNameStart(); + return NULL; +} + +static bool isNSString(llvm::StringRef ClassName) { + return ClassName == "NSString" || ClassName == "NSMutableString"; +} + +static inline bool isNil(SVal X) { + return isa(X); +} + +//===----------------------------------------------------------------------===// +// NilArgChecker - Check for prohibited nil arguments to ObjC method calls. +//===----------------------------------------------------------------------===// + +namespace { + class NilArgChecker : public CheckerVisitor { + APIMisuse *BT; + void WarnNilArg(CheckerContext &C, const ObjCMessageExpr* ME, unsigned Arg); + public: + NilArgChecker() : BT(0) {} + static void *getTag() { static int x = 0; return &x; } + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); + }; +} + +void NilArgChecker::WarnNilArg(CheckerContext &C, + const clang::ObjCMessageExpr *ME, + unsigned int Arg) +{ + if (!BT) + BT = new APIMisuse("nil argument"); + + if (ExplodedNode *N = C.generateSink()) { + llvm::SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + os << "Argument to '" << GetReceiverNameType(ME) << "' method '" + << ME->getSelector().getAsString() << "' cannot be nil"; + + RangedBugReport *R = new RangedBugReport(*BT, os.str(), N); + R->addRange(ME->getArg(Arg)->getSourceRange()); + C.EmitReport(R); + } +} + +void NilArgChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) +{ + const ObjCInterfaceType *ReceiverType = GetReceiverType(ME); + if (!ReceiverType) + return; + + if (isNSString(ReceiverType->getDecl()->getIdentifier()->getName())) { + Selector S = ME->getSelector(); + + if (S.isUnarySelector()) + return; + + // FIXME: This is going to be really slow doing these checks with + // lexical comparisons. + + std::string NameStr = S.getAsString(); + llvm::StringRef Name(NameStr); + assert(!Name.empty()); + + // FIXME: Checking for initWithFormat: will not work in most cases + // yet because [NSString alloc] returns id, not NSString*. We will + // need support for tracking expected-type information in the analyzer + // to find these errors. + if (Name == "caseInsensitiveCompare:" || + Name == "compare:" || + Name == "compare:options:" || + Name == "compare:options:range:" || + Name == "compare:options:range:locale:" || + Name == "componentsSeparatedByCharactersInSet:" || + Name == "initWithFormat:") { + if (isNil(C.getState()->getSVal(ME->getArg(0)))) + WarnNilArg(C, ME, 0); + } + } +} + +//===----------------------------------------------------------------------===// +// Error reporting. +//===----------------------------------------------------------------------===// + +namespace { +class CFNumberCreateChecker : public CheckerVisitor { + APIMisuse* BT; + IdentifierInfo* II; +public: + CFNumberCreateChecker() : BT(0), II(0) {} + ~CFNumberCreateChecker() {} + static void *getTag() { static int x = 0; return &x; } + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +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 +}; + +namespace { + template + class Optional { + bool IsKnown; + T Val; + public: + Optional() : IsKnown(false), Val(0) {} + Optional(const T& val) : IsKnown(true), Val(val) {} + + bool isKnown() const { return IsKnown; } + + const T& getValue() const { + assert (isKnown()); + return Val; + } + + operator const T&() const { + return getValue(); + } + }; +} + +static Optional 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 Optional(); + } + + 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 CFNumberCreateChecker::PreVisitCallExpr(CheckerContext &C, + const CallExpr *CE) +{ + const Expr* Callee = CE->getCallee(); + const GRState *state = C.getState(); + SVal CallV = state->getSVal(Callee); + const FunctionDecl* FD = CallV.getAsFunctionDecl(); + + if (!FD) + return; + + ASTContext &Ctx = C.getASTContext(); + if (!II) + II = &Ctx.Idents.get("CFNumberCreate"); + + if (FD->getIdentifier() != II || CE->getNumArgs() != 3) + return; + + // Get the value of the "theType" argument. + SVal TheTypeVal = state->getSVal(CE->getArg(1)); + + // FIXME: We really should allow ranges of valid theType values, and + // bifurcate the state appropriately. + nonloc::ConcreteInt* V = dyn_cast(&TheTypeVal); + if (!V) + return; + + uint64_t NumberKind = V->getValue().getLimitedValue(); + Optional TargetSize = GetCFNumberSize(Ctx, NumberKind); + + // FIXME: In some cases we can emit an error. + if (!TargetSize.isKnown()) + return; + + // 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 = state->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. + loc::MemRegionVal* LV = dyn_cast(&TheValueExpr); + if (!LV) + return; + + const TypedRegion* R = dyn_cast(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->isIntegerType()) + return; + + uint64_t SourceSize = Ctx.getTypeSize(T); + + // CHECK: is SourceSize == TargetSize + if (SourceSize == TargetSize) + return; + + // Generate an error. Only generate a sink if 'SourceSize < TargetSize'; + // otherwise generate a regular node. + // + // FIXME: We can actually create an abstract "CFNumber" object that has + // the bits initialized to the provided values. + // + if (ExplodedNode *N = SourceSize < TargetSize ? C.generateSink() + : C.generateNode()) { + llvm::SmallString<128> sbuf; + llvm::raw_svector_ostream os(sbuf); + + os << (SourceSize == 8 ? "An " : "A ") + << SourceSize << " bit integer is used to initialize a CFNumber " + "object that represents " + << (TargetSize == 8 ? "an " : "a ") + << TargetSize << " bit integer. "; + + if (SourceSize < TargetSize) + os << (TargetSize - SourceSize) + << " bits of the CFNumber value will be garbage." ; + else + os << (SourceSize - TargetSize) + << " bits of the input integer will be lost."; + + if (!BT) + BT = new APIMisuse("Bad use of CFNumberCreate"); + + RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + report->addRange(CE->getArg(2)->getSourceRange()); + C.EmitReport(report); + } +} + +//===----------------------------------------------------------------------===// +// CFRetain/CFRelease checking for null arguments. +//===----------------------------------------------------------------------===// + +namespace { +class CFRetainReleaseChecker : public CheckerVisitor { + APIMisuse *BT; + IdentifierInfo *Retain, *Release; +public: + CFRetainReleaseChecker(): BT(0), Retain(0), Release(0) {} + static void *getTag() { static int x = 0; return &x; } + void PreVisitCallExpr(CheckerContext& C, const CallExpr* CE); +}; +} // end anonymous namespace + + +void CFRetainReleaseChecker::PreVisitCallExpr(CheckerContext& C, + const CallExpr* CE) { + // If the CallExpr doesn't have exactly 1 argument just give up checking. + if (CE->getNumArgs() != 1) + return; + + // Get the function declaration of the callee. + const GRState* state = C.getState(); + SVal X = state->getSVal(CE->getCallee()); + const FunctionDecl* FD = X.getAsFunctionDecl(); + + if (!FD) + return; + + if (!BT) { + ASTContext &Ctx = C.getASTContext(); + Retain = &Ctx.Idents.get("CFRetain"); + Release = &Ctx.Idents.get("CFRelease"); + BT = new APIMisuse("null passed to CFRetain/CFRelease"); + } + + // Check if we called CFRetain/CFRelease. + const IdentifierInfo *FuncII = FD->getIdentifier(); + if (!(FuncII == Retain || FuncII == Release)) + return; + + // FIXME: The rest of this just checks that the argument is non-null. + // It should probably be refactored and combined with AttrNonNullChecker. + + // Get the argument's value. + const Expr *Arg = CE->getArg(0); + SVal ArgVal = state->getSVal(Arg); + DefinedSVal *DefArgVal = dyn_cast(&ArgVal); + if (!DefArgVal) + return; + + // Get a NULL value. + SValBuilder &svalBuilder = C.getSValBuilder(); + DefinedSVal zero = cast(svalBuilder.makeZeroVal(Arg->getType())); + + // Make an expression asserting that they're equal. + DefinedOrUnknownSVal ArgIsNull = svalBuilder.evalEQ(state, zero, *DefArgVal); + + // Are they equal? + const GRState *stateTrue, *stateFalse; + llvm::tie(stateTrue, stateFalse) = state->assume(ArgIsNull); + + if (stateTrue && !stateFalse) { + ExplodedNode *N = C.generateSink(stateTrue); + if (!N) + return; + + const char *description = (FuncII == Retain) + ? "Null pointer argument in call to CFRetain" + : "Null pointer argument in call to CFRelease"; + + EnhancedBugReport *report = new EnhancedBugReport(*BT, description, N); + report->addRange(Arg->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Arg); + C.EmitReport(report); + return; + } + + // From here on, we know the argument is non-null. + C.addTransition(stateFalse); +} + +//===----------------------------------------------------------------------===// +// Check for sending 'retain', 'release', or 'autorelease' directly to a Class. +//===----------------------------------------------------------------------===// + +namespace { +class ClassReleaseChecker : public CheckerVisitor { + Selector releaseS; + Selector retainS; + Selector autoreleaseS; + Selector drainS; + BugType *BT; +public: + ClassReleaseChecker() + : BT(0) {} + + static void *getTag() { static int x = 0; return &x; } + + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); +}; +} + +void ClassReleaseChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) { + + if (!BT) { + BT = new APIMisuse("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); + } + + ObjCInterfaceDecl *Class = 0; + + switch (ME->getReceiverKind()) { + case ObjCMessageExpr::Class: + Class = ME->getClassReceiver()->getAs()->getInterface(); + break; + case ObjCMessageExpr::SuperClass: + Class = ME->getSuperType()->getAs()->getInterface(); + break; + case ObjCMessageExpr::Instance: + case ObjCMessageExpr::SuperInstance: + return; + } + + Selector S = ME->getSelector(); + if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS)) + return; + + if (ExplodedNode *N = C.generateNode()) { + llvm::SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + + os << "The '" << S.getAsString() << "' message should be sent to instances " + "of class '" << Class->getName() + << "' and not the class directly"; + + RangedBugReport *report = new RangedBugReport(*BT, os.str(), N); + report->addRange(ME->getSourceRange()); + C.EmitReport(report); + } +} + +//===----------------------------------------------------------------------===// +// Check registration. +//===----------------------------------------------------------------------===// + +void ento::RegisterAppleChecks(ExprEngine& Eng, const Decl &D) { + Eng.registerCheck(new NilArgChecker()); + Eng.registerCheck(new CFNumberCreateChecker()); + RegisterNSErrorChecks(Eng.getBugReporter(), Eng, D); + RegisterNSAutoreleasePoolChecks(Eng); + Eng.registerCheck(new CFRetainReleaseChecker()); + Eng.registerCheck(new ClassReleaseChecker()); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/BasicObjCFoundationChecks.h b/lib/StaticAnalyzer/EntoSA/Checkers/BasicObjCFoundationChecks.h new file mode 100644 index 0000000000..f4966e8ae8 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/BasicObjCFoundationChecks.h @@ -0,0 +1,36 @@ +//== BasicObjCFoundationChecks.h - 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. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_BASICOBJCFOUNDATIONCHECKS +#define LLVM_CLANG_GR_BASICOBJCFOUNDATIONCHECKS + +namespace clang { + +class ASTContext; +class Decl; + +namespace ento { + +class BugReporter; +class ExprEngine; + +void RegisterNSErrorChecks(BugReporter& BR, ExprEngine &Eng, const Decl &D); +void RegisterNSAutoreleasePoolChecks(ExprEngine &Eng); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/BuiltinFunctionChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/BuiltinFunctionChecker.cpp new file mode 100644 index 0000000000..2e6f1b90e1 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/BuiltinFunctionChecker.cpp @@ -0,0 +1,83 @@ +//=== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" +#include "clang/Basic/Builtins.h" + +using namespace clang; +using namespace ento; + +namespace { + +class BuiltinFunctionChecker : public Checker { +public: + static void *getTag() { static int tag = 0; return &tag; } + virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE); +}; + +} + +void ento::RegisterBuiltinFunctionChecker(ExprEngine &Eng) { + Eng.registerCheck(new BuiltinFunctionChecker()); +} + +bool BuiltinFunctionChecker::evalCallExpr(CheckerContext &C,const CallExpr *CE){ + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + + if (!FD) + return false; + + unsigned id = FD->getBuiltinID(); + + if (!id) + return false; + + switch (id) { + case Builtin::BI__builtin_expect: { + // For __builtin_expect, just return the value of the subexpression. + assert (CE->arg_begin() != CE->arg_end()); + SVal X = state->getSVal(*(CE->arg_begin())); + C.generateNode(state->BindExpr(CE, X)); + return true; + } + + case Builtin::BI__builtin_alloca: { + // FIXME: Refactor into StoreManager itself? + MemRegionManager& RM = C.getStoreManager().getRegionManager(); + const AllocaRegion* R = + RM.getAllocaRegion(CE, C.getNodeBuilder().getCurrentBlockCount(), + C.getPredecessor()->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. + DefinedOrUnknownSVal Size = + cast(state->getSVal(*(CE->arg_begin()))); + + SValBuilder& svalBuilder = C.getSValBuilder(); + DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); + DefinedOrUnknownSVal extentMatchesSizeArg = + svalBuilder.evalEQ(state, Extent, Size); + state = state->assume(extentMatchesSizeArg, true); + + C.generateNode(state->BindExpr(CE, loc::MemRegionVal(R))); + return true; + } + } + + return false; +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/CMakeLists.txt b/lib/StaticAnalyzer/EntoSA/Checkers/CMakeLists.txt new file mode 100644 index 0000000000..e4b9541c23 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/CMakeLists.txt @@ -0,0 +1,53 @@ +add_clang_library(clangStaticAnalyzerCheckers + AdjustedReturnValueChecker.cpp + AnalysisConsumer.cpp + ArrayBoundChecker.cpp + ArrayBoundCheckerV2.cpp + AttrNonNullChecker.cpp + BasicObjCFoundationChecks.cpp + BuiltinFunctionChecker.cpp + CStringChecker.cpp + CallAndMessageChecker.cpp + CastSizeChecker.cpp + CastToStructChecker.cpp + CheckDeadStores.cpp + CheckObjCDealloc.cpp + CheckObjCInstMethSignature.cpp + CheckSecuritySyntaxOnly.cpp + CheckSizeofPointer.cpp + ChrootChecker.cpp + DereferenceChecker.cpp + DivZeroChecker.cpp + ExprEngine.cpp + ExprEngineExperimentalChecks.cpp + FixedAddressChecker.cpp + FrontendActions.cpp + IdempotentOperationChecker.cpp + LLVMConventionsChecker.cpp + MacOSXAPIChecker.cpp + MallocChecker.cpp + NSAutoreleasePoolChecker.cpp + NSErrorChecker.cpp + NoReturnFunctionChecker.cpp + OSAtomicChecker.cpp + ObjCAtSyncChecker.cpp + ObjCUnusedIVarsChecker.cpp + PointerArithChecker.cpp + PointerSubChecker.cpp + PthreadLockChecker.cpp + ReturnPointerRangeChecker.cpp + ReturnUndefChecker.cpp + StackAddrLeakChecker.cpp + StreamChecker.cpp + UndefBranchChecker.cpp + UndefCapturedBlockVarChecker.cpp + UndefResultChecker.cpp + UndefinedArraySubscriptChecker.cpp + UndefinedAssignmentChecker.cpp + UnixAPIChecker.cpp + UnreachableCodeChecker.cpp + VLASizeChecker.cpp + ) + +add_dependencies(clangStaticAnalyzerCore ClangAttrClasses ClangAttrList ClangDeclNodes + ClangStmtNodes) diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/CStringChecker.cpp new file mode 100644 index 0000000000..b7513c39c1 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/CStringChecker.cpp @@ -0,0 +1,1048 @@ +//= CStringChecker.h - 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 . +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineExperimentalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; +using namespace ento; + +namespace { +class CStringChecker : public CheckerVisitor { + BugType *BT_Null, *BT_Bounds, *BT_BoundsWrite, *BT_Overlap, *BT_NotCString; +public: + CStringChecker() + : BT_Null(0), BT_Bounds(0), BT_BoundsWrite(0), BT_Overlap(0), BT_NotCString(0) + {} + static void *getTag() { static int tag; return &tag; } + + bool evalCallExpr(CheckerContext &C, const CallExpr *CE); + void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS); + void MarkLiveSymbols(const GRState *state, SymbolReaper &SR); + void evalDeadSymbols(CheckerContext &C, SymbolReaper &SR); + bool WantsRegionChangeUpdate(const GRState *state); + + const GRState *EvalRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End, + bool*); + + typedef void (CStringChecker::*FnCheck)(CheckerContext &, const CallExpr *); + + void evalMemcpy(CheckerContext &C, const CallExpr *CE); + void evalMemmove(CheckerContext &C, const CallExpr *CE); + void evalBcopy(CheckerContext &C, const CallExpr *CE); + void evalCopyCommon(CheckerContext &C, const GRState *state, + const Expr *Size, const Expr *Source, const Expr *Dest, + bool Restricted = false); + + void evalMemcmp(CheckerContext &C, const CallExpr *CE); + + void evalstrLength(CheckerContext &C, const CallExpr *CE); + + void evalStrcpy(CheckerContext &C, const CallExpr *CE); + void evalStpcpy(CheckerContext &C, const CallExpr *CE); + void evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, bool returnEnd); + + // Utility methods + std::pair + assumeZero(CheckerContext &C, const GRState *state, SVal V, QualType Ty); + + const GRState *setCStringLength(const GRState *state, const MemRegion *MR, + SVal strLength); + SVal getCStringLengthForRegion(CheckerContext &C, const GRState *&state, + const Expr *Ex, const MemRegion *MR); + SVal getCStringLength(CheckerContext &C, const GRState *&state, + const Expr *Ex, SVal Buf); + + const GRState *InvalidateBuffer(CheckerContext &C, const GRState *state, + const Expr *Ex, SVal V); + + bool SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, + const MemRegion *MR); + + // Re-usable checks + const GRState *checkNonNull(CheckerContext &C, const GRState *state, + const Expr *S, SVal l); + const GRState *CheckLocation(CheckerContext &C, const GRState *state, + const Expr *S, SVal l, + bool IsDestination = false); + const GRState *CheckBufferAccess(CheckerContext &C, const GRState *state, + const Expr *Size, + const Expr *FirstBuf, + const Expr *SecondBuf = NULL, + bool FirstIsDestination = false); + const GRState *CheckOverlap(CheckerContext &C, const GRState *state, + const Expr *Size, const Expr *First, + const Expr *Second); + void emitOverlapBug(CheckerContext &C, const GRState *state, + const Stmt *First, const Stmt *Second); +}; + +class CStringLength { +public: + typedef llvm::ImmutableMap EntryMap; +}; +} //end anonymous namespace + +namespace clang { +namespace ento { + template <> + struct GRStateTrait + : public GRStatePartialTrait { + static void *GDMIndex() { return CStringChecker::getTag(); } + }; +} +} + +void ento::RegisterCStringChecker(ExprEngine &Eng) { + Eng.registerCheck(new CStringChecker()); +} + +//===----------------------------------------------------------------------===// +// Individual checks and utility methods. +//===----------------------------------------------------------------------===// + +std::pair +CStringChecker::assumeZero(CheckerContext &C, const GRState *state, SVal V, + QualType Ty) { + DefinedSVal *val = dyn_cast(&V); + if (!val) + return std::pair(state, state); + + SValBuilder &svalBuilder = C.getSValBuilder(); + DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal(Ty); + return state->assume(svalBuilder.evalEQ(state, *val, zero)); +} + +const GRState *CStringChecker::checkNonNull(CheckerContext &C, + const GRState *state, + const Expr *S, SVal l) { + // If a previous check has failed, propagate the failure. + if (!state) + return NULL; + + const GRState *stateNull, *stateNonNull; + llvm::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType()); + + if (stateNull && !stateNonNull) { + ExplodedNode *N = C.generateSink(stateNull); + if (!N) + return NULL; + + if (!BT_Null) + BT_Null = new BuiltinBug("API", + "Null pointer argument in call to byte string function"); + + // Generate a report for this bug. + BuiltinBug *BT = static_cast(BT_Null); + EnhancedBugReport *report = new EnhancedBugReport(*BT, + BT->getDescription(), N); + + report->addRange(S->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, S); + C.EmitReport(report); + return NULL; + } + + // From here on, assume that the value is non-null. + assert(stateNonNull); + return stateNonNull; +} + +// FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor? +const GRState *CStringChecker::CheckLocation(CheckerContext &C, + const GRState *state, + const Expr *S, SVal l, + bool IsDestination) { + // If a previous check has failed, propagate the failure. + if (!state) + return NULL; + + // Check for out of bound array element access. + const MemRegion *R = l.getAsRegion(); + if (!R) + return state; + + const ElementRegion *ER = dyn_cast(R); + if (!ER) + return state; + + assert(ER->getValueType() == C.getASTContext().CharTy && + "CheckLocation should only be called with char* ElementRegions"); + + // Get the size of the array. + const SubRegion *superReg = cast(ER->getSuperRegion()); + SValBuilder &svalBuilder = C.getSValBuilder(); + SVal Extent = svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder)); + DefinedOrUnknownSVal Size = cast(Extent); + + // Get the index of the accessed element. + DefinedOrUnknownSVal Idx = cast(ER->getIndex()); + + const GRState *StInBound = state->assumeInBound(Idx, Size, true); + const GRState *StOutBound = state->assumeInBound(Idx, Size, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.generateSink(StOutBound); + if (!N) + return NULL; + + BuiltinBug *BT; + if (IsDestination) { + if (!BT_BoundsWrite) { + BT_BoundsWrite = new BuiltinBug("Out-of-bound array access", + "Byte string function overflows destination buffer"); + } + BT = static_cast(BT_BoundsWrite); + } else { + if (!BT_Bounds) { + BT_Bounds = new BuiltinBug("Out-of-bound array access", + "Byte string function accesses out-of-bound array element"); + } + BT = static_cast(BT_Bounds); + } + + // 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. + RangedBugReport *report = new RangedBugReport(*BT, BT->getDescription(), N); + + report->addRange(S->getSourceRange()); + C.EmitReport(report); + return NULL; + } + + // Array bound check succeeded. From this point forward the array bound + // should always succeed. + return StInBound; +} + +const GRState *CStringChecker::CheckBufferAccess(CheckerContext &C, + const GRState *state, + const Expr *Size, + const Expr *FirstBuf, + const Expr *SecondBuf, + bool FirstIsDestination) { + // If a previous check has failed, propagate the failure. + if (!state) + return NULL; + + SValBuilder &svalBuilder = C.getSValBuilder(); + ASTContext &Ctx = C.getASTContext(); + + QualType sizeTy = Size->getType(); + QualType PtrTy = Ctx.getPointerType(Ctx.CharTy); + + // Check that the first buffer is non-null. + SVal BufVal = state->getSVal(FirstBuf); + state = checkNonNull(C, state, FirstBuf, BufVal); + if (!state) + return NULL; + + // Get the access length and make sure it is known. + SVal LengthVal = state->getSVal(Size); + NonLoc *Length = dyn_cast(&LengthVal); + if (!Length) + return state; + + // Compute the offset of the last element to be accessed: size-1. + NonLoc One = cast(svalBuilder.makeIntVal(1, sizeTy)); + NonLoc LastOffset = cast(svalBuilder.evalBinOpNN(state, BO_Sub, + *Length, One, sizeTy)); + + // Check that the first buffer is sufficently long. + SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType()); + if (Loc *BufLoc = dyn_cast(&BufStart)) { + SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, + LastOffset, PtrTy); + state = CheckLocation(C, state, FirstBuf, BufEnd, FirstIsDestination); + + // If the buffer isn't large enough, abort. + if (!state) + return NULL; + } + + // If there's a second buffer, check it as well. + if (SecondBuf) { + BufVal = state->getSVal(SecondBuf); + state = checkNonNull(C, state, SecondBuf, BufVal); + if (!state) + return NULL; + + BufStart = svalBuilder.evalCast(BufVal, PtrTy, SecondBuf->getType()); + if (Loc *BufLoc = dyn_cast(&BufStart)) { + SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, + LastOffset, PtrTy); + state = CheckLocation(C, state, SecondBuf, BufEnd); + } + } + + // Large enough or not, return this state! + return state; +} + +const GRState *CStringChecker::CheckOverlap(CheckerContext &C, + const GRState *state, + const Expr *Size, + const Expr *First, + const Expr *Second) { + // 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 NULL; + + const GRState *stateTrue, *stateFalse; + + // Get the buffer values and make sure they're known locations. + SVal firstVal = state->getSVal(First); + SVal secondVal = state->getSVal(Second); + + Loc *firstLoc = dyn_cast(&firstVal); + if (!firstLoc) + return state; + + Loc *secondLoc = dyn_cast(&secondVal); + if (!secondLoc) + return state; + + // Are the two values the same? + SValBuilder &svalBuilder = C.getSValBuilder(); + llvm::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 NULL; + } + + // assume the two expressions are not equal. + assert(stateFalse); + state = stateFalse; + + // Which value comes first? + ASTContext &Ctx = svalBuilder.getContext(); + QualType cmpTy = Ctx.IntTy; + SVal reverse = svalBuilder.evalBinOpLL(state, BO_GT, + *firstLoc, *secondLoc, cmpTy); + DefinedOrUnknownSVal *reverseTest = dyn_cast(&reverse); + if (!reverseTest) + return state; + + llvm::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. + Loc *tmpLoc = firstLoc; + firstLoc = secondLoc; + secondLoc = tmpLoc; + + // Switch the Exprs as well, so that they still correspond. + const Expr *tmpExpr = First; + First = Second; + Second = tmpExpr; + } + } + + // Get the length, and make sure it too is known. + SVal LengthVal = state->getSVal(Size); + NonLoc *Length = dyn_cast(&LengthVal); + if (!Length) + return state; + + // Convert the first buffer's start address to char*. + // Bail out if the cast fails. + QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy); + SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy, First->getType()); + Loc *FirstStartLoc = dyn_cast(&FirstStart); + 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); + Loc *FirstEndLoc = dyn_cast(&FirstEnd); + 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); + DefinedOrUnknownSVal *OverlapTest = dyn_cast(&Overlap); + if (!OverlapTest) + return state; + + llvm::tie(stateTrue, stateFalse) = state->assume(*OverlapTest); + + if (stateTrue && !stateFalse) { + // Overlap! + emitOverlapBug(C, stateTrue, First, Second); + return NULL; + } + + // assume the two expressions don't overlap. + assert(stateFalse); + return stateFalse; +} + +void CStringChecker::emitOverlapBug(CheckerContext &C, const GRState *state, + const Stmt *First, const Stmt *Second) { + ExplodedNode *N = C.generateSink(state); + if (!N) + return; + + if (!BT_Overlap) + BT_Overlap = new BugType("Unix API", "Improper arguments"); + + // Generate a report for this bug. + RangedBugReport *report = + new RangedBugReport(*BT_Overlap, + "Arguments must not be overlapping buffers", N); + report->addRange(First->getSourceRange()); + report->addRange(Second->getSourceRange()); + + C.EmitReport(report); +} + +const GRState *CStringChecker::setCStringLength(const GRState *state, + const MemRegion *MR, + SVal strLength) { + assert(!strLength.isUndef() && "Attempt to set an undefined string length"); + if (strLength.isUnknown()) + return state; + + 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: + return state->set(MR, strLength); + + 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; + } +} + +SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C, + const GRState *&state, + const Expr *Ex, + const MemRegion *MR) { + // If there's a recorded length, go ahead and return it. + const SVal *Recorded = state->get(MR); + if (Recorded) + return *Recorded; + + // Otherwise, get a new symbol and update the state. + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + SValBuilder &svalBuilder = C.getSValBuilder(); + QualType sizeTy = svalBuilder.getContext().getSizeType(); + SVal strLength = svalBuilder.getMetadataSymbolVal(getTag(), MR, Ex, sizeTy, Count); + state = state->set(MR, strLength); + return strLength; +} + +SVal CStringChecker::getCStringLength(CheckerContext &C, const GRState *&state, + const Expr *Ex, SVal Buf) { + 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 (loc::GotoLabel *Label = dyn_cast(&Buf)) { + if (ExplodedNode *N = C.generateNode(state)) { + if (!BT_NotCString) + BT_NotCString = new BuiltinBug("API", + "Argument is not a null-terminated string."); + + llvm::SmallString<120> buf; + llvm::raw_svector_ostream os(buf); + os << "Argument to byte string function is the address of the label '" + << Label->getLabel()->getID()->getName() + << "', which is not a null-terminated string"; + + // Generate a report for this bug. + EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString, + os.str(), N); + + report->addRange(Ex->getSourceRange()); + C.EmitReport(report); + } + + 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(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); + 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 (ExplodedNode *N = C.generateNode(state)) { + if (!BT_NotCString) + BT_NotCString = new BuiltinBug("API", + "Argument is not a null-terminated string."); + + llvm::SmallString<120> buf; + llvm::raw_svector_ostream os(buf); + + os << "Argument to byte string function is "; + + if (SummarizeRegion(os, C.getASTContext(), MR)) + os << ", which is not a null-terminated string"; + else + os << "not a null-terminated string"; + + // Generate a report for this bug. + EnhancedBugReport *report = new EnhancedBugReport(*BT_NotCString, + os.str(), N); + + report->addRange(Ex->getSourceRange()); + C.EmitReport(report); + } + + return UndefinedVal(); + } +} + +const GRState *CStringChecker::InvalidateBuffer(CheckerContext &C, + const GRState *state, + const Expr *E, SVal V) { + Loc *L = dyn_cast(&V); + 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 (loc::MemRegionVal* MR = dyn_cast(L)) { + 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(R)) { + R = ER->getSuperRegion(); + // FIXME: What about layers of ElementRegions? + } + + // Invalidate this region. + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + return state->InvalidateRegion(R, E, Count, NULL); + } + + // 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->unbindLoc(*L); +} + +bool CStringChecker::SummarizeRegion(llvm::raw_ostream& os, ASTContext& Ctx, + const MemRegion *MR) { + const TypedRegion *TR = dyn_cast(MR); + if (!TR) + return false; + + switch (TR->getKind()) { + case MemRegion::FunctionTextRegionKind: { + const FunctionDecl *FD = cast(TR)->getDecl(); + if (FD) + os << "the address of the function '" << FD << "'"; + else + os << "the address of a function"; + return true; + } + case MemRegion::BlockTextRegionKind: + 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 " << TR->getValueType().getAsString(); + return true; + case MemRegion::VarRegionKind: + os << "a variable of type" << TR->getValueType().getAsString(); + return true; + case MemRegion::FieldRegionKind: + os << "a field of type " << TR->getValueType().getAsString(); + return true; + case MemRegion::ObjCIvarRegionKind: + os << "an instance variable of type " << TR->getValueType().getAsString(); + return true; + default: + return false; + } +} + +//===----------------------------------------------------------------------===// +// evaluation of individual function calls. +//===----------------------------------------------------------------------===// + +void CStringChecker::evalCopyCommon(CheckerContext &C, const GRState *state, + const Expr *Size, const Expr *Dest, + const Expr *Source, bool Restricted) { + // See if the size argument is zero. + SVal sizeVal = state->getSVal(Size); + QualType sizeTy = Size->getType(); + + const GRState *stateZeroSize, *stateNonZeroSize; + llvm::tie(stateZeroSize, stateNonZeroSize) = assumeZero(C, state, sizeVal, sizeTy); + + // If the size is zero, there won't be any actual memory access. + if (stateZeroSize) + C.addTransition(stateZeroSize); + + // If the size can be nonzero, we have to check the other arguments. + if (stateNonZeroSize) { + state = stateNonZeroSize; + state = CheckBufferAccess(C, state, Size, Dest, Source, + /* FirstIsDst = */ true); + if (Restricted) + state = CheckOverlap(C, state, Size, Dest, Source); + + if (state) { + // Invalidate the destination. + // 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, state->getSVal(Dest)); + C.addTransition(state); + } + } +} + + +void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) { + // 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); + const GRState *state = C.getState(); + state = state->BindExpr(CE, state->getSVal(Dest)); + evalCopyCommon(C, state, CE->getArg(2), Dest, CE->getArg(1), true); +} + +void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) { + // 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); + const GRState *state = C.getState(); + state = state->BindExpr(CE, state->getSVal(Dest)); + evalCopyCommon(C, state, CE->getArg(2), Dest, CE->getArg(1)); +} + +void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) { + // void bcopy(const void *src, void *dst, size_t n); + evalCopyCommon(C, C.getState(), CE->getArg(2), CE->getArg(1), CE->getArg(0)); +} + +void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) { + // int memcmp(const void *s1, const void *s2, size_t n); + const Expr *Left = CE->getArg(0); + const Expr *Right = CE->getArg(1); + const Expr *Size = CE->getArg(2); + + const GRState *state = C.getState(); + SValBuilder &svalBuilder = C.getSValBuilder(); + + // See if the size argument is zero. + SVal sizeVal = state->getSVal(Size); + QualType sizeTy = Size->getType(); + + const GRState *stateZeroSize, *stateNonZeroSize; + llvm::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, 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 = cast(state->getSVal(Left)); + DefinedOrUnknownSVal RV = cast(state->getSVal(Right)); + + // See if they are the same. + DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV); + const GRState *StSameBuf, *StNotSameBuf; + llvm::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf); + + // If the two arguments might be the same buffer, we know the result is zero, + // and we only need to check one size. + if (StSameBuf) { + state = StSameBuf; + state = CheckBufferAccess(C, state, Size, Left); + if (state) { + state = StSameBuf->BindExpr(CE, 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. + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + SVal CmpV = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); + state = state->BindExpr(CE, CmpV); + C.addTransition(state); + } + } + } +} + +void CStringChecker::evalstrLength(CheckerContext &C, const CallExpr *CE) { + // size_t strlen(const char *s); + const GRState *state = C.getState(); + const Expr *Arg = CE->getArg(0); + SVal ArgVal = state->getSVal(Arg); + + // Check that the argument is non-null. + state = checkNonNull(C, state, Arg, ArgVal); + + if (state) { + 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; + + // If getCStringLength couldn't figure out the length, conjure a return + // value, so it can be used in constraints, at least. + if (strLength.isUnknown()) { + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + strLength = C.getSValBuilder().getConjuredSymbolVal(NULL, CE, Count); + } + + // Bind the return value. + state = state->BindExpr(CE, strLength); + C.addTransition(state); + } +} + +void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) { + // char *strcpy(char *restrict dst, const char *restrict src); + evalStrcpyCommon(C, CE, /* returnEnd = */ false); +} + +void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) { + // char *stpcpy(char *restrict dst, const char *restrict src); + evalStrcpyCommon(C, CE, /* returnEnd = */ true); +} + +void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE, + bool returnEnd) { + const GRState *state = C.getState(); + + // Check that the destination is non-null + const Expr *Dst = CE->getArg(0); + SVal DstVal = state->getSVal(Dst); + + 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); + 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; + + SVal Result = (returnEnd ? UnknownVal() : DstVal); + + // If the destination is a MemRegion, try to check for a buffer overflow and + // record the new string length. + if (loc::MemRegionVal *dstRegVal = dyn_cast(&DstVal)) { + // If the length is known, we can check for an overflow. + if (NonLoc *knownStrLength = dyn_cast(&strLength)) { + SVal lastElement = + C.getSValBuilder().evalBinOpLN(state, BO_Add, *dstRegVal, + *knownStrLength, Dst->getType()); + + state = CheckLocation(C, state, Dst, lastElement, /* IsDst = */ true); + if (!state) + return; + + // If this is a stpcpy-style copy, the last element is the return value. + if (returnEnd) + Result = lastElement; + } + + // Invalidate the destination. 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); + + // Set the C string length of the destination. + state = setCStringLength(state, dstRegVal->getRegion(), strLength); + } + + // 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()) { + SValBuilder &svalBuilder = C.getSValBuilder(); + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + strLength = svalBuilder.getConjuredSymbolVal(NULL, CE, Count); + } + + // Set the return value. + state = state->BindExpr(CE, Result); + C.addTransition(state); +} + +//===----------------------------------------------------------------------===// +// The driver method, and other Checker callbacks. +//===----------------------------------------------------------------------===// + +bool CStringChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { + // Get the callee. All the functions we care about are C functions + // with simple identifiers. + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const FunctionDecl *FD = state->getSVal(Callee).getAsFunctionDecl(); + + if (!FD) + return false; + + // 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 false; + llvm::StringRef Name = II->getName(); + if (Name.startswith("__builtin_")) + Name = Name.substr(10); + + FnCheck evalFunction = llvm::StringSwitch(Name) + .Cases("memcpy", "__memcpy_chk", &CStringChecker::evalMemcpy) + .Cases("memcmp", "bcmp", &CStringChecker::evalMemcmp) + .Cases("memmove", "__memmove_chk", &CStringChecker::evalMemmove) + .Cases("strcpy", "__strcpy_chk", &CStringChecker::evalStrcpy) + .Cases("stpcpy", "__stpcpy_chk", &CStringChecker::evalStpcpy) + .Case("strlen", &CStringChecker::evalstrLength) + .Case("bcopy", &CStringChecker::evalBcopy) + .Default(NULL); + + // 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); + return true; +} + +void CStringChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { + // Record string length for char a[] = "abc"; + const GRState *state = C.getState(); + + for (DeclStmt::const_decl_iterator I = DS->decl_begin(), E = DS->decl_end(); + I != E; ++I) { + const VarDecl *D = dyn_cast(*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(Init)) + continue; + + Loc VarLoc = state->getLValue(D, C.getPredecessor()->getLocationContext()); + const MemRegion *MR = VarLoc.getAsRegion(); + if (!MR) + continue; + + SVal StrVal = state->getSVal(Init); + assert(StrVal.isValid() && "Initializer string is unknown or undefined"); + DefinedOrUnknownSVal strLength + = cast(getCStringLength(C, state, Init, StrVal)); + + state = state->set(MR, strLength); + } + + C.addTransition(state); +} + +bool CStringChecker::WantsRegionChangeUpdate(const GRState *state) { + CStringLength::EntryMap Entries = state->get(); + return !Entries.isEmpty(); +} + +const GRState *CStringChecker::EvalRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End, + bool *) { + CStringLength::EntryMap Entries = state->get(); + if (Entries.isEmpty()) + return state; + + llvm::SmallPtrSet Invalidated; + llvm::SmallPtrSet SuperRegions; + + // First build sets for the changed regions and their super-regions. + for ( ; Begin != End; ++Begin) { + const MemRegion *MR = *Begin; + Invalidated.insert(MR); + + SuperRegions.insert(MR); + while (const SubRegion *SR = dyn_cast(MR)) { + MR = SR->getSuperRegion(); + SuperRegions.insert(MR); + } + } + + CStringLength::EntryMap::Factory &F = state->get_context(); + + // Then loop over the entries in the current state. + for (CStringLength::EntryMap::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(Super)) { + Super = SR->getSuperRegion(); + if (Invalidated.count(Super)) { + Entries = F.remove(Entries, MR); + break; + } + } + } + + return state->set(Entries); +} + +void CStringChecker::MarkLiveSymbols(const GRState *state, SymbolReaper &SR) { + // Mark all symbols in our string length map as valid. + CStringLength::EntryMap Entries = state->get(); + + for (CStringLength::EntryMap::iterator I = Entries.begin(), E = Entries.end(); + I != E; ++I) { + SVal Len = I.getData(); + if (SymbolRef Sym = Len.getAsSymbol()) + SR.markInUse(Sym); + } +} + +void CStringChecker::evalDeadSymbols(CheckerContext &C, SymbolReaper &SR) { + if (!SR.hasDeadSymbols()) + return; + + const GRState *state = C.getState(); + CStringLength::EntryMap Entries = state->get(); + if (Entries.isEmpty()) + return; + + CStringLength::EntryMap::Factory &F = state->get_context(); + for (CStringLength::EntryMap::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(Entries); + C.generateNode(state); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/CallAndMessageChecker.cpp new file mode 100644 index 0000000000..2998406da0 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/CallAndMessageChecker.cpp @@ -0,0 +1,350 @@ +//===--- 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 "ExprEngineInternalChecks.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class CallAndMessageChecker + : public CheckerVisitor { + BugType *BT_call_null; + BugType *BT_call_undef; + BugType *BT_call_arg; + BugType *BT_msg_undef; + BugType *BT_msg_arg; + BugType *BT_msg_ret; +public: + CallAndMessageChecker() : + BT_call_null(0), BT_call_undef(0), BT_call_arg(0), + BT_msg_undef(0), BT_msg_arg(0), BT_msg_ret(0) {} + + static void *getTag() { + static int x = 0; + return &x; + } + + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); + bool evalNilReceiver(CheckerContext &C, const ObjCMessageExpr *ME); + +private: + bool PreVisitProcessArg(CheckerContext &C, const Expr *Ex, + const char *BT_desc, BugType *&BT); + + void EmitBadCall(BugType *BT, CheckerContext &C, const CallExpr *CE); + void emitNilReceiverBug(CheckerContext &C, const ObjCMessageExpr *ME, + ExplodedNode *N); + + void HandleNilReceiver(CheckerContext &C, const GRState *state, + const ObjCMessageExpr *ME); + + void LazyInit_BT(const char *desc, BugType *&BT) { + if (!BT) + BT = new BuiltinBug(desc); + } +}; +} // end anonymous namespace + +void ento::RegisterCallAndMessageChecker(ExprEngine &Eng) { + Eng.registerCheck(new CallAndMessageChecker()); +} + +void CallAndMessageChecker::EmitBadCall(BugType *BT, CheckerContext &C, + const CallExpr *CE) { + ExplodedNode *N = C.generateSink(); + if (!N) + return; + + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetCalleeExpr(N)); + C.EmitReport(R); +} + +bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C, + const Expr *Ex, + const char *BT_desc, + BugType *&BT) { + + const SVal &V = C.getState()->getSVal(Ex); + + if (V.isUndef()) { + if (ExplodedNode *N = C.generateSink()) { + LazyInit_BT(BT_desc, BT); + + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + R->addRange(Ex->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + C.EmitReport(R); + } + return true; + } + + if (const nonloc::LazyCompoundVal *LV = + dyn_cast(&V)) { + + class FindUninitializedField { + public: + llvm::SmallVector FieldChain; + private: + ASTContext &C; + StoreManager &StoreMgr; + MemRegionManager &MrMgr; + Store store; + public: + FindUninitializedField(ASTContext &c, StoreManager &storeMgr, + MemRegionManager &mrMgr, Store s) + : C(c), StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {} + + bool Find(const TypedRegion *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 (RecordDecl::field_iterator I = + RD->field_begin(), E = RD->field_end(); I!=E; ++I) { + 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.Retrieve(store, loc::MemRegionVal(FR)); + if (V.isUndef()) + return true; + } + FieldChain.pop_back(); + } + } + + return false; + } + }; + + const LazyCompoundValData *D = LV->getCVData(); + FindUninitializedField F(C.getASTContext(), + C.getState()->getStateManager().getStoreManager(), + C.getSValBuilder().getRegionManager(), + D->getStore()); + + if (F.Find(D->getRegion())) { + if (ExplodedNode *N = C.generateSink()) { + LazyInit_BT(BT_desc, BT); + llvm::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 (llvm::SmallVectorImpl::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. + EnhancedBugReport *R = new EnhancedBugReport(*BT, os.str(), N); + R->addRange(Ex->getSourceRange()); + + // FIXME: enhance track back for uninitialized value for arbitrary + // memregions + C.EmitReport(R); + } + return true; + } + } + + return false; +} + +void CallAndMessageChecker::PreVisitCallExpr(CheckerContext &C, + const CallExpr *CE){ + + const Expr *Callee = CE->getCallee()->IgnoreParens(); + SVal L = C.getState()->getSVal(Callee); + + if (L.isUndef()) { + if (!BT_call_undef) + BT_call_undef = + new BuiltinBug("Called function pointer is an uninitalized pointer value"); + EmitBadCall(BT_call_undef, C, CE); + return; + } + + if (isa(L)) { + if (!BT_call_null) + BT_call_null = + new BuiltinBug("Called function pointer is null (null dereference)"); + EmitBadCall(BT_call_null, C, CE); + } + + for (CallExpr::const_arg_iterator I = CE->arg_begin(), E = CE->arg_end(); + I != E; ++I) + if (PreVisitProcessArg(C, *I, + "Function call argument is an uninitialized value", + BT_call_arg)) + return; +} + +void CallAndMessageChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) { + + const GRState *state = C.getState(); + + // FIXME: Handle 'super'? + if (const Expr *receiver = ME->getInstanceReceiver()) + if (state->getSVal(receiver).isUndef()) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT_msg_undef) + BT_msg_undef = + new BuiltinBug("Receiver in message expression is an uninitialized value"); + EnhancedBugReport *R = + new EnhancedBugReport(*BT_msg_undef, BT_msg_undef->getName(), N); + R->addRange(receiver->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + receiver); + C.EmitReport(R); + } + return; + } + + // Check for any arguments that are uninitialized/undefined. + for (ObjCMessageExpr::const_arg_iterator I = ME->arg_begin(), + E = ME->arg_end(); I != E; ++I) + if (PreVisitProcessArg(C, *I, + "Argument in message expression " + "is an uninitialized value", BT_msg_arg)) + return; +} + +bool CallAndMessageChecker::evalNilReceiver(CheckerContext &C, + const ObjCMessageExpr *ME) { + HandleNilReceiver(C, C.getState(), ME); + return true; // Nil receiver is not handled elsewhere. +} + +void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C, + const ObjCMessageExpr *ME, + ExplodedNode *N) { + + if (!BT_msg_ret) + BT_msg_ret = + new BuiltinBug("Receiver in message expression is " + "'nil' and returns a garbage value"); + + llvm::SmallString<200> buf; + llvm::raw_svector_ostream os(buf); + os << "The receiver of message '" << ME->getSelector().getAsString() + << "' is nil and returns a value of type '" + << ME->getType().getAsString() << "' that will be garbage"; + + EnhancedBugReport *report = new EnhancedBugReport(*BT_msg_ret, os.str(), N); + if (const Expr *receiver = ME->getInstanceReceiver()) { + report->addRange(receiver->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + receiver); + } + C.EmitReport(report); +} + +static bool supportsNilWithFloatRet(const llvm::Triple &triple) { + return triple.getVendor() == llvm::Triple::Apple && + (triple.getDarwinMajorNumber() >= 9 || + triple.getArch() == llvm::Triple::arm || + triple.getArch() == llvm::Triple::thumb); +} + +void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C, + const GRState *state, + const ObjCMessageExpr *ME) { + + // 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 = ME->getType(); + + ASTContext &Ctx = C.getASTContext(); + CanQualType CanRetTy = Ctx.getCanonicalType(RetTy); + + if (CanRetTy->isStructureOrClassType()) { + // FIXME: At some point we shouldn't rely on isConsumedExpr(), but instead + // have the "use of undefined value" be smarter about where the + // undefined value came from. + if (C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { + if (ExplodedNode* N = C.generateSink(state)) + emitNilReceiverBug(C, ME, N); + return; + } + + // The result is not consumed by a surrounding expression. Just propagate + // the current state. + C.addTransition(state); + return; + } + + // Other cases: check if the return type is smaller than void*. + if (CanRetTy != Ctx.VoidTy && + C.getPredecessor()->getParentMap().isConsumedExpr(ME)) { + // Compute: sizeof(void *) and sizeof(return type) + const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy); + const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy); + + if (voidPtrSize < returnTypeSize && + !(supportsNilWithFloatRet(Ctx.Target.getTriple()) && + (Ctx.FloatTy == CanRetTy || + Ctx.DoubleTy == CanRetTy || + Ctx.LongDoubleTy == CanRetTy || + Ctx.LongLongTy == CanRetTy || + Ctx.UnsignedLongLongTy == CanRetTy))) { + if (ExplodedNode* N = C.generateSink(state)) + emitNilReceiverBug(C, ME, 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(ME->getType()); + C.generateNode(state->BindExpr(ME, V)); + return; + } + + C.addTransition(state); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/CastSizeChecker.cpp new file mode 100644 index 0000000000..9329ea39d4 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/CastSizeChecker.cpp @@ -0,0 +1,91 @@ +//=== 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/AST/CharUnits.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "ExprEngineInternalChecks.h" + +using namespace clang; +using namespace ento; + +namespace { +class CastSizeChecker : public CheckerVisitor { + BuiltinBug *BT; +public: + CastSizeChecker() : BT(0) {} + static void *getTag(); + void PreVisitCastExpr(CheckerContext &C, const CastExpr *B); +}; +} + +void *CastSizeChecker::getTag() { + static int x; + return &x; +} + +void CastSizeChecker::PreVisitCastExpr(CheckerContext &C, const CastExpr *CE) { + const Expr *E = CE->getSubExpr(); + ASTContext &Ctx = C.getASTContext(); + QualType ToTy = Ctx.getCanonicalType(CE->getType()); + PointerType *ToPTy = dyn_cast(ToTy.getTypePtr()); + + if (!ToPTy) + return; + + QualType ToPointeeTy = ToPTy->getPointeeType(); + + // Only perform the check if 'ToPointeeTy' is a complete type. + if (ToPointeeTy->isIncompleteType()) + return; + + const GRState *state = C.getState(); + const MemRegion *R = state->getSVal(E).getAsRegion(); + if (R == 0) + return; + + const SymbolicRegion *SR = dyn_cast(R); + if (SR == 0) + 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) { + if (ExplodedNode *errorNode = C.generateSink()) { + if (!BT) + BT = new BuiltinBug("Cast region with wrong size.", + "Cast a region whose size is not a multiple of the" + " destination type size."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), + errorNode); + R->addRange(CE->getSourceRange()); + C.EmitReport(R); + } + } +} + + +void ento::RegisterCastSizeChecker(ExprEngine &Eng) { + Eng.registerCheck(new CastSizeChecker()); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/CastToStructChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/CastToStructChecker.cpp new file mode 100644 index 0000000000..dd6ac7ea84 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/CastToStructChecker.cpp @@ -0,0 +1,79 @@ +//=== CastToStructChecker.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 CastToStructChecker, a builtin checker that checks for +// cast from non-struct pointer to struct pointer. +// This check corresponds to CWE-588. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "ExprEngineInternalChecks.h" + +using namespace clang; +using namespace ento; + +namespace { +class CastToStructChecker + : public CheckerVisitor { + BuiltinBug *BT; +public: + CastToStructChecker() : BT(0) {} + static void *getTag(); + void PreVisitCastExpr(CheckerContext &C, const CastExpr *B); +}; +} + +void *CastToStructChecker::getTag() { + static int x; + return &x; +} + +void CastToStructChecker::PreVisitCastExpr(CheckerContext &C, + const CastExpr *CE) { + const Expr *E = CE->getSubExpr(); + ASTContext &Ctx = C.getASTContext(); + QualType OrigTy = Ctx.getCanonicalType(E->getType()); + QualType ToTy = Ctx.getCanonicalType(CE->getType()); + + PointerType *OrigPTy = dyn_cast(OrigTy.getTypePtr()); + PointerType *ToPTy = dyn_cast(ToTy.getTypePtr()); + + if (!ToPTy || !OrigPTy) + return; + + QualType OrigPointeeTy = OrigPTy->getPointeeType(); + QualType ToPointeeTy = ToPTy->getPointeeType(); + + if (!ToPointeeTy->isStructureOrClassType()) + return; + + // We allow cast from void*. + if (OrigPointeeTy->isVoidType()) + return; + + // Now the cast-to-type is struct pointer, the original type is not void*. + if (!OrigPointeeTy->isRecordType()) { + if (ExplodedNode *N = C.generateNode()) { + if (!BT) + BT = new BuiltinBug("Cast from non-struct type to struct type", + "Casting a non-structure type to a structure type " + "and accessing a field can lead to memory access " + "errors or data corruption."); + RangedBugReport *R = new RangedBugReport(*BT,BT->getDescription(), N); + R->addRange(CE->getSourceRange()); + C.EmitReport(R); + } + } +} + +void ento::RegisterCastToStructChecker(ExprEngine &Eng) { + Eng.registerCheck(new CastToStructChecker()); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/CheckDeadStores.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/CheckDeadStores.cpp new file mode 100644 index 0000000000..1fe40c56e8 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/CheckDeadStores.cpp @@ -0,0 +1,290 @@ +//==- DeadStores.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/LocalCheckers.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/Visitors/CFGRecStmtVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/Analysis/Visitors/CFGRecStmtDeclVisitor.h" +#include "clang/Basic/Diagnostic.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ParentMap.h" +#include "llvm/ADT/SmallPtrSet.h" + +using namespace clang; +using namespace ento; + +namespace { + +class DeadStoreObs : public LiveVariables::ObserverTy { + ASTContext &Ctx; + BugReporter& BR; + ParentMap& Parents; + llvm::SmallPtrSet Escaped; + + enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit }; + +public: + DeadStoreObs(ASTContext &ctx, BugReporter& br, ParentMap& parents, + llvm::SmallPtrSet &escaped) + : Ctx(ctx), BR(br), Parents(parents), Escaped(escaped) {} + + virtual ~DeadStoreObs() {} + + void Report(VarDecl* V, DeadStoreKind dsk, SourceLocation L, SourceRange R) { + if (Escaped.count(V)) + return; + + std::string name = V->getNameAsString(); + + const char* BugType = 0; + std::string msg; + + switch (dsk) { + default: + assert(false && "Impossible dead store type."); + + case DeadInit: + BugType = "Dead initialization"; + msg = "Value stored to '" + name + + "' during its initialization is never read"; + break; + + case DeadIncrement: + BugType = "Dead increment"; + case Standard: + if (!BugType) BugType = "Dead assignment"; + msg = "Value stored to '" + name + "' is never read"; + break; + + case Enclosing: + BugType = "Dead nested assignment"; + msg = "Although the value stored to '" + name + + "' is used in the enclosing expression, the value is never actually" + " read from '" + name + "'"; + break; + } + + BR.EmitBasicReport(BugType, "Dead store", msg, L, R); + } + + void CheckVarDecl(VarDecl* VD, Expr* Ex, Expr* Val, + DeadStoreKind dsk, + const LiveVariables::AnalysisDataTy& AD, + const LiveVariables::ValTy& Live) { + + if (!VD->hasLocalStorage()) + return; + // Reference types confuse the dead stores checker. Skip them + // for now. + if (VD->getType()->getAs()) + return; + + if (!Live(VD, AD) && + !(VD->getAttr() || VD->getAttr())) + Report(VD, dsk, Ex->getSourceRange().getBegin(), + Val->getSourceRange()); + } + + void CheckDeclRef(DeclRefExpr* DR, Expr* Val, DeadStoreKind dsk, + const LiveVariables::AnalysisDataTy& AD, + const LiveVariables::ValTy& Live) { + if (VarDecl* VD = dyn_cast(DR->getDecl())) + CheckVarDecl(VD, DR, Val, dsk, AD, Live); + } + + bool isIncrement(VarDecl* VD, BinaryOperator* B) { + if (B->isCompoundAssignmentOp()) + return true; + + Expr* RHS = B->getRHS()->IgnoreParenCasts(); + BinaryOperator* BRHS = dyn_cast(RHS); + + if (!BRHS) + return false; + + DeclRefExpr *DR; + + if ((DR = dyn_cast(BRHS->getLHS()->IgnoreParenCasts()))) + if (DR->getDecl() == VD) + return true; + + if ((DR = dyn_cast(BRHS->getRHS()->IgnoreParenCasts()))) + if (DR->getDecl() == VD) + return true; + + return false; + } + + virtual void ObserveStmt(Stmt* S, + const LiveVariables::AnalysisDataTy& AD, + const LiveVariables::ValTy& Live) { + + // Skip statements in macros. + if (S->getLocStart().isMacroID()) + return; + + if (BinaryOperator* B = dyn_cast(S)) { + if (!B->isAssignmentOp()) return; // Skip non-assignments. + + if (DeclRefExpr* DR = dyn_cast(B->getLHS())) + if (VarDecl *VD = dyn_cast(DR->getDecl())) { + // Special case: check for assigning null to a pointer. + // This is a common form of defensive programming. + QualType T = VD->getType(); + if (T->isPointerType() || T->isObjCObjectPointerType()) { + if (B->getRHS()->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) + return; + } + + Expr* RHS = B->getRHS()->IgnoreParenCasts(); + // Special case: self-assignments. These are often used to shut up + // "unused variable" compiler warnings. + if (DeclRefExpr* RhsDR = dyn_cast(RHS)) + if (VD == dyn_cast(RhsDR->getDecl())) + return; + + // Otherwise, issue a warning. + DeadStoreKind dsk = Parents.isConsumedExpr(B) + ? Enclosing + : (isIncrement(VD,B) ? DeadIncrement : Standard); + + CheckVarDecl(VD, DR, B->getRHS(), dsk, AD, Live); + } + } + else if (UnaryOperator* U = dyn_cast(S)) { + if (!U->isIncrementOp()) + return; + + // Handle: ++x within a subexpression. The solution is not warn + // about preincrements to dead variables when the preincrement occurs + // as a subexpression. This can lead to false negatives, e.g. "(++x);" + // A generalized dead code checker should find such issues. + if (U->isPrefix() && Parents.isConsumedExpr(U)) + return; + + Expr *Ex = U->getSubExpr()->IgnoreParenCasts(); + + if (DeclRefExpr* DR = dyn_cast(Ex)) + CheckDeclRef(DR, U, DeadIncrement, AD, Live); + } + else if (DeclStmt* DS = dyn_cast(S)) + // Iterate through the decls. Warn if any initializers are complex + // expressions that are not live (never used). + for (DeclStmt::decl_iterator DI=DS->decl_begin(), DE=DS->decl_end(); + DI != DE; ++DI) { + + VarDecl* V = dyn_cast(*DI); + + if (!V) + continue; + + if (V->hasLocalStorage()) { + // Reference types confuse the dead stores checker. Skip them + // for now. + if (V->getType()->getAs()) + return; + + if (Expr* E = V->getInit()) { + // Don't warn on C++ objects (yet) until we can show that their + // constructors/destructors don't have side effects. + if (isa(E)) + return; + + if (isa(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'. + if (!Live(V, AD) && V->getAttr() == 0) { + // 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->isConstantInitializer(Ctx, false)) + return; + + if (DeclRefExpr *DRE=dyn_cast(E->IgnoreParenCasts())) + if (VarDecl *VD = dyn_cast(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(VD) && VD->getType()->isScalarType()) + return; + } + + Report(V, DeadInit, V->getLocation(), E->getSourceRange()); + } + } + } + } + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Driver function to invoke the Dead-Stores checker on a CFG. +//===----------------------------------------------------------------------===// + +namespace { +class FindEscaped : public CFGRecStmtDeclVisitor{ + CFG *cfg; +public: + FindEscaped(CFG *c) : cfg(c) {} + + CFG& getCFG() { return *cfg; } + + llvm::SmallPtrSet Escaped; + + void VisitUnaryOperator(UnaryOperator* U) { + // Check for '&'. Any VarDecl whose value has its address-taken we + // treat as escaped. + Expr* E = U->getSubExpr()->IgnoreParenCasts(); + if (U->getOpcode() == UO_AddrOf) + if (DeclRefExpr* DR = dyn_cast(E)) + if (VarDecl* VD = dyn_cast(DR->getDecl())) { + Escaped.insert(VD); + return; + } + Visit(E); + } +}; +} // end anonymous namespace + + +void ento::CheckDeadStores(CFG &cfg, LiveVariables &L, ParentMap &pmap, + BugReporter& BR) { + FindEscaped FS(&cfg); + FS.getCFG().VisitBlockStmts(FS); + DeadStoreObs A(BR.getContext(), BR, pmap, FS.Escaped); + L.runOnAllBlocks(cfg, &A); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/CheckObjCDealloc.cpp new file mode 100644 index 0000000000..932a6f5945 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/CheckObjCDealloc.cpp @@ -0,0 +1,262 @@ +//==- 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 file defines a CheckObjCDealloc, a checker that +// analyzes an Objective-C class's implementation to determine if it +// correctly implements -dealloc. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/LangOptions.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +static bool scan_dealloc(Stmt* S, Selector Dealloc) { + + if (ObjCMessageExpr* ME = dyn_cast(S)) + if (ME->getSelector() == Dealloc) { + switch (ME->getReceiverKind()) { + case ObjCMessageExpr::Instance: return false; + case ObjCMessageExpr::SuperInstance: return true; + case ObjCMessageExpr::Class: break; + case ObjCMessageExpr::SuperClass: break; + } + } + + // Recurse to children. + + for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) + if (*I && scan_dealloc(*I, Dealloc)) + return true; + + return false; +} + +static bool scan_ivar_release(Stmt* S, ObjCIvarDecl* ID, + const ObjCPropertyDecl* PD, + Selector Release, + IdentifierInfo* SelfII, + ASTContext& Ctx) { + + // [mMyIvar release] + if (ObjCMessageExpr* ME = dyn_cast(S)) + if (ME->getSelector() == Release) + if (ME->getInstanceReceiver()) + if (Expr* Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) + if (ObjCIvarRefExpr* E = dyn_cast(Receiver)) + if (E->getDecl() == ID) + return true; + + // [self setMyIvar:nil]; + if (ObjCMessageExpr* ME = dyn_cast(S)) + if (ME->getInstanceReceiver()) + if (Expr* Receiver = ME->getInstanceReceiver()->IgnoreParenCasts()) + if (DeclRefExpr* E = dyn_cast(Receiver)) + if (E->getDecl()->getIdentifier() == SelfII) + if (ME->getMethodDecl() == PD->getSetterMethodDecl() && + ME->getNumArgs() == 1 && + ME->getArg(0)->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) + return true; + + // self.myIvar = nil; + if (BinaryOperator* BO = dyn_cast(S)) + if (BO->isAssignmentOp()) + if (ObjCPropertyRefExpr* PRE = + dyn_cast(BO->getLHS()->IgnoreParenCasts())) + if (PRE->isExplicitProperty() && PRE->getExplicitProperty() == PD) + if (BO->getRHS()->isNullPointerConstant(Ctx, + Expr::NPC_ValueDependentIsNull)) { + // This is only a 'release' if the property kind is not + // 'assign'. + return PD->getSetterKind() != ObjCPropertyDecl::Assign;; + } + + // Recurse to children. + for (Stmt::child_iterator I = S->child_begin(), E= S->child_end(); I!=E; ++I) + if (*I && scan_ivar_release(*I, ID, PD, Release, SelfII, Ctx)) + return true; + + return false; +} + +void ento::CheckObjCDealloc(const ObjCImplementationDecl* D, + const LangOptions& LOpts, BugReporter& BR) { + + assert (LOpts.getGCMode() != LangOptions::GCOnly); + + ASTContext& Ctx = BR.getContext(); + const ObjCInterfaceDecl* ID = D->getClassInterface(); + + // Does the class contain any ivars that are pointers (or id<...>)? + // If not, skip the check entirely. + // NOTE: This is motivated by PR 2517: + // http://llvm.org/bugs/show_bug.cgi?id=2517 + + bool containsPointerIvar = false; + + for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), E=ID->ivar_end(); + I!=E; ++I) { + + ObjCIvarDecl* ID = *I; + QualType T = ID->getType(); + + if (!T->isObjCObjectPointerType() || + ID->getAttr() || // Skip IBOutlets. + ID->getAttr()) // Skip IBOutletCollections. + continue; + + containsPointerIvar = true; + break; + } + + if (!containsPointerIvar) + return; + + // Determine if the class subclasses NSObject. + IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject"); + IdentifierInfo* SenTestCaseII = &Ctx.Idents.get("SenTestCase"); + + + for ( ; ID ; ID = ID->getSuperClass()) { + IdentifierInfo *II = ID->getIdentifier(); + + if (II == NSObjectII) + break; + + // FIXME: For now, ignore classes that subclass SenTestCase, 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 == SenTestCaseII) + return; + } + + if (!ID) + return; + + // Get the "dealloc" selector. + IdentifierInfo* II = &Ctx.Idents.get("dealloc"); + Selector S = Ctx.Selectors.getSelector(0, &II); + ObjCMethodDecl* MD = 0; + + // Scan the instance methods for "dealloc". + for (ObjCImplementationDecl::instmeth_iterator I = D->instmeth_begin(), + E = D->instmeth_end(); I!=E; ++I) { + + if ((*I)->getSelector() == S) { + MD = *I; + break; + } + } + + if (!MD) { // No dealloc found. + + const char* name = LOpts.getGCMode() == LangOptions::NonGC + ? "missing -dealloc" + : "missing -dealloc (Hybrid MM, non-GC)"; + + std::string buf; + llvm::raw_string_ostream os(buf); + os << "Objective-C class '" << D << "' lacks a 'dealloc' instance method"; + + BR.EmitBasicReport(name, os.str(), D->getLocStart()); + return; + } + + // dealloc found. Scan for missing [super dealloc]. + if (MD->getBody() && !scan_dealloc(MD->getBody(), S)) { + + const char* name = LOpts.getGCMode() == LangOptions::NonGC + ? "missing [super dealloc]" + : "missing [super dealloc] (Hybrid MM, non-GC)"; + + std::string buf; + llvm::raw_string_ostream os(buf); + os << "The 'dealloc' instance method in Objective-C class '" << D + << "' does not send a 'dealloc' message to its super class" + " (missing [super dealloc])"; + + BR.EmitBasicReport(name, os.str(), D->getLocStart()); + return; + } + + // Get the "release" selector. + IdentifierInfo* RII = &Ctx.Idents.get("release"); + Selector RS = Ctx.Selectors.getSelector(0, &RII); + + // Get the "self" identifier + IdentifierInfo* SelfII = &Ctx.Idents.get("self"); + + // Scan for missing and extra releases of ivars used by implementations + // of synthesized properties + for (ObjCImplementationDecl::propimpl_iterator I = D->propimpl_begin(), + E = D->propimpl_end(); I!=E; ++I) { + + // We can only check the synthesized properties + if ((*I)->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize) + continue; + + ObjCIvarDecl* ID = (*I)->getPropertyIvarDecl(); + if (!ID) + continue; + + QualType T = ID->getType(); + if (!T->isObjCObjectPointerType()) // Skip non-pointer ivars + continue; + + const ObjCPropertyDecl* PD = (*I)->getPropertyDecl(); + if (!PD) + continue; + + // ivars cannot be set via read-only properties, so we'll skip them + if (PD->isReadOnly()) + continue; + + // ivar must be released if and only if the kind of setter was not 'assign' + bool requiresRelease = PD->getSetterKind() != ObjCPropertyDecl::Assign; + if (scan_ivar_release(MD->getBody(), ID, PD, RS, SelfII, Ctx) + != requiresRelease) { + const char *name; + const char* category = "Memory (Core Foundation/Objective-C)"; + + std::string buf; + llvm::raw_string_ostream os(buf); + + if (requiresRelease) { + name = LOpts.getGCMode() == LangOptions::NonGC + ? "missing ivar release (leak)" + : "missing ivar release (Hybrid MM, non-GC)"; + + os << "The '" << ID + << "' instance variable was retained by a synthesized property but " + "wasn't released in 'dealloc'"; + } else { + name = LOpts.getGCMode() == LangOptions::NonGC + ? "extra ivar release (use-after-release)" + : "extra ivar release (Hybrid MM, non-GC)"; + + os << "The '" << ID + << "' instance variable was not retained by a synthesized property " + "but was released in 'dealloc'"; + } + + BR.EmitBasicReport(name, category, os.str(), (*I)->getLocation()); + } + } +} + diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/CheckObjCInstMethSignature.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/CheckObjCInstMethSignature.cpp new file mode 100644 index 0000000000..57e388f44d --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/CheckObjCInstMethSignature.cpp @@ -0,0 +1,120 @@ +//=- 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/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Type.h" +#include "clang/AST/ASTContext.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) { + + QualType ResDerived = MethDerived->getResultType(); + QualType ResAncestor = MethAncestor->getResultType(); + + 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().getAsString() + << "' 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."; + + BR.EmitBasicReport("Incompatible instance method return type", + os.str(), MethDerived->getLocStart()); + } +} + +void ento::CheckObjCInstMethSignature(const ObjCImplementationDecl* ID, + BugReporter& BR) { + + 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 MapTy; + MapTy IMeths; + unsigned NumMethods = 0; + + for (ObjCImplementationDecl::instmeth_iterator I=ID->instmeth_begin(), + E=ID->instmeth_end(); I!=E; ++I) { + + ObjCMethodDecl* M = *I; + IMeths[M->getSelector()] = M; + ++NumMethods; + } + + // Now recurse the class hierarchy chain looking for methods with the + // same signatures. + while (C && NumMethods) { + for (ObjCInterfaceDecl::instmeth_iterator I=C->instmeth_begin(), + E=C->instmeth_end(); I!=E; ++I) { + + ObjCMethodDecl* M = *I; + Selector S = M->getSelector(); + + MapTy::iterator MI = IMeths.find(S); + + if (MI == IMeths.end() || MI->second == 0) + continue; + + --NumMethods; + ObjCMethodDecl* MethDerived = MI->second; + MI->second = 0; + + CompareReturnTypes(MethDerived, M, BR, Ctx, ID); + } + + C = C->getSuperClass(); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp new file mode 100644 index 0000000000..b30e985f38 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp @@ -0,0 +1,503 @@ +//==- 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/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/AST/StmtVisitor.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +static bool isArc4RandomAvailable(const ASTContext &Ctx) { + const llvm::Triple &T = Ctx.Target.getTriple(); + return T.getVendor() == llvm::Triple::Apple || + T.getOS() == llvm::Triple::FreeBSD; +} + +namespace { +class WalkAST : public StmtVisitor { + BugReporter &BR; + IdentifierInfo *II_gets; + IdentifierInfo *II_getpw; + IdentifierInfo *II_mktemp; + enum { num_rands = 9 }; + IdentifierInfo *II_rand[num_rands]; + IdentifierInfo *II_random; + enum { num_setids = 6 }; + IdentifierInfo *II_setid[num_setids]; + + const bool CheckRand; + +public: + WalkAST(BugReporter &br) : BR(br), + II_gets(0), II_getpw(0), II_mktemp(0), + II_rand(), II_random(0), II_setid(), + CheckRand(isArc4RandomAvailable(BR.getContext())) {} + + // 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. + IdentifierInfo *GetIdentifier(IdentifierInfo *& II, const char *str); + + // Checker-specific methods. + void CheckLoopConditionForFloat(const ForStmt *FS); + 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_rand(const CallExpr *CE, const FunctionDecl *FD); + void CheckCall_random(const CallExpr *CE, const FunctionDecl *FD); + void CheckUncheckedReturnValue(CallExpr *CE); +}; +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// Helper methods. +//===----------------------------------------------------------------------===// + +IdentifierInfo *WalkAST::GetIdentifier(IdentifierInfo *& II, const char *str) { + if (!II) + II = &BR.getContext().Idents.get(str); + + return II; +} + +//===----------------------------------------------------------------------===// +// AST walking. +//===----------------------------------------------------------------------===// + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) + Visit(child); +} + +void WalkAST::VisitCallExpr(CallExpr *CE) { + if (const FunctionDecl *FD = CE->getDirectCallee()) { + CheckCall_gets(CE, FD); + CheckCall_getpw(CE, FD); + CheckCall_mktemp(CE, FD); + if (CheckRand) { + CheckCall_rand(CE, FD); + CheckCall_random(CE, FD); + } + } + + // Recurse and check children. + VisitChildren(CE); +} + +void WalkAST::VisitCompoundStmt(CompoundStmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) { + if (CallExpr *CE = dyn_cast(child)) + CheckUncheckedReturnValue(CE); + Visit(child); + } +} + +void WalkAST::VisitForStmt(ForStmt *FS) { + CheckLoopConditionForFloat(FS); + + // Recurse and check children. + VisitChildren(FS); +} + +//===----------------------------------------------------------------------===// +// Check: floating poing variable used as loop counter. +// Originally: +// 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(expr)) { + if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() || + B->getOpcode() == BO_Comma)) + return NULL; + + if (const DeclRefExpr *lhs = GetIncrementedVar(B->getLHS(), x, y)) + return lhs; + + if (const DeclRefExpr *rhs = GetIncrementedVar(B->getRHS(), x, y)) + return rhs; + + return NULL; + } + + if (const DeclRefExpr *DR = dyn_cast(expr)) { + const NamedDecl *ND = DR->getDecl(); + return ND == x || ND == y ? DR : NULL; + } + + if (const UnaryOperator *U = dyn_cast(expr)) + return U->isIncrementDecrementOp() + ? GetIncrementedVar(U->getSubExpr(), x, y) : NULL; + + return NULL; +} + +/// 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) { + // 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(condition); + + if (!B) + return; + + // Is this a comparison? + if (!(B->isRelationalOp() || B->isEqualityOp())) + return; + + // Are we comparing variables? + const DeclRefExpr *drLHS = + dyn_cast(B->getLHS()->IgnoreParenLValueCasts()); + const DeclRefExpr *drRHS = + dyn_cast(B->getRHS()->IgnoreParenLValueCasts()); + + // Does at least one of the variables have a floating point type? + drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : NULL; + drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : NULL; + + if (!drLHS && !drRHS) + return; + + const VarDecl *vdLHS = drLHS ? dyn_cast(drLHS->getDecl()) : NULL; + const VarDecl *vdRHS = drRHS ? dyn_cast(drRHS->getDecl()) : NULL; + + 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. + const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS; + + llvm::SmallVector ranges; + llvm::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"; + BR.EmitBasicReport(bugType, "Security", os.str(), + FS->getLocStart(), ranges.data(), ranges.size()); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'gets' is insecure. +// Originally: +// 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 (FD->getIdentifier() != GetIdentifier(II_gets, "gets")) + return; + + const FunctionProtoType *FPT + = dyn_cast(FD->getType().IgnoreParens()); + if (!FPT) + return; + + // Verify that the function takes a single argument. + if (FPT->getNumArgs() != 1) + return; + + // Is the argument a 'char*'? + const PointerType *PT = dyn_cast(FPT->getArgType(0)); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a warning. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("Potential buffer overflow in call to 'gets'", + "Security", + "Call to function 'gets' is extremely insecure as it can " + "always result in a buffer overflow", + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: Any use of 'getpwd' is insecure. +// CWE-477: Use of Obsolete Functions +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_getpw(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_getpw, "getpw")) + return; + + const FunctionProtoType *FPT + = dyn_cast(FD->getType().IgnoreParens()); + if (!FPT) + return; + + // Verify that the function takes two arguments. + if (FPT->getNumArgs() != 2) + return; + + // Verify the first argument type is integer. + if (!FPT->getArgType(0)->isIntegerType()) + return; + + // Verify the second argument type is char*. + const PointerType *PT = dyn_cast(FPT->getArgType(1)); + if (!PT) + return; + + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a warning. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("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().", + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// 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 (FD->getIdentifier() != GetIdentifier(II_mktemp, "mktemp")) + return; + + const FunctionProtoType *FPT + = dyn_cast(FD->getType().IgnoreParens()); + if(!FPT) + return; + + // Verify that the funcion takes a single argument. + if (FPT->getNumArgs() != 1) + return; + + // Verify that the argument is Pointer Type. + const PointerType *PT = dyn_cast(FPT->getArgType(0)); + if (!PT) + return; + + // Verify that the argument is a 'char*'. + if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy) + return; + + // Issue a waring. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("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", + CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: Linear congruent random number generators should not be used +// Originally: +// CWE-338: Use of cryptographically weak prng +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_rand(const CallExpr *CE, const FunctionDecl *FD) { + if (II_rand[0] == NULL) { + // This check applies to these functions + static const char * const identifiers[num_rands] = { + "drand48", "erand48", "jrand48", "lrand48", "mrand48", "nrand48", + "lcong48", + "rand", "rand_r" + }; + + for (size_t i = 0; i < num_rands; i++) + II_rand[i] = &BR.getContext().Idents.get(identifiers[i]); + } + + const IdentifierInfo *id = FD->getIdentifier(); + size_t identifierid; + + for (identifierid = 0; identifierid < num_rands; identifierid++) + if (id == II_rand[identifierid]) + break; + + if (identifierid >= num_rands) + return; + + const FunctionProtoType *FTP + = dyn_cast(FD->getType().IgnoreParens()); + if (!FTP) + return; + + if (FTP->getNumArgs() == 1) { + // Is the argument an 'unsigned short *'? + // (Actually any integer type is allowed.) + const PointerType *PT = dyn_cast(FTP->getArgType(0)); + if (!PT) + return; + + if (! PT->getPointeeType()->isIntegerType()) + return; + } + else if (FTP->getNumArgs() != 0) + return; + + // Issue a warning. + llvm::SmallString<256> buf1; + llvm::raw_svector_ostream os1(buf1); + os1 << '\'' << FD << "' is a poor random number generator"; + + llvm::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"; + + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: 'random' should not be used +// Originally: +//===----------------------------------------------------------------------===// + +void WalkAST::CheckCall_random(const CallExpr *CE, const FunctionDecl *FD) { + if (FD->getIdentifier() != GetIdentifier(II_random, "random")) + return; + + const FunctionProtoType *FTP + = dyn_cast(FD->getType().IgnoreParens()); + if (!FTP) + return; + + // Verify that the function takes no argument. + if (FTP->getNumArgs() != 0) + return; + + // Issue a warning. + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport("'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", CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Check: Should check whether privileges are dropped successfully. +// Originally: +//===----------------------------------------------------------------------===// + +void WalkAST::CheckUncheckedReturnValue(CallExpr *CE) { + const FunctionDecl *FD = CE->getDirectCallee(); + if (!FD) + return; + + if (II_setid[0] == NULL) { + 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 + = dyn_cast(FD->getType().IgnoreParens()); + if (!FTP) + return; + + // Verify that the function takes one or two arguments (depending on + // the function). + if (FTP->getNumArgs() != (identifierid < 4 ? 1 : 2)) + return; + + // The arguments must be integers. + for (unsigned i = 0; i < FTP->getNumArgs(); i++) + if (! FTP->getArgType(i)->isIntegerType()) + return; + + // Issue a warning. + llvm::SmallString<256> buf1; + llvm::raw_svector_ostream os1(buf1); + os1 << "Return value is not checked in call to '" << FD << '\''; + + llvm::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"; + + SourceRange R = CE->getCallee()->getSourceRange(); + BR.EmitBasicReport(os1.str(), "Security", os2.str(),CE->getLocStart(), &R, 1); +} + +//===----------------------------------------------------------------------===// +// Entry point for check. +//===----------------------------------------------------------------------===// + +void ento::CheckSecuritySyntaxOnly(const Decl *D, BugReporter &BR) { + WalkAST walker(BR); + walker.Visit(D->getBody()); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/CheckSizeofPointer.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/CheckSizeofPointer.cpp new file mode 100644 index 0000000000..ed060b27fb --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/CheckSizeofPointer.cpp @@ -0,0 +1,72 @@ +//==- 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/BugReporter/BugReporter.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" + +using namespace clang; +using namespace ento; + +namespace { +class WalkAST : public StmtVisitor { + BugReporter &BR; + +public: + WalkAST(BugReporter &br) : BR(br) {} + void VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E); + void VisitStmt(Stmt *S) { VisitChildren(S); } + void VisitChildren(Stmt *S); +}; +} + +void WalkAST::VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end(); I!=E; ++I) + if (Stmt *child = *I) + Visit(child); +} + +// CWE-467: Use of sizeof() on a Pointer Type +void WalkAST::VisitSizeOfAlignOfExpr(SizeOfAlignOfExpr *E) { + if (!E->isSizeOf()) + return; + + // If an explicit type is used in the code, usually the coder knows what he is + // 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(ArgEx->IgnoreParens())) + return; + + SourceRange R = ArgEx->getSourceRange(); + BR.EmitBasicReport("Potential unintended use of sizeof() on pointer type", + "Logic", + "The code calls sizeof() on a pointer type. " + "This can produce an unexpected result.", + E->getLocStart(), &R, 1); + } +} + +void ento::CheckSizeofPointer(const Decl *D, BugReporter &BR) { + WalkAST walker(BR); + walker.Visit(D->getBody()); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/ChrootChecker.cpp new file mode 100644 index 0000000000..a3f4daa3b0 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/ChrootChecker.cpp @@ -0,0 +1,162 @@ +//===- 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 "ExprEngineExperimentalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.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 CheckerVisitor { + IdentifierInfo *II_chroot, *II_chdir; + // This bug refers to possibly break out of a chroot() jail. + BuiltinBug *BT_BreakJail; + +public: + ChrootChecker() : II_chroot(0), II_chdir(0), BT_BreakJail(0) {} + + static void *getTag() { + static int x; + return &x; + } + + virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE); + virtual void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); + +private: + void Chroot(CheckerContext &C, const CallExpr *CE); + void Chdir(CheckerContext &C, const CallExpr *CE); +}; + +} // end anonymous namespace + +void ento::RegisterChrootChecker(ExprEngine &Eng) { + Eng.registerCheck(new ChrootChecker()); +} + +bool ChrootChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + 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 GRState *state = C.getState(); + GRStateManager &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 GRState *state = C.getState(); + GRStateManager &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 = state->getSVal(ArgExpr); + + if (const MemRegion *R = ArgVal.getAsRegion()) { + R = R->StripCasts(); + if (const StringRegion* StrRegion= dyn_cast(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::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + 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"); + + // Ingnore chroot and chdir. + if (FD->getIdentifier() == II_chroot || FD->getIdentifier() == II_chdir) + return; + + // If jail state is ROOT_CHANGED, generate BugReport. + void* const* k = state->FindGDM(ChrootChecker::getTag()); + if (k) + if (isRootChanged((intptr_t) *k)) + if (ExplodedNode *N = C.generateNode()) { + if (!BT_BreakJail) + BT_BreakJail = new BuiltinBug("Break out of jail", + "No call of chdir(\"/\") immediately " + "after chroot"); + BugReport *R = new BugReport(*BT_BreakJail, + BT_BreakJail->getDescription(), N); + C.EmitReport(R); + } + + return; +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/DereferenceChecker.cpp new file mode 100644 index 0000000000..7dc8ec84df --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/DereferenceChecker.cpp @@ -0,0 +1,204 @@ +//== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class DereferenceChecker : public Checker { + BuiltinBug *BT_null; + BuiltinBug *BT_undef; + llvm::SmallVector ImplicitNullDerefNodes; +public: + DereferenceChecker() : BT_null(0), BT_undef(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void visitLocation(CheckerContext &C, const Stmt *S, SVal location); + + std::pair + getImplicitNodes() const { + return std::make_pair(ImplicitNullDerefNodes.data(), + ImplicitNullDerefNodes.data() + + ImplicitNullDerefNodes.size()); + } + void AddDerefSource(llvm::raw_ostream &os, + llvm::SmallVectorImpl &Ranges, + const Expr *Ex, bool loadedFrom = false); +}; +} // end anonymous namespace + +void ento::RegisterDereferenceChecker(ExprEngine &Eng) { + Eng.registerCheck(new DereferenceChecker()); +} + +std::pair +ento::GetImplicitNullDereferences(ExprEngine &Eng) { + DereferenceChecker *checker = Eng.getChecker(); + if (!checker) + return std::make_pair((ExplodedNode * const *) 0, + (ExplodedNode * const *) 0); + return checker->getImplicitNodes(); +} + +void DereferenceChecker::AddDerefSource(llvm::raw_ostream &os, + llvm::SmallVectorImpl &Ranges, + const Expr *Ex, + bool loadedFrom) { + Ex = Ex->IgnoreParenLValueCasts(); + switch (Ex->getStmtClass()) { + default: + return; + case Stmt::DeclRefExprClass: { + const DeclRefExpr *DR = cast(Ex); + if (const VarDecl *VD = dyn_cast(DR->getDecl())) { + os << " (" << (loadedFrom ? "loaded from" : "from") + << " variable '" << VD->getName() << "')"; + Ranges.push_back(DR->getSourceRange()); + } + return; + } + case Stmt::MemberExprClass: { + const MemberExpr *ME = cast(Ex); + os << " (" << (loadedFrom ? "loaded from" : "via") + << " field '" << ME->getMemberNameInfo() << "')"; + SourceLocation L = ME->getMemberLoc(); + Ranges.push_back(SourceRange(L, L)); + break; + } + } +} + +void DereferenceChecker::visitLocation(CheckerContext &C, const Stmt *S, + SVal l) { + // Check for dereference of an undefined value. + if (l.isUndef()) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT_undef) + BT_undef = new BuiltinBug("Dereference of undefined pointer value"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDerefExpr(N)); + C.EmitReport(report); + } + return; + } + + DefinedOrUnknownSVal location = cast(l); + + // Check for null dereferences. + if (!isa(location)) + return; + + const GRState *state = C.getState(); + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->assume(location); + + // The explicit NULL case. + if (nullState) { + if (!notNullState) { + // Generate an error node. + ExplodedNode *N = C.generateSink(nullState); + 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 = new BuiltinBug("Dereference of null pointer"); + + llvm::SmallString<100> buf; + llvm::SmallVector Ranges; + + // Walk through lvalue casts to get the original expression + // that syntactically caused the load. + if (const Expr *expr = dyn_cast(S)) + S = expr->IgnoreParenLValueCasts(); + + switch (S->getStmtClass()) { + case Stmt::ArraySubscriptExprClass: { + llvm::raw_svector_ostream os(buf); + os << "Array access"; + const ArraySubscriptExpr *AE = cast(S); + AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts()); + os << " results in a null pointer dereference"; + break; + } + case Stmt::UnaryOperatorClass: { + llvm::raw_svector_ostream os(buf); + os << "Dereference of null pointer"; + const UnaryOperator *U = cast(S); + AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(), true); + break; + } + case Stmt::MemberExprClass: { + const MemberExpr *M = cast(S); + if (M->isArrow()) { + llvm::raw_svector_ostream os(buf); + os << "Access to field '" << M->getMemberNameInfo() + << "' results in a dereference of a null pointer"; + AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(), true); + } + break; + } + case Stmt::ObjCIvarRefExprClass: { + const ObjCIvarRefExpr *IV = cast(S); + if (const DeclRefExpr *DR = + dyn_cast(IV->getBase()->IgnoreParenCasts())) { + if (const VarDecl *VD = dyn_cast(DR->getDecl())) { + llvm::raw_svector_ostream os(buf); + os << "Instance variable access (via '" << VD->getName() + << "') results in a null pointer dereference"; + } + } + Ranges.push_back(IV->getSourceRange()); + break; + } + default: + break; + } + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_null, + buf.empty() ? BT_null->getDescription():buf.str(), + N); + + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDerefExpr(N)); + + for (llvm::SmallVectorImpl::iterator + I = Ranges.begin(), E = Ranges.end(); I!=E; ++I) + report->addRange(*I); + + C.EmitReport(report); + return; + } + else { + // 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)) + ImplicitNullDerefNodes.push_back(N); + } + } + + // From this point forward, we know that the location is not null. + C.addTransition(notNullState); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/DivZeroChecker.cpp new file mode 100644 index 0000000000..8332af8f5d --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/DivZeroChecker.cpp @@ -0,0 +1,86 @@ +//== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class DivZeroChecker : public CheckerVisitor { + BuiltinBug *BT; +public: + DivZeroChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} // end anonymous namespace + +void ento::RegisterDivZeroChecker(ExprEngine &Eng) { + Eng.registerCheck(new DivZeroChecker()); +} + +void *DivZeroChecker::getTag() { + static int x; + return &x; +} + +void DivZeroChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + BinaryOperator::Opcode Op = B->getOpcode(); + if (Op != BO_Div && + Op != BO_Rem && + Op != BO_DivAssign && + Op != BO_RemAssign) + return; + + if (!B->getRHS()->getType()->isIntegerType() || + !B->getRHS()->getType()->isScalarType()) + return; + + SVal Denom = C.getState()->getSVal(B->getRHS()); + const DefinedSVal *DV = dyn_cast(&Denom); + + // 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(); + const GRState *stateNotZero, *stateZero; + llvm::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV); + + if (stateZero && !stateNotZero) { + if (ExplodedNode *N = C.generateSink(stateZero)) { + if (!BT) + BT = new BuiltinBug("Division by zero"); + + EnhancedBugReport *R = + new EnhancedBugReport(*BT, BT->getDescription(), N); + + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + bugreporter::GetDenomExpr(N)); + + C.EmitReport(R); + } + return; + } + + // If we get here, then the denom should not be zero. We abandon the implicit + // zero denom case for now. + C.addTransition(stateNotZero); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngine.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngine.cpp new file mode 100644 index 0000000000..17bc339f75 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngine.cpp @@ -0,0 +1,3513 @@ +//=-- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- 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 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. +// +//===----------------------------------------------------------------------===// + +// FIXME: Restructure checker registration. +#include "ExprEngineInternalChecks.h" + +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngineBuilders.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/ParentMap.h" +#include "clang/AST/StmtObjC.h" +#include "clang/AST/DeclCXX.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/PrettyStackTrace.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/ADT/ImmutableList.h" + +#ifndef NDEBUG +#include "llvm/Support/GraphWriter.h" +#endif + +using namespace clang; +using namespace ento; +using llvm::dyn_cast; +using llvm::dyn_cast_or_null; +using llvm::cast; +using llvm::APSInt; + +namespace { + // Trait class for recording returned expression in the state. + struct ReturnExpr { + static int TagInt; + typedef const Stmt *data_type; + }; + int ReturnExpr::TagInt; +} + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static inline Selector GetNullarySelector(const char* name, ASTContext& Ctx) { + IdentifierInfo* II = &Ctx.Idents.get(name); + return Ctx.Selectors.getSelector(0, &II); +} + +//===----------------------------------------------------------------------===// +// Checker worklist routines. +//===----------------------------------------------------------------------===// + +void ExprEngine::CheckerVisit(const Stmt *S, ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, CallbackKind Kind) { + + // Determine if we already have a cached 'CheckersOrdered' vector + // specifically tailored for the provided . This + // can reduce the number of checkers actually called. + CheckersOrdered *CO = &Checkers; + llvm::OwningPtr NewCO; + + // The cache key is made up of the and the callback kind (pre- or post-visit) + // and the statement kind. + CallbackTag K = GetCallbackTag(Kind, S->getStmtClass()); + + CheckersOrdered *& CO_Ref = COCache[K]; + + if (!CO_Ref) { + // If we have no previously cached CheckersOrdered vector for this + // statement kind, then create one. + NewCO.reset(new CheckersOrdered); + } + else { + // Use the already cached set. + CO = CO_Ref; + } + + if (CO->empty()) { + // If there are no checkers, return early without doing any + // more work. + Dst.insert(Src); + return; + } + + ExplodedNodeSet Tmp; + ExplodedNodeSet *PrevSet = &Src; + unsigned checkersEvaluated = 0; + + for (CheckersOrdered::iterator I=CO->begin(), E=CO->end(); I!=E; ++I) { + // If all nodes are sunk, bail out early. + if (PrevSet->empty()) + break; + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + void *tag = I->first; + Checker *checker = I->second; + bool respondsToCallback = true; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) { + + checker->GR_Visit(*CurrSet, *Builder, *this, S, *NI, tag, + Kind == PreVisitStmtCallback, respondsToCallback); + + } + + PrevSet = CurrSet; + + if (NewCO.get()) { + ++checkersEvaluated; + if (respondsToCallback) + NewCO->push_back(*I); + } + } + + // If we built NewCO, check if we called all the checkers. This is important + // so that we know that we accurately determined the entire set of checkers + // that responds to this callback. Note that 'checkersEvaluated' might + // not be the same as Checkers.size() if one of the Checkers generates + // a sink node. + if (NewCO.get() && checkersEvaluated == Checkers.size()) + CO_Ref = NewCO.take(); + + // Don't autotransition. The CheckerContext objects should do this + // automatically. +} + +void ExprEngine::CheckerEvalNilReceiver(const ObjCMessageExpr *ME, + ExplodedNodeSet &Dst, + const GRState *state, + ExplodedNode *Pred) { + bool evaluated = false; + ExplodedNodeSet DstTmp; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + + if (checker->GR_evalNilReceiver(DstTmp, *Builder, *this, ME, Pred, state, + tag)) { + evaluated = true; + break; + } else + // The checker didn't evaluate the expr. Restore the Dst. + DstTmp.clear(); + } + + if (evaluated) + Dst.insert(DstTmp); + else + Dst.insert(Pred); +} + +// CheckerEvalCall returns true if one of the checkers processed the node. +// This may return void when all call evaluation logic goes to some checker +// in the future. +bool ExprEngine::CheckerEvalCall(const CallExpr *CE, + ExplodedNodeSet &Dst, + ExplodedNode *Pred) { + bool evaluated = false; + ExplodedNodeSet DstTmp; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + + if (checker->GR_evalCallExpr(DstTmp, *Builder, *this, CE, Pred, tag)) { + evaluated = true; + break; + } else + // The checker didn't evaluate the expr. Restore the DstTmp set. + DstTmp.clear(); + } + + if (evaluated) + Dst.insert(DstTmp); + else + Dst.insert(Pred); + + return evaluated; +} + +// FIXME: This is largely copy-paste from CheckerVisit(). Need to +// unify. +void ExprEngine::CheckerVisitBind(const Stmt *StoreE, ExplodedNodeSet &Dst, + ExplodedNodeSet &Src, SVal location, + SVal val, bool isPrevisit) { + + if (Checkers.empty()) { + Dst.insert(Src); + return; + } + + ExplodedNodeSet Tmp; + ExplodedNodeSet *PrevSet = &Src; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) + { + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + + void *tag = I->first; + Checker *checker = I->second; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) + checker->GR_VisitBind(*CurrSet, *Builder, *this, StoreE, + *NI, tag, location, val, isPrevisit); + + // Update which NodeSet is the current one. + PrevSet = CurrSet; + } + + // Don't autotransition. The CheckerContext objects should do this + // automatically. +} +//===----------------------------------------------------------------------===// +// Engine construction and deletion. +//===----------------------------------------------------------------------===// + +static void RegisterInternalChecks(ExprEngine &Eng) { + // Register internal "built-in" BugTypes with the BugReporter. These BugTypes + // are different than what probably many checks will do since they don't + // create BugReports on-the-fly but instead wait until ExprEngine finishes + // analyzing a function. Generation of BugReport objects is done via a call + // to 'FlushReports' from BugReporter. + // The following checks do not need to have their associated BugTypes + // explicitly registered with the BugReporter. If they issue any BugReports, + // their associated BugType will get registered with the BugReporter + // automatically. Note that the check itself is owned by the ExprEngine + // object. + RegisterAdjustedReturnValueChecker(Eng); + // CallAndMessageChecker should be registered before AttrNonNullChecker, + // where we assume arguments are not undefined. + RegisterCallAndMessageChecker(Eng); + RegisterAttrNonNullChecker(Eng); + RegisterDereferenceChecker(Eng); + RegisterVLASizeChecker(Eng); + RegisterDivZeroChecker(Eng); + RegisterReturnUndefChecker(Eng); + RegisterUndefinedArraySubscriptChecker(Eng); + RegisterUndefinedAssignmentChecker(Eng); + RegisterUndefBranchChecker(Eng); + RegisterUndefCapturedBlockVarChecker(Eng); + RegisterUndefResultChecker(Eng); + RegisterStackAddrLeakChecker(Eng); + RegisterObjCAtSyncChecker(Eng); + + // This is not a checker yet. + RegisterNoReturnFunctionChecker(Eng); + RegisterBuiltinFunctionChecker(Eng); + RegisterOSAtomicChecker(Eng); + RegisterUnixAPIChecker(Eng); + RegisterMacOSXAPIChecker(Eng); +} + +ExprEngine::ExprEngine(AnalysisManager &mgr, TransferFuncs *tf) + : AMgr(mgr), + Engine(*this), + G(Engine.getGraph()), + Builder(NULL), + StateMgr(getContext(), mgr.getStoreManagerCreator(), + mgr.getConstraintManagerCreator(), G.getAllocator(), + *this), + SymMgr(StateMgr.getSymbolManager()), + svalBuilder(StateMgr.getSValBuilder()), + EntryNode(NULL), currentStmt(NULL), + NSExceptionII(NULL), NSExceptionInstanceRaiseSelectors(NULL), + RaiseSel(GetNullarySelector("raise", getContext())), + BR(mgr, *this), TF(tf) { + // Register internal checks. + RegisterInternalChecks(*this); + + // FIXME: Eventually remove the TF object entirely. + TF->RegisterChecks(*this); + TF->RegisterPrinters(getStateManager().Printers); +} + +ExprEngine::~ExprEngine() { + BR.FlushReports(); + delete [] NSExceptionInstanceRaiseSelectors; + + // Delete the set of checkers. + for (CheckersOrdered::iterator I=Checkers.begin(), E=Checkers.end(); I!=E;++I) + delete I->second; + + for (CheckersOrderedCache::iterator I=COCache.begin(), E=COCache.end(); + I!=E;++I) + delete I->second; +} + +//===----------------------------------------------------------------------===// +// Utility methods. +//===----------------------------------------------------------------------===// + +const GRState* ExprEngine::getInitialState(const LocationContext *InitLoc) { + const GRState *state = StateMgr.getInitialState(InitLoc); + + // Preconditions. + + // FIXME: It would be nice if we had a more general mechanism to add + // such preconditions. Some day. + do { + const Decl *D = InitLoc->getDecl(); + if (const FunctionDecl *FD = dyn_cast(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(); + if (!T->isIntegerType()) + 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), + getContext().IntTy); + + DefinedOrUnknownSVal *Constraint = + dyn_cast(&Constraint_untested); + + if (!Constraint) + break; + + if (const GRState *newState = state->assume(*Constraint, true)) + state = newState; + + break; + } + + if (const ObjCMethodDecl *MD = dyn_cast(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 (const Loc *LV = dyn_cast(&V)) { + // Assume that the pointer value in 'self' is non-null. + state = state->assume(*LV, true); + assert(state && "'self' cannot be null"); + } + } + } while (0); + + return state; +} + +//===----------------------------------------------------------------------===// +// Top-level transfer function logic (Dispatcher). +//===----------------------------------------------------------------------===// + +/// evalAssume - Called by ConstraintManager. Used to call checker-specific +/// logic for handling assumptions on symbolic values. +const GRState *ExprEngine::ProcessAssume(const GRState *state, SVal cond, + bool assumption) { + // Determine if we already have a cached 'CheckersOrdered' vector + // specifically tailored for processing assumptions. This + // can reduce the number of checkers actually called. + CheckersOrdered *CO = &Checkers; + llvm::OwningPtr NewCO; + + CallbackTag K = GetCallbackTag(ProcessAssumeCallback); + CheckersOrdered *& CO_Ref = COCache[K]; + + if (!CO_Ref) { + // If we have no previously cached CheckersOrdered vector for this + // statement kind, then create one. + NewCO.reset(new CheckersOrdered); + } + else { + // Use the already cached set. + CO = CO_Ref; + } + + if (!CO->empty()) { + // Let the checkers have a crack at the assume before the transfer functions + // get their turn. + for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I!=E; ++I) { + + // If any checker declares the state infeasible (or if it starts that + // way), bail out. + if (!state) + return NULL; + + Checker *C = I->second; + bool respondsToCallback = true; + + state = C->evalAssume(state, cond, assumption, &respondsToCallback); + + // Check if we're building the cache of checkers that care about + // assumptions. + if (NewCO.get() && respondsToCallback) + NewCO->push_back(*I); + } + + // If we got through all the checkers, and we built a list of those that + // care about assumptions, save it. + if (NewCO.get()) + CO_Ref = NewCO.take(); + } + + // If the state is infeasible at this point, bail out. + if (!state) + return NULL; + + return TF->evalAssume(state, cond, assumption); +} + +bool ExprEngine::WantsRegionChangeUpdate(const GRState* state) { + CallbackTag K = GetCallbackTag(EvalRegionChangesCallback); + CheckersOrdered *CO = COCache[K]; + + if (!CO) + CO = &Checkers; + + for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I != E; ++I) { + Checker *C = I->second; + if (C->WantsRegionChangeUpdate(state)) + return true; + } + + return false; +} + +const GRState * +ExprEngine::ProcessRegionChanges(const GRState *state, + const MemRegion * const *Begin, + const MemRegion * const *End) { + // FIXME: Most of this method is copy-pasted from ProcessAssume. + + // Determine if we already have a cached 'CheckersOrdered' vector + // specifically tailored for processing region changes. This + // can reduce the number of checkers actually called. + CheckersOrdered *CO = &Checkers; + llvm::OwningPtr NewCO; + + CallbackTag K = GetCallbackTag(EvalRegionChangesCallback); + CheckersOrdered *& CO_Ref = COCache[K]; + + if (!CO_Ref) { + // If we have no previously cached CheckersOrdered vector for this + // callback, then create one. + NewCO.reset(new CheckersOrdered); + } + else { + // Use the already cached set. + CO = CO_Ref; + } + + // If there are no checkers, just return the state as is. + if (CO->empty()) + return state; + + for (CheckersOrdered::iterator I = CO->begin(), E = CO->end(); I != E; ++I) { + // If any checker declares the state infeasible (or if it starts that way), + // bail out. + if (!state) + return NULL; + + Checker *C = I->second; + bool respondsToCallback = true; + + state = C->EvalRegionChanges(state, Begin, End, &respondsToCallback); + + // See if we're building a cache of checkers that care about region changes. + if (NewCO.get() && respondsToCallback) + NewCO->push_back(*I); + } + + // If we got through all the checkers, and we built a list of those that + // care about region changes, save it. + if (NewCO.get()) + CO_Ref = NewCO.take(); + + return state; +} + +void ExprEngine::ProcessEndWorklist(bool hasWorkRemaining) { + for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); + I != E; ++I) { + I->second->VisitEndAnalysis(G, BR, *this); + } +} + +void ExprEngine::ProcessElement(const CFGElement E, + StmtNodeBuilder& builder) { + switch (E.getKind()) { + case CFGElement::Statement: + ProcessStmt(E.getAs(), builder); + break; + case CFGElement::Initializer: + ProcessInitializer(E.getAs(), builder); + break; + case CFGElement::ImplicitDtor: + ProcessImplicitDtor(E.getAs(), builder); + break; + default: + // Suppress compiler warning. + llvm_unreachable("Unexpected CFGElement kind."); + } +} + +void ExprEngine::ProcessStmt(const CFGStmt S, StmtNodeBuilder& builder) { + currentStmt = S.getStmt(); + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + currentStmt->getLocStart(), + "Error evaluating statement"); + + Builder = &builder; + EntryNode = builder.getBasePredecessor(); + + // Create the cleaned state. + const LocationContext *LC = EntryNode->getLocationContext(); + SymbolReaper SymReaper(LC, currentStmt, SymMgr); + + if (AMgr.shouldPurgeDead()) { + const GRState *St = EntryNode->getState(); + + for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); + I != E; ++I) { + Checker *checker = I->second; + checker->MarkLiveSymbols(St, SymReaper); + } + + const StackFrameContext *SFC = LC->getCurrentStackFrame(); + CleanedState = StateMgr.RemoveDeadBindings(St, SFC, SymReaper); + } else { + CleanedState = EntryNode->getState(); + } + + // Process any special transfer function for dead symbols. + ExplodedNodeSet Tmp; + + if (!SymReaper.hasDeadSymbols()) + Tmp.Add(EntryNode); + else { + SaveAndRestore OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->HasGeneratedNode); + + SaveAndRestore OldPurgeDeadSymbols(Builder->PurgingDeadSymbols); + Builder->PurgingDeadSymbols = true; + + // FIXME: This should soon be removed. + ExplodedNodeSet Tmp2; + getTF().evalDeadSymbols(Tmp2, *this, *Builder, EntryNode, + CleanedState, SymReaper); + + if (Checkers.empty()) + Tmp.insert(Tmp2); + else { + ExplodedNodeSet Tmp3; + ExplodedNodeSet *SrcSet = &Tmp2; + for (CheckersOrdered::iterator I = Checkers.begin(), E = Checkers.end(); + I != E; ++I) { + ExplodedNodeSet *DstSet = 0; + if (I+1 == E) + DstSet = &Tmp; + else { + DstSet = (SrcSet == &Tmp2) ? &Tmp3 : &Tmp2; + DstSet->clear(); + } + + void *tag = I->first; + Checker *checker = I->second; + for (ExplodedNodeSet::iterator NI = SrcSet->begin(), NE = SrcSet->end(); + NI != NE; ++NI) + checker->GR_evalDeadSymbols(*DstSet, *Builder, *this, currentStmt, + *NI, SymReaper, tag); + SrcSet = DstSet; + } + } + + if (!Builder->BuildSinks && !Builder->HasGeneratedNode) + Tmp.Add(EntryNode); + } + + bool HasAutoGenerated = false; + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + ExplodedNodeSet Dst; + + // Set the cleaned state. + Builder->SetCleanedState(*I == EntryNode ? CleanedState : GetState(*I)); + + // Visit the statement. + Visit(currentStmt, *I, Dst); + + // Do we need to auto-generate a node? We only need to do this to generate + // a node with a "cleaned" state; CoreEngine will actually handle + // auto-transitions for other cases. + if (Dst.size() == 1 && *Dst.begin() == EntryNode + && !Builder->HasGeneratedNode && !HasAutoGenerated) { + HasAutoGenerated = true; + builder.generateNode(currentStmt, GetState(EntryNode), *I); + } + } + + // NULL out these variables to cleanup. + CleanedState = NULL; + EntryNode = NULL; + + currentStmt = 0; + + Builder = NULL; +} + +void ExprEngine::ProcessInitializer(const CFGInitializer Init, + StmtNodeBuilder &builder) { + // We don't set EntryNode and currentStmt. And we don't clean up state. + const CXXBaseOrMemberInitializer *BMI = Init.getInitializer(); + + ExplodedNode *Pred = builder.getBasePredecessor(); + const LocationContext *LC = Pred->getLocationContext(); + + if (BMI->isAnyMemberInitializer()) { + ExplodedNodeSet Dst; + + // Evaluate the initializer. + Visit(BMI->getInit(), Pred, Dst); + + for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end(); I != E; ++I){ + ExplodedNode *Pred = *I; + const GRState *state = Pred->getState(); + + const FieldDecl *FD = BMI->getAnyMember(); + const RecordDecl *RD = FD->getParent(); + const CXXThisRegion *ThisR = getCXXThisRegion(cast(RD), + cast(LC)); + + SVal ThisV = state->getSVal(ThisR); + SVal FieldLoc = state->getLValue(FD, ThisV); + SVal InitVal = state->getSVal(BMI->getInit()); + state = state->bindLoc(FieldLoc, InitVal); + + // Use a custom node building process. + PostInitializer PP(BMI, LC); + // Builder automatically add the generated node to the deferred set, + // which are processed in the builder's dtor. + builder.generateNode(PP, state, Pred); + } + } +} + +void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D, + StmtNodeBuilder &builder) { + Builder = &builder; + + switch (D.getDtorKind()) { + case CFGElement::AutomaticObjectDtor: + ProcessAutomaticObjDtor(cast(D), builder); + break; + case CFGElement::BaseDtor: + ProcessBaseDtor(cast(D), builder); + break; + case CFGElement::MemberDtor: + ProcessMemberDtor(cast(D), builder); + break; + case CFGElement::TemporaryDtor: + ProcessTemporaryDtor(cast(D), builder); + break; + default: + llvm_unreachable("Unexpected dtor kind."); + } +} + +void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor dtor, + StmtNodeBuilder &builder) { + ExplodedNode *pred = builder.getBasePredecessor(); + const GRState *state = pred->getState(); + const VarDecl *varDecl = dtor.getVarDecl(); + + QualType varType = varDecl->getType(); + + if (const ReferenceType *refType = varType->getAs()) + varType = refType->getPointeeType(); + + const CXXRecordDecl *recordDecl = varType->getAsCXXRecordDecl(); + assert(recordDecl && "get CXXRecordDecl fail"); + const CXXDestructorDecl *dtorDecl = recordDecl->getDestructor(); + + Loc dest = state->getLValue(varDecl, pred->getLocationContext()); + + ExplodedNodeSet dstSet; + VisitCXXDestructor(dtorDecl, cast(dest).getRegion(), + dtor.getTriggerStmt(), pred, dstSet); +} + +void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D, + StmtNodeBuilder &builder) { +} + +void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D, + StmtNodeBuilder &builder) { +} + +void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D, + StmtNodeBuilder &builder) { +} + +void ExprEngine::Visit(const Stmt* S, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + S->getLocStart(), + "Error evaluating statement"); + + // Expressions to ignore. + if (const Expr *Ex = dyn_cast(S)) + S = Ex->IgnoreParens(); + + // FIXME: add metadata to the CFG so that we can disable + // this check when we KNOW that there is no block-level subexpression. + // The motivation is that this check requires a hashtable lookup. + + if (S != currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(S)) { + Dst.Add(Pred); + return; + } + + switch (S->getStmtClass()) { + // C++ stuff we don't support yet. + case Stmt::CXXBindTemporaryExprClass: + case Stmt::CXXCatchStmtClass: + case Stmt::CXXDefaultArgExprClass: + case Stmt::CXXDependentScopeMemberExprClass: + case Stmt::ExprWithCleanupsClass: + case Stmt::CXXNullPtrLiteralExprClass: + case Stmt::CXXPseudoDestructorExprClass: + case Stmt::CXXTemporaryObjectExprClass: + case Stmt::CXXThrowExprClass: + case Stmt::CXXTryStmtClass: + case Stmt::CXXTypeidExprClass: + case Stmt::CXXUuidofExprClass: + case Stmt::CXXUnresolvedConstructExprClass: + case Stmt::CXXScalarValueInitExprClass: + case Stmt::DependentScopeDeclRefExprClass: + case Stmt::UnaryTypeTraitExprClass: + case Stmt::BinaryTypeTraitExprClass: + case Stmt::UnresolvedLookupExprClass: + case Stmt::UnresolvedMemberExprClass: + case Stmt::CXXNoexceptExprClass: + { + SaveAndRestore OldSink(Builder->BuildSinks); + Builder->BuildSinks = true; + MakeNode(Dst, S, Pred, GetState(Pred)); + break; + } + + case Stmt::ParenExprClass: + llvm_unreachable("ParenExprs 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::DefaultStmtClass: + case Stmt::DoStmtClass: + case Stmt::GotoStmtClass: + case Stmt::IndirectGotoStmtClass: + case Stmt::LabelStmtClass: + case Stmt::NoStmtClass: + case Stmt::NullStmtClass: + case Stmt::SwitchCaseClass: + case Stmt::OpaqueValueExprClass: + llvm_unreachable("Stmt should not be in analyzer evaluation loop"); + break; + + case Stmt::GNUNullExprClass: { + MakeNode(Dst, S, Pred, GetState(Pred)->BindExpr(S, svalBuilder.makeNull())); + break; + } + + case Stmt::ObjCAtSynchronizedStmtClass: + VisitObjCAtSynchronizedStmt(cast(S), Pred, Dst); + break; + + // Cases not handled yet; but will handle some day. + case Stmt::DesignatedInitExprClass: + case Stmt::ExtVectorElementExprClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::ImplicitValueInitExprClass: + case Stmt::ObjCAtCatchStmtClass: + case Stmt::ObjCAtFinallyStmtClass: + case Stmt::ObjCAtTryStmtClass: + case Stmt::ObjCEncodeExprClass: + case Stmt::ObjCIsaExprClass: + case Stmt::ObjCPropertyRefExprClass: + case Stmt::ObjCProtocolExprClass: + case Stmt::ObjCSelectorExprClass: + case Stmt::ObjCStringLiteralClass: + case Stmt::ParenListExprClass: + case Stmt::PredefinedExprClass: + case Stmt::ShuffleVectorExprClass: + case Stmt::VAArgExprClass: + // Fall through. + + // Cases we intentionally don't evaluate, since they don't need + // to be explicitly evaluated. + case Stmt::AddrLabelExprClass: + case Stmt::IntegerLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::CXXBoolLiteralExprClass: + case Stmt::FloatingLiteralClass: + Dst.Add(Pred); // No-op. Simply propagate the current state unchanged. + break; + + case Stmt::ArraySubscriptExprClass: + VisitLvalArraySubscriptExpr(cast(S), Pred, Dst); + break; + + case Stmt::AsmStmtClass: + VisitAsmStmt(cast(S), Pred, Dst); + break; + + case Stmt::BlockDeclRefExprClass: { + const BlockDeclRefExpr *BE = cast(S); + VisitCommonDeclRefExpr(BE, BE->getDecl(), Pred, Dst); + break; + } + + case Stmt::BlockExprClass: + VisitBlockExpr(cast(S), Pred, Dst); + break; + + case Stmt::BinaryOperatorClass: { + const BinaryOperator* B = cast(S); + if (B->isLogicalOp()) { + VisitLogicalExpr(B, Pred, Dst); + break; + } + else if (B->getOpcode() == BO_Comma) { + const GRState* state = GetState(Pred); + MakeNode(Dst, B, Pred, state->BindExpr(B, state->getSVal(B->getRHS()))); + break; + } + + if (AMgr.shouldEagerlyAssume() && + (B->isRelationalOp() || B->isEqualityOp())) { + ExplodedNodeSet Tmp; + VisitBinaryOperator(cast(S), Pred, Tmp); + evalEagerlyAssume(Dst, Tmp, cast(S)); + } + else + VisitBinaryOperator(cast(S), Pred, Dst); + + break; + } + + case Stmt::CallExprClass: { + const CallExpr* C = cast(S); + VisitCall(C, Pred, C->arg_begin(), C->arg_end(), Dst); + break; + } + + case Stmt::CXXConstructExprClass: { + const CXXConstructExpr *C = cast(S); + // For block-level CXXConstructExpr, we don't have a destination region. + // Let VisitCXXConstructExpr() create one. + VisitCXXConstructExpr(C, 0, Pred, Dst); + break; + } + + case Stmt::CXXMemberCallExprClass: { + const CXXMemberCallExpr *MCE = cast(S); + VisitCXXMemberCallExpr(MCE, Pred, Dst); + break; + } + + case Stmt::CXXOperatorCallExprClass: { + const CXXOperatorCallExpr *C = cast(S); + VisitCXXOperatorCallExpr(C, Pred, Dst); + break; + } + + case Stmt::CXXNewExprClass: { + const CXXNewExpr *NE = cast(S); + VisitCXXNewExpr(NE, Pred, Dst); + break; + } + + case Stmt::CXXDeleteExprClass: { + const CXXDeleteExpr *CDE = cast(S); + VisitCXXDeleteExpr(CDE, Pred, 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 + const ChooseExpr* C = cast(S); + VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); + break; + } + + case Stmt::CompoundAssignOperatorClass: + VisitBinaryOperator(cast(S), Pred, Dst); + break; + + case Stmt::CompoundLiteralExprClass: + VisitCompoundLiteralExpr(cast(S), Pred, Dst); + break; + + case Stmt::ConditionalOperatorClass: { // '?' operator + const ConditionalOperator* C = cast(S); + VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst); + break; + } + + case Stmt::CXXThisExprClass: + VisitCXXThisExpr(cast(S), Pred, Dst); + break; + + case Stmt::DeclRefExprClass: { + const DeclRefExpr *DE = cast(S); + VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst); + break; + } + + case Stmt::DeclStmtClass: + VisitDeclStmt(cast(S), Pred, Dst); + break; + + case Stmt::ForStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); + break; + + case Stmt::ImplicitCastExprClass: + case Stmt::CStyleCastExprClass: + case Stmt::CXXStaticCastExprClass: + case Stmt::CXXDynamicCastExprClass: + case Stmt::CXXReinterpretCastExprClass: + case Stmt::CXXConstCastExprClass: + case Stmt::CXXFunctionalCastExprClass: { + const CastExpr* C = cast(S); + VisitCast(C, C->getSubExpr(), Pred, Dst); + break; + } + + case Stmt::IfStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); + break; + + case Stmt::InitListExprClass: + VisitInitListExpr(cast(S), Pred, Dst); + break; + + case Stmt::MemberExprClass: + VisitMemberExpr(cast(S), Pred, Dst); + break; + case Stmt::ObjCIvarRefExprClass: + VisitLvalObjCIvarRefExpr(cast(S), Pred, Dst); + break; + + case Stmt::ObjCForCollectionStmtClass: + VisitObjCForCollectionStmt(cast(S), Pred, Dst); + break; + + case Stmt::ObjCMessageExprClass: + VisitObjCMessageExpr(cast(S), Pred, Dst); + break; + + case Stmt::ObjCAtThrowStmtClass: { + // FIXME: This is not complete. We basically treat @throw as + // an abort. + SaveAndRestore OldSink(Builder->BuildSinks); + Builder->BuildSinks = true; + MakeNode(Dst, S, Pred, GetState(Pred)); + break; + } + + case Stmt::ReturnStmtClass: + VisitReturnStmt(cast(S), Pred, Dst); + break; + + case Stmt::OffsetOfExprClass: + VisitOffsetOfExpr(cast(S), Pred, Dst); + break; + + case Stmt::SizeOfAlignOfExprClass: + VisitSizeOfAlignOfExpr(cast(S), Pred, Dst); + break; + + case Stmt::StmtExprClass: { + const StmtExpr* SE = cast(S); + + if (SE->getSubStmt()->body_empty()) { + // Empty statement expression. + assert(SE->getType() == getContext().VoidTy + && "Empty statement expression must have void type."); + Dst.Add(Pred); + break; + } + + if (Expr* LastExpr = dyn_cast(*SE->getSubStmt()->body_rbegin())) { + const GRState* state = GetState(Pred); + MakeNode(Dst, SE, Pred, state->BindExpr(SE, state->getSVal(LastExpr))); + } + else + Dst.Add(Pred); + + break; + } + + case Stmt::StringLiteralClass: { + const GRState* state = GetState(Pred); + SVal V = state->getLValue(cast(S)); + MakeNode(Dst, S, Pred, state->BindExpr(S, V)); + return; + } + + case Stmt::SwitchStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); + break; + + case Stmt::UnaryOperatorClass: { + const UnaryOperator *U = cast(S); + if (AMgr.shouldEagerlyAssume()&&(U->getOpcode() == UO_LNot)) { + ExplodedNodeSet Tmp; + VisitUnaryOperator(U, Pred, Tmp); + evalEagerlyAssume(Dst, Tmp, U); + } + else + VisitUnaryOperator(U, Pred, Dst); + break; + } + + case Stmt::WhileStmtClass: + // This case isn't for branch processing, but for handling the + // initialization of a condition variable. + VisitCondInit(cast(S)->getConditionVariable(), S, Pred, Dst); + break; + } +} + +//===----------------------------------------------------------------------===// +// Block entrance. (Update counters). +//===----------------------------------------------------------------------===// + +bool ExprEngine::ProcessBlockEntrance(const CFGBlock* B, + const ExplodedNode *Pred, + BlockCounter BC) { + return BC.getNumVisited(Pred->getLocationContext()->getCurrentStackFrame(), + B->getBlockID()) < AMgr.getMaxVisit(); +} + +//===----------------------------------------------------------------------===// +// Generic node creation. +//===----------------------------------------------------------------------===// + +ExplodedNode* ExprEngine::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, + ExplodedNode* Pred, const GRState* St, + ProgramPoint::Kind K, const void *tag) { + assert (Builder && "StmtNodeBuilder not present."); + SaveAndRestore OldTag(Builder->Tag); + Builder->Tag = tag; + return Builder->MakeNode(Dst, S, Pred, St, K); +} + +//===----------------------------------------------------------------------===// +// Branch processing. +//===----------------------------------------------------------------------===// + +const GRState* ExprEngine::MarkBranch(const GRState* state, + const Stmt* Terminator, + bool branchTaken) { + + switch (Terminator->getStmtClass()) { + default: + return state; + + case Stmt::BinaryOperatorClass: { // '&&' and '||' + + const BinaryOperator* B = cast(Terminator); + BinaryOperator::Opcode Op = B->getOpcode(); + + assert (Op == BO_LAnd || Op == BO_LOr); + + // For &&, if we take the true branch, then the value of the whole + // expression is that of the RHS expression. + // + // For ||, if we take the false branch, then the value of the whole + // expression is that of the RHS expression. + + const Expr* Ex = (Op == BO_LAnd && branchTaken) || + (Op == BO_LOr && !branchTaken) + ? B->getRHS() : B->getLHS(); + + return state->BindExpr(B, UndefinedVal(Ex)); + } + + case Stmt::ConditionalOperatorClass: { // ?: + + const ConditionalOperator* C = cast(Terminator); + + // For ?, if branchTaken == true then the value is either the LHS or + // the condition itself. (GNU extension). + + const Expr* Ex; + + if (branchTaken) + Ex = C->getLHS() ? C->getLHS() : C->getCond(); + else + Ex = C->getRHS(); + + return state->BindExpr(C, UndefinedVal(Ex)); + } + + case Stmt::ChooseExprClass: { // ?: + + const ChooseExpr* C = cast(Terminator); + + const Expr* Ex = branchTaken ? C->getLHS() : C->getRHS(); + return state->BindExpr(C, UndefinedVal(Ex)); + } + } +} + +/// 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(GRStateManager& StateMgr, const GRState* state, + const Stmt* Condition, ASTContext& Ctx) { + + const Expr *Ex = dyn_cast(Condition); + if (!Ex) + return UnknownVal(); + + uint64_t bits = 0; + bool bitsInit = false; + + while (const CastExpr *CE = dyn_cast(Ex)) { + QualType T = CE->getType(); + + if (!T->isIntegerType()) + 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->isIntegerType() || Ctx.getTypeSize(T) > bits) + return UnknownVal(); + + return state->getSVal(Ex); +} + +void ExprEngine::ProcessBranch(const Stmt* Condition, const Stmt* Term, + BranchNodeBuilder& builder) { + + // Check for NULL conditions; e.g. "for(;;)" + if (!Condition) { + builder.markInfeasible(false); + return; + } + + PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(), + Condition->getLocStart(), + "Error evaluating branch"); + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end();I!=E;++I) { + void *tag = I->first; + Checker *checker = I->second; + checker->VisitBranchCondition(builder, *this, Condition, tag); + } + + // If the branch condition is undefined, return; + if (!builder.isFeasible(true) && !builder.isFeasible(false)) + return; + + const GRState* PrevState = builder.getState(); + SVal X = PrevState->getSVal(Condition); + + if (X.isUnknown()) { + // Give it a chance to recover from unknown. + if (const Expr *Ex = dyn_cast(Condition)) { + if (Ex->getType()->isIntegerType()) { + // 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(getStateManager(), + builder.getState(), Condition, + getContext()); + + if (!recovered.isUnknown()) { + X = recovered; + } + } + } + // If the condition is still unknown, give up. + if (X.isUnknown()) { + builder.generateNode(MarkBranch(PrevState, Term, true), true); + builder.generateNode(MarkBranch(PrevState, Term, false), false); + return; + } + } + + DefinedSVal V = cast(X); + + // Process the true branch. + if (builder.isFeasible(true)) { + if (const GRState *state = PrevState->assume(V, true)) + builder.generateNode(MarkBranch(state, Term, true), true); + else + builder.markInfeasible(true); + } + + // Process the false branch. + if (builder.isFeasible(false)) { + if (const GRState *state = PrevState->assume(V, false)) + builder.generateNode(MarkBranch(state, Term, false), false); + else + builder.markInfeasible(false); + } +} + +/// ProcessIndirectGoto - Called by CoreEngine. Used to generate successor +/// nodes by processing the 'effects' of a computed goto jump. +void ExprEngine::ProcessIndirectGoto(IndirectGotoNodeBuilder& builder) { + + const GRState *state = builder.getState(); + SVal V = state->getSVal(builder.getTarget()); + + // 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. + // + + typedef IndirectGotoNodeBuilder::iterator iterator; + + if (isa(V)) { + const LabelStmt* L = cast(V).getLabel(); + + for (iterator I=builder.begin(), E=builder.end(); I != E; ++I) { + if (I.getLabel() == L) { + builder.generateNode(I, state); + return; + } + } + + assert (false && "No block with label."); + return; + } + + if (isa(V) || isa(V)) { + // 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::VisitGuardedExpr(const Expr* Ex, const Expr* L, + const Expr* R, + ExplodedNode* Pred, ExplodedNodeSet& Dst) { + + assert(Ex == currentStmt && + Pred->getLocationContext()->getCFG()->isBlkExpr(Ex)); + + const GRState* state = GetState(Pred); + SVal X = state->getSVal(Ex); + + assert (X.isUndef()); + + const Expr *SE = (Expr*) cast(X).getData(); + assert(SE); + X = state->getSVal(SE); + + // Make sure that we invalidate the previous binding. + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, X, true)); +} + +/// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path +/// nodes when the control reaches the end of a function. +void ExprEngine::ProcessEndPath(EndPathNodeBuilder& builder) { + getTF().evalEndPath(*this, builder); + StateMgr.EndPath(builder.getState()); + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E;++I){ + void *tag = I->first; + Checker *checker = I->second; + checker->evalEndPath(builder, tag, *this); + } +} + +/// ProcessSwitch - Called by CoreEngine. Used to generate successor +/// nodes by processing the 'effects' of a switch statement. +void ExprEngine::ProcessSwitch(SwitchNodeBuilder& builder) { + typedef SwitchNodeBuilder::iterator iterator; + const GRState* state = builder.getState(); + const Expr* CondE = builder.getCondition(); + SVal CondV_untested = state->getSVal(CondE); + + if (CondV_untested.isUndef()) { + //ExplodedNode* N = builder.generateDefaultCaseNode(state, true); + // FIXME: add checker + //UndefBranches.insert(N); + + return; + } + DefinedOrUnknownSVal CondV = cast(CondV_untested); + + const GRState *DefaultSt = state; + + iterator I = builder.begin(), EI = builder.end(); + bool defaultIsFeasible = I == EI; + + for ( ; I != EI; ++I) { + const CaseStmt* Case = I.getCase(); + + // Evaluate the LHS of the case value. + Expr::EvalResult V1; + bool b = Case->getLHS()->Evaluate(V1, getContext()); + + // Sanity checks. These go away in Release builds. + assert(b && V1.Val.isInt() && !V1.HasSideEffects + && "Case condition must evaluate to an integer constant."); + (void)b; // silence unused variable warning + assert(V1.Val.getInt().getBitWidth() == + getContext().getTypeSize(CondE->getType())); + + // Get the RHS of the case, if it exists. + Expr::EvalResult V2; + + if (const Expr* E = Case->getRHS()) { + b = E->Evaluate(V2, getContext()); + assert(b && V2.Val.isInt() && !V2.HasSideEffects + && "Case condition must evaluate to an integer constant."); + (void)b; // silence unused variable warning + } + else + V2 = V1; + + // FIXME: Eventually we should replace the logic below with a range + // comparison, rather than concretize the values within the range. + // This should be easy once we have "ranges" for NonLVals. + + do { + nonloc::ConcreteInt CaseVal(getBasicVals().getValue(V1.Val.getInt())); + DefinedOrUnknownSVal Res = svalBuilder.evalEQ(DefaultSt ? DefaultSt : state, + CondV, CaseVal); + + // Now "assume" that the case matches. + if (const GRState* stateNew = state->assume(Res, true)) { + builder.generateCaseStmtNode(I, stateNew); + + // If CondV evaluates to a constant, then we know that this + // is the *only* case that we can take, so stop evaluating the + // others. + if (isa(CondV)) + return; + } + + // Now "assume" that the case doesn't match. Add this state + // to the default state (if it is feasible). + if (DefaultSt) { + if (const GRState *stateNew = DefaultSt->assume(Res, false)) { + defaultIsFeasible = true; + DefaultSt = stateNew; + } + else { + defaultIsFeasible = false; + DefaultSt = NULL; + } + } + + // Concretize the next value in the range. + if (V1.Val.getInt() == V2.Val.getInt()) + break; + + ++V1.Val.getInt(); + assert (V1.Val.getInt() <= V2.Val.getInt()); + + } while (true); + } + + 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()) { + if (SS->isAllEnumCasesCovered()) + return; + } + + builder.generateDefaultCaseNode(DefaultSt); +} + +void ExprEngine::ProcessCallEnter(CallEnterNodeBuilder &B) { + const GRState *state = B.getState()->EnterStackFrame(B.getCalleeContext()); + B.generateNode(state); +} + +void ExprEngine::ProcessCallExit(CallExitNodeBuilder &B) { + const GRState *state = B.getState(); + const ExplodedNode *Pred = B.getPredecessor(); + const StackFrameContext *calleeCtx = + cast(Pred->getLocationContext()); + const Stmt *CE = calleeCtx->getCallSite(); + + // If the callee returns an expression, bind its value to CallExpr. + const Stmt *ReturnedExpr = state->get(); + if (ReturnedExpr) { + SVal RetVal = state->getSVal(ReturnedExpr); + state = state->BindExpr(CE, RetVal); + // Clear the return expr GDM. + state = state->remove(); + } + + // Bind the constructed object value to CXXConstructExpr. + if (const CXXConstructExpr *CCE = dyn_cast(CE)) { + const CXXThisRegion *ThisR = + getCXXThisRegion(CCE->getConstructor()->getParent(), calleeCtx); + + SVal ThisV = state->getSVal(ThisR); + // Always bind the region to the CXXConstructExpr. + state = state->BindExpr(CCE, ThisV); + } + + B.generateNode(state); +} + +//===----------------------------------------------------------------------===// +// Transfer functions: logical operations ('&&', '||'). +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + assert(B->getOpcode() == BO_LAnd || + B->getOpcode() == BO_LOr); + + assert(B==currentStmt && Pred->getLocationContext()->getCFG()->isBlkExpr(B)); + + const GRState* state = GetState(Pred); + SVal X = state->getSVal(B); + assert(X.isUndef()); + + const Expr *Ex = (const Expr*) cast(X).getData(); + assert(Ex); + + if (Ex == B->getRHS()) { + X = state->getSVal(Ex); + + // Handle undefined values. + if (X.isUndef()) { + MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + return; + } + + DefinedOrUnknownSVal XD = cast(X); + + // We took the RHS. Because the value of the '&&' or '||' expression must + // evaluate to 0 or 1, we must assume the value of the RHS evaluates to 0 + // or 1. Alternatively, we could take a lazy approach, and calculate this + // value later when necessary. We don't have the machinery in place for + // this right now, and since most logical expressions are used for branches, + // the payoff is not likely to be large. Instead, we do eager evaluation. + if (const GRState *newState = state->assume(XD, true)) + MakeNode(Dst, B, Pred, + newState->BindExpr(B, svalBuilder.makeIntVal(1U, B->getType()))); + + if (const GRState *newState = state->assume(XD, false)) + MakeNode(Dst, B, Pred, + newState->BindExpr(B, svalBuilder.makeIntVal(0U, B->getType()))); + } + else { + // We took the LHS expression. Depending on whether we are '&&' or + // '||' we know what the value of the expression is via properties of + // the short-circuiting. + X = svalBuilder.makeIntVal(B->getOpcode() == BO_LAnd ? 0U : 1U, + B->getType()); + MakeNode(Dst, B, Pred, state->BindExpr(B, X)); + } +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Loads and stores. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + ExplodedNodeSet Tmp; + + CanQualType T = getContext().getCanonicalType(BE->getType()); + SVal V = svalBuilder.getBlockPointer(BE->getBlockDecl(), T, + Pred->getLocationContext()); + + MakeNode(Tmp, BE, Pred, GetState(Pred)->BindExpr(BE, V), + ProgramPoint::PostLValueKind); + + // Post-visit the BlockExpr. + CheckerVisit(BE, Dst, Tmp, PostVisitStmtCallback); +} + +void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + const GRState *state = GetState(Pred); + + if (const VarDecl* VD = dyn_cast(D)) { + assert(Ex->isLValue()); + SVal V = state->getLValue(VD, Pred->getLocationContext()); + + // For references, the 'lvalue' is the pointer address stored in the + // reference region. + if (VD->getType()->isReferenceType()) { + if (const MemRegion *R = V.getAsRegion()) + V = state->getSVal(R); + else + V = UnknownVal(); + } + + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), + ProgramPoint::PostLValueKind); + return; + } + if (const EnumConstantDecl* ED = dyn_cast(D)) { + assert(!Ex->isLValue()); + SVal V = svalBuilder.makeIntVal(ED->getInitVal()); + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V)); + return; + } + if (const FunctionDecl* FD = dyn_cast(D)) { + SVal V = svalBuilder.getFunctionPointer(FD); + MakeNode(Dst, Ex, Pred, state->BindExpr(Ex, V), + ProgramPoint::PostLValueKind); + return; + } + assert (false && + "ValueDecl support for this ValueDecl not implemented."); +} + +/// VisitArraySubscriptExpr - Transfer function for array accesses +void ExprEngine::VisitLvalArraySubscriptExpr(const ArraySubscriptExpr* A, + ExplodedNode* Pred, + ExplodedNodeSet& Dst){ + + const Expr* Base = A->getBase()->IgnoreParens(); + const Expr* Idx = A->getIdx()->IgnoreParens(); + + // Evaluate the base. + ExplodedNodeSet Tmp; + Visit(Base, Pred, Tmp); + + for (ExplodedNodeSet::iterator I1=Tmp.begin(), E1=Tmp.end(); I1!=E1; ++I1) { + ExplodedNodeSet Tmp2; + Visit(Idx, *I1, Tmp2); // Evaluate the index. + ExplodedNodeSet Tmp3; + CheckerVisit(A, Tmp3, Tmp2, PreVisitStmtCallback); + + for (ExplodedNodeSet::iterator I2=Tmp3.begin(),E2=Tmp3.end();I2!=E2; ++I2) { + const GRState* state = GetState(*I2); + SVal V = state->getLValue(A->getType(), state->getSVal(Idx), + state->getSVal(Base)); + assert(A->isLValue()); + MakeNode(Dst, A, *I2, state->BindExpr(A, V), ProgramPoint::PostLValueKind); + } + } +} + +/// VisitMemberExpr - Transfer function for member expressions. +void ExprEngine::VisitMemberExpr(const MemberExpr* M, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + Expr *baseExpr = M->getBase()->IgnoreParens(); + ExplodedNodeSet dstBase; + Visit(baseExpr, Pred, dstBase); + + FieldDecl *field = dyn_cast(M->getMemberDecl()); + if (!field) // FIXME: skipping member expressions for non-fields + return; + + for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end(); + I != E; ++I) { + const GRState* state = GetState(*I); + SVal baseExprVal = state->getSVal(baseExpr); + if (isa(baseExprVal) || + isa(baseExprVal)) { + MakeNode(Dst, M, *I, state->BindExpr(M, UnknownVal())); + continue; + } + + // FIXME: Should we insert some assumption logic in here to determine + // if "Base" is a valid piece of memory? Before we put this assumption + // later when using FieldOffset lvals (which we no longer have). + + // For all other cases, compute an lvalue. + SVal L = state->getLValue(field, baseExprVal); + if (M->isLValue()) + MakeNode(Dst, M, *I, state->BindExpr(M, L), ProgramPoint::PostLValueKind); + else + evalLoad(Dst, M, *I, state, L); + } +} + +/// 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, const GRState* state, + SVal location, SVal Val, bool atDeclInit) { + + + // Do a previsit of the bind. + ExplodedNodeSet CheckedSet, Src; + Src.Add(Pred); + CheckerVisitBind(StoreE, CheckedSet, Src, location, Val, true); + + for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); + I!=E; ++I) { + + if (Pred != *I) + state = GetState(*I); + + const GRState* newState = 0; + + if (atDeclInit) { + const VarRegion *VR = + cast(cast(location).getRegion()); + + newState = state->bindDecl(VR, Val); + } + else { + if (location.isUnknown()) { + // We know that the new state will be the same as the old state since + // the location of the binding is "unknown". Consequently, there + // is no reason to just create a new node. + newState = state; + } + else { + // We are binding to a value other than 'unknown'. Perform the binding + // using the StoreManager. + newState = state->bindLoc(cast(location), Val); + } + } + + // The next thing to do is check if the TransferFuncs object wants to + // update the state based on the new binding. If the GRTransferFunc object + // doesn't do anything, just auto-propagate the current state. + + // NOTE: We use 'AssignE' for the location of the PostStore if 'AssignE' + // is non-NULL. Checkers typically care about + + StmtNodeBuilderRef BuilderRef(Dst, *Builder, *this, *I, newState, StoreE, + true); + + getTF().evalBind(BuilderRef, location, Val); + } +} + +/// 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 LocatioinE 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, + const GRState* state, SVal location, SVal Val, + const void *tag) { + + assert(Builder && "StmtNodeBuilder must be defined."); + + // Evaluate the location (checks for bad dereferences). + ExplodedNodeSet Tmp; + evalLocation(Tmp, LocationE, Pred, state, location, tag, false); + + if (Tmp.empty()) + return; + + assert(!location.isUndef()); + + SaveAndRestore OldSPointKind(Builder->PointKind, + ProgramPoint::PostStoreKind); + SaveAndRestore OldTag(Builder->Tag, 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; + + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) + evalBind(Dst, StoreE, *NI, GetState(*NI), location, Val); +} + +void ExprEngine::evalLoad(ExplodedNodeSet& Dst, const Expr *Ex, + ExplodedNode* Pred, + const GRState* state, SVal location, + const void *tag, QualType LoadTy) { + assert(!isa(location) && "location cannot be a NonLoc."); + + // Are we loading from a region? This actually results in two loads; one + // to fetch the address of the referenced value and one to fetch the + // referenced value. + if (const TypedRegion *TR = + dyn_cast_or_null(location.getAsRegion())) { + + QualType ValTy = TR->getValueType(); + if (const ReferenceType *RT = ValTy->getAs()) { + static int loadReferenceTag = 0; + ExplodedNodeSet Tmp; + evalLoadCommon(Tmp, Ex, Pred, state, location, &loadReferenceTag, + getContext().getPointerType(RT->getPointeeType())); + + // Perform the load from the referenced value. + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end() ; I!=E; ++I) { + state = GetState(*I); + location = state->getSVal(Ex); + evalLoadCommon(Dst, Ex, *I, state, location, tag, LoadTy); + } + return; + } + } + + evalLoadCommon(Dst, Ex, Pred, state, location, tag, LoadTy); +} + +void ExprEngine::evalLoadCommon(ExplodedNodeSet& Dst, const Expr *Ex, + ExplodedNode* Pred, + const GRState* state, SVal location, + const void *tag, QualType LoadTy) { + + // Evaluate the location (checks for bad dereferences). + ExplodedNodeSet Tmp; + evalLocation(Tmp, Ex, Pred, state, location, tag, true); + + if (Tmp.empty()) + return; + + assert(!location.isUndef()); + + SaveAndRestore OldSPointKind(Builder->PointKind); + SaveAndRestore OldTag(Builder->Tag); + + // Proceed with the load. + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { + state = GetState(*NI); + + if (location.isUnknown()) { + // This is important. We must nuke the old binding. + MakeNode(Dst, Ex, *NI, state->BindExpr(Ex, UnknownVal()), + ProgramPoint::PostLoadKind, tag); + } + else { + if (LoadTy.isNull()) + LoadTy = Ex->getType(); + SVal V = state->getSVal(cast(location), LoadTy); + MakeNode(Dst, Ex, *NI, state->bindExprAndLocation(Ex, location, V), + ProgramPoint::PostLoadKind, tag); + } + } +} + +void ExprEngine::evalLocation(ExplodedNodeSet &Dst, const Stmt *S, + ExplodedNode* Pred, + const GRState* state, SVal location, + const void *tag, bool isLoad) { + // Early checks for performance reason. + if (location.isUnknown() || Checkers.empty()) { + Dst.Add(Pred); + return; + } + + ExplodedNodeSet Src, Tmp; + Src.Add(Pred); + ExplodedNodeSet *PrevSet = &Src; + + for (CheckersOrdered::iterator I=Checkers.begin(),E=Checkers.end(); I!=E; ++I) + { + ExplodedNodeSet *CurrSet = 0; + if (I+1 == E) + CurrSet = &Dst; + else { + CurrSet = (PrevSet == &Tmp) ? &Src : &Tmp; + CurrSet->clear(); + } + + void *tag = I->first; + Checker *checker = I->second; + + for (ExplodedNodeSet::iterator NI = PrevSet->begin(), NE = PrevSet->end(); + NI != NE; ++NI) { + // Use the 'state' argument only when the predecessor node is the + // same as Pred. This allows us to catch updates to the state. + checker->GR_visitLocation(*CurrSet, *Builder, *this, S, *NI, + *NI == Pred ? state : GetState(*NI), + location, tag, isLoad); + } + + // Update which NodeSet is the current one. + PrevSet = CurrSet; + } +} + +bool ExprEngine::InlineCall(ExplodedNodeSet &Dst, const CallExpr *CE, + ExplodedNode *Pred) { + const GRState *state = GetState(Pred); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + // Check if the function definition is in the same translation unit. + if (FD->hasBody(FD)) { + const StackFrameContext *stackFrame = + AMgr.getStackFrame(AMgr.getAnalysisContext(FD), + Pred->getLocationContext(), + CE, Builder->getBlock(), Builder->getIndex()); + // Now we have the definition of the callee, create a CallEnter node. + CallEnter Loc(CE, stackFrame, Pred->getLocationContext()); + + ExplodedNode *N = Builder->generateNode(Loc, state, Pred); + Dst.Add(N); + return true; + } + + // Check if we can find the function definition in other translation units. + if (AMgr.hasIndexer()) { + AnalysisContext *C = AMgr.getAnalysisContextInAnotherTU(FD); + if (C == 0) + return false; + const StackFrameContext *stackFrame = + AMgr.getStackFrame(C, Pred->getLocationContext(), + CE, Builder->getBlock(), Builder->getIndex()); + CallEnter Loc(CE, stackFrame, Pred->getLocationContext()); + ExplodedNode *N = Builder->generateNode(Loc, state, Pred); + Dst.Add(N); + return true; + } + + return false; +} + +void ExprEngine::VisitCall(const CallExpr* CE, ExplodedNode* Pred, + CallExpr::const_arg_iterator AI, + CallExpr::const_arg_iterator AE, + ExplodedNodeSet& Dst) { + + // Determine the type of function we're calling (if available). + const FunctionProtoType *Proto = NULL; + QualType FnType = CE->getCallee()->IgnoreParens()->getType(); + if (const PointerType *FnTypePtr = FnType->getAs()) + Proto = FnTypePtr->getPointeeType()->getAs(); + + // Evaluate the arguments. + ExplodedNodeSet ArgsEvaluated; + evalArguments(CE->arg_begin(), CE->arg_end(), Proto, Pred, ArgsEvaluated); + + // Now process the call itself. + ExplodedNodeSet DstTmp; + const Expr* Callee = CE->getCallee()->IgnoreParens(); + + for (ExplodedNodeSet::iterator NI=ArgsEvaluated.begin(), + NE=ArgsEvaluated.end(); NI != NE; ++NI) { + // Evaluate the callee. + ExplodedNodeSet DstTmp2; + Visit(Callee, *NI, DstTmp2); + // Perform the previsit of the CallExpr, storing the results in DstTmp. + CheckerVisit(CE, DstTmp, DstTmp2, PreVisitStmtCallback); + } + + // Finally, evaluate the function call. We try each of the checkers + // to see if the can evaluate the function call. + ExplodedNodeSet DstTmp3; + + for (ExplodedNodeSet::iterator DI = DstTmp.begin(), DE = DstTmp.end(); + DI != DE; ++DI) { + + const GRState* state = GetState(*DI); + SVal L = state->getSVal(Callee); + + // FIXME: Add support for symbolic function calls (calls involving + // function pointer values that are symbolic). + SaveAndRestore OldSink(Builder->BuildSinks); + ExplodedNodeSet DstChecker; + + // If the callee is processed by a checker, skip the rest logic. + if (CheckerEvalCall(CE, DstChecker, *DI)) + DstTmp3.insert(DstChecker); + else if (AMgr.shouldInlineCall() && InlineCall(Dst, CE, *DI)) { + // Callee is inlined. We shouldn't do post call checking. + return; + } + else { + for (ExplodedNodeSet::iterator DI_Checker = DstChecker.begin(), + DE_Checker = DstChecker.end(); + DI_Checker != DE_Checker; ++DI_Checker) { + + // Dispatch to the plug-in transfer function. + unsigned oldSize = DstTmp3.size(); + SaveOr OldHasGen(Builder->HasGeneratedNode); + Pred = *DI_Checker; + + // Dispatch to transfer function logic to handle the call itself. + // FIXME: Allow us to chain together transfer functions. + assert(Builder && "StmtNodeBuilder must be defined."); + getTF().evalCall(DstTmp3, *this, *Builder, CE, L, Pred); + + // Handle the case where no nodes where generated. Auto-generate that + // contains the updated state if we aren't generating sinks. + if (!Builder->BuildSinks && DstTmp3.size() == oldSize && + !Builder->HasGeneratedNode) + MakeNode(DstTmp3, CE, Pred, state); + } + } + } + + // Finally, perform the post-condition check of the CallExpr and store + // the created nodes in 'Dst'. + CheckerVisit(CE, Dst, DstTmp3, PostVisitStmtCallback); +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C ivar references. +//===----------------------------------------------------------------------===// + +static std::pair EagerlyAssumeTag + = std::pair(&EagerlyAssumeTag,static_cast(0)); + +void ExprEngine::evalEagerlyAssume(ExplodedNodeSet &Dst, ExplodedNodeSet &Src, + const Expr *Ex) { + for (ExplodedNodeSet::iterator I=Src.begin(), E=Src.end(); I!=E; ++I) { + ExplodedNode *Pred = *I; + + // 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 (!isa(P) || cast(P).getStmt() != Ex) { + Dst.Add(Pred); + continue; + } + + const GRState* state = GetState(Pred); + SVal V = state->getSVal(Ex); + if (nonloc::SymExprVal *SEV = dyn_cast(&V)) { + // First assume that the condition is true. + if (const GRState *stateTrue = state->assume(*SEV, true)) { + stateTrue = stateTrue->BindExpr(Ex, + svalBuilder.makeIntVal(1U, Ex->getType())); + Dst.Add(Builder->generateNode(PostStmtCustom(Ex, + &EagerlyAssumeTag, Pred->getLocationContext()), + stateTrue, Pred)); + } + + // Next, assume that the condition is false. + if (const GRState *stateFalse = state->assume(*SEV, false)) { + stateFalse = stateFalse->BindExpr(Ex, + svalBuilder.makeIntVal(0U, Ex->getType())); + Dst.Add(Builder->generateNode(PostStmtCustom(Ex, &EagerlyAssumeTag, + Pred->getLocationContext()), + stateFalse, Pred)); + } + } + else + Dst.Add(Pred); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C @synchronized. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S, + ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + + // The mutex expression is a CFGElement, so we don't need to explicitly + // visit it since it will already be processed. + + // Pre-visit the ObjCAtSynchronizedStmt. + ExplodedNodeSet Tmp; + Tmp.Add(Pred); + CheckerVisit(S, Dst, Tmp, PreVisitStmtCallback); +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C ivar references. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr* Ex, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + // Visit the base expression, which is needed for computing the lvalue + // of the ivar. + ExplodedNodeSet dstBase; + const Expr *baseExpr = Ex->getBase(); + Visit(baseExpr, Pred, dstBase); + + // Using the base, compute the lvalue of the instance variable. + for (ExplodedNodeSet::iterator I = dstBase.begin(), E = dstBase.end(); + I!=E; ++I) { + ExplodedNode *nodeBase = *I; + const GRState *state = GetState(nodeBase); + SVal baseVal = state->getSVal(baseExpr); + SVal location = state->getLValue(Ex->getDecl(), baseVal); + MakeNode(Dst, Ex, *I, state->BindExpr(Ex, location)); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C fast enumeration 'for' statements. +//===----------------------------------------------------------------------===// + +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(); + SVal ElementV; + + if (const DeclStmt* DS = dyn_cast(elem)) { + const VarDecl* ElemD = cast(DS->getSingleDecl()); + assert (ElemD->getInit() == 0); + ElementV = GetState(Pred)->getLValue(ElemD, Pred->getLocationContext()); + VisitObjCForCollectionStmtAux(S, Pred, Dst, ElementV); + return; + } + + ExplodedNodeSet Tmp; + Visit(cast(elem), Pred, Tmp); + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + VisitObjCForCollectionStmtAux(S, *I, Dst, state->getSVal(elem)); + } +} + +void ExprEngine::VisitObjCForCollectionStmtAux(const ObjCForCollectionStmt* S, + ExplodedNode* Pred, ExplodedNodeSet& Dst, + SVal ElementV) { + + // Check if the location we are writing back to is a null pointer. + const Stmt* elem = S->getElement(); + ExplodedNodeSet Tmp; + evalLocation(Tmp, elem, Pred, GetState(Pred), ElementV, NULL, false); + + if (Tmp.empty()) + return; + + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) { + Pred = *NI; + const GRState *state = GetState(Pred); + + // Handle the case where the container still has elements. + SVal TrueV = svalBuilder.makeTruthVal(1); + const GRState *hasElems = state->BindExpr(S, TrueV); + + // Handle the case where the container has no elements. + SVal FalseV = svalBuilder.makeTruthVal(0); + const GRState *noElems = state->BindExpr(S, FalseV); + + if (loc::MemRegionVal* MV = dyn_cast(&ElementV)) + if (const TypedRegion* R = dyn_cast(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)); + unsigned Count = Builder->getCurrentBlockCount(); + SymbolRef Sym = SymMgr.getConjuredSymbol(elem, T, Count); + SVal V = svalBuilder.makeLoc(Sym); + hasElems = hasElems->bindLoc(ElementV, V); + + // Bind the location to 'nil' on the false branch. + SVal nilV = svalBuilder.makeIntVal(0, T); + noElems = noElems->bindLoc(ElementV, nilV); + } + + // Create the new nodes. + MakeNode(Dst, S, Pred, hasElems); + MakeNode(Dst, S, Pred, noElems); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function: Objective-C message expressions. +//===----------------------------------------------------------------------===// + +namespace { +class ObjCMsgWLItem { +public: + ObjCMessageExpr::const_arg_iterator I; + ExplodedNode *N; + + ObjCMsgWLItem(const ObjCMessageExpr::const_arg_iterator &i, ExplodedNode *n) + : I(i), N(n) {} +}; +} // end anonymous namespace + +void ExprEngine::VisitObjCMessageExpr(const ObjCMessageExpr* ME, + ExplodedNode* Pred, + ExplodedNodeSet& Dst){ + + // Create a worklist to process both the arguments. + llvm::SmallVector WL; + + // But first evaluate the receiver (if any). + ObjCMessageExpr::const_arg_iterator AI = ME->arg_begin(), AE = ME->arg_end(); + if (const Expr *Receiver = ME->getInstanceReceiver()) { + ExplodedNodeSet Tmp; + Visit(Receiver, Pred, Tmp); + + if (Tmp.empty()) + return; + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) + WL.push_back(ObjCMsgWLItem(AI, *I)); + } + else + WL.push_back(ObjCMsgWLItem(AI, Pred)); + + // Evaluate the arguments. + ExplodedNodeSet ArgsEvaluated; + while (!WL.empty()) { + ObjCMsgWLItem Item = WL.back(); + WL.pop_back(); + + if (Item.I == AE) { + ArgsEvaluated.insert(Item.N); + continue; + } + + // Evaluate the subexpression. + ExplodedNodeSet Tmp; + + // FIXME: [Objective-C++] handle arguments that are references + Visit(*Item.I, Item.N, Tmp); + + // Enqueue evaluating the next argument on the worklist. + ++(Item.I); + for (ExplodedNodeSet::iterator NI=Tmp.begin(), NE=Tmp.end(); NI!=NE; ++NI) + WL.push_back(ObjCMsgWLItem(Item.I, *NI)); + } + + // Now that the arguments are processed, handle the previsits checks. + ExplodedNodeSet DstPrevisit; + CheckerVisit(ME, DstPrevisit, ArgsEvaluated, PreVisitStmtCallback); + + // Proceed with evaluate the message expression. + ExplodedNodeSet dstEval; + + for (ExplodedNodeSet::iterator DI = DstPrevisit.begin(), + DE = DstPrevisit.end(); DI != DE; ++DI) { + + Pred = *DI; + bool RaisesException = false; + unsigned oldSize = dstEval.size(); + SaveAndRestore OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->HasGeneratedNode); + + if (const Expr *Receiver = ME->getInstanceReceiver()) { + const GRState *state = GetState(Pred); + + // Bifurcate the state into nil and non-nil ones. + DefinedOrUnknownSVal receiverVal = + cast(state->getSVal(Receiver)); + + const GRState *notNilState, *nilState; + llvm::tie(notNilState, nilState) = state->assume(receiverVal); + + // There are three cases: can be nil or non-nil, must be nil, must be + // non-nil. We handle must be nil, and merge the rest two into non-nil. + if (nilState && !notNilState) { + CheckerEvalNilReceiver(ME, dstEval, nilState, Pred); + continue; + } + + // Check if the "raise" message was sent. + assert(notNilState); + if (ME->getSelector() == RaiseSel) + RaisesException = true; + + // Check if we raise an exception. For now treat these as sinks. + // Eventually we will want to handle exceptions properly. + if (RaisesException) + Builder->BuildSinks = true; + + // Dispatch to plug-in transfer function. + evalObjCMessageExpr(dstEval, ME, Pred, notNilState); + } + else if (ObjCInterfaceDecl *Iface = ME->getReceiverInterface()) { + IdentifierInfo* ClsName = Iface->getIdentifier(); + Selector S = ME->getSelector(); + + // Check for special instance methods. + if (!NSExceptionII) { + ASTContext& Ctx = getContext(); + NSExceptionII = &Ctx.Idents.get("NSException"); + } + + if (ClsName == NSExceptionII) { + enum { NUM_RAISE_SELECTORS = 2 }; + + // Lazily create a cache of the selectors. + if (!NSExceptionInstanceRaiseSelectors) { + ASTContext& Ctx = getContext(); + NSExceptionInstanceRaiseSelectors = + new Selector[NUM_RAISE_SELECTORS]; + llvm::SmallVector II; + unsigned idx = 0; + + // raise:format: + II.push_back(&Ctx.Idents.get("raise")); + II.push_back(&Ctx.Idents.get("format")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); + + // raise:format::arguments: + II.push_back(&Ctx.Idents.get("arguments")); + NSExceptionInstanceRaiseSelectors[idx++] = + Ctx.Selectors.getSelector(II.size(), &II[0]); + } + + for (unsigned i = 0; i < NUM_RAISE_SELECTORS; ++i) + if (S == NSExceptionInstanceRaiseSelectors[i]) { + RaisesException = true; + break; + } + } + + // Check if we raise an exception. For now treat these as sinks. + // Eventually we will want to handle exceptions properly. + if (RaisesException) + Builder->BuildSinks = true; + + // Dispatch to plug-in transfer function. + evalObjCMessageExpr(dstEval, ME, Pred, Builder->GetState(Pred)); + } + + // Handle the case where no nodes where generated. Auto-generate that + // contains the updated state if we aren't generating sinks. + if (!Builder->BuildSinks && dstEval.size() == oldSize && + !Builder->HasGeneratedNode) + MakeNode(dstEval, ME, Pred, GetState(Pred)); + } + + // Finally, perform the post-condition check of the ObjCMessageExpr and store + // the created nodes in 'Dst'. + CheckerVisit(ME, Dst, dstEval, PostVisitStmtCallback); +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Miscellaneous statements. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex, + ExplodedNode *Pred, ExplodedNodeSet &Dst) { + + ExplodedNodeSet S1; + Visit(Ex, Pred, S1); + ExplodedNodeSet S2; + CheckerVisit(CastE, S2, S1, PreVisitStmtCallback); + + if (CastE->getCastKind() == CK_LValueToRValue) { + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I!=E; ++I) { + ExplodedNode *subExprNode = *I; + const GRState *state = GetState(subExprNode); + evalLoad(Dst, CastE, subExprNode, state, state->getSVal(Ex)); + } + return; + } + + // All other casts. + QualType T = CastE->getType(); + QualType ExTy = Ex->getType(); + + if (const ExplicitCastExpr *ExCast=dyn_cast_or_null(CastE)) + T = ExCast->getTypeAsWritten(); + +#if 0 + // If we are evaluating the cast in an lvalue context, we implicitly want + // the cast to evaluate to a location. + if (asLValue) { + ASTContext &Ctx = getContext(); + T = Ctx.getPointerType(Ctx.getCanonicalType(T)); + ExTy = Ctx.getPointerType(Ctx.getCanonicalType(ExTy)); + } +#endif + + switch (CastE->getCastKind()) { + case CK_ToVoid: + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) + Dst.Add(*I); + return; + + case CK_LValueToRValue: + case CK_NoOp: + case CK_FunctionToPointerDecay: + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { + // Copy the SVal of Ex to CastE. + ExplodedNode *N = *I; + const GRState *state = GetState(N); + SVal V = state->getSVal(Ex); + state = state->BindExpr(CastE, V); + MakeNode(Dst, CastE, N, state); + } + return; + + case CK_GetObjCProperty: + case CK_Dependent: + case CK_ArrayToPointerDecay: + case CK_BitCast: + case CK_LValueBitCast: + case CK_IntegralCast: + case CK_NullToPointer: + case CK_IntegralToPointer: + case CK_PointerToIntegral: + case CK_PointerToBoolean: + 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_AnyPointerToObjCPointerCast: + case CK_AnyPointerToBlockPointerCast: + + case CK_ObjCObjectLValueCast: { + // Delegate to SValBuilder to process. + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { + ExplodedNode* N = *I; + const GRState* state = GetState(N); + SVal V = state->getSVal(Ex); + V = svalBuilder.evalCast(V, T, ExTy); + state = state->BindExpr(CastE, V); + MakeNode(Dst, CastE, N, state); + } + return; + } + + case CK_DerivedToBase: + case CK_UncheckedDerivedToBase: + // For DerivedToBase cast, delegate to the store manager. + for (ExplodedNodeSet::iterator I = S2.begin(), E = S2.end(); I != E; ++I) { + ExplodedNode *node = *I; + const GRState *state = GetState(node); + SVal val = state->getSVal(Ex); + val = getStoreManager().evalDerivedToBase(val, T); + state = state->BindExpr(CastE, val); + MakeNode(Dst, CastE, node, state); + } + return; + + // Various C++ casts that are not handled yet. + case CK_Dynamic: + case CK_ToUnion: + case CK_BaseToDerived: + case CK_NullToMemberPointer: + case CK_BaseToDerivedMemberPointer: + case CK_DerivedToBaseMemberPointer: + case CK_UserDefinedConversion: + case CK_ConstructorConversion: + case CK_VectorSplat: + case CK_MemberPointerToBoolean: { + SaveAndRestore OldSink(Builder->BuildSinks); + Builder->BuildSinks = true; + MakeNode(Dst, CastE, Pred, GetState(Pred)); + return; + } + } +} + +void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr* CL, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + const InitListExpr* ILE + = cast(CL->getInitializer()->IgnoreParens()); + ExplodedNodeSet Tmp; + Visit(ILE, Pred, Tmp); + + for (ExplodedNodeSet::iterator I = Tmp.begin(), EI = Tmp.end(); I!=EI; ++I) { + const GRState* state = GetState(*I); + SVal ILV = state->getSVal(ILE); + const LocationContext *LC = (*I)->getLocationContext(); + state = state->bindCompoundLiteral(CL, LC, ILV); + + if (CL->isLValue()) { + MakeNode(Dst, CL, *I, state->BindExpr(CL, state->getLValue(CL, LC))); + } + else + MakeNode(Dst, CL, *I, state->BindExpr(CL, ILV)); + } +} + +void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred, + ExplodedNodeSet& Dst) { + + // The CFG has one DeclStmt per Decl. + const Decl* D = *DS->decl_begin(); + + if (!D || !isa(D)) + return; + + const VarDecl* VD = dyn_cast(D); + const Expr* InitEx = VD->getInit(); + + // FIXME: static variables may have an initializer, but the second + // time a function is called those values may not be current. + ExplodedNodeSet Tmp; + + if (InitEx) { + if (VD->getType()->isReferenceType() && !InitEx->isLValue()) { + // If the initializer is C++ record type, it should already has a + // temp object. + if (!InitEx->getType()->isRecordType()) + CreateCXXTemporaryObject(InitEx, Pred, Tmp); + else + Tmp.Add(Pred); + } else + Visit(InitEx, Pred, Tmp); + } else + Tmp.Add(Pred); + + ExplodedNodeSet Tmp2; + CheckerVisit(DS, Tmp2, Tmp, PreVisitStmtCallback); + + for (ExplodedNodeSet::iterator I=Tmp2.begin(), E=Tmp2.end(); I!=E; ++I) { + ExplodedNode *N = *I; + const GRState *state = GetState(N); + + // Decls without InitExpr are not initialized explicitly. + const LocationContext *LC = N->getLocationContext(); + + if (InitEx) { + SVal InitVal = state->getSVal(InitEx); + + // We bound the temp obj region to the CXXConstructExpr. Now recover + // the lazy compound value when the variable is not a reference. + if (AMgr.getLangOptions().CPlusPlus && VD->getType()->isRecordType() && + !VD->getType()->isReferenceType() && isa(InitVal)){ + InitVal = state->getSVal(cast(InitVal).getRegion()); + assert(isa(InitVal)); + } + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if ((InitVal.isUnknown() || + !getConstraintManager().canReasonAbout(InitVal)) && + !VD->getType()->isReferenceType()) { + InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, + Builder->getCurrentBlockCount()); + } + + evalBind(Dst, DS, *I, state, + loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); + } + else { + state = state->bindDeclWithNoInit(state->getRegion(VD, LC)); + MakeNode(Dst, DS, *I, state); + } + } +} + +void ExprEngine::VisitCondInit(const VarDecl *VD, const Stmt *S, + ExplodedNode *Pred, ExplodedNodeSet& Dst) { + + const Expr* InitEx = VD->getInit(); + ExplodedNodeSet Tmp; + Visit(InitEx, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + ExplodedNode *N = *I; + const GRState *state = GetState(N); + + const LocationContext *LC = N->getLocationContext(); + SVal InitVal = state->getSVal(InitEx); + + // Recover some path-sensitivity if a scalar value evaluated to + // UnknownVal. + if (InitVal.isUnknown() || + !getConstraintManager().canReasonAbout(InitVal)) { + InitVal = svalBuilder.getConjuredSymbolVal(NULL, InitEx, + Builder->getCurrentBlockCount()); + } + + evalBind(Dst, S, N, state, + loc::MemRegionVal(state->getRegion(VD, LC)), InitVal, true); + } +} + +namespace { + // This class is used by VisitInitListExpr as an item in a worklist + // for processing the values contained in an InitListExpr. +class InitListWLItem { +public: + llvm::ImmutableList Vals; + ExplodedNode* N; + InitListExpr::const_reverse_iterator Itr; + + InitListWLItem(ExplodedNode* n, llvm::ImmutableList vals, + InitListExpr::const_reverse_iterator itr) + : Vals(vals), N(n), Itr(itr) {} +}; +} + + +void ExprEngine::VisitInitListExpr(const InitListExpr* E, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + const GRState* state = GetState(Pred); + QualType T = getContext().getCanonicalType(E->getType()); + unsigned NumInitElements = E->getNumInits(); + + if (T->isArrayType() || T->isRecordType() || T->isVectorType()) { + llvm::ImmutableList StartVals = getBasicVals().getEmptySValList(); + + // Handle base case where the initializer has no elements. + // e.g: static int* myArray[] = {}; + if (NumInitElements == 0) { + SVal V = svalBuilder.makeCompoundVal(T, StartVals); + MakeNode(Dst, E, Pred, state->BindExpr(E, V)); + return; + } + + // Create a worklist to process the initializers. + llvm::SmallVector WorkList; + WorkList.reserve(NumInitElements); + WorkList.push_back(InitListWLItem(Pred, StartVals, E->rbegin())); + InitListExpr::const_reverse_iterator ItrEnd = E->rend(); + assert(!(E->rbegin() == E->rend())); + + // Process the worklist until it is empty. + while (!WorkList.empty()) { + InitListWLItem X = WorkList.back(); + WorkList.pop_back(); + + ExplodedNodeSet Tmp; + Visit(*X.Itr, X.N, Tmp); + + InitListExpr::const_reverse_iterator NewItr = X.Itr + 1; + + for (ExplodedNodeSet::iterator NI=Tmp.begin(),NE=Tmp.end();NI!=NE;++NI) { + // Get the last initializer value. + state = GetState(*NI); + SVal InitV = state->getSVal(cast(*X.Itr)); + + // Construct the new list of values by prepending the new value to + // the already constructed list. + llvm::ImmutableList NewVals = + getBasicVals().consVals(InitV, X.Vals); + + if (NewItr == ItrEnd) { + // Now we have a list holding all init values. Make CompoundValData. + SVal V = svalBuilder.makeCompoundVal(T, NewVals); + + // Make final state and node. + MakeNode(Dst, E, *NI, state->BindExpr(E, V)); + } + else { + // Still some initializer values to go. Push them onto the worklist. + WorkList.push_back(InitListWLItem(*NI, NewVals, NewItr)); + } + } + } + + return; + } + + if (Loc::IsLocType(T) || T->isIntegerType()) { + assert (E->getNumInits() == 1); + ExplodedNodeSet Tmp; + const Expr* Init = E->getInit(0); + Visit(Init, Pred, Tmp); + for (ExplodedNodeSet::iterator I=Tmp.begin(), EI=Tmp.end(); I != EI; ++I) { + state = GetState(*I); + MakeNode(Dst, E, *I, state->BindExpr(E, state->getSVal(Init))); + } + return; + } + + assert(0 && "unprocessed InitListExpr type"); +} + +/// VisitSizeOfAlignOfExpr - Transfer function for sizeof(type). +void ExprEngine::VisitSizeOfAlignOfExpr(const SizeOfAlignOfExpr* Ex, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + QualType T = Ex->getTypeOfArgument(); + CharUnits amt; + + if (Ex->isSizeOf()) { + if (T == getContext().VoidTy) { + // sizeof(void) == 1 byte. + amt = CharUnits::One(); + } + else if (!T->isConstantSizeType()) { + assert(T->isVariableArrayType() && "Unknown non-constant-sized type."); + + // FIXME: Add support for VLA type arguments, not just VLA expressions. + // When that happens, we should probably refactor VLASizeChecker's code. + if (Ex->isArgumentType()) { + Dst.Add(Pred); + return; + } + + // Get the size by getting the extent of the sub-expression. + // First, visit the sub-expression to find its region. + const Expr *Arg = Ex->getArgumentExpr(); + ExplodedNodeSet Tmp; + Visit(Arg, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + const MemRegion *MR = state->getSVal(Arg).getAsRegion(); + + // If the subexpression can't be resolved to a region, we don't know + // anything about its size. Just leave the state as is and continue. + if (!MR) { + Dst.Add(*I); + continue; + } + + // The result is the extent of the VLA. + SVal Extent = cast(MR)->getExtent(svalBuilder); + MakeNode(Dst, Ex, *I, state->BindExpr(Ex, Extent)); + } + + return; + } + else if (T->getAs()) { + // Some code tries to take the sizeof an ObjCObjectType, relying that + // the compiler has laid out its representation. Just report Unknown + // for these. + Dst.Add(Pred); + return; + } + else { + // All other cases. + amt = getContext().getTypeSizeInChars(T); + } + } + else // Get alignment of the type. + amt = getContext().getTypeAlignInChars(T); + + MakeNode(Dst, Ex, Pred, + GetState(Pred)->BindExpr(Ex, + svalBuilder.makeIntVal(amt.getQuantity(), Ex->getType()))); +} + +void ExprEngine::VisitOffsetOfExpr(const OffsetOfExpr* OOE, + ExplodedNode* Pred, ExplodedNodeSet& Dst) { + Expr::EvalResult Res; + if (OOE->Evaluate(Res, getContext()) && Res.Val.isInt()) { + const APSInt &IV = Res.Val.getInt(); + assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType())); + assert(OOE->getType()->isIntegerType()); + assert(IV.isSigned() == OOE->getType()->isSignedIntegerType()); + SVal X = svalBuilder.makeIntVal(IV); + MakeNode(Dst, OOE, Pred, GetState(Pred)->BindExpr(OOE, X)); + return; + } + // FIXME: Handle the case where __builtin_offsetof is not a constant. + Dst.Add(Pred); +} + +void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + + switch (U->getOpcode()) { + + default: + break; + + case UO_Real: { + const Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + Dst.Add(*I); + continue; + } + + // For all other types, UO_Real is an identity operation. + assert (U->getType() == Ex->getType()); + const GRState* state = GetState(*I); + MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); + } + + return; + } + + case UO_Imag: { + + const Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + // FIXME: We don't have complex SValues yet. + if (Ex->getType()->isAnyComplexType()) { + // Just report "Unknown." + Dst.Add(*I); + continue; + } + + // For all other types, UO_Imag returns 0. + const GRState* state = GetState(*I); + SVal X = svalBuilder.makeZeroVal(Ex->getType()); + MakeNode(Dst, U, *I, state->BindExpr(U, X)); + } + + return; + } + + case UO_Plus: + assert(!U->isLValue()); + // FALL-THROUGH. + case UO_Deref: + case UO_AddrOf: + case UO_Extension: { + + // 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(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + MakeNode(Dst, U, *I, state->BindExpr(U, state->getSVal(Ex))); + } + + return; + } + + case UO_LNot: + case UO_Minus: + case UO_Not: { + assert (!U->isLValue()); + const Expr* Ex = U->getSubExpr()->IgnoreParens(); + ExplodedNodeSet Tmp; + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end(); I!=E; ++I) { + const GRState* state = GetState(*I); + + // Get the value of the subexpression. + SVal V = state->getSVal(Ex); + + if (V.isUnknownOrUndef()) { + MakeNode(Dst, U, *I, state->BindExpr(U, V)); + continue; + } + +// QualType DstT = getContext().getCanonicalType(U->getType()); +// QualType SrcT = getContext().getCanonicalType(Ex->getType()); +// +// if (DstT != SrcT) // Perform promotions. +// V = evalCast(V, DstT); +// +// if (V.isUnknownOrUndef()) { +// MakeNode(Dst, U, *I, BindExpr(St, U, V)); +// continue; +// } + + switch (U->getOpcode()) { + default: + assert(false && "Invalid Opcode."); + break; + + case UO_Not: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, evalComplement(cast(V))); + break; + + case UO_Minus: + // FIXME: Do we need to handle promotions? + state = state->BindExpr(U, evalMinus(cast(V))); + 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 (isa(V)) { + Loc X = svalBuilder.makeNull(); + Result = evalBinOp(state, BO_EQ, cast(V), X, + U->getType()); + } + else { + nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType())); + Result = evalBinOp(state, BO_EQ, cast(V), X, + U->getType()); + } + + state = state->BindExpr(U, Result); + + break; + } + + MakeNode(Dst, U, *I, state); + } + + return; + } + } + + // Handle ++ and -- (both pre- and post-increment). + assert (U->isIncrementDecrementOp()); + ExplodedNodeSet Tmp; + const Expr* Ex = U->getSubExpr()->IgnoreParens(); + Visit(Ex, Pred, Tmp); + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I!=E; ++I) { + + const GRState* state = GetState(*I); + SVal loc = state->getSVal(Ex); + + // Perform a load. + ExplodedNodeSet Tmp2; + evalLoad(Tmp2, Ex, *I, state, loc); + + for (ExplodedNodeSet::iterator I2=Tmp2.begin(), E2=Tmp2.end();I2!=E2;++I2) { + + state = GetState(*I2); + SVal V2_untested = state->getSVal(Ex); + + // Propagate unknown and undefined values. + if (V2_untested.isUnknownOrUndef()) { + MakeNode(Dst, U, *I2, state->BindExpr(U, V2_untested)); + continue; + } + DefinedSVal V2 = cast(V2_untested); + + // 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; + + if (U->getType()->isAnyPointerType()) + RHS = svalBuilder.makeIntValWithPtrWidth(1, false); + else + RHS = svalBuilder.makeIntVal(1, U->getType()); + + SVal Result = evalBinOp(state, Op, V2, RHS, U->getType()); + + // Conjure a new symbol if necessary to recover precision. + if (Result.isUnknown() || !getConstraintManager().canReasonAbout(Result)){ + DefinedOrUnknownSVal SymVal = + svalBuilder.getConjuredSymbolVal(NULL, Ex, + Builder->getCurrentBlockCount()); + 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->isPrefix() && U->isLValue()) + state = state->BindExpr(U, loc); + else + state = state->BindExpr(U, V2); + + // Perform the store. + evalStore(Dst, NULL, U, *I2, state, loc, Result); + } + } +} + +void ExprEngine::VisitAsmStmt(const AsmStmt* A, ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + VisitAsmStmtHelperOutputs(A, A->begin_outputs(), A->end_outputs(), Pred, Dst); +} + +void ExprEngine::VisitAsmStmtHelperOutputs(const AsmStmt* A, + AsmStmt::const_outputs_iterator I, + AsmStmt::const_outputs_iterator E, + ExplodedNode* Pred, ExplodedNodeSet& Dst) { + if (I == E) { + VisitAsmStmtHelperInputs(A, A->begin_inputs(), A->end_inputs(), Pred, Dst); + return; + } + + ExplodedNodeSet Tmp; + Visit(*I, Pred, Tmp); + ++I; + + for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end();NI != NE;++NI) + VisitAsmStmtHelperOutputs(A, I, E, *NI, Dst); +} + +void ExprEngine::VisitAsmStmtHelperInputs(const AsmStmt* A, + AsmStmt::const_inputs_iterator I, + AsmStmt::const_inputs_iterator E, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + if (I == E) { + + // 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. + + const GRState* state = GetState(Pred); + + for (AsmStmt::const_outputs_iterator OI = A->begin_outputs(), + OE = A->end_outputs(); OI != OE; ++OI) { + + SVal X = state->getSVal(*OI); + assert (!isa(X)); // Should be an Lval, or unknown, undef. + + if (isa(X)) + state = state->bindLoc(cast(X), UnknownVal()); + } + + MakeNode(Dst, A, Pred, state); + return; + } + + ExplodedNodeSet Tmp; + Visit(*I, Pred, Tmp); + + ++I; + + for (ExplodedNodeSet::iterator NI = Tmp.begin(), NE = Tmp.end(); NI!=NE; ++NI) + VisitAsmStmtHelperInputs(A, I, E, *NI, Dst); +} + +void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred, + ExplodedNodeSet &Dst) { + ExplodedNodeSet Src; + if (const Expr *RetE = RS->getRetValue()) { + // Record the returned expression in the state. It will be used in + // ProcessCallExit to bind the return value to the call expr. + { + static int Tag = 0; + SaveAndRestore OldTag(Builder->Tag, &Tag); + const GRState *state = GetState(Pred); + state = state->set(RetE); + Pred = Builder->generateNode(RetE, state, Pred); + } + // We may get a NULL Pred because we generated a cached node. + if (Pred) + Visit(RetE, Pred, Src); + } + else { + Src.Add(Pred); + } + + ExplodedNodeSet CheckedSet; + CheckerVisit(RS, CheckedSet, Src, PreVisitStmtCallback); + + for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end(); + I != E; ++I) { + + assert(Builder && "StmtNodeBuilder must be defined."); + + Pred = *I; + unsigned size = Dst.size(); + + SaveAndRestore OldSink(Builder->BuildSinks); + SaveOr OldHasGen(Builder->HasGeneratedNode); + + getTF().evalReturn(Dst, *this, *Builder, RS, Pred); + + // Handle the case where no nodes where generated. + if (!Builder->BuildSinks && Dst.size() == size && + !Builder->HasGeneratedNode) + MakeNode(Dst, RS, Pred, GetState(Pred)); + } +} + +//===----------------------------------------------------------------------===// +// Transfer functions: Binary operators. +//===----------------------------------------------------------------------===// + +void ExprEngine::VisitBinaryOperator(const BinaryOperator* B, + ExplodedNode* Pred, + ExplodedNodeSet& Dst) { + ExplodedNodeSet Tmp1; + Expr* LHS = B->getLHS()->IgnoreParens(); + Expr* RHS = B->getRHS()->IgnoreParens(); + + Visit(LHS, Pred, Tmp1); + ExplodedNodeSet Tmp3; + + for (ExplodedNodeSet::iterator I1=Tmp1.begin(), E1=Tmp1.end(); I1!=E1; ++I1) { + SVal LeftV = GetState(*I1)->getSVal(LHS); + ExplodedNodeSet Tmp2; + Visit(RHS, *I1, Tmp2); + + ExplodedNodeSet CheckedSet; + CheckerVisit(B, CheckedSet, Tmp2, PreVisitStmtCallback); + + // With both the LHS and RHS evaluated, process the operation itself. + + for (ExplodedNodeSet::iterator I2=CheckedSet.begin(), E2=CheckedSet.end(); + I2 != E2; ++I2) { + + const GRState *state = GetState(*I2); + SVal RightV = state->getSVal(RHS); + + BinaryOperator::Opcode Op = B->getOpcode(); + + if (Op == BO_Assign) { + // EXPERIMENTAL: "Conjured" symbols. + // FIXME: Handle structs. + QualType T = RHS->getType(); + + if (RightV.isUnknown() ||!getConstraintManager().canReasonAbout(RightV)) + { + unsigned Count = Builder->getCurrentBlockCount(); + RightV = svalBuilder.getConjuredSymbolVal(NULL, B->getRHS(), Count); + } + + SVal ExprVal = B->isLValue() ? LeftV : RightV; + + // Simulate the effects of a "store": bind the value of the RHS + // to the L-Value represented by the LHS. + evalStore(Tmp3, B, LHS, *I2, state->BindExpr(B, ExprVal), LeftV,RightV); + continue; + } + + if (!B->isAssignmentOp()) { + // 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()) { + MakeNode(Tmp3, B, *I2, state); + continue; + } + + state = state->BindExpr(B, Result); + + MakeNode(Tmp3, B, *I2, state); + continue; + } + + assert (B->isCompoundAssignmentOp()); + + switch (Op) { + default: + assert(0 && "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 Tmp4; + SVal location = state->getSVal(LHS); + evalLoad(Tmp4, LHS, *I2, state, location); + + for (ExplodedNodeSet::iterator I4=Tmp4.begin(), E4=Tmp4.end(); I4!=E4; + ++I4) { + state = GetState(*I4); + SVal V = state->getSVal(LHS); + + // Get the computation type. + QualType CTy = + cast(B)->getComputationResultType(); + CTy = getContext().getCanonicalType(CTy); + + QualType CLHSTy = + cast(B)->getComputationLHSType(); + CLHSTy = getContext().getCanonicalType(CLHSTy); + + QualType LTy = getContext().getCanonicalType(LHS->getType()); + QualType RTy = getContext().getCanonicalType(RHS->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() || + !getConstraintManager().canReasonAbout(Result)) { + + unsigned Count = Builder->getCurrentBlockCount(); + + // 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.getConjuredSymbolVal(NULL, B->getRHS(), LTy, Count); + + // 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); + } + + evalStore(Tmp3, B, LHS, *I4, state->BindExpr(B, Result), + location, LHSVal); + } + } + } + + CheckerVisit(B, Dst, Tmp3, PostVisitStmtCallback); +} + +//===----------------------------------------------------------------------===// +// Checker registration/lookup. +//===----------------------------------------------------------------------===// + +Checker *ExprEngine::lookupChecker(void *tag) const { + CheckerMap::const_iterator I = CheckerM.find(tag); + return (I == CheckerM.end()) ? NULL : Checkers[I->second].second; +} + +//===----------------------------------------------------------------------===// +// Visualization. +//===----------------------------------------------------------------------===// + +#ifndef NDEBUG +static ExprEngine* GraphPrintCheckerState; +static SourceManager* GraphPrintSourceManager; + +namespace llvm { +template<> +struct DOTGraphTraits : + public DefaultDOTGraphTraits { + + DOTGraphTraits (bool isSimple=false) : DefaultDOTGraphTraits(isSimple) {} + + // FIXME: Since we do not cache error nodes in ExprEngine now, this does not + // work. + static std::string getNodeAttributes(const ExplodedNode* N, void*) { + +#if 0 + // FIXME: Replace with a general scheme to tell if the node is + // an error node. + if (GraphPrintCheckerState->isImplicitNullDeref(N) || + GraphPrintCheckerState->isExplicitNullDeref(N) || + GraphPrintCheckerState->isUndefDeref(N) || + GraphPrintCheckerState->isUndefStore(N) || + GraphPrintCheckerState->isUndefControlFlow(N) || + GraphPrintCheckerState->isUndefResult(N) || + GraphPrintCheckerState->isBadCall(N) || + GraphPrintCheckerState->isUndefArg(N)) + return "color=\"red\",style=\"filled\""; + + if (GraphPrintCheckerState->isNoReturnCall(N)) + return "color=\"blue\",style=\"filled\""; +#endif + return ""; + } + + static std::string getNodeLabel(const ExplodedNode* N, void*){ + + std::string sbuf; + llvm::raw_string_ostream Out(sbuf); + + // Program Location. + ProgramPoint Loc = N->getLocation(); + + switch (Loc.getKind()) { + case ProgramPoint::BlockEntranceKind: + Out << "Block Entrance: B" + << cast(Loc).getBlock()->getBlockID(); + break; + + case ProgramPoint::BlockExitKind: + assert (false); + break; + + case ProgramPoint::CallEnterKind: + Out << "CallEnter"; + break; + + case ProgramPoint::CallExitKind: + Out << "CallExit"; + break; + + default: { + if (StmtPoint *L = dyn_cast(&Loc)) { + const Stmt* S = L->getStmt(); + SourceLocation SLoc = S->getLocStart(); + + Out << S->getStmtClassName() << ' ' << (void*) S << ' '; + LangOptions LO; // FIXME. + S->printPretty(Out, 0, PrintingPolicy(LO)); + + if (SLoc.isFileID()) { + Out << "\\lline=" + << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) + << " col=" + << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc) + << "\\l"; + } + + if (isa(Loc)) + Out << "\\lPreStmt\\l;"; + else if (isa(Loc)) + Out << "\\lPostLoad\\l;"; + else if (isa(Loc)) + Out << "\\lPostStore\\l"; + else if (isa(Loc)) + Out << "\\lPostLValue\\l"; + +#if 0 + // FIXME: Replace with a general scheme to determine + // the name of the check. + if (GraphPrintCheckerState->isImplicitNullDeref(N)) + Out << "\\|Implicit-Null Dereference.\\l"; + else if (GraphPrintCheckerState->isExplicitNullDeref(N)) + Out << "\\|Explicit-Null Dereference.\\l"; + else if (GraphPrintCheckerState->isUndefDeref(N)) + Out << "\\|Dereference of undefialied value.\\l"; + else if (GraphPrintCheckerState->isUndefStore(N)) + Out << "\\|Store to Undefined Loc."; + else if (GraphPrintCheckerState->isUndefResult(N)) + Out << "\\|Result of operation is undefined."; + else if (GraphPrintCheckerState->isNoReturnCall(N)) + Out << "\\|Call to function marked \"noreturn\"."; + else if (GraphPrintCheckerState->isBadCall(N)) + Out << "\\|Call to NULL/Undefined."; + else if (GraphPrintCheckerState->isUndefArg(N)) + Out << "\\|Argument in call is undefined"; +#endif + + break; + } + + const BlockEdge& E = cast(Loc); + Out << "Edge: (B" << E.getSrc()->getBlockID() << ", B" + << E.getDst()->getBlockID() << ')'; + + if (const Stmt* T = E.getSrc()->getTerminator()) { + + SourceLocation SLoc = T->getLocStart(); + + Out << "\\|Terminator: "; + LangOptions LO; // FIXME. + E.getSrc()->printTerminator(Out, LO); + + if (SLoc.isFileID()) { + Out << "\\lline=" + << GraphPrintSourceManager->getInstantiationLineNumber(SLoc) + << " col=" + << GraphPrintSourceManager->getInstantiationColumnNumber(SLoc); + } + + if (isa(T)) { + const Stmt* Label = E.getDst()->getLabel(); + + if (Label) { + if (const CaseStmt* C = dyn_cast(Label)) { + Out << "\\lcase "; + LangOptions LO; // FIXME. + C->getLHS()->printPretty(Out, 0, PrintingPolicy(LO)); + + if (const Stmt* RHS = C->getRHS()) { + Out << " .. "; + RHS->printPretty(Out, 0, PrintingPolicy(LO)); + } + + Out << ":"; + } + else { + assert (isa(Label)); + Out << "\\ldefault:"; + } + } + else + Out << "\\l(implicit) default:"; + } + else if (isa(T)) { + // FIXME + } + else { + Out << "\\lCondition: "; + if (*E.getSrc()->succ_begin() == E.getDst()) + Out << "true"; + else + Out << "false"; + } + + Out << "\\l"; + } + +#if 0 + // FIXME: Replace with a general scheme to determine + // the name of the check. + if (GraphPrintCheckerState->isUndefControlFlow(N)) { + Out << "\\|Control-flow based on\\lUndefined value.\\l"; + } +#endif + } + } + + const GRState *state = N->getState(); + Out << "\\|StateID: " << (void*) state + << " NodeID: " << (void*) N << "\\|"; + state->printDOT(Out, *N->getLocationContext()->getCFG()); + Out << "\\l"; + return Out.str(); + } +}; +} // end llvm namespace +#endif + +#ifndef NDEBUG +template +ExplodedNode* GetGraphNode(ITERATOR I) { return *I; } + +template <> ExplodedNode* +GetGraphNode::iterator> + (llvm::DenseMap::iterator I) { + return I->first; +} +#endif + +void ExprEngine::ViewGraph(bool trim) { +#ifndef NDEBUG + if (trim) { + std::vector Src; + + // Flush any outstanding reports to make sure we cover all the nodes. + // This does not cause them to get displayed. + for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) + const_cast(*I)->FlushReports(BR); + + // Iterate through the reports and get their nodes. + for (BugReporter::iterator I=BR.begin(), E=BR.end(); I!=E; ++I) { + for (BugType::const_iterator I2=(*I)->begin(), E2=(*I)->end(); + I2!=E2; ++I2) { + const BugReportEquivClass& EQ = *I2; + const BugReport &R = **EQ.begin(); + ExplodedNode *N = const_cast(R.getErrorNode()); + if (N) Src.push_back(N); + } + } + + ViewGraph(&Src[0], &Src[0]+Src.size()); + } + else { + GraphPrintCheckerState = this; + GraphPrintSourceManager = &getContext().getSourceManager(); + + llvm::ViewGraph(*G.roots_begin(), "ExprEngine"); + + GraphPrintCheckerState = NULL; + GraphPrintSourceManager = NULL; + } +#endif +} + +void ExprEngine::ViewGraph(ExplodedNode** Beg, ExplodedNode** End) { +#ifndef NDEBUG + GraphPrintCheckerState = this; + GraphPrintSourceManager = &getContext().getSourceManager(); + + std::auto_ptr TrimmedG(G.Trim(Beg, End).first); + + if (!TrimmedG.get()) + llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n"; + else + llvm::ViewGraph(*TrimmedG->roots_begin(), "TrimmedExprEngine"); + + GraphPrintCheckerState = NULL; + GraphPrintSourceManager = NULL; +#endif +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp new file mode 100644 index 0000000000..52a3aced2d --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp @@ -0,0 +1,46 @@ +//=-- ExprEngineExperimentalChecks.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 functions to instantiate and register experimental +// checks in ExprEngine. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "ExprEngineExperimentalChecks.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" + +using namespace clang; +using namespace ento; + +void ento::RegisterExperimentalChecks(ExprEngine &Eng) { + // These are checks that never belong as internal checks + // within ExprEngine. + RegisterCStringChecker(Eng); + RegisterChrootChecker(Eng); + RegisterMallocChecker(Eng); + RegisterPthreadLockChecker(Eng); + RegisterStreamChecker(Eng); + RegisterUnreachableCodeChecker(Eng); +} + +void ento::RegisterExperimentalInternalChecks(ExprEngine &Eng) { + // These are internal checks that should eventually migrate to + // RegisterInternalChecks() once they have been further tested. + + // Note that this must be registered after ReturnStackAddresEngsChecker. + RegisterReturnPointerRangeChecker(Eng); + + RegisterArrayBoundChecker(Eng); + RegisterCastSizeChecker(Eng); + RegisterCastToStructChecker(Eng); + RegisterFixedAddressChecker(Eng); + RegisterPointerArithChecker(Eng); + RegisterPointerSubChecker(Eng); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineExperimentalChecks.h b/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineExperimentalChecks.h new file mode 100644 index 0000000000..f8359d1c5c --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineExperimentalChecks.h @@ -0,0 +1,37 @@ +//=-- ExprEngineExperimentalChecks.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 functions to instantiate and register experimental +// checks in ExprEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_ExprEngine_EXPERIMENTAL_CHECKS +#define LLVM_CLANG_GR_ExprEngine_EXPERIMENTAL_CHECKS + +namespace clang { + +namespace ento { + +class ExprEngine; + +void RegisterAnalyzerStatsChecker(ExprEngine &Eng); +void RegisterChrootChecker(ExprEngine &Eng); +void RegisterCStringChecker(ExprEngine &Eng); +void RegisterIdempotentOperationChecker(ExprEngine &Eng); +void RegisterMallocChecker(ExprEngine &Eng); +void RegisterPthreadLockChecker(ExprEngine &Eng); +void RegisterStreamChecker(ExprEngine &Eng); +void RegisterUnreachableCodeChecker(ExprEngine &Eng); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineInternalChecks.h b/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineInternalChecks.h new file mode 100644 index 0000000000..f67371da0f --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineInternalChecks.h @@ -0,0 +1,59 @@ +//=-- ExprEngineInternalChecks.h- Builtin ExprEngine 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 functions to instantiate and register the "built-in" +// checks in ExprEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_ExprEngine_INTERNAL_CHECKS +#define LLVM_CLANG_GR_ExprEngine_INTERNAL_CHECKS + +namespace clang { + +namespace ento { + +class ExprEngine; + +// Foundational checks that handle basic semantics. +void RegisterAdjustedReturnValueChecker(ExprEngine &Eng); +void RegisterArrayBoundChecker(ExprEngine &Eng); +void RegisterArrayBoundCheckerV2(ExprEngine &Eng); +void RegisterAttrNonNullChecker(ExprEngine &Eng); +void RegisterBuiltinFunctionChecker(ExprEngine &Eng); +void RegisterCallAndMessageChecker(ExprEngine &Eng); +void RegisterCastToStructChecker(ExprEngine &Eng); +void RegisterCastSizeChecker(ExprEngine &Eng); +void RegisterDereferenceChecker(ExprEngine &Eng); +void RegisterDivZeroChecker(ExprEngine &Eng); +void RegisterFixedAddressChecker(ExprEngine &Eng); +void RegisterNoReturnFunctionChecker(ExprEngine &Eng); +void RegisterObjCAtSyncChecker(ExprEngine &Eng); +void RegisterPointerArithChecker(ExprEngine &Eng); +void RegisterPointerSubChecker(ExprEngine &Eng); +void RegisterReturnPointerRangeChecker(ExprEngine &Eng); +void RegisterReturnUndefChecker(ExprEngine &Eng); +void RegisterStackAddrLeakChecker(ExprEngine &Eng); +void RegisterUndefBranchChecker(ExprEngine &Eng); +void RegisterUndefCapturedBlockVarChecker(ExprEngine &Eng); +void RegisterUndefResultChecker(ExprEngine &Eng); +void RegisterUndefinedArraySubscriptChecker(ExprEngine &Eng); +void RegisterUndefinedAssignmentChecker(ExprEngine &Eng); +void RegisterVLASizeChecker(ExprEngine &Eng); + +// API checks. +void RegisterMacOSXAPIChecker(ExprEngine &Eng); +void RegisterOSAtomicChecker(ExprEngine &Eng); +void RegisterUnixAPIChecker(ExprEngine &Eng); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/FixedAddressChecker.cpp new file mode 100644 index 0000000000..77e9876d47 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/FixedAddressChecker.cpp @@ -0,0 +1,72 @@ +//=== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class FixedAddressChecker + : public CheckerVisitor { + BuiltinBug *BT; +public: + FixedAddressChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *FixedAddressChecker::getTag() { + static int x; + return &x; +} + +void FixedAddressChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + // 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; + + const GRState *state = C.getState(); + + SVal RV = state->getSVal(B->getRHS()); + + if (!RV.isConstant() || RV.isZeroConstant()) + return; + + if (ExplodedNode *N = C.generateNode()) { + if (!BT) + BT = new BuiltinBug("Use fixed address", + "Using a fixed address is not portable because that " + "address will probably not be valid in all " + "environments or platforms."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getRHS()->getSourceRange()); + C.EmitReport(R); + } +} + +void ento::RegisterFixedAddressChecker(ExprEngine &Eng) { + Eng.registerCheck(new FixedAddressChecker()); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/FrontendActions.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/FrontendActions.cpp new file mode 100644 index 0000000000..b0e8e5d9bb --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/FrontendActions.cpp @@ -0,0 +1,22 @@ +//===--- 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/FrontendActions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/StaticAnalyzer/AnalysisConsumer.h" +using namespace clang; +using namespace ento; + +ASTConsumer *AnalysisAction::CreateASTConsumer(CompilerInstance &CI, + llvm::StringRef InFile) { + return CreateAnalysisConsumer(CI.getPreprocessor(), + CI.getFrontendOpts().OutputFile, + CI.getAnalyzerOpts()); +} + diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/IdempotentOperationChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/IdempotentOperationChecker.cpp new file mode 100644 index 0000000000..435d3d4a48 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/IdempotentOperationChecker.cpp @@ -0,0 +1,834 @@ +//==- IdempotentOperationChecker.cpp - Idempotent Operations ----*- 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 path-sensitive checks for idempotent and/or +// tautological operations. Each potential operation is checked along all paths +// to see if every path results in a pointless operation. +// +-------------------------------------------+ +// |Table of idempotent/tautological operations| +// +-------------------------------------------+ +//+--------------------------------------------------------------------------+ +//|Operator | x op x | x op 1 | 1 op x | x op 0 | 0 op x | x op ~0 | ~0 op x | +//+--------------------------------------------------------------------------+ +// +, += | | | | x | x | | +// -, -= | | | | x | -x | | +// *, *= | | x | x | 0 | 0 | | +// /, /= | 1 | x | | N/A | 0 | | +// &, &= | x | | | 0 | 0 | x | x +// |, |= | x | | | x | x | ~0 | ~0 +// ^, ^= | 0 | | | x | x | | +// <<, <<= | | | | x | 0 | | +// >>, >>= | | | | x | 0 | | +// || | 1 | 1 | 1 | x | x | 1 | 1 +// && | 1 | x | x | 0 | 0 | x | x +// = | x | | | | | | +// == | 1 | | | | | | +// >= | 1 | | | | | | +// <= | 1 | | | | | | +// > | 0 | | | | | | +// < | 0 | | | | | | +// != | 0 | | | | | | +//===----------------------------------------------------------------------===// +// +// Things TODO: +// - Improved error messages +// - Handle mixed assumptions (which assumptions can belong together?) +// - Finer grained false positive control (levels) +// - Handling ~0 values + +#include "ExprEngineExperimentalChecks.h" +#include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" +#include "clang/AST/Stmt.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallSet.h" +#include "llvm/Support/ErrorHandling.h" +#include + +using namespace clang; +using namespace ento; + +namespace { +class IdempotentOperationChecker + : public CheckerVisitor { +public: + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); + void PostVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); + void VisitEndAnalysis(ExplodedGraph &G, BugReporter &B, ExprEngine &Eng); + +private: + // Our assumption about a particular operation. + enum Assumption { Possible = 0, Impossible, Equal, LHSis1, RHSis1, LHSis0, + RHSis0 }; + + void UpdateAssumption(Assumption &A, const Assumption &New); + + // False positive reduction methods + static bool isSelfAssign(const Expr *LHS, const Expr *RHS); + static bool isUnused(const Expr *E, AnalysisContext *AC); + static bool isTruncationExtensionAssignment(const Expr *LHS, + const Expr *RHS); + bool PathWasCompletelyAnalyzed(const CFG *C, + const CFGBlock *CB, + const CFGStmtMap *CBM, + const CoreEngine &CE); + static bool CanVary(const Expr *Ex, + AnalysisContext *AC); + static bool isConstantOrPseudoConstant(const DeclRefExpr *DR, + AnalysisContext *AC); + static bool containsNonLocalVarDecl(const Stmt *S); + const ExplodedNodeSet getLastRelevantNodes(const CFGBlock *Begin, + const ExplodedNode *N); + + // Hash table and related data structures + struct BinaryOperatorData { + BinaryOperatorData() : assumption(Possible), analysisContext(0) {} + + Assumption assumption; + AnalysisContext *analysisContext; + ExplodedNodeSet explodedNodes; // Set of ExplodedNodes that refer to a + // BinaryOperator + }; + typedef llvm::DenseMap + AssumptionMap; + AssumptionMap hash; + + // A class that performs reachability queries for CFGBlocks. Several internal + // checks in this checker require reachability information. The requests all + // tend to have a common destination, so we lazily do a predecessor search + // from the destination node and cache the results to prevent work + // duplication. + class CFGReachabilityAnalysis { + typedef llvm::SmallSet ReachableSet; + typedef llvm::DenseMap ReachableMap; + ReachableSet analyzed; + ReachableMap reachable; + public: + inline bool isReachable(const CFGBlock *Src, const CFGBlock *Dst); + private: + void MapReachability(const CFGBlock *Dst); + }; + CFGReachabilityAnalysis CRA; +}; +} + +void *IdempotentOperationChecker::getTag() { + static int x = 0; + return &x; +} + +void ento::RegisterIdempotentOperationChecker(ExprEngine &Eng) { + Eng.registerCheck(new IdempotentOperationChecker()); +} + +void IdempotentOperationChecker::PreVisitBinaryOperator( + CheckerContext &C, + const BinaryOperator *B) { + // Find or create an entry in the hash for this BinaryOperator instance. + // If we haven't done a lookup before, it will get default initialized to + // 'Possible'. At this stage we do not store the ExplodedNode, as it has not + // been created yet. + BinaryOperatorData &Data = hash[B]; + Assumption &A = Data.assumption; + AnalysisContext *AC = C.getCurrentAnalysisContext(); + Data.analysisContext = AC; + + // If we already have visited this node on a path that does not contain an + // idempotent operation, return immediately. + if (A == Impossible) + return; + + // Retrieve both sides of the operator and determine if they can vary (which + // may mean this is a false positive. + const Expr *LHS = B->getLHS(); + const Expr *RHS = B->getRHS(); + + // At this stage we can calculate whether each side contains a false positive + // that applies to all operators. We only need to calculate this the first + // time. + bool LHSContainsFalsePositive = false, RHSContainsFalsePositive = false; + if (A == Possible) { + // An expression contains a false positive if it can't vary, or if it + // contains a known false positive VarDecl. + LHSContainsFalsePositive = !CanVary(LHS, AC) + || containsNonLocalVarDecl(LHS); + RHSContainsFalsePositive = !CanVary(RHS, AC) + || containsNonLocalVarDecl(RHS); + } + + const GRState *state = C.getState(); + + SVal LHSVal = state->getSVal(LHS); + SVal RHSVal = state->getSVal(RHS); + + // If either value is unknown, we can't be 100% sure of all paths. + if (LHSVal.isUnknownOrUndef() || RHSVal.isUnknownOrUndef()) { + A = Impossible; + return; + } + BinaryOperator::Opcode Op = B->getOpcode(); + + // Dereference the LHS SVal if this is an assign operation + switch (Op) { + default: + break; + + // Fall through intentional + case BO_AddAssign: + case BO_SubAssign: + case BO_MulAssign: + case BO_DivAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + case BO_ShlAssign: + case BO_ShrAssign: + case BO_Assign: + // Assign statements have one extra level of indirection + if (!isa(LHSVal)) { + A = Impossible; + return; + } + LHSVal = state->getSVal(cast(LHSVal), LHS->getType()); + } + + + // We now check for various cases which result in an idempotent operation. + + // x op x + switch (Op) { + default: + break; // We don't care about any other operators. + + // Fall through intentional + case BO_Assign: + // x Assign x can be used to silence unused variable warnings intentionally. + // If this is a self assignment and the variable is referenced elsewhere, + // and the assignment is not a truncation or extension, then it is a false + // positive. + if (isSelfAssign(LHS, RHS)) { + if (!isUnused(LHS, AC) && !isTruncationExtensionAssignment(LHS, RHS)) { + UpdateAssumption(A, Equal); + return; + } + else { + A = Impossible; + return; + } + } + + case BO_SubAssign: + case BO_DivAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + case BO_Sub: + case BO_Div: + case BO_And: + case BO_Or: + case BO_Xor: + case BO_LOr: + case BO_LAnd: + case BO_EQ: + case BO_NE: + if (LHSVal != RHSVal || LHSContainsFalsePositive + || RHSContainsFalsePositive) + break; + UpdateAssumption(A, Equal); + return; + } + + // x op 1 + switch (Op) { + default: + break; // We don't care about any other operators. + + // Fall through intentional + case BO_MulAssign: + case BO_DivAssign: + case BO_Mul: + case BO_Div: + case BO_LOr: + case BO_LAnd: + if (!RHSVal.isConstant(1) || RHSContainsFalsePositive) + break; + UpdateAssumption(A, RHSis1); + return; + } + + // 1 op x + switch (Op) { + default: + break; // We don't care about any other operators. + + // Fall through intentional + case BO_MulAssign: + case BO_Mul: + case BO_LOr: + case BO_LAnd: + if (!LHSVal.isConstant(1) || LHSContainsFalsePositive) + break; + UpdateAssumption(A, LHSis1); + return; + } + + // x op 0 + switch (Op) { + default: + break; // We don't care about any other operators. + + // Fall through intentional + case BO_AddAssign: + case BO_SubAssign: + case BO_MulAssign: + case BO_AndAssign: + case BO_OrAssign: + case BO_XorAssign: + case BO_Add: + case BO_Sub: + case BO_Mul: + case BO_And: + case BO_Or: + case BO_Xor: + case BO_Shl: + case BO_Shr: + case BO_LOr: + case BO_LAnd: + if (!RHSVal.isConstant(0) || RHSContainsFalsePositive) + break; + UpdateAssumption(A, RHSis0); + return; + } + + // 0 op x + switch (Op) { + default: + break; // We don't care about any other operators. + + // Fall through intentional + //case BO_AddAssign: // Common false positive + case BO_SubAssign: // Check only if unsigned + case BO_MulAssign: + case BO_DivAssign: + case BO_AndAssign: + //case BO_OrAssign: // Common false positive + //case BO_XorAssign: // Common false positive + case BO_ShlAssign: + case BO_ShrAssign: + case BO_Add: + case BO_Sub: + case BO_Mul: + case BO_Div: + case BO_And: + case BO_Or: + case BO_Xor: + case BO_Shl: + case BO_Shr: + case BO_LOr: + case BO_LAnd: + if (!LHSVal.isConstant(0) || LHSContainsFalsePositive) + break; + UpdateAssumption(A, LHSis0); + return; + } + + // If we get to this point, there has been a valid use of this operation. + A = Impossible; +} + +// At the post visit stage, the predecessor ExplodedNode will be the +// BinaryOperator that was just created. We use this hook to collect the +// ExplodedNode. +void IdempotentOperationChecker::PostVisitBinaryOperator( + CheckerContext &C, + const BinaryOperator *B) { + // Add the ExplodedNode we just visited + BinaryOperatorData &Data = hash[B]; + assert(isa(cast(C.getPredecessor() + ->getLocation()).getStmt())); + Data.explodedNodes.Add(C.getPredecessor()); +} + +void IdempotentOperationChecker::VisitEndAnalysis(ExplodedGraph &G, + BugReporter &BR, + ExprEngine &Eng) { + BugType *BT = new BugType("Idempotent operation", "Dead code"); + // Iterate over the hash to see if we have any paths with definite + // idempotent operations. + for (AssumptionMap::const_iterator i = hash.begin(); i != hash.end(); ++i) { + // Unpack the hash contents + const BinaryOperatorData &Data = i->second; + const Assumption &A = Data.assumption; + AnalysisContext *AC = Data.analysisContext; + const ExplodedNodeSet &ES = Data.explodedNodes; + + const BinaryOperator *B = i->first; + + if (A == Impossible) + continue; + + // If the analyzer did not finish, check to see if we can still emit this + // warning + if (Eng.hasWorkRemaining()) { + const CFGStmtMap *CBM = CFGStmtMap::Build(AC->getCFG(), + &AC->getParentMap()); + + // If we can trace back + if (!PathWasCompletelyAnalyzed(AC->getCFG(), + CBM->getBlock(B), CBM, + Eng.getCoreEngine())) + continue; + + delete CBM; + } + + // Select the error message and SourceRanges to report. + llvm::SmallString<128> buf; + llvm::raw_svector_ostream os(buf); + bool LHSRelevant = false, RHSRelevant = false; + switch (A) { + case Equal: + LHSRelevant = true; + RHSRelevant = true; + if (B->getOpcode() == BO_Assign) + os << "Assigned value is always the same as the existing value"; + else + os << "Both operands to '" << B->getOpcodeStr() + << "' always have the same value"; + break; + case LHSis1: + LHSRelevant = true; + os << "The left operand to '" << B->getOpcodeStr() << "' is always 1"; + break; + case RHSis1: + RHSRelevant = true; + os << "The right operand to '" << B->getOpcodeStr() << "' is always 1"; + break; + case LHSis0: + LHSRelevant = true; + os << "The left operand to '" << B->getOpcodeStr() << "' is always 0"; + break; + case RHSis0: + RHSRelevant = true; + os << "The right operand to '" << B->getOpcodeStr() << "' is always 0"; + break; + case Possible: + llvm_unreachable("Operation was never marked with an assumption"); + case Impossible: + llvm_unreachable(0); + } + + // Add a report for each ExplodedNode + for (ExplodedNodeSet::iterator I = ES.begin(), E = ES.end(); I != E; ++I) { + EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), *I); + + // Add source ranges and visitor hooks + if (LHSRelevant) { + const Expr *LHS = i->first->getLHS(); + report->addRange(LHS->getSourceRange()); + report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, LHS); + } + if (RHSRelevant) { + const Expr *RHS = i->first->getRHS(); + report->addRange(i->first->getRHS()->getSourceRange()); + report->addVisitorCreator(bugreporter::registerVarDeclsLastStore, RHS); + } + + BR.EmitReport(report); + } + } +} + +// Updates the current assumption given the new assumption +inline void IdempotentOperationChecker::UpdateAssumption(Assumption &A, + const Assumption &New) { +// If the assumption is the same, there is nothing to do + if (A == New) + return; + + switch (A) { + // If we don't currently have an assumption, set it + case Possible: + A = New; + return; + + // If we have determined that a valid state happened, ignore the new + // assumption. + case Impossible: + return; + + // Any other case means that we had a different assumption last time. We don't + // currently support mixing assumptions for diagnostic reasons, so we set + // our assumption to be impossible. + default: + A = Impossible; + return; + } +} + +// Check for a statement where a variable is self assigned to possibly avoid an +// unused variable warning. +bool IdempotentOperationChecker::isSelfAssign(const Expr *LHS, const Expr *RHS) { + LHS = LHS->IgnoreParenCasts(); + RHS = RHS->IgnoreParenCasts(); + + const DeclRefExpr *LHS_DR = dyn_cast(LHS); + if (!LHS_DR) + return false; + + const VarDecl *VD = dyn_cast(LHS_DR->getDecl()); + if (!VD) + return false; + + const DeclRefExpr *RHS_DR = dyn_cast(RHS); + if (!RHS_DR) + return false; + + if (VD != RHS_DR->getDecl()) + return false; + + return true; +} + +// Returns true if the Expr points to a VarDecl that is not read anywhere +// outside of self-assignments. +bool IdempotentOperationChecker::isUnused(const Expr *E, + AnalysisContext *AC) { + if (!E) + return false; + + const DeclRefExpr *DR = dyn_cast(E->IgnoreParenCasts()); + if (!DR) + return false; + + const VarDecl *VD = dyn_cast(DR->getDecl()); + if (!VD) + return false; + + if (AC->getPseudoConstantAnalysis()->wasReferenced(VD)) + return false; + + return true; +} + +// Check for self casts truncating/extending a variable +bool IdempotentOperationChecker::isTruncationExtensionAssignment( + const Expr *LHS, + const Expr *RHS) { + + const DeclRefExpr *LHS_DR = dyn_cast(LHS->IgnoreParenCasts()); + if (!LHS_DR) + return false; + + const VarDecl *VD = dyn_cast(LHS_DR->getDecl()); + if (!VD) + return false; + + const DeclRefExpr *RHS_DR = dyn_cast(RHS->IgnoreParenCasts()); + if (!RHS_DR) + return false; + + if (VD != RHS_DR->getDecl()) + return false; + + return dyn_cast(RHS->IgnoreParenLValueCasts()) == NULL; +} + +// Returns false if a path to this block was not completely analyzed, or true +// otherwise. +bool IdempotentOperationChecker::PathWasCompletelyAnalyzed( + const CFG *C, + const CFGBlock *CB, + const CFGStmtMap *CBM, + const CoreEngine &CE) { + // Test for reachability from any aborted blocks to this block + typedef CoreEngine::BlocksAborted::const_iterator AbortedIterator; + for (AbortedIterator I = CE.blocks_aborted_begin(), + E = CE.blocks_aborted_end(); I != E; ++I) { + const BlockEdge &BE = I->first; + + // The destination block on the BlockEdge is the first block that was not + // analyzed. If we can reach this block from the aborted block, then this + // block was not completely analyzed. + if (CRA.isReachable(BE.getDst(), CB)) + return false; + } + + // For the items still on the worklist, see if they are in blocks that + // can eventually reach 'CB'. + class VisitWL : public WorkList::Visitor { + const CFGStmtMap *CBM; + const CFGBlock *TargetBlock; + CFGReachabilityAnalysis &CRA; + public: + VisitWL(const CFGStmtMap *cbm, const CFGBlock *targetBlock, + CFGReachabilityAnalysis &cra) + : CBM(cbm), TargetBlock(targetBlock), CRA(cra) {} + virtual bool Visit(const WorkListUnit &U) { + ProgramPoint P = U.getNode()->getLocation(); + const CFGBlock *B = 0; + if (StmtPoint *SP = dyn_cast(&P)) { + B = CBM->getBlock(SP->getStmt()); + } + else if (BlockEdge *BE = dyn_cast(&P)) { + B = BE->getDst(); + } + else if (BlockEntrance *BEnt = dyn_cast(&P)) { + B = BEnt->getBlock(); + } + else if (BlockExit *BExit = dyn_cast(&P)) { + B = BExit->getBlock(); + } + if (!B) + return true; + + return CRA.isReachable(B, TargetBlock); + } + }; + VisitWL visitWL(CBM, CB, CRA); + // Were there any items in the worklist that could potentially reach + // this block? + if (CE.getWorkList()->VisitItemsInWorkList(visitWL)) + return false; + + // Verify that this block is reachable from the entry block + if (!CRA.isReachable(&C->getEntry(), CB)) + return false; + + // If we get to this point, there is no connection to the entry block or an + // aborted block. This path is unreachable and we can report the error. + return true; +} + +// Recursive function that determines whether an expression contains any element +// that varies. This could be due to a compile-time constant like sizeof. An +// expression may also involve a variable that behaves like a constant. The +// function returns true if the expression varies, and false otherwise. +bool IdempotentOperationChecker::CanVary(const Expr *Ex, + AnalysisContext *AC) { + // Parentheses and casts are irrelevant here + Ex = Ex->IgnoreParenCasts(); + + if (Ex->getLocStart().isMacroID()) + return false; + + switch (Ex->getStmtClass()) { + // Trivially true cases + case Stmt::ArraySubscriptExprClass: + case Stmt::MemberExprClass: + case Stmt::StmtExprClass: + case Stmt::CallExprClass: + case Stmt::VAArgExprClass: + case Stmt::ShuffleVectorExprClass: + return true; + default: + return true; + + // Trivially false cases + case Stmt::IntegerLiteralClass: + case Stmt::CharacterLiteralClass: + case Stmt::FloatingLiteralClass: + case Stmt::PredefinedExprClass: + case Stmt::ImaginaryLiteralClass: + case Stmt::StringLiteralClass: + case Stmt::OffsetOfExprClass: + case Stmt::CompoundLiteralExprClass: + case Stmt::AddrLabelExprClass: + case Stmt::BinaryTypeTraitExprClass: + case Stmt::GNUNullExprClass: + case Stmt::InitListExprClass: + case Stmt::DesignatedInitExprClass: + case Stmt::BlockExprClass: + case Stmt::BlockDeclRefExprClass: + return false; + + // Cases requiring custom logic + case Stmt::SizeOfAlignOfExprClass: { + const SizeOfAlignOfExpr *SE = cast(Ex); + if (!SE->isSizeOf()) + return false; + return SE->getTypeOfArgument()->isVariableArrayType(); + } + case Stmt::DeclRefExprClass: + // Check for constants/pseudoconstants + return !isConstantOrPseudoConstant(cast(Ex), AC); + + // The next cases require recursion for subexpressions + case Stmt::BinaryOperatorClass: { + const BinaryOperator *B = cast(Ex); + + // Exclude cases involving pointer arithmetic. These are usually + // false positives. + if (B->getOpcode() == BO_Sub || B->getOpcode() == BO_Add) + if (B->getLHS()->getType()->getAs()) + return false; + + return CanVary(B->getRHS(), AC) + || CanVary(B->getLHS(), AC); + } + case Stmt::UnaryOperatorClass: { + const UnaryOperator *U = cast(Ex); + // Handle trivial case first + switch (U->getOpcode()) { + case UO_Extension: + return false; + default: + return CanVary(U->getSubExpr(), AC); + } + } + case Stmt::ChooseExprClass: + return CanVary(cast(Ex)->getChosenSubExpr( + AC->getASTContext()), AC); + case Stmt::ConditionalOperatorClass: + return CanVary(cast(Ex)->getCond(), AC); + } +} + +// Returns true if a DeclRefExpr is or behaves like a constant. +bool IdempotentOperationChecker::isConstantOrPseudoConstant( + const DeclRefExpr *DR, + AnalysisContext *AC) { + // Check if the type of the Decl is const-qualified + if (DR->getType().isConstQualified()) + return true; + + // Check for an enum + if (isa(DR->getDecl())) + return true; + + const VarDecl *VD = dyn_cast(DR->getDecl()); + if (!VD) + return true; + + // Check if the Decl behaves like a constant. This check also takes care of + // static variables, which can only change between function calls if they are + // modified in the AST. + PseudoConstantAnalysis *PCA = AC->getPseudoConstantAnalysis(); + if (PCA->isPseudoConstant(VD)) + return true; + + return false; +} + +// Recursively find any substatements containing VarDecl's with storage other +// than local +bool IdempotentOperationChecker::containsNonLocalVarDecl(const Stmt *S) { + const DeclRefExpr *DR = dyn_cast(S); + + if (DR) + if (const VarDecl *VD = dyn_cast(DR->getDecl())) + if (!VD->hasLocalStorage()) + return true; + + for (Stmt::const_child_iterator I = S->child_begin(); I != S->child_end(); + ++I) + if (const Stmt *child = *I) + if (containsNonLocalVarDecl(child)) + return true; + + return false; +} + +// Returns the successor nodes of N whose CFGBlocks cannot reach N's CFGBlock. +// This effectively gives us a set of points in the ExplodedGraph where +// subsequent execution could not affect the idempotent operation on this path. +// This is useful for displaying paths after the point of the error, providing +// an example of how this idempotent operation cannot change. +const ExplodedNodeSet IdempotentOperationChecker::getLastRelevantNodes( + const CFGBlock *Begin, const ExplodedNode *N) { + std::deque WorkList; + llvm::SmallPtrSet Visited; + ExplodedNodeSet Result; + + WorkList.push_back(N); + + while (!WorkList.empty()) { + const ExplodedNode *Head = WorkList.front(); + WorkList.pop_front(); + Visited.insert(Head); + + const ProgramPoint &PP = Head->getLocation(); + if (const BlockEntrance *BE = dyn_cast(&PP)) { + // Get the CFGBlock and test the reachability + const CFGBlock *CB = BE->getBlock(); + + // If we cannot reach the beginning CFGBlock from this block, then we are + // finished + if (!CRA.isReachable(CB, Begin)) { + Result.Add(const_cast(Head)); + continue; + } + } + + // Add unvisited children to the worklist + for (ExplodedNode::const_succ_iterator I = Head->succ_begin(), + E = Head->succ_end(); I != E; ++I) + if (!Visited.count(*I)) + WorkList.push_back(*I); + } + + // Return the ExplodedNodes that were found + return Result; +} + +bool IdempotentOperationChecker::CFGReachabilityAnalysis::isReachable( + const CFGBlock *Src, + const CFGBlock *Dst) { + const unsigned DstBlockID = Dst->getBlockID(); + + // If we haven't analyzed the destination node, run the analysis now + if (!analyzed.count(DstBlockID)) { + MapReachability(Dst); + analyzed.insert(DstBlockID); + } + + // Return the cached result + return reachable[DstBlockID].count(Src->getBlockID()); +} + +// Maps reachability to a common node by walking the predecessors of the +// destination node. +void IdempotentOperationChecker::CFGReachabilityAnalysis::MapReachability( + const CFGBlock *Dst) { + std::deque WorkList; + // Maintain a visited list to ensure we don't get stuck on cycles + llvm::SmallSet Visited; + ReachableSet &DstReachability = reachable[Dst->getBlockID()]; + + // Start searching from the destination node, since we commonly will perform + // multiple queries relating to a destination node. + WorkList.push_back(Dst); + + bool firstRun = true; + while (!WorkList.empty()) { + const CFGBlock *Head = WorkList.front(); + WorkList.pop_front(); + Visited.insert(Head->getBlockID()); + + // Update reachability information for this node -> Dst + if (!firstRun) + // Don't insert Dst -> Dst unless it was a predecessor of itself + DstReachability.insert(Head->getBlockID()); + else + firstRun = false; + + // Add the predecessors to the worklist unless we have already visited them + for (CFGBlock::const_pred_iterator I = Head->pred_begin(); + I != Head->pred_end(); ++I) + if (!Visited.count((*I)->getBlockID())) + WorkList.push_back(*I); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/LLVMConventionsChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/LLVMConventionsChecker.cpp new file mode 100644 index 0000000000..eefad95f21 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/LLVMConventionsChecker.cpp @@ -0,0 +1,313 @@ +//=== 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/AST/DeclTemplate.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include +#include "llvm/ADT/StringRef.h" + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Generic type checking routines. +//===----------------------------------------------------------------------===// + +static bool IsLLVMStringRef(QualType T) { + const RecordType *RT = T->getAs(); + if (!RT) + return false; + + return llvm::StringRef(QualType(RT, 0).getAsString()) == + "class llvm::StringRef"; +} + +/// Check whether the declaration is semantically inside the top-level +/// namespace named by ns. +static bool InNamespace(const Decl *D, llvm::StringRef NS) { + const DeclContext *DC = D->getDeclContext(); + const NamespaceDecl *ND = dyn_cast(D->getDeclContext()); + if (!ND) + return false; + const IdentifierInfo *II = ND->getIdentifier(); + if (!II || !II->getName().equals(NS)) + return false; + DC = ND->getDeclContext(); + return isa(DC); +} + +static bool IsStdString(QualType T) { + if (const ElaboratedType *QT = T->getAs()) + T = QT->getNamedType(); + + const TypedefType *TT = T->getAs(); + if (!TT) + return false; + + const TypedefDecl *TD = TT->getDecl(); + + if (!InNamespace(TD, "std")) + 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(); + 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(); + 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 llvm::StringRef should not be bound to a temporary std::string whose +// lifetime is shorter than the StringRef's. +//===----------------------------------------------------------------------===// + +namespace { +class StringRefCheckerVisitor : public StmtVisitor { + BugReporter &BR; +public: + StringRefCheckerVisitor(BugReporter &br) : BR(br) {} + void VisitChildren(Stmt *S) { + for (Stmt::child_iterator I = S->child_begin(), E = S->child_end() ; + I != E; ++I) + if (Stmt *child = *I) + 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) { + StringRefCheckerVisitor walker(BR); + walker.Visit(D->getBody()); +} + +void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) { + VisitChildren(S); + + for (DeclStmt::decl_iterator I = S->decl_begin(), E = S->decl_end();I!=E; ++I) + if (VarDecl *VD = dyn_cast(*I)) + VisitVarDecl(VD); +} + +void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) { + Expr *Init = VD->getInit(); + if (!Init) + return; + + // Pattern match for: + // llvm::StringRef x = call() (where call returns std::string) + if (!IsLLVMStringRef(VD->getType())) + return; + ExprWithCleanups *Ex1 = dyn_cast(Init); + if (!Ex1) + return; + CXXConstructExpr *Ex2 = dyn_cast(Ex1->getSubExpr()); + if (!Ex2 || Ex2->getNumArgs() != 1) + return; + ImplicitCastExpr *Ex3 = dyn_cast(Ex2->getArg(0)); + if (!Ex3) + return; + CXXConstructExpr *Ex4 = dyn_cast(Ex3->getSubExpr()); + if (!Ex4 || Ex4->getNumArgs() != 1) + return; + ImplicitCastExpr *Ex5 = dyn_cast(Ex4->getArg(0)); + if (!Ex5) + return; + CXXBindTemporaryExpr *Ex6 = dyn_cast(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"; + + BR.EmitBasicReport(desc, "LLVM Conventions", desc, + VD->getLocStart(), 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 (CXXRecordDecl::base_class_const_iterator I = R->bases_begin(), + E = R->bases_end(); I!=E; ++I) { + CXXBaseSpecifier BS = *I; + QualType T = BS.getType(); + if (const RecordType *baseT = T->getAs()) { + CXXRecordDecl *baseD = cast(baseT->getDecl()); + if (IsPartOfAST(baseD)) + return true; + } + } + + return false; +} + +namespace { +class ASTFieldVisitor { + llvm::SmallVector FieldChain; + CXXRecordDecl *Root; + BugReporter &BR; +public: + ASTFieldVisitor(CXXRecordDecl *root, BugReporter &br) + : Root(root), BR(br) {} + + void Visit(FieldDecl *D); + void ReportError(QualType T); +}; +} // end anonymous namespace + +static void CheckASTMemory(CXXRecordDecl *R, BugReporter &BR) { + if (!IsPartOfAST(R)) + return; + + for (RecordDecl::field_iterator I = R->field_begin(), E = R->field_end(); + I != E; ++I) { + ASTFieldVisitor walker(R, BR); + 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()) { + const RecordDecl *RD = RT->getDecl()->getDefinition(); + for (RecordDecl::field_iterator I = RD->field_begin(), E = RD->field_end(); + I != E; ++I) + Visit(*I); + } + + FieldChain.pop_back(); +} + +void ASTFieldVisitor::ReportError(QualType T) { + llvm::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 (llvm::SmallVectorImpl::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() << ")"; + os.flush(); + + // 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). + BR.EmitBasicReport("AST node allocates heap memory", "LLVM Conventions", + os.str(), FieldChain.front()->getLocStart()); +} + +//===----------------------------------------------------------------------===// +// Entry point for all checks. +//===----------------------------------------------------------------------===// + +static void ScanCodeDecls(DeclContext *DC, BugReporter &BR) { + for (DeclContext::decl_iterator I=DC->decls_begin(), E=DC->decls_end(); + I!=E ; ++I) { + + Decl *D = *I; + + if (D->hasBody()) + CheckStringRefAssignedTemporary(D, BR); + + if (CXXRecordDecl *R = dyn_cast(D)) + if (R->isDefinition()) + CheckASTMemory(R, BR); + + if (DeclContext *DC_child = dyn_cast(D)) + ScanCodeDecls(DC_child, BR); + } +} + +void ento::CheckLLVMConventions(TranslationUnitDecl &TU, + BugReporter &BR) { + ScanCodeDecls(&TU, BR); +} + diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/MacOSXAPIChecker.cpp new file mode 100644 index 0000000000..44887fa769 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/MacOSXAPIChecker.cpp @@ -0,0 +1,142 @@ +// 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 Mac OS X functions. +// +// FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated +// to here, using the new Checker interface. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.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 CheckerVisitor { + enum SubChecks { + DispatchOnce = 0, + DispatchOnceF, + NumChecks + }; + + BugType *BTypes[NumChecks]; + +public: + MacOSXAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); } + static void *getTag() { static unsigned tag = 0; return &tag; } + + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; +} //end anonymous namespace + +void ento::RegisterMacOSXAPIChecker(ExprEngine &Eng) { + if (Eng.getContext().Target.getTriple().getVendor() == llvm::Triple::Apple) + Eng.registerCheck(new MacOSXAPIChecker()); +} + +//===----------------------------------------------------------------------===// +// dispatch_once and dispatch_once_f +//===----------------------------------------------------------------------===// + +static void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE, + BugType *&BT, const IdentifierInfo *FI) { + + if (!BT) { + llvm::SmallString<128> S; + llvm::raw_svector_ostream os(S); + os << "Improper use of '" << FI->getName() << '\''; + BT = new BugType(os.str(), "Mac OS X API"); + } + + 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. + const GRState *state = C.getState(); + const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); + if (!R || !isa(R->getMemorySpace())) + return; + + ExplodedNode *N = C.generateSink(state); + if (!N) + return; + + llvm::SmallString<256> S; + llvm::raw_svector_ostream os(S); + os << "Call to '" << FI->getName() << "' uses"; + if (const VarRegion *VR = dyn_cast(R)) + os << " the local variable '" << VR->getDecl()->getName() << '\''; + else + os << " stack allocated memory"; + os << " for the predicate value. Using such transient memory for " + "the predicate is potentially dangerous."; + if (isa(R) && isa(R->getMemorySpace())) + os << " Perhaps you intended to declare the variable as 'static'?"; + + EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N); + report->addRange(CE->getArg(0)->getSourceRange()); + C.EmitReport(report); +} + +//===----------------------------------------------------------------------===// +// Central dispatch function. +//===----------------------------------------------------------------------===// + +typedef void (*SubChecker)(CheckerContext &C, const CallExpr *CE, BugType *&BT, + const IdentifierInfo *FI); +namespace { + class SubCheck { + SubChecker SC; + BugType **BT; + public: + SubCheck(SubChecker sc, BugType *& bt) : SC(sc), BT(&bt) {} + SubCheck() : SC(NULL), BT(NULL) {} + + void run(CheckerContext &C, const CallExpr *CE, + const IdentifierInfo *FI) const { + if (SC) + SC(C, CE, *BT, FI); + } + }; +} // end anonymous namespace + +void MacOSXAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { + // FIXME: Mostly copy and paste from UnixAPIChecker. Should refactor. + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const FunctionTextRegion *Fn = + dyn_cast_or_null(state->getSVal(Callee).getAsRegion()); + + if (!Fn) + return; + + const IdentifierInfo *FI = Fn->getDecl()->getIdentifier(); + if (!FI) + return; + + const SubCheck &SC = + llvm::StringSwitch(FI->getName()) + .Case("dispatch_once", SubCheck(CheckDispatchOnce, BTypes[DispatchOnce])) + .Case("dispatch_once_f", SubCheck(CheckDispatchOnce, + BTypes[DispatchOnceF])) + .Default(SubCheck()); + + SC.run(C, CE, FI); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/Makefile b/lib/StaticAnalyzer/EntoSA/Checkers/Makefile new file mode 100644 index 0000000000..d4de35c1f5 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/Makefile @@ -0,0 +1,17 @@ +##===- clang/lib/Checker/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements analyses built on top of source-level CFGs. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../../.. +LIBRARYNAME := clangStaticAnalyzerCheckers + +include $(CLANG_LEVEL)/Makefile diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/MallocChecker.cpp new file mode 100644 index 0000000000..42243cb525 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/MallocChecker.cpp @@ -0,0 +1,733 @@ +//=== 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 "ExprEngineExperimentalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.h" +using namespace clang; +using namespace ento; + +namespace { + +class RefState { + enum Kind { AllocateUnchecked, AllocateFailed, Released, Escaped, + Relinquished } K; + const Stmt *S; + +public: + RefState(Kind k, const Stmt *s) : K(k), S(s) {} + + bool isAllocated() const { return K == AllocateUnchecked; } + //bool isFailed() const { return K == AllocateFailed; } + bool isReleased() const { return K == Released; } + //bool isEscaped() const { return K == Escaped; } + //bool isRelinquished() const { return K == Relinquished; } + + bool operator==(const RefState &X) const { + return K == X.K && S == X.S; + } + + static RefState getAllocateUnchecked(const Stmt *s) { + return RefState(AllocateUnchecked, s); + } + static RefState getAllocateFailed() { + return RefState(AllocateFailed, 0); + } + static RefState getReleased(const Stmt *s) { return RefState(Released, s); } + static RefState getEscaped(const Stmt *s) { return RefState(Escaped, s); } + static RefState getRelinquished(const Stmt *s) { + return RefState(Relinquished, s); + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(K); + ID.AddPointer(S); + } +}; + +class RegionState {}; + +class MallocChecker : public CheckerVisitor { + BuiltinBug *BT_DoubleFree; + BuiltinBug *BT_Leak; + BuiltinBug *BT_UseFree; + BuiltinBug *BT_UseRelinquished; + BuiltinBug *BT_BadFree; + IdentifierInfo *II_malloc, *II_free, *II_realloc, *II_calloc; + +public: + MallocChecker() + : BT_DoubleFree(0), BT_Leak(0), BT_UseFree(0), BT_UseRelinquished(0), + BT_BadFree(0), + II_malloc(0), II_free(0), II_realloc(0), II_calloc(0) {} + static void *getTag(); + bool evalCallExpr(CheckerContext &C, const CallExpr *CE); + void evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper); + void evalEndPath(EndPathNodeBuilder &B, void *tag, ExprEngine &Eng); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); + const GRState *evalAssume(const GRState *state, SVal Cond, bool Assumption, + bool *respondsToCallback); + void visitLocation(CheckerContext &C, const Stmt *S, SVal l); + virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE, + SVal location, SVal val); + +private: + void MallocMem(CheckerContext &C, const CallExpr *CE); + void MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att); + const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE, + const Expr *SizeEx, SVal Init, + const GRState *state) { + return MallocMemAux(C, CE, state->getSVal(SizeEx), Init, state); + } + const GRState *MallocMemAux(CheckerContext &C, const CallExpr *CE, + SVal SizeEx, SVal Init, + const GRState *state); + + void FreeMem(CheckerContext &C, const CallExpr *CE); + void FreeMemAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att); + const GRState *FreeMemAux(CheckerContext &C, const CallExpr *CE, + const GRState *state, unsigned Num, bool Hold); + + void ReallocMem(CheckerContext &C, const CallExpr *CE); + void CallocMem(CheckerContext &C, const CallExpr *CE); + + bool SummarizeValue(llvm::raw_ostream& os, SVal V); + bool SummarizeRegion(llvm::raw_ostream& os, const MemRegion *MR); + void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange range); +}; +} // end anonymous namespace + +typedef llvm::ImmutableMap RegionStateTy; + +namespace clang { +namespace ento { + template <> + struct GRStateTrait + : public GRStatePartialTrait { + static void *GDMIndex() { return MallocChecker::getTag(); } + }; +} +} + +void ento::RegisterMallocChecker(ExprEngine &Eng) { + Eng.registerCheck(new MallocChecker()); +} + +void *MallocChecker::getTag() { + static int x; + return &x; +} + +bool MallocChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + ASTContext &Ctx = C.getASTContext(); + if (!II_malloc) + II_malloc = &Ctx.Idents.get("malloc"); + if (!II_free) + II_free = &Ctx.Idents.get("free"); + if (!II_realloc) + II_realloc = &Ctx.Idents.get("realloc"); + if (!II_calloc) + II_calloc = &Ctx.Idents.get("calloc"); + + if (FD->getIdentifier() == II_malloc) { + MallocMem(C, CE); + return true; + } + + if (FD->getIdentifier() == II_free) { + FreeMem(C, CE); + return true; + } + + if (FD->getIdentifier() == II_realloc) { + ReallocMem(C, CE); + return true; + } + + if (FD->getIdentifier() == II_calloc) { + CallocMem(C, CE); + return true; + } + + // Check all the attributes, if there are any. + // There can be multiple of these attributes. + bool rv = false; + if (FD->hasAttrs()) { + for (specific_attr_iterator + i = FD->specific_attr_begin(), + e = FD->specific_attr_end(); + i != e; ++i) { + switch ((*i)->getOwnKind()) { + case OwnershipAttr::Returns: { + MallocMemReturnsAttr(C, CE, *i); + rv = true; + break; + } + case OwnershipAttr::Takes: + case OwnershipAttr::Holds: { + FreeMemAttr(C, CE, *i); + rv = true; + break; + } + default: + break; + } + } + } + return rv; +} + +void MallocChecker::MallocMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), + C.getState()); + C.addTransition(state); +} + +void MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att) { + if (Att->getModule() != "malloc") + return; + + OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); + if (I != E) { + const GRState *state = + MallocMemAux(C, CE, CE->getArg(*I), UndefinedVal(), C.getState()); + C.addTransition(state); + return; + } + const GRState *state = MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), + C.getState()); + C.addTransition(state); +} + +const GRState *MallocChecker::MallocMemAux(CheckerContext &C, + const CallExpr *CE, + SVal Size, SVal Init, + const GRState *state) { + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + SValBuilder &svalBuilder = C.getSValBuilder(); + + // Set the return value. + SVal retVal = svalBuilder.getConjuredSymbolVal(NULL, CE, CE->getType(), Count); + state = state->BindExpr(CE, retVal); + + // Fill the region with the initialization value. + state = state->bindDefault(retVal, Init); + + // Set the region's extent equal to the Size parameter. + const SymbolicRegion *R = cast(retVal.getAsRegion()); + DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder); + DefinedOrUnknownSVal DefinedSize = cast(Size); + DefinedOrUnknownSVal extentMatchesSize = + svalBuilder.evalEQ(state, Extent, DefinedSize); + + state = state->assume(extentMatchesSize, true); + assert(state); + + SymbolRef Sym = retVal.getAsLocSymbol(); + assert(Sym); + + // Set the symbol's state to Allocated. + return state->set(Sym, RefState::getAllocateUnchecked(CE)); +} + +void MallocChecker::FreeMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = FreeMemAux(C, CE, C.getState(), 0, false); + + if (state) + C.addTransition(state); +} + +void MallocChecker::FreeMemAttr(CheckerContext &C, const CallExpr *CE, + const OwnershipAttr* Att) { + if (Att->getModule() != "malloc") + return; + + for (OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end(); + I != E; ++I) { + const GRState *state = FreeMemAux(C, CE, C.getState(), *I, + Att->getOwnKind() == OwnershipAttr::Holds); + if (state) + C.addTransition(state); + } +} + +const GRState *MallocChecker::FreeMemAux(CheckerContext &C, const CallExpr *CE, + const GRState *state, unsigned Num, + bool Hold) { + const Expr *ArgExpr = CE->getArg(Num); + SVal ArgVal = state->getSVal(ArgExpr); + + DefinedOrUnknownSVal location = cast(ArgVal); + + // Check for null dereferences. + if (!isa(location)) + return state; + + // FIXME: Technically using 'Assume' here can result in a path + // bifurcation. In such cases we need to return two states, not just one. + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->assume(location); + + // The explicit NULL case, no operation is performed. + if (nullState && !notNullState) + return nullState; + + assert(notNullState); + + // Unknown values could easily be okay + // Undefined values are handled elsewhere + if (ArgVal.isUnknownOrUndef()) + return notNullState; + + 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()); + return NULL; + } + + R = R->StripCasts(); + + // Blocks might show up as heap data, but should not be free()d + if (isa(R)) { + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange()); + return NULL; + } + + const MemSpaceRegion *MS = R->getMemorySpace(); + + // Parameters, locals, statics, and globals shouldn't be freed. + if (!(isa(MS) || isa(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. + + ReportBadFree(C, ArgVal, ArgExpr->getSourceRange()); + return NULL; + } + + const SymbolicRegion *SR = dyn_cast(R); + // Various cases could lead to non-symbol values here. + // For now, ignore them. + if (!SR) + return notNullState; + + SymbolRef Sym = SR->getSymbol(); + const RefState *RS = state->get(Sym); + + // If the symbol has not been tracked, return. This is possible when free() is + // called on a pointer that does not get its pointee directly from malloc(). + // Full support of this requires inter-procedural analysis. + if (!RS) + return notNullState; + + // Check double free. + if (RS->isReleased()) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT_DoubleFree) + BT_DoubleFree + = new BuiltinBug("Double free", + "Try to free a memory block that has been released"); + // FIXME: should find where it's freed last time. + BugReport *R = new BugReport(*BT_DoubleFree, + BT_DoubleFree->getDescription(), N); + C.EmitReport(R); + } + return NULL; + } + + // Normal free. + if (Hold) + return notNullState->set(Sym, RefState::getRelinquished(CE)); + return notNullState->set(Sym, RefState::getReleased(CE)); +} + +bool MallocChecker::SummarizeValue(llvm::raw_ostream& os, SVal V) { + if (nonloc::ConcreteInt *IntVal = dyn_cast(&V)) + os << "an integer (" << IntVal->getValue() << ")"; + else if (loc::ConcreteInt *ConstAddr = dyn_cast(&V)) + os << "a constant address (" << ConstAddr->getValue() << ")"; + else if (loc::GotoLabel *Label = dyn_cast(&V)) + os << "the address of the label '" + << Label->getLabel()->getID()->getName() + << "'"; + else + return false; + + return true; +} + +bool MallocChecker::SummarizeRegion(llvm::raw_ostream& os, + const MemRegion *MR) { + switch (MR->getKind()) { + case MemRegion::FunctionTextRegionKind: { + const FunctionDecl *FD = cast(MR)->getDecl(); + if (FD) + os << "the address of the function '" << FD << "'"; + else + os << "the address of a function"; + return true; + } + case MemRegion::BlockTextRegionKind: + 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(); + + switch (MS->getKind()) { + case MemRegion::StackLocalsSpaceRegionKind: { + const VarRegion *VR = dyn_cast(MR); + const VarDecl *VD; + if (VR) + VD = VR->getDecl(); + else + VD = NULL; + + if (VD) + os << "the address of the local variable '" << VD->getName() << "'"; + else + os << "the address of a local stack variable"; + return true; + } + case MemRegion::StackArgumentsSpaceRegionKind: { + const VarRegion *VR = dyn_cast(MR); + const VarDecl *VD; + if (VR) + VD = VR->getDecl(); + else + VD = NULL; + + if (VD) + os << "the address of the parameter '" << VD->getName() << "'"; + else + os << "the address of a parameter"; + return true; + } + case MemRegion::NonStaticGlobalSpaceRegionKind: + case MemRegion::StaticGlobalSpaceRegionKind: { + const VarRegion *VR = dyn_cast(MR); + const VarDecl *VD; + if (VR) + VD = VR->getDecl(); + else + VD = NULL; + + 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; + } + default: + return false; + } + } + } +} + +void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal, + SourceRange range) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT_BadFree) + BT_BadFree = new BuiltinBug("Bad free"); + + llvm::SmallString<100> buf; + llvm::raw_svector_ostream os(buf); + + const MemRegion *MR = ArgVal.getAsRegion(); + if (MR) { + while (const ElementRegion *ER = dyn_cast(MR)) + MR = ER->getSuperRegion(); + + // Special case for alloca() + if (isa(MR)) + os << "Argument to free() was allocated by alloca(), not malloc()"; + else { + os << "Argument to free() is "; + if (SummarizeRegion(os, MR)) + os << ", which is not memory allocated by malloc()"; + else + os << "not memory allocated by malloc()"; + } + } else { + os << "Argument to free() is "; + if (SummarizeValue(os, ArgVal)) + os << ", which is not memory allocated by malloc()"; + else + os << "not memory allocated by malloc()"; + } + + EnhancedBugReport *R = new EnhancedBugReport(*BT_BadFree, os.str(), N); + R->addRange(range); + C.EmitReport(R); + } +} + +void MallocChecker::ReallocMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *arg0Expr = CE->getArg(0); + DefinedOrUnknownSVal arg0Val + = cast(state->getSVal(arg0Expr)); + + SValBuilder &svalBuilder = C.getSValBuilder(); + + DefinedOrUnknownSVal PtrEQ = + svalBuilder.evalEQ(state, arg0Val, svalBuilder.makeNull()); + + // If the ptr is NULL, the call is equivalent to malloc(size). + if (const GRState *stateEqual = state->assume(PtrEQ, true)) { + // Hack: set the NULL symbolic region to released to suppress false warning. + // In the future we should add more states for allocated regions, e.g., + // CheckedNull, CheckedNonNull. + + SymbolRef Sym = arg0Val.getAsLocSymbol(); + if (Sym) + stateEqual = stateEqual->set(Sym, RefState::getReleased(CE)); + + const GRState *stateMalloc = MallocMemAux(C, CE, CE->getArg(1), + UndefinedVal(), stateEqual); + C.addTransition(stateMalloc); + } + + if (const GRState *stateNotEqual = state->assume(PtrEQ, false)) { + const Expr *Arg1 = CE->getArg(1); + DefinedOrUnknownSVal Arg1Val = + cast(stateNotEqual->getSVal(Arg1)); + DefinedOrUnknownSVal SizeZero = + svalBuilder.evalEQ(stateNotEqual, Arg1Val, + svalBuilder.makeIntValWithPtrWidth(0, false)); + + if (const GRState *stateSizeZero = stateNotEqual->assume(SizeZero, true)) + if (const GRState *stateFree = FreeMemAux(C, CE, stateSizeZero, 0, false)) + C.addTransition(stateFree->BindExpr(CE, UndefinedVal(), true)); + + if (const GRState *stateSizeNotZero = stateNotEqual->assume(SizeZero,false)) + if (const GRState *stateFree = FreeMemAux(C, CE, stateSizeNotZero, + 0, false)) { + // FIXME: We should copy the content of the original buffer. + const GRState *stateRealloc = MallocMemAux(C, CE, CE->getArg(1), + UnknownVal(), stateFree); + C.addTransition(stateRealloc); + } + } +} + +void MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + SValBuilder &svalBuilder = C.getSValBuilder(); + + SVal count = state->getSVal(CE->getArg(0)); + SVal elementSize = state->getSVal(CE->getArg(1)); + SVal TotalSize = svalBuilder.evalBinOp(state, BO_Mul, count, elementSize, + svalBuilder.getContext().getSizeType()); + SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy); + + C.addTransition(MallocMemAux(C, CE, TotalSize, zeroVal, state)); +} + +void MallocChecker::evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper) +{ + if (!SymReaper.hasDeadSymbols()) + return; + + const GRState *state = C.getState(); + RegionStateTy RS = state->get(); + RegionStateTy::Factory &F = state->get_context(); + + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + if (SymReaper.isDead(I->first)) { + if (I->second.isAllocated()) { + if (ExplodedNode *N = C.generateNode()) { + if (!BT_Leak) + BT_Leak = new BuiltinBug("Memory leak", + "Allocated memory never released. Potential memory leak."); + // FIXME: where it is allocated. + BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); + C.EmitReport(R); + } + } + + // Remove the dead symbol from the map. + RS = F.remove(RS, I->first); + } + } + C.generateNode(state->set(RS)); +} + +void MallocChecker::evalEndPath(EndPathNodeBuilder &B, void *tag, + ExprEngine &Eng) { + SaveAndRestore OldHasGen(B.HasGeneratedNode); + const GRState *state = B.getState(); + RegionStateTy M = state->get(); + + for (RegionStateTy::iterator I = M.begin(), E = M.end(); I != E; ++I) { + RefState RS = I->second; + if (RS.isAllocated()) { + ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + if (N) { + if (!BT_Leak) + BT_Leak = new BuiltinBug("Memory leak", + "Allocated memory never released. Potential memory leak."); + BugReport *R = new BugReport(*BT_Leak, BT_Leak->getDescription(), N); + Eng.getBugReporter().EmitReport(R); + } + } + } +} + +void MallocChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { + const Expr *retExpr = S->getRetValue(); + if (!retExpr) + return; + + const GRState *state = C.getState(); + + SymbolRef Sym = state->getSVal(retExpr).getAsSymbol(); + if (!Sym) + return; + + const RefState *RS = state->get(Sym); + if (!RS) + return; + + // FIXME: check other cases. + if (RS->isAllocated()) + state = state->set(Sym, RefState::getEscaped(S)); + + C.addTransition(state); +} + +const GRState *MallocChecker::evalAssume(const GRState *state, SVal Cond, + bool Assumption, + bool * /* respondsToCallback */) { + // If a symblic region is assumed to NULL, set its state to AllocateFailed. + // FIXME: should also check symbols assumed to non-null. + + RegionStateTy RS = state->get(); + + for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) { + if (state->getSymVal(I.getKey())) + state = state->set(I.getKey(),RefState::getAllocateFailed()); + } + + return state; +} + +// Check if the location is a freed symbolic region. +void MallocChecker::visitLocation(CheckerContext &C, const Stmt *S, SVal l) { + SymbolRef Sym = l.getLocSymbolInBase(); + if (Sym) { + const RefState *RS = C.getState()->get(Sym); + if (RS && RS->isReleased()) { + if (ExplodedNode *N = C.generateNode()) { + if (!BT_UseFree) + BT_UseFree = new BuiltinBug("Use dynamically allocated memory after" + " it is freed."); + + BugReport *R = new BugReport(*BT_UseFree, BT_UseFree->getDescription(), + N); + C.EmitReport(R); + } + } + } +} + +void MallocChecker::PreVisitBind(CheckerContext &C, + const Stmt *StoreE, + SVal location, + SVal val) { + // The PreVisitBind implements the same algorithm as already used by the + // Objective C ownership checker: if the pointer escaped from this scope by + // assignment, let it go. However, assigning to fields of a stack-storage + // structure does not transfer ownership. + + const GRState *state = C.getState(); + DefinedOrUnknownSVal l = cast(location); + + // Check for null dereferences. + if (!isa(l)) + return; + + // Before checking if the state is null, check if 'val' has a RefState. + // Only then should we check for null and bifurcate the state. + SymbolRef Sym = val.getLocSymbolInBase(); + if (Sym) { + if (const RefState *RS = state->get(Sym)) { + // If ptr is NULL, no operation is performed. + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->assume(l); + + // Generate a transition for 'nullState' to record the assumption + // that the state was null. + if (nullState) + C.addTransition(nullState); + + if (!notNullState) + return; + + if (RS->isAllocated()) { + // Something we presently own is being assigned somewhere. + const MemRegion *AR = location.getAsRegion(); + if (!AR) + return; + AR = AR->StripCasts()->getBaseRegion(); + do { + // If it is on the stack, we still own it. + if (AR->hasStackNonParametersStorage()) + break; + + // If the state can't represent this binding, we still own it. + if (notNullState == (notNullState->bindLoc(cast(location), + UnknownVal()))) + break; + + // We no longer own this pointer. + notNullState = + notNullState->set(Sym, + RefState::getRelinquished(StoreE)); + } + while (false); + } + C.addTransition(notNullState); + } + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp new file mode 100644 index 0000000000..6ef242ba42 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp @@ -0,0 +1,87 @@ +//=- 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 it's 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/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "BasicObjCFoundationChecks.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Decl.h" + +using namespace clang; +using namespace ento; + +namespace { +class NSAutoreleasePoolChecker + : public CheckerVisitor { + + Selector releaseS; + +public: + NSAutoreleasePoolChecker(Selector release_s) : releaseS(release_s) {} + + static void *getTag() { + static int x = 0; + return &x; + } + + void PreVisitObjCMessageExpr(CheckerContext &C, const ObjCMessageExpr *ME); +}; + +} // end anonymous namespace + + +void ento::RegisterNSAutoreleasePoolChecks(ExprEngine &Eng) { + ASTContext &Ctx = Eng.getContext(); + if (Ctx.getLangOptions().getGCMode() != LangOptions::NonGC) { + Eng.registerCheck(new NSAutoreleasePoolChecker(GetNullarySelector("release", + Ctx))); + } +} + +void +NSAutoreleasePoolChecker::PreVisitObjCMessageExpr(CheckerContext &C, + const ObjCMessageExpr *ME) { + + const Expr *receiver = ME->getInstanceReceiver(); + if (!receiver) + return; + + // FIXME: Enhance with value-tracking information instead of consulting + // the type of the expression. + const ObjCObjectPointerType* PT = + receiver->getType()->getAs(); + + if (!PT) + return; + const ObjCInterfaceDecl* OD = PT->getInterfaceDecl(); + if (!OD) + return; + if (!OD->getIdentifier()->getName().equals("NSAutoreleasePool")) + return; + + // Sending 'release' message? + if (ME->getSelector() != releaseS) + return; + + SourceRange R = ME->getSourceRange(); + + C.getBugReporter().EmitBasicReport("Use -drain instead of -release", + "API Upgrade (Apple)", + "Use -drain instead of -release when using NSAutoreleasePool " + "and garbage collection", ME->getLocStart(), &R, 1); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/NSErrorChecker.cpp new file mode 100644 index 0000000000..54e6118893 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/NSErrorChecker.cpp @@ -0,0 +1,238 @@ +//=- NSErrorCheckerer.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/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" +#include "BasicObjCFoundationChecks.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/Decl.h" +#include "llvm/ADT/SmallVector.h" + +using namespace clang; +using namespace ento; + +namespace { +class NSErrorChecker : public BugType { + const Decl &CodeDecl; + const bool isNSErrorWarning; + IdentifierInfo * const II; + ExprEngine &Eng; + + void CheckSignature(const ObjCMethodDecl& MD, QualType& ResultTy, + llvm::SmallVectorImpl& ErrorParams); + + void CheckSignature(const FunctionDecl& MD, QualType& ResultTy, + llvm::SmallVectorImpl& ErrorParams); + + bool CheckNSErrorArgument(QualType ArgTy); + bool CheckCFErrorArgument(QualType ArgTy); + + void CheckParamDeref(const VarDecl *V, const LocationContext *LC, + const GRState *state, BugReporter& BR); + + void EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl); + +public: + NSErrorChecker(const Decl &D, bool isNSError, ExprEngine& eng) + : BugType(isNSError ? "NSError** null dereference" + : "CFErrorRef* null dereference", + "Coding conventions (Apple)"), + CodeDecl(D), + isNSErrorWarning(isNSError), + II(&eng.getContext().Idents.get(isNSErrorWarning ? "NSError":"CFErrorRef")), + Eng(eng) {} + + void FlushReports(BugReporter& BR); +}; + +} // end anonymous namespace + +void ento::RegisterNSErrorChecks(BugReporter& BR, ExprEngine &Eng, + const Decl &D) { + BR.Register(new NSErrorChecker(D, true, Eng)); + BR.Register(new NSErrorChecker(D, false, Eng)); +} + +void NSErrorChecker::FlushReports(BugReporter& BR) { + // Get the analysis engine and the exploded analysis graph. + ExplodedGraph& G = Eng.getGraph(); + + // Get the ASTContext, which is useful for querying type information. + ASTContext &Ctx = BR.getContext(); + + QualType ResultTy; + llvm::SmallVector ErrorParams; + + if (const ObjCMethodDecl* MD = dyn_cast(&CodeDecl)) + CheckSignature(*MD, ResultTy, ErrorParams); + else if (const FunctionDecl* FD = dyn_cast(&CodeDecl)) + CheckSignature(*FD, ResultTy, ErrorParams); + else + return; + + if (ErrorParams.empty()) + return; + + if (ResultTy == Ctx.VoidTy) EmitRetTyWarning(BR, CodeDecl); + + for (ExplodedGraph::roots_iterator RI=G.roots_begin(), RE=G.roots_end(); + RI!=RE; ++RI) { + // Scan the parameters for an implicit null dereference. + for (llvm::SmallVectorImpl::iterator I=ErrorParams.begin(), + E=ErrorParams.end(); I!=E; ++I) + CheckParamDeref(*I, (*RI)->getLocationContext(), (*RI)->getState(), BR); + } +} + +void NSErrorChecker::EmitRetTyWarning(BugReporter& BR, const Decl& CodeDecl) { + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + if (isa(CodeDecl)) + os << "Method"; + else + os << "Function"; + + os << " accepting "; + os << (isNSErrorWarning ? "NSError**" : "CFErrorRef*"); + os << " should have a non-void return value to indicate whether or not an " + "error occurred"; + + BR.EmitBasicReport(isNSErrorWarning + ? "Bad return type when passing NSError**" + : "Bad return type when passing CFError*", + getCategory(), os.str(), + CodeDecl.getLocation()); +} + +void +NSErrorChecker::CheckSignature(const ObjCMethodDecl& M, QualType& ResultTy, + llvm::SmallVectorImpl& ErrorParams) { + + ResultTy = M.getResultType(); + + for (ObjCMethodDecl::param_iterator I=M.param_begin(), + E=M.param_end(); I!=E; ++I) { + + QualType T = (*I)->getType(); + + if (isNSErrorWarning) { + if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); + } + else if (CheckCFErrorArgument(T)) + ErrorParams.push_back(*I); + } +} + +void +NSErrorChecker::CheckSignature(const FunctionDecl& F, QualType& ResultTy, + llvm::SmallVectorImpl& ErrorParams) { + + ResultTy = F.getResultType(); + + for (FunctionDecl::param_const_iterator I = F.param_begin(), + E = F.param_end(); I != E; ++I) { + + QualType T = (*I)->getType(); + + if (isNSErrorWarning) { + if (CheckNSErrorArgument(T)) ErrorParams.push_back(*I); + } + else if (CheckCFErrorArgument(T)) + ErrorParams.push_back(*I); + } +} + + +bool NSErrorChecker::CheckNSErrorArgument(QualType ArgTy) { + + const PointerType* PPT = ArgTy->getAs(); + if (!PPT) + return false; + + const ObjCObjectPointerType* PT = + PPT->getPointeeType()->getAs(); + + if (!PT) + return false; + + const ObjCInterfaceDecl *ID = PT->getInterfaceDecl(); + + // FIXME: Can ID ever be NULL? + if (ID) + return II == ID->getIdentifier(); + + return false; +} + +bool NSErrorChecker::CheckCFErrorArgument(QualType ArgTy) { + + const PointerType* PPT = ArgTy->getAs(); + if (!PPT) return false; + + const TypedefType* TT = PPT->getPointeeType()->getAs(); + if (!TT) return false; + + return TT->getDecl()->getIdentifier() == II; +} + +void NSErrorChecker::CheckParamDeref(const VarDecl *Param, + const LocationContext *LC, + const GRState *rootState, + BugReporter& BR) { + + SVal ParamL = rootState->getLValue(Param, LC); + const MemRegion* ParamR = cast(ParamL).getRegionAs(); + assert (ParamR && "Parameters always have VarRegions."); + SVal ParamSVal = rootState->getSVal(ParamR); + + // FIXME: For now assume that ParamSVal is symbolic. We need to generalize + // this later. + SymbolRef ParamSym = ParamSVal.getAsLocSymbol(); + if (!ParamSym) + return; + + // Iterate over the implicit-null dereferences. + ExplodedNode *const* I, *const* E; + llvm::tie(I, E) = GetImplicitNullDereferences(Eng); + for ( ; I != E; ++I) { + const GRState *state = (*I)->getState(); + SVal location = state->getSVal((*I)->getLocationAs()->getStmt()); + if (location.getAsSymbol() != ParamSym) + continue; + + // Emit an error. + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + os << "Potential null dereference. According to coding standards "; + + if (isNSErrorWarning) + os << "in 'Creating and Returning NSError Objects' the parameter '"; + else + os << "documented in CoreFoundation/CFError.h the parameter '"; + + os << Param << "' may be null."; + + BugReport *report = new BugReport(*this, os.str(), *I); + // FIXME: Notable symbols are now part of the report. We should + // add support for notable symbols in BugReport. + // BR.addNotableSymbol(SV->getSymbol()); + BR.EmitReport(report); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/NoReturnFunctionChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/NoReturnFunctionChecker.cpp new file mode 100644 index 0000000000..8cdc04f9ff --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/NoReturnFunctionChecker.cpp @@ -0,0 +1,80 @@ +//=== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "llvm/ADT/StringSwitch.h" + +using namespace clang; +using namespace ento; + +namespace { + +class NoReturnFunctionChecker : public CheckerVisitor { +public: + static void *getTag() { static int tag = 0; return &tag; } + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; + +} + +void ento::RegisterNoReturnFunctionChecker(ExprEngine &Eng) { + Eng.registerCheck(new NoReturnFunctionChecker()); +} + +void NoReturnFunctionChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + + bool BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn(); + + if (!BuildSinks) { + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + return; + + if (FD->getAttr()) + BuildSinks = true; + else if (const IdentifierInfo *II = FD->getIdentifier()) { + // 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(llvm::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("__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(CE); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/OSAtomicChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/OSAtomicChecker.cpp new file mode 100644 index 0000000000..9931734842 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/OSAtomicChecker.cpp @@ -0,0 +1,203 @@ +//=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- 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 OSAtomic functions. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" +#include "clang/Basic/Builtins.h" + +using namespace clang; +using namespace ento; + +namespace { + +class OSAtomicChecker : public Checker { +public: + static void *getTag() { static int tag = 0; return &tag; } + virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE); + +private: + bool evalOSAtomicCompareAndSwap(CheckerContext &C, const CallExpr *CE); +}; + +} + +void ento::RegisterOSAtomicChecker(ExprEngine &Eng) { + Eng.registerCheck(new OSAtomicChecker()); +} + +bool OSAtomicChecker::evalCallExpr(CheckerContext &C,const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + + const FunctionDecl* FD = L.getAsFunctionDecl(); + if (!FD) + return false; + + const IdentifierInfo *II = FD->getIdentifier(); + if (!II) + return false; + + llvm::StringRef FName(II->getName()); + + // Check for compare and swap. + if (FName.startswith("OSAtomicCompareAndSwap") || + FName.startswith("objc_atomicCompareAndSwap")) + return evalOSAtomicCompareAndSwap(C, CE); + + // FIXME: Other atomics. + return false; +} + +bool OSAtomicChecker::evalOSAtomicCompareAndSwap(CheckerContext &C, + const CallExpr *CE) { + // Not enough arguments to match OSAtomicCompareAndSwap? + if (CE->getNumArgs() != 3) + return false; + + ASTContext &Ctx = C.getASTContext(); + const Expr *oldValueExpr = CE->getArg(0); + QualType oldValueType = Ctx.getCanonicalType(oldValueExpr->getType()); + + const Expr *newValueExpr = CE->getArg(1); + QualType newValueType = Ctx.getCanonicalType(newValueExpr->getType()); + + // Do the types of 'oldValue' and 'newValue' match? + if (oldValueType != newValueType) + return false; + + const Expr *theValueExpr = CE->getArg(2); + const PointerType *theValueType=theValueExpr->getType()->getAs(); + + // theValueType not a pointer? + if (!theValueType) + return false; + + QualType theValueTypePointee = + Ctx.getCanonicalType(theValueType->getPointeeType()).getUnqualifiedType(); + + // The pointee must match newValueType and oldValueType. + if (theValueTypePointee != newValueType) + return false; + + static unsigned magic_load = 0; + static unsigned magic_store = 0; + + const void *OSAtomicLoadTag = &magic_load; + const void *OSAtomicStoreTag = &magic_store; + + // Load 'theValue'. + ExprEngine &Engine = C.getEngine(); + const GRState *state = C.getState(); + ExplodedNodeSet Tmp; + SVal location = state->getSVal(theValueExpr); + // Here we should use the value type of the region as the load type, because + // we are simulating the semantics of the function, not the semantics of + // passing argument. So the type of theValue expr is not we are loading. + // But usually the type of the varregion is not the type we want either, + // we still need to do a CastRetrievedVal in store manager. So actually this + // LoadTy specifying can be omitted. But we put it here to emphasize the + // semantics. + QualType LoadTy; + if (const TypedRegion *TR = + dyn_cast_or_null(location.getAsRegion())) { + LoadTy = TR->getValueType(); + } + Engine.evalLoad(Tmp, theValueExpr, C.getPredecessor(), + state, location, OSAtomicLoadTag, LoadTy); + + if (Tmp.empty()) { + // If no nodes were generated, other checkers must generated sinks. But + // since the builder state was restored, we set it manually to prevent + // auto transition. + // FIXME: there should be a better approach. + C.getNodeBuilder().BuildSinks = true; + return true; + } + + for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); + I != E; ++I) { + + ExplodedNode *N = *I; + const GRState *stateLoad = N->getState(); + SVal theValueVal_untested = stateLoad->getSVal(theValueExpr); + SVal oldValueVal_untested = stateLoad->getSVal(oldValueExpr); + + // FIXME: Issue an error. + if (theValueVal_untested.isUndef() || oldValueVal_untested.isUndef()) { + return false; + } + + DefinedOrUnknownSVal theValueVal = + cast(theValueVal_untested); + DefinedOrUnknownSVal oldValueVal = + cast(oldValueVal_untested); + + SValBuilder &svalBuilder = Engine.getSValBuilder(); + + // Perform the comparison. + DefinedOrUnknownSVal Cmp = svalBuilder.evalEQ(stateLoad,theValueVal,oldValueVal); + + const GRState *stateEqual = stateLoad->assume(Cmp, true); + + // Were they equal? + if (stateEqual) { + // Perform the store. + ExplodedNodeSet TmpStore; + SVal val = stateEqual->getSVal(newValueExpr); + + // Handle implicit value casts. + if (const TypedRegion *R = + dyn_cast_or_null(location.getAsRegion())) { + val = svalBuilder.evalCast(val,R->getValueType(), newValueExpr->getType()); + } + + Engine.evalStore(TmpStore, NULL, theValueExpr, N, + stateEqual, location, val, OSAtomicStoreTag); + + if (TmpStore.empty()) { + // If no nodes were generated, other checkers must generated sinks. But + // since the builder state was restored, we set it manually to prevent + // auto transition. + // FIXME: there should be a better approach. + C.getNodeBuilder().BuildSinks = true; + return true; + } + + // Now bind the result of the comparison. + for (ExplodedNodeSet::iterator I2 = TmpStore.begin(), + E2 = TmpStore.end(); I2 != E2; ++I2) { + ExplodedNode *predNew = *I2; + const GRState *stateNew = predNew->getState(); + // Check for 'void' return type if we have a bogus function prototype. + SVal Res = UnknownVal(); + QualType T = CE->getType(); + if (!T->isVoidType()) + Res = Engine.getSValBuilder().makeTruthVal(true, T); + C.generateNode(stateNew->BindExpr(CE, Res), predNew); + } + } + + // Were they not equal? + if (const GRState *stateNotEqual = stateLoad->assume(Cmp, false)) { + // Check for 'void' return type if we have a bogus function prototype. + SVal Res = UnknownVal(); + QualType T = CE->getType(); + if (!T->isVoidType()) + Res = Engine.getSValBuilder().makeTruthVal(false, CE->getType()); + C.generateNode(stateNotEqual->BindExpr(CE, Res), N); + } + } + + return true; +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/ObjCAtSyncChecker.cpp new file mode 100644 index 0000000000..2d94e8ce91 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/ObjCAtSyncChecker.cpp @@ -0,0 +1,95 @@ +//== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/Checkers/DereferenceChecker.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class ObjCAtSyncChecker : public CheckerVisitor { + BuiltinBug *BT_null; + BuiltinBug *BT_undef; +public: + ObjCAtSyncChecker() : BT_null(0), BT_undef(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PreVisitObjCAtSynchronizedStmt(CheckerContext &C, + const ObjCAtSynchronizedStmt *S); +}; +} // end anonymous namespace + +void ento::RegisterObjCAtSyncChecker(ExprEngine &Eng) { + // @synchronized is an Objective-C 2 feature. + if (Eng.getContext().getLangOptions().ObjC2) + Eng.registerCheck(new ObjCAtSyncChecker()); +} + +void ObjCAtSyncChecker::PreVisitObjCAtSynchronizedStmt(CheckerContext &C, + const ObjCAtSynchronizedStmt *S) { + + const Expr *Ex = S->getSynchExpr(); + const GRState *state = C.getState(); + SVal V = state->getSVal(Ex); + + // Uninitialized value used for the mutex? + if (isa(V)) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT_undef) + BT_undef = new BuiltinBug("Uninitialized value used as mutex " + "for @synchronized"); + EnhancedBugReport *report = + new EnhancedBugReport(*BT_undef, BT_undef->getDescription(), N); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + C.EmitReport(report); + } + return; + } + + if (V.isUnknown()) + return; + + // Check for null mutexes. + const GRState *notNullState, *nullState; + llvm::tie(notNullState, nullState) = state->assume(cast(V)); + + 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.generateNode(nullState)) { + if (!BT_null) + BT_null = new BuiltinBug("Nil value used as mutex for @synchronized() " + "(no synchronization will occur)"); + EnhancedBugReport *report = + new EnhancedBugReport(*BT_null, BT_null->getDescription(), N); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + Ex); + + C.EmitReport(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); +} + diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp new file mode 100644 index 0000000000..799da332b9 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp @@ -0,0 +1,164 @@ +//==- 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/LocalCheckers.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/AST/ExprObjC.h" +#include "clang/AST/Expr.h" +#include "clang/AST/DeclObjC.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" + +using namespace clang; +using namespace ento; + +enum IVarState { Unused, Used }; +typedef llvm::DenseMap IvarUsageMap; + +static void Scan(IvarUsageMap& M, const Stmt* S) { + if (!S) + return; + + if (const ObjCIvarRefExpr *Ex = dyn_cast(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(S)) { + Scan(M, BE->getBody()); + return; + } + + for (Stmt::const_child_iterator I=S->child_begin(),E=S->child_end(); I!=E;++I) + Scan(M, *I); +} + +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 (ObjCContainerDecl::instmeth_iterator I = D->instmeth_begin(), + E = D->instmeth_end(); I!=E; ++I) + Scan(M, (*I)->getBody()); + + if (const ObjCImplementationDecl *ID = dyn_cast(D)) { + // Scan for @synthesized property methods that act as setters/getters + // to an ivar. + for (ObjCImplementationDecl::propimpl_iterator I = ID->propimpl_begin(), + E = ID->propimpl_end(); I!=E; ++I) + Scan(M, *I); + + // Scan the associated categories as well. + for (const ObjCCategoryDecl *CD = + ID->getClassInterface()->getCategoryList(); CD ; + CD = CD->getNextClassCategory()) { + if (const ObjCCategoryImplDecl *CID = CD->getImplementation()) + Scan(M, CID); + } + } +} + +static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID, + SourceManager &SM) { + for (DeclContext::decl_iterator I=C->decls_begin(), E=C->decls_end(); + I!=E; ++I) + if (const FunctionDecl *FD = dyn_cast(*I)) { + SourceLocation L = FD->getLocStart(); + if (SM.getFileID(L) == FID) + Scan(M, FD->getBody()); + } +} + +void ento::CheckObjCUnusedIvar(const ObjCImplementationDecl *D, + BugReporter &BR) { + + const ObjCInterfaceDecl* ID = D->getClassInterface(); + IvarUsageMap M; + + // Iterate over the ivars. + for (ObjCInterfaceDecl::ivar_iterator I=ID->ivar_begin(), + E=ID->ivar_end(); I!=E; ++I) { + + const ObjCIvarDecl* ID = *I; + + // Ignore ivars that... + // (a) aren't private + // (b) explicitly marked unused + // (c) are iboutlets + // (d) are unnamed bitfields + if (ID->getAccessControl() != ObjCIvarDecl::Private || + ID->getAttr() || ID->getAttr() || + ID->getAttr() || + ID->isUnnamedBitfield()) + continue; + + M[ID] = 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)."; + + BR.EmitBasicReport("Unused instance variable", "Optimization", + os.str(), I->first->getLocation()); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/PointerArithChecker.cpp new file mode 100644 index 0000000000..2b03dbcd9c --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/PointerArithChecker.cpp @@ -0,0 +1,72 @@ +//=== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class PointerArithChecker + : public CheckerVisitor { + BuiltinBug *BT; +public: + PointerArithChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *PointerArithChecker::getTag() { + static int x; + return &x; +} + +void PointerArithChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + if (B->getOpcode() != BO_Sub && B->getOpcode() != BO_Add) + return; + + const GRState *state = C.getState(); + SVal LV = state->getSVal(B->getLHS()); + SVal RV = state->getSVal(B->getRHS()); + + const MemRegion *LR = LV.getAsRegion(); + + if (!LR || !RV.isConstant()) + return; + + // If pointer arithmetic is done on variables of non-array type, this often + // means behavior rely on memory organization, which is dangerous. + if (isa(LR) || isa(LR) || + isa(LR)) { + + if (ExplodedNode *N = C.generateNode()) { + if (!BT) + BT = new BuiltinBug("Dangerous pointer arithmetic", + "Pointer arithmetic done on non-array variables " + "means reliance on memory layout, which is " + "dangerous."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getSourceRange()); + C.EmitReport(R); + } + } +} + +void ento::RegisterPointerArithChecker(ExprEngine &Eng) { + Eng.registerCheck(new PointerArithChecker()); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/PointerSubChecker.cpp new file mode 100644 index 0000000000..1ba60f7541 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/PointerSubChecker.cpp @@ -0,0 +1,79 @@ +//=== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class PointerSubChecker + : public CheckerVisitor { + BuiltinBug *BT; +public: + PointerSubChecker() : BT(0) {} + static void *getTag(); + void PreVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} + +void *PointerSubChecker::getTag() { + static int x; + return &x; +} + +void PointerSubChecker::PreVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + // 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; + + const GRState *state = C.getState(); + SVal LV = state->getSVal(B->getLHS()); + SVal RV = state->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(BaseLR) || isa(BaseRR)) + return; + + if (ExplodedNode *N = C.generateNode()) { + if (!BT) + BT = new BuiltinBug("Pointer subtraction", + "Subtraction of two pointers that do not point to " + "the same memory chunk may cause incorrect result."); + RangedBugReport *R = new RangedBugReport(*BT, BT->getDescription(), N); + R->addRange(B->getSourceRange()); + C.EmitReport(R); + } +} + +void ento::RegisterPointerSubChecker(ExprEngine &Eng) { + Eng.registerCheck(new PointerSubChecker()); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/PthreadLockChecker.cpp new file mode 100644 index 0000000000..7a91696c92 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/PthreadLockChecker.cpp @@ -0,0 +1,147 @@ +//===--- PthreadLockChecker.h - 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 PthreadLockChecker, a simple lock -> unlock checker. Eventually +// this shouldn't be registered with ExprEngineInternalChecks. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "ExprEngineExperimentalChecks.h" +#include "llvm/ADT/ImmutableSet.h" + +using namespace clang; +using namespace ento; + +namespace { +class PthreadLockChecker + : public CheckerVisitor { + BugType *BT; +public: + PthreadLockChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PostVisitCallExpr(CheckerContext &C, const CallExpr *CE); + + void AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock); + + void ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock); + +}; +} // end anonymous namespace + +// GDM Entry for tracking lock state. +namespace { class LockSet {}; } +namespace clang { +namespace ento { +template <> struct GRStateTrait : + public GRStatePartialTrait > { + static void* GDMIndex() { return PthreadLockChecker::getTag(); } +}; +} // end GR namespace +} // end clang namespace + +void ento::RegisterPthreadLockChecker(ExprEngine &Eng) { + Eng.registerCheck(new PthreadLockChecker()); +} + + +void PthreadLockChecker::PostVisitCallExpr(CheckerContext &C, + const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const FunctionTextRegion *R = + dyn_cast_or_null(state->getSVal(Callee).getAsRegion()); + + if (!R) + return; + + IdentifierInfo *II = R->getDecl()->getIdentifier(); + if (!II) // if no identifier, not a simple C function + return; + llvm::StringRef FName = II->getName(); + + if (FName == "pthread_mutex_lock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), false); + } + else if (FName == "pthread_mutex_trylock") { + if (CE->getNumArgs() != 1) + return; + AcquireLock(C, CE, state->getSVal(CE->getArg(0)), true); + } + else if (FName == "pthread_mutex_unlock") { + if (CE->getNumArgs() != 1) + return; + ReleaseLock(C, CE, state->getSVal(CE->getArg(0))); + } +} + +void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE, + SVal lock, bool isTryLock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + SVal X = state->getSVal(CE); + if (X.isUnknownOrUndef()) + return; + + DefinedSVal retVal = cast(X); + const GRState *lockSucc = state; + + if (isTryLock) { + // Bifurcate the state, and allow a mode where the lock acquisition fails. + const GRState *lockFail; + llvm::tie(lockFail, lockSucc) = state->assume(retVal); + assert(lockFail && lockSucc); + C.addTransition(C.generateNode(CE, lockFail)); + } + else { + // Assume that the return value was 0. + lockSucc = state->assume(retVal, false); + assert(lockSucc); + } + + // Record that the lock was acquired. + lockSucc = lockSucc->add(lockR); + + C.addTransition(lockSucc != state ? C.generateNode(CE, lockSucc) : + C.getPredecessor()); +} + +void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE, + SVal lock) { + + const MemRegion *lockR = lock.getAsRegion(); + if (!lockR) + return; + + const GRState *state = C.getState(); + + // Record that the lock was released. + // FIXME: Handle unlocking locks that were never acquired. This may + // require IPA for wrappers. + const GRState *unlockState = state->remove(lockR); + + if (state == unlockState) + return; + + C.addTransition(C.generateNode(CE, unlockState)); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/ReturnPointerRangeChecker.cpp new file mode 100644 index 0000000000..a56c1b6584 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/ReturnPointerRangeChecker.cpp @@ -0,0 +1,95 @@ +//== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class ReturnPointerRangeChecker : + public CheckerVisitor { + BuiltinBug *BT; +public: + ReturnPointerRangeChecker() : BT(0) {} + static void *getTag(); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); +}; +} + +void ento::RegisterReturnPointerRangeChecker(ExprEngine &Eng) { + Eng.registerCheck(new ReturnPointerRangeChecker()); +} + +void *ReturnPointerRangeChecker::getTag() { + static int x = 0; return &x; +} + +void ReturnPointerRangeChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + const GRState *state = C.getState(); + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + SVal V = state->getSVal(RetE); + const MemRegion *R = V.getAsRegion(); + + const ElementRegion *ER = dyn_cast_or_null(R); + if (!ER) + return; + + DefinedOrUnknownSVal Idx = cast(ER->getIndex()); + // 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()); + + const GRState *StInBound = state->assumeInBound(Idx, NumElements, true); + const GRState *StOutBound = state->assumeInBound(Idx, NumElements, false); + if (StOutBound && !StInBound) { + ExplodedNode *N = C.generateSink(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 = new BuiltinBug("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. + RangedBugReport *report = + new RangedBugReport(*BT, BT->getDescription(), N); + + report->addRange(RetE->getSourceRange()); + C.EmitReport(report); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/ReturnUndefChecker.cpp new file mode 100644 index 0000000000..50ffd322db --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/ReturnUndefChecker.cpp @@ -0,0 +1,69 @@ +//== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class ReturnUndefChecker : + public CheckerVisitor { + BuiltinBug *BT; +public: + ReturnUndefChecker() : BT(0) {} + static void *getTag(); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); +}; +} + +void ento::RegisterReturnUndefChecker(ExprEngine &Eng) { + Eng.registerCheck(new ReturnUndefChecker()); +} + +void *ReturnUndefChecker::getTag() { + static int x = 0; return &x; +} + +void ReturnUndefChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + if (!C.getState()->getSVal(RetE).isUndef()) + return; + + ExplodedNode *N = C.generateSink(); + + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Garbage return value", + "Undefined or garbage value returned to caller"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT, BT->getDescription(), N); + + report->addRange(RetE->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, RetE); + + C.EmitReport(report); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/StackAddrLeakChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/StackAddrLeakChecker.cpp new file mode 100644 index 0000000000..1ec5c32aff --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/StackAddrLeakChecker.cpp @@ -0,0 +1,205 @@ +//=== StackAddrLeakChecker.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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/ADT/SmallString.h" +using namespace clang; +using namespace ento; + +namespace { +class StackAddrLeakChecker : public CheckerVisitor { + BuiltinBug *BT_stackleak; + BuiltinBug *BT_returnstack; + +public: + StackAddrLeakChecker() : BT_stackleak(0), BT_returnstack(0) {} + static void *getTag() { + static int x; + return &x; + } + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *RS); + void evalEndPath(EndPathNodeBuilder &B, void *tag, ExprEngine &Eng); +private: + void EmitStackError(CheckerContext &C, const MemRegion *R, const Expr *RetE); + SourceRange GenName(llvm::raw_ostream &os, const MemRegion *R, + SourceManager &SM); +}; +} + +void ento::RegisterStackAddrLeakChecker(ExprEngine &Eng) { + Eng.registerCheck(new StackAddrLeakChecker()); +} + +SourceRange StackAddrLeakChecker::GenName(llvm::raw_ostream &os, + const MemRegion *R, + SourceManager &SM) { + // Get the base region, stripping away fields and elements. + R = R->getBaseRegion(); + SourceRange range; + os << "Address of "; + + // Check if the region is a compound literal. + if (const CompoundLiteralRegion* CR = dyn_cast(R)) { + const CompoundLiteralExpr* CL = CR->getLiteralExpr(); + os << "stack memory associated with a compound literal " + "declared on line " + << SM.getInstantiationLineNumber(CL->getLocStart()) + << " returned to caller"; + range = CL->getSourceRange(); + } + else if (const AllocaRegion* AR = dyn_cast(R)) { + const Expr* ARE = AR->getExpr(); + SourceLocation L = ARE->getLocStart(); + range = ARE->getSourceRange(); + os << "stack memory allocated by call to alloca() on line " + << SM.getInstantiationLineNumber(L); + } + else if (const BlockDataRegion *BR = dyn_cast(R)) { + const BlockDecl *BD = BR->getCodeRegion()->getDecl(); + SourceLocation L = BD->getLocStart(); + range = BD->getSourceRange(); + os << "stack-allocated block declared on line " + << SM.getInstantiationLineNumber(L); + } + else if (const VarRegion *VR = dyn_cast(R)) { + os << "stack memory associated with local variable '" + << VR->getString() << '\''; + range = VR->getDecl()->getSourceRange(); + } + else { + assert(false && "Invalid region in ReturnStackAddressChecker."); + } + + return range; +} + +void StackAddrLeakChecker::EmitStackError(CheckerContext &C, const MemRegion *R, + const Expr *RetE) { + ExplodedNode *N = C.generateSink(); + + if (!N) + return; + + if (!BT_returnstack) + BT_returnstack=new BuiltinBug("Return of address to stack-allocated memory"); + + // Generate a report for this bug. + llvm::SmallString<512> buf; + llvm::raw_svector_ostream os(buf); + SourceRange range = GenName(os, R, C.getSourceManager()); + os << " returned to caller"; + RangedBugReport *report = new RangedBugReport(*BT_returnstack, os.str(), N); + report->addRange(RetE->getSourceRange()); + if (range.isValid()) + report->addRange(range); + + C.EmitReport(report); +} + +void StackAddrLeakChecker::PreVisitReturnStmt(CheckerContext &C, + const ReturnStmt *RS) { + + const Expr *RetE = RS->getRetValue(); + if (!RetE) + return; + + SVal V = C.getState()->getSVal(RetE); + const MemRegion *R = V.getAsRegion(); + + if (!R || !R->hasStackStorage()) + return; + + if (R->hasStackStorage()) { + EmitStackError(C, R, RetE); + return; + } +} + +void StackAddrLeakChecker::evalEndPath(EndPathNodeBuilder &B, void *tag, + ExprEngine &Eng) { + SaveAndRestore OldHasGen(B.HasGeneratedNode); + const GRState *state = B.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: + const StackFrameContext *CurSFC; + public: + llvm::SmallVector, 10> V; + + CallBack(const LocationContext *LCtx) + : CurSFC(LCtx->getCurrentStackFrame()) {} + + bool HandleBinding(StoreManager &SMgr, Store store, + const MemRegion *region, SVal val) { + + if (!isa(region->getMemorySpace())) + return true; + + const MemRegion *vR = val.getAsRegion(); + if (!vR) + return true; + + if (const StackSpaceRegion *SSR = + dyn_cast(vR->getMemorySpace())) { + // If the global variable holds a location in the current stack frame, + // record the binding to emit a warning. + if (SSR->getStackFrame() == CurSFC) + V.push_back(std::make_pair(region, vR)); + } + + return true; + } + }; + + CallBack cb(B.getPredecessor()->getLocationContext()); + state->getStateManager().getStoreManager().iterBindings(state->getStore(),cb); + + if (cb.V.empty()) + return; + + // Generate an error node. + ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + if (!N) + return; + + if (!BT_stackleak) + BT_stackleak = + new BuiltinBug("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 (unsigned i = 0, e = cb.V.size(); i != e; ++i) { + // Generate a report for this bug. + llvm::SmallString<512> buf; + llvm::raw_svector_ostream os(buf); + SourceRange range = GenName(os, cb.V[i].second, + Eng.getContext().getSourceManager()); + os << " is still referred to by the global variable '"; + const VarRegion *VR = cast(cb.V[i].first->getBaseRegion()); + os << VR->getDecl()->getNameAsString() + << "' upon returning to the caller. This will be a dangling reference"; + RangedBugReport *report = new RangedBugReport(*BT_stackleak, os.str(), N); + if (range.isValid()) + report->addRange(range); + + Eng.getBugReporter().EmitReport(report); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/StreamChecker.cpp new file mode 100644 index 0000000000..a6d1e079c4 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/StreamChecker.cpp @@ -0,0 +1,466 @@ +//===-- 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 "ExprEngineExperimentalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" +#include "llvm/ADT/ImmutableMap.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 CheckerVisitor { + 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; + BuiltinBug *BT_nullfp, *BT_illegalwhence, *BT_doubleclose, *BT_ResourceLeak; + +public: + StreamChecker() + : II_fopen(0), II_tmpfile(0) ,II_fclose(0), II_fread(0), II_fwrite(0), + II_fseek(0), II_ftell(0), II_rewind(0), II_fgetpos(0), II_fsetpos(0), + II_clearerr(0), II_feof(0), II_ferror(0), II_fileno(0), + BT_nullfp(0), BT_illegalwhence(0), BT_doubleclose(0), + BT_ResourceLeak(0) {} + + static void *getTag() { + static int x; + return &x; + } + + virtual bool evalCallExpr(CheckerContext &C, const CallExpr *CE); + void evalDeadSymbols(CheckerContext &C, SymbolReaper &SymReaper); + void evalEndPath(EndPathNodeBuilder &B, void *tag, ExprEngine &Eng); + void PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S); + +private: + void Fopen(CheckerContext &C, const CallExpr *CE); + void Tmpfile(CheckerContext &C, const CallExpr *CE); + void Fclose(CheckerContext &C, const CallExpr *CE); + void Fread(CheckerContext &C, const CallExpr *CE); + void Fwrite(CheckerContext &C, const CallExpr *CE); + void Fseek(CheckerContext &C, const CallExpr *CE); + void Ftell(CheckerContext &C, const CallExpr *CE); + void Rewind(CheckerContext &C, const CallExpr *CE); + void Fgetpos(CheckerContext &C, const CallExpr *CE); + void Fsetpos(CheckerContext &C, const CallExpr *CE); + void Clearerr(CheckerContext &C, const CallExpr *CE); + void Feof(CheckerContext &C, const CallExpr *CE); + void Ferror(CheckerContext &C, const CallExpr *CE); + void Fileno(CheckerContext &C, const CallExpr *CE); + + void OpenFileAux(CheckerContext &C, const CallExpr *CE); + + const GRState *CheckNullStream(SVal SV, const GRState *state, + CheckerContext &C); + const GRState *CheckDoubleClose(const CallExpr *CE, const GRState *state, + CheckerContext &C); +}; + +} // end anonymous namespace + +namespace clang { +namespace ento { + template <> + struct GRStateTrait + : public GRStatePartialTrait > { + static void *GDMIndex() { return StreamChecker::getTag(); } + }; +} +} + +void ento::RegisterStreamChecker(ExprEngine &Eng) { + Eng.registerCheck(new StreamChecker()); +} + +bool StreamChecker::evalCallExpr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + SVal L = state->getSVal(Callee); + const FunctionDecl *FD = L.getAsFunctionDecl(); + if (!FD) + 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) { + OpenFileAux(C, CE); +} + +void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) { + OpenFileAux(C, CE); +} + +void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + unsigned Count = C.getNodeBuilder().getCurrentBlockCount(); + SValBuilder &svalBuilder = C.getSValBuilder(); + DefinedSVal RetVal = + cast(svalBuilder.getConjuredSymbolVal(0, CE, Count)); + state = state->BindExpr(CE, RetVal); + + ConstraintManager &CM = C.getConstraintManager(); + // Bifurcate the state into two: one with a valid FILE* pointer, the other + // with a NULL. + const GRState *stateNotNull, *stateNull; + llvm::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(Sym,StreamState::getOpened(CE)); + stateNull = + stateNull->set(Sym, StreamState::getOpenFailed(CE)); + + C.addTransition(stateNotNull); + C.addTransition(stateNull); + } +} + +void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) { + const GRState *state = CheckDoubleClose(CE, C.getState(), C); + if (state) + C.addTransition(state); +} + +void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) + return; +} + +void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(3)), state, C)) + return; +} + +void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!(state = CheckNullStream(state->getSVal(CE->getArg(0)), state, C))) + return; + // Check the legality of the 'whence' argument of 'fseek'. + SVal Whence = state->getSVal(CE->getArg(2)); + const nonloc::ConcreteInt *CI = dyn_cast(&Whence); + + if (!CI) + return; + + int64_t x = CI->getValue().getSExtValue(); + if (x >= 0 && x <= 2) + return; + + if (ExplodedNode *N = C.generateNode(state)) { + if (!BT_illegalwhence) + BT_illegalwhence = new BuiltinBug("Illegal whence argument", + "The whence argument to fseek() should be " + "SEEK_SET, SEEK_END, or SEEK_CUR."); + BugReport *R = new BugReport(*BT_illegalwhence, + BT_illegalwhence->getDescription(), N); + C.EmitReport(R); + } +} + +void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) { + const GRState *state = C.getState(); + if (!CheckNullStream(state->getSVal(CE->getArg(0)), state, C)) + return; +} + +const GRState *StreamChecker::CheckNullStream(SVal SV, const GRState *state, + CheckerContext &C) { + const DefinedSVal *DV = dyn_cast(&SV); + if (!DV) + return 0; + + ConstraintManager &CM = C.getConstraintManager(); + const GRState *stateNotNull, *stateNull; + llvm::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV); + + if (!stateNotNull && stateNull) { + if (ExplodedNode *N = C.generateSink(stateNull)) { + if (!BT_nullfp) + BT_nullfp = new BuiltinBug("NULL stream pointer", + "Stream pointer might be NULL."); + BugReport *R =new BugReport(*BT_nullfp, BT_nullfp->getDescription(), N); + C.EmitReport(R); + } + return 0; + } + return stateNotNull; +} + +const GRState *StreamChecker::CheckDoubleClose(const CallExpr *CE, + const GRState *state, + CheckerContext &C) { + SymbolRef Sym = state->getSVal(CE->getArg(0)).getAsSymbol(); + if (!Sym) + return state; + + const StreamState *SS = state->get(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.generateSink(); + if (N) { + if (!BT_doubleclose) + BT_doubleclose = new BuiltinBug("Double fclose", + "Try to close a file Descriptor already" + " closed. Cause undefined behaviour."); + BugReport *R = new BugReport(*BT_doubleclose, + BT_doubleclose->getDescription(), N); + C.EmitReport(R); + } + return NULL; + } + + // Close the File Descriptor. + return state->set(Sym, StreamState::getClosed(CE)); +} + +void StreamChecker::evalDeadSymbols(CheckerContext &C,SymbolReaper &SymReaper) { + for (SymbolReaper::dead_iterator I = SymReaper.dead_begin(), + E = SymReaper.dead_end(); I != E; ++I) { + SymbolRef Sym = *I; + const GRState *state = C.getState(); + const StreamState *SS = state->get(Sym); + if (!SS) + return; + + if (SS->isOpened()) { + ExplodedNode *N = C.generateSink(); + if (N) { + if (!BT_ResourceLeak) + BT_ResourceLeak = new BuiltinBug("Resource Leak", + "Opened File never closed. Potential Resource leak."); + BugReport *R = new BugReport(*BT_ResourceLeak, + BT_ResourceLeak->getDescription(), N); + C.EmitReport(R); + } + } + } +} + +void StreamChecker::evalEndPath(EndPathNodeBuilder &B, void *tag, + ExprEngine &Eng) { + SaveAndRestore OldHasGen(B.HasGeneratedNode); + const GRState *state = B.getState(); + typedef llvm::ImmutableMap SymMap; + SymMap M = state->get(); + + for (SymMap::iterator I = M.begin(), E = M.end(); I != E; ++I) { + StreamState SS = I->second; + if (SS.isOpened()) { + ExplodedNode *N = B.generateNode(state, tag, B.getPredecessor()); + if (N) { + if (!BT_ResourceLeak) + BT_ResourceLeak = new BuiltinBug("Resource Leak", + "Opened File never closed. Potential Resource leak."); + BugReport *R = new BugReport(*BT_ResourceLeak, + BT_ResourceLeak->getDescription(), N); + Eng.getBugReporter().EmitReport(R); + } + } + } +} + +void StreamChecker::PreVisitReturnStmt(CheckerContext &C, const ReturnStmt *S) { + const Expr *RetE = S->getRetValue(); + if (!RetE) + return; + + const GRState *state = C.getState(); + SymbolRef Sym = state->getSVal(RetE).getAsSymbol(); + + if (!Sym) + return; + + const StreamState *SS = state->get(Sym); + if(!SS) + return; + + if (SS->isOpened()) + state = state->set(Sym, StreamState::getEscaped(S)); + + C.addTransition(state); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/UndefBranchChecker.cpp new file mode 100644 index 0000000000..c0b3b7afec --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/UndefBranchChecker.cpp @@ -0,0 +1,120 @@ +//=== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" + +using namespace clang; +using namespace ento; + +namespace { + +class UndefBranchChecker : public Checker { + BuiltinBug *BT; + + struct FindUndefExpr { + GRStateManager& VM; + const GRState* St; + + FindUndefExpr(GRStateManager& V, const GRState* S) : VM(V), St(S) {} + + const Expr* FindExpr(const Expr* Ex) { + if (!MatchesCriteria(Ex)) + return 0; + + for (Stmt::const_child_iterator I = Ex->child_begin(), + E = Ex->child_end();I!=E;++I) + if (const Expr* ExI = dyn_cast_or_null(*I)) { + const Expr* E2 = FindExpr(ExI); + if (E2) return E2; + } + + return Ex; + } + + bool MatchesCriteria(const Expr* Ex) { return St->getSVal(Ex).isUndef(); } + }; + +public: + UndefBranchChecker() : BT(0) {} + static void *getTag(); + void VisitBranchCondition(BranchNodeBuilder &Builder, ExprEngine &Eng, + const Stmt *Condition, void *tag); +}; + +} + +void ento::RegisterUndefBranchChecker(ExprEngine &Eng) { + Eng.registerCheck(new UndefBranchChecker()); +} + +void *UndefBranchChecker::getTag() { + static int x; + return &x; +} + +void UndefBranchChecker::VisitBranchCondition(BranchNodeBuilder &Builder, + ExprEngine &Eng, + const Stmt *Condition, void *tag){ + const GRState *state = Builder.getState(); + SVal X = state->getSVal(Condition); + if (X.isUndef()) { + ExplodedNode *N = Builder.generateNode(state, true); + if (N) { + N->markAsSink(); + if (!BT) + BT = new BuiltinBug("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. + BlockEdge B = cast(N->getLocation()); + const Expr* Ex = cast(B.getSrc()->getTerminatorCondition()); + assert (Ex && "Block must have a terminator."); + + // 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. + assert (!N->pred_empty()); + + // 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. + ExplodedNode *PrevN = *N->pred_begin(); + ProgramPoint P = PrevN->getLocation(); + const GRState* St = N->getState(); + + if (PostStmt* PS = dyn_cast(&P)) + if (PS->getStmt() == Ex) + St = PrevN->getState(); + + FindUndefExpr FindIt(Eng.getStateManager(), St); + Ex = FindIt.FindExpr(Ex); + + // Emit the bug report. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getDescription(),N); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + R->addRange(Ex->getSourceRange()); + + Eng.getBugReporter().EmitReport(R); + } + + Builder.markInfeasible(true); + Builder.markInfeasible(false); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/UndefCapturedBlockVarChecker.cpp new file mode 100644 index 0000000000..57e698bb7c --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefCapturedBlockVarChecker + : public CheckerVisitor { + BugType *BT; + +public: + UndefCapturedBlockVarChecker() : BT(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PostVisitBlockExpr(CheckerContext &C, const BlockExpr *BE); +}; +} // end anonymous namespace + +void ento::RegisterUndefCapturedBlockVarChecker(ExprEngine &Eng) { + Eng.registerCheck(new UndefCapturedBlockVarChecker()); +} + +static const BlockDeclRefExpr *FindBlockDeclRefExpr(const Stmt *S, + const VarDecl *VD){ + if (const BlockDeclRefExpr *BR = dyn_cast(S)) + if (BR->getDecl() == VD) + return BR; + + for (Stmt::const_child_iterator I = S->child_begin(), E = S->child_end(); + I!=E; ++I) + if (const Stmt *child = *I) { + const BlockDeclRefExpr *BR = FindBlockDeclRefExpr(child, VD); + if (BR) + return BR; + } + + return NULL; +} + +void +UndefCapturedBlockVarChecker::PostVisitBlockExpr(CheckerContext &C, + const BlockExpr *BE) { + if (!BE->hasBlockDeclRefExprs()) + return; + + const GRState *state = C.getState(); + const BlockDataRegion *R = + cast(state->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; + const VarDecl *VD = VR->getDecl(); + + if (VD->getAttr() || !VD->hasLocalStorage()) + continue; + + // Get the VarRegion associated with VD in the local stack frame. + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + VR = C.getSValBuilder().getRegionManager().getVarRegion(VD, LC); + + if (state->getSVal(VR).isUndef()) + if (ExplodedNode *N = C.generateSink()) { + if (!BT) + BT = new BuiltinBug("Captured block variable is uninitialized"); + + // Generate a bug report. + llvm::SmallString<128> buf; + llvm::raw_svector_ostream os(buf); + + os << "Variable '" << VD->getName() << "' is captured by block with " + "a garbage value"; + + EnhancedBugReport *R = new EnhancedBugReport(*BT, os.str(), N); + if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD)) + R->addRange(Ex->getSourceRange()); + R->addVisitorCreator(bugreporter::registerFindLastStore, VR); + // need location of block + C.EmitReport(R); + } + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/UndefResultChecker.cpp new file mode 100644 index 0000000000..91e489bd2a --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/UndefResultChecker.cpp @@ -0,0 +1,87 @@ +//=== 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefResultChecker + : public CheckerVisitor { + + BugType *BT; + +public: + UndefResultChecker() : BT(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PostVisitBinaryOperator(CheckerContext &C, const BinaryOperator *B); +}; +} // end anonymous namespace + +void ento::RegisterUndefResultChecker(ExprEngine &Eng) { + Eng.registerCheck(new UndefResultChecker()); +} + +void UndefResultChecker::PostVisitBinaryOperator(CheckerContext &C, + const BinaryOperator *B) { + const GRState *state = C.getState(); + if (state->getSVal(B).isUndef()) { + // Generate an error node. + ExplodedNode *N = C.generateSink(); + if (!N) + return; + + if (!BT) + BT = new BuiltinBug("Result of operation is garbage or undefined"); + + llvm::SmallString<256> sbuf; + llvm::raw_svector_ostream OS(sbuf); + const Expr *Ex = NULL; + bool isLeft = true; + + if (state->getSVal(B->getLHS()).isUndef()) { + Ex = B->getLHS()->IgnoreParenCasts(); + isLeft = true; + } + else if (state->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"; + } + else { + // Neither operand was undefined, but the result is undefined. + OS << "The result of the '" + << BinaryOperator::getOpcodeStr(B->getOpcode()) + << "' expression is undefined"; + } + EnhancedBugReport *report = new EnhancedBugReport(*BT, OS.str(), N); + if (Ex) { + report->addRange(Ex->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, Ex); + } + else + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, B); + C.EmitReport(report); + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp new file mode 100644 index 0000000000..2cd293b2f4 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp @@ -0,0 +1,57 @@ +//===--- 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 "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefinedArraySubscriptChecker + : public CheckerVisitor { + BugType *BT; +public: + UndefinedArraySubscriptChecker() : BT(0) {} + static void *getTag() { + static int x = 0; + return &x; + } + void PreVisitArraySubscriptExpr(CheckerContext &C, + const ArraySubscriptExpr *A); +}; +} // end anonymous namespace + +void ento::RegisterUndefinedArraySubscriptChecker(ExprEngine &Eng) { + Eng.registerCheck(new UndefinedArraySubscriptChecker()); +} + +void +UndefinedArraySubscriptChecker::PreVisitArraySubscriptExpr(CheckerContext &C, + const ArraySubscriptExpr *A) { + if (C.getState()->getSVal(A->getIdx()).isUndef()) { + if (ExplodedNode *N = C.generateSink()) { + if (!BT) + BT = new BuiltinBug("Array subscript is undefined"); + + // Generate a report for this bug. + EnhancedBugReport *R = new EnhancedBugReport(*BT, BT->getName(), N); + R->addRange(A->getIdx()->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + A->getIdx()); + C.EmitReport(R); + } + } +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/UndefinedAssignmentChecker.cpp new file mode 100644 index 0000000000..3146bbda41 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/UndefinedAssignmentChecker.cpp @@ -0,0 +1,94 @@ +//===--- 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 UndefinedAssginmentChecker, a builtin check in ExprEngine that +// checks for assigning undefined values. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" + +using namespace clang; +using namespace ento; + +namespace { +class UndefinedAssignmentChecker + : public CheckerVisitor { + BugType *BT; +public: + UndefinedAssignmentChecker() : BT(0) {} + static void *getTag(); + virtual void PreVisitBind(CheckerContext &C, const Stmt *StoreE, + SVal location, SVal val); +}; +} + +void ento::RegisterUndefinedAssignmentChecker(ExprEngine &Eng){ + Eng.registerCheck(new UndefinedAssignmentChecker()); +} + +void *UndefinedAssignmentChecker::getTag() { + static int x = 0; + return &x; +} + +void UndefinedAssignmentChecker::PreVisitBind(CheckerContext &C, + const Stmt *StoreE, + SVal location, + SVal val) { + if (!val.isUndef()) + return; + + ExplodedNode *N = C.generateSink(); + + if (!N) + return; + + const char *str = "Assigned value is garbage or undefined"; + + if (!BT) + BT = new BuiltinBug(str); + + // Generate a report for this bug. + const Expr *ex = 0; + + while (StoreE) { + if (const BinaryOperator *B = dyn_cast(StoreE)) { + if (B->isCompoundAssignmentOp()) { + const GRState *state = C.getState(); + if (state->getSVal(B->getLHS()).isUndef()) { + str = "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(StoreE)) { + const VarDecl* VD = dyn_cast(DS->getSingleDecl()); + ex = VD->getInit(); + } + + break; + } + + EnhancedBugReport *R = new EnhancedBugReport(*BT, str, N); + if (ex) { + R->addRange(ex->getSourceRange()); + R->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, ex); + } + C.EmitReport(R); +} + diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/UnixAPIChecker.cpp new file mode 100644 index 0000000000..9092f93ba7 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/UnixAPIChecker.cpp @@ -0,0 +1,277 @@ +//= 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 "ExprEngineInternalChecks.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/StringSwitch.h" +#include + +using namespace clang; +using namespace ento; +using llvm::Optional; + +namespace { +class UnixAPIChecker : public CheckerVisitor { + enum SubChecks { + OpenFn = 0, + PthreadOnceFn = 1, + MallocZero = 2, + NumChecks + }; + + BugType *BTypes[NumChecks]; + +public: + Optional Val_O_CREAT; + +public: + UnixAPIChecker() { memset(BTypes, 0, sizeof(*BTypes) * NumChecks); } + static void *getTag() { static unsigned tag = 0; return &tag; } + + void PreVisitCallExpr(CheckerContext &C, const CallExpr *CE); +}; +} //end anonymous namespace + +void ento::RegisterUnixAPIChecker(ExprEngine &Eng) { + Eng.registerCheck(new UnixAPIChecker()); +} + +//===----------------------------------------------------------------------===// +// Utility functions. +//===----------------------------------------------------------------------===// + +static inline void LazyInitialize(BugType *&BT, const char *name) { + if (BT) + return; + BT = new BugType(name, "Unix API"); +} + +//===----------------------------------------------------------------------===// +// "open" (man 2 open) +//===----------------------------------------------------------------------===// + +static void CheckOpen(CheckerContext &C, UnixAPIChecker &UC, + const CallExpr *CE, BugType *&BT) { + // The definition of O_CREAT is platform specific. We need a better way + // of querying this information from the checking environment. + if (!UC.Val_O_CREAT.hasValue()) { + if (C.getASTContext().Target.getTriple().getVendor() == llvm::Triple::Apple) + UC.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. + return; + } + } + + LazyInitialize(BT, "Improper use of 'open'"); + + // Look at the 'oflags' argument for the O_CREAT flag. + const GRState *state = C.getState(); + + if (CE->getNumArgs() < 2) { + // The frontend should issue a warning for this case, so this is a sanity + // check. + return; + } + + // Now check if oflags has O_CREAT set. + const Expr *oflagsEx = CE->getArg(1); + const SVal V = state->getSVal(oflagsEx); + if (!isa(V)) { + // 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 = cast(V); + NonLoc ocreateFlag = + cast(C.getSValBuilder().makeIntVal(UC.Val_O_CREAT.getValue(), + oflagsEx->getType())); + SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And, + oflags, ocreateFlag, + oflagsEx->getType()); + if (maskedFlagsUC.isUnknownOrUndef()) + return; + DefinedSVal maskedFlags = cast(maskedFlagsUC); + + // Check if maskedFlags is non-zero. + const GRState *trueState, *falseState; + llvm::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() < 3) { + ExplodedNode *N = C.generateSink(trueState); + if (!N) + return; + + EnhancedBugReport *report = + new EnhancedBugReport(*BT, + "Call to 'open' requires a third argument when " + "the 'O_CREAT' flag is set", N); + report->addRange(oflagsEx->getSourceRange()); + C.EmitReport(report); + } +} + +//===----------------------------------------------------------------------===// +// pthread_once +//===----------------------------------------------------------------------===// + +static void CheckPthreadOnce(CheckerContext &C, UnixAPIChecker &, + const CallExpr *CE, BugType *&BT) { + + // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker. + // They can possibly be refactored. + + LazyInitialize(BT, "Improper use of 'pthread_once'"); + + 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. + const GRState *state = C.getState(); + const MemRegion *R = state->getSVal(CE->getArg(0)).getAsRegion(); + if (!R || !isa(R->getMemorySpace())) + return; + + ExplodedNode *N = C.generateSink(state); + if (!N) + return; + + llvm::SmallString<256> S; + llvm::raw_svector_ostream os(S); + os << "Call to 'pthread_once' uses"; + if (const VarRegion *VR = dyn_cast(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(R) && isa(R->getMemorySpace())) + os << " Perhaps you intended to declare the variable as 'static'?"; + + EnhancedBugReport *report = new EnhancedBugReport(*BT, os.str(), N); + report->addRange(CE->getArg(0)->getSourceRange()); + C.EmitReport(report); +} + +//===----------------------------------------------------------------------===// +// "malloc" with allocation size 0 +//===----------------------------------------------------------------------===// + +// FIXME: Eventually this should be rolled into the MallocChecker, but this +// check is more basic and is valuable for widespread use. +static void CheckMallocZero(CheckerContext &C, UnixAPIChecker &UC, + const CallExpr *CE, BugType *&BT) { + + // Sanity check that malloc takes one argument. + if (CE->getNumArgs() != 1) + return; + + // Check if the allocation size is 0. + const GRState *state = C.getState(); + SVal argVal = state->getSVal(CE->getArg(0)); + + if (argVal.isUnknownOrUndef()) + return; + + const GRState *trueState, *falseState; + llvm::tie(trueState, falseState) = state->assume(cast(argVal)); + + // Is the value perfectly constrained to zero? + if (falseState && !trueState) { + ExplodedNode *N = C.generateSink(falseState); + if (!N) + return; + + // FIXME: Add reference to CERT advisory, and/or C99 standard in bug + // output. + + LazyInitialize(BT, "Undefined allocation of 0 bytes"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT, "Call to 'malloc' has an allocation size" + " of 0 bytes", N); + report->addRange(CE->getArg(0)->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, + CE->getArg(0)); + C.EmitReport(report); + return; + } + // Assume the the value is non-zero going forward. + assert(trueState); + if (trueState != state) { + C.addTransition(trueState); + } +} + +//===----------------------------------------------------------------------===// +// Central dispatch function. +//===----------------------------------------------------------------------===// + +typedef void (*SubChecker)(CheckerContext &C, UnixAPIChecker &UC, + const CallExpr *CE, BugType *&BT); +namespace { + class SubCheck { + SubChecker SC; + UnixAPIChecker *UC; + BugType **BT; + public: + SubCheck(SubChecker sc, UnixAPIChecker *uc, BugType *& bt) : SC(sc), UC(uc), + BT(&bt) {} + SubCheck() : SC(NULL), UC(NULL), BT(NULL) {} + + void run(CheckerContext &C, const CallExpr *CE) const { + if (SC) + SC(C, *UC, CE, *BT); + } + }; +} // end anonymous namespace + +void UnixAPIChecker::PreVisitCallExpr(CheckerContext &C, const CallExpr *CE) { + // Get the callee. All the functions we care about are C functions + // with simple identifiers. + const GRState *state = C.getState(); + const Expr *Callee = CE->getCallee(); + const FunctionTextRegion *Fn = + dyn_cast_or_null(state->getSVal(Callee).getAsRegion()); + + if (!Fn) + return; + + const IdentifierInfo *FI = Fn->getDecl()->getIdentifier(); + if (!FI) + return; + + const SubCheck &SC = + llvm::StringSwitch(FI->getName()) + .Case("open", + SubCheck(CheckOpen, this, BTypes[OpenFn])) + .Case("pthread_once", + SubCheck(CheckPthreadOnce, this, BTypes[PthreadOnceFn])) + .Case("malloc", + SubCheck(CheckMallocZero, this, BTypes[MallocZero])) + .Default(SubCheck()); + + SC.run(C, CE); +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/UnreachableCodeChecker.cpp new file mode 100644 index 0000000000..98834485eb --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/UnreachableCodeChecker.cpp @@ -0,0 +1,223 @@ +//==- 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/AST/ParentMap.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/SourceManager.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerHelpers.h" +#include "clang/StaticAnalyzer/BugReporter/BugReporter.h" +#include "ExprEngineExperimentalChecks.h" +#include "llvm/ADT/SmallPtrSet.h" + +// The number of CFGBlock pointers we want to reserve memory for. This is used +// once for each function we analyze. +#define DEFAULT_CFGBLOCKS 256 + +using namespace clang; +using namespace ento; + +namespace { +class UnreachableCodeChecker : public Checker { +public: + static void *getTag(); + void VisitEndAnalysis(ExplodedGraph &G, + BugReporter &B, + ExprEngine &Eng); +private: + static inline const Stmt *getUnreachableStmt(const CFGBlock *CB); + void FindUnreachableEntryPoints(const CFGBlock *CB); + static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM); + static inline bool isEmptyCFGBlock(const CFGBlock *CB); + + llvm::SmallSet reachable; + llvm::SmallSet visited; +}; +} + +void *UnreachableCodeChecker::getTag() { + static int x = 0; + return &x; +} + +void ento::RegisterUnreachableCodeChecker(ExprEngine &Eng) { + Eng.registerCheck(new UnreachableCodeChecker()); +} + +void UnreachableCodeChecker::VisitEndAnalysis(ExplodedGraph &G, + BugReporter &B, + ExprEngine &Eng) { + // Bail out if we didn't cover all paths + if (Eng.hasWorkRemaining()) + return; + + CFG *C = 0; + ParentMap *PM = 0; + // Iterate over ExplodedGraph + for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end(); + I != E; ++I) { + const ProgramPoint &P = I->getLocation(); + const LocationContext *LC = P.getLocationContext(); + + // Save the CFG if we don't have it already + if (!C) + C = LC->getAnalysisContext()->getUnoptimizedCFG(); + if (!PM) + PM = &LC->getParentMap(); + + if (const BlockEntrance *BE = dyn_cast(&P)) { + const CFGBlock *CB = BE->getBlock(); + reachable.insert(CB->getBlockID()); + } + } + + // Bail out if we didn't get the CFG or the ParentMap. + if (!C || !PM) + return; + + ASTContext &Ctx = B.getContext(); + + // 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); + + // 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 (CB->size() > 0 && isInvalidPath(CB, *PM)) + continue; + + // Special case for __builtin_unreachable. + // FIXME: This should be extended to include other unreachable markers, + // such as llvm_unreachable. + if (!CB->empty()) { + CFGElement First = CB->front(); + if (CFGStmt S = First.getAs()) { + if (const CallExpr *CE = dyn_cast(S.getStmt())) { + if (CE->isBuiltinCall(Ctx) == Builtin::BI__builtin_unreachable) + continue; + } + } + } + + // We found a block that wasn't covered - find the statement to report + SourceRange SR; + SourceLocation SL; + if (const Stmt *S = getUnreachableStmt(CB)) { + SR = S->getSourceRange(); + SL = S->getLocStart(); + if (SR.isInvalid() || SL.isInvalid()) + 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("Unreachable code", "Dead code", "This statement is never" + " executed", SL, SR); + } +} + +// Recursively finds the entry point(s) for this dead CFGBlock. +void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB) { + visited.insert(CB->getBlockID()); + + for (CFGBlock::const_pred_iterator I = CB->pred_begin(), E = CB->pred_end(); + I != E; ++I) { + 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); + } + } +} + +// 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 (CFGStmt S = I->getAs()) + return S; + } + if (const Stmt *S = CB->getTerminator()) + return S; + else + return 0; +} + +// 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(); + + // Get the predecessor block's terminator conditon + 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 + if (containsMacro(cond) || containsEnum(cond) + || containsStaticLocal(cond) || containsBuiltinOffsetOf(cond) + || containsStmt(cond)) + return true; + + return false; +} + +// Returns true if the given CFGBlock is empty +bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) { + return CB->getLabel() == 0 // No labels + && CB->size() == 0 // No statements + && CB->getTerminator() == 0; // No terminator +} diff --git a/lib/StaticAnalyzer/EntoSA/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/EntoSA/Checkers/VLASizeChecker.cpp new file mode 100644 index 0000000000..e24521af53 --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/Checkers/VLASizeChecker.cpp @@ -0,0 +1,138 @@ +//=== 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 "ExprEngineInternalChecks.h" +#include "clang/AST/CharUnits.h" +#include "clang/StaticAnalyzer/BugReporter/BugType.h" +#include "clang/StaticAnalyzer/PathSensitive/CheckerVisitor.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" + +using namespace clang; +using namespace ento; + +namespace { +class VLASizeChecker : public CheckerVisitor { + BugType *BT_zero; + BugType *BT_undef; + +public: + VLASizeChecker() : BT_zero(0), BT_undef(0) {} + static void *getTag() { static int tag = 0; return &tag; } + void PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS); +}; +} // end anonymous namespace + +void ento::RegisterVLASizeChecker(ExprEngine &Eng) { + Eng.registerCheck(new VLASizeChecker()); +} + +void VLASizeChecker::PreVisitDeclStmt(CheckerContext &C, const DeclStmt *DS) { + if (!DS->isSingleDecl()) + return; + + const VarDecl *VD = dyn_cast(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(); + const GRState *state = C.getState(); + SVal sizeV = state->getSVal(SE); + + if (sizeV.isUndef()) { + // Generate an error node. + ExplodedNode *N = C.generateSink(); + if (!N) + return; + + if (!BT_undef) + BT_undef = new BuiltinBug("Declared variable-length array (VLA) uses a " + "garbage value as its size"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_undef, BT_undef->getName(), N); + report->addRange(SE->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + C.EmitReport(report); + 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 zero. + DefinedSVal sizeD = cast(sizeV); + + const GRState *stateNotZero, *stateZero; + llvm::tie(stateNotZero, stateZero) = state->assume(sizeD); + + if (stateZero && !stateNotZero) { + ExplodedNode* N = C.generateSink(stateZero); + if (!BT_zero) + BT_zero = new BuiltinBug("Declared variable-length array (VLA) has zero " + "size"); + + EnhancedBugReport *report = + new EnhancedBugReport(*BT_zero, BT_zero->getName(), N); + report->addRange(SE->getSourceRange()); + report->addVisitorCreator(bugreporter::registerTrackNullOrUndefValue, SE); + C.EmitReport(report); + 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. + + // Convert the array length to size_t. + SValBuilder &svalBuilder = C.getSValBuilder(); + QualType SizeTy = Ctx.getSizeType(); + NonLoc ArrayLength = cast(svalBuilder.evalCast(sizeD, SizeTy, + SE->getType())); + + // 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, + cast(EleSizeVal), SizeTy); + + // Finally, assume that the array's extent matches the given size. + const LocationContext *LC = C.getPredecessor()->getLocationContext(); + DefinedOrUnknownSVal Extent = + state->getRegion(VD, LC)->getExtent(svalBuilder); + DefinedOrUnknownSVal ArraySize = cast(ArraySizeVal); + 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); +} diff --git a/lib/StaticAnalyzer/EntoSA/CoreEngine.cpp b/lib/StaticAnalyzer/EntoSA/CoreEngine.cpp new file mode 100644 index 0000000000..a23c7f9d2d --- /dev/null +++ b/lib/StaticAnalyzer/EntoSA/CoreEngine.cpp @@ -0,0 +1,809 @@ +//==- CoreEngine.cpp - Path-Sensitive Dataflow Engine ------------*- 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 generic engine for intraprocedural, path-sensitive, +// dataflow analysis via graph reachability engine. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathSensitive/AnalysisManager.h" +#include "clang/StaticAnalyzer/PathSensitive/CoreEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/Index/TranslationUnit.h" +#include "clang/AST/Expr.h" +#include "llvm/Support/Casting.h" +#include "llvm/ADT/DenseMap.h" +#include +#include + +using llvm::cast; +using llvm::isa; +using namespace clang; +using namespace ento; + +// This should be removed in the future. +namespace clang { +namespace ento { +TransferFuncs* MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, + const LangOptions& lopts); +} +} + +//===----------------------------------------------------------------------===// +// Worklist classes for exploration of reachable states. +//===----------------------------------------------------------------------===// + +WorkList::Visitor::~Visitor() {} + +namespace { +class DFS : public WorkList { + llvm::SmallVector Stack; +public: + virtual bool hasWork() const { + return !Stack.empty(); + } + + virtual void Enqueue(const WorkListUnit& U) { + Stack.push_back(U); + } + + virtual WorkListUnit Dequeue() { + assert (!Stack.empty()); + const WorkListUnit& U = Stack.back(); + Stack.pop_back(); // This technically "invalidates" U, but we are fine. + return U; + } + + virtual bool VisitItemsInWorkList(Visitor &V) { + for (llvm::SmallVectorImpl::iterator + I = Stack.begin(), E = Stack.end(); I != E; ++I) { + if (V.Visit(*I)) + return true; + } + return false; + } +}; + +class BFS : public WorkList { + std::deque Queue; +public: + virtual bool hasWork() const { + return !Queue.empty(); + } + + virtual void Enqueue(const WorkListUnit& U) { + Queue.push_front(U); + } + + virtual WorkListUnit Dequeue() { + WorkListUnit U = Queue.front(); + Queue.pop_front(); + return U; + } + + virtual bool VisitItemsInWorkList(Visitor &V) { + for (std::deque::iterator + I = Queue.begin(), E = Queue.end(); I != E; ++I) { + if (V.Visit(*I)) + return true; + } + return false; + } +}; + +} // end anonymous 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() {} + +WorkList *WorkList::MakeDFS() { return new DFS(); } +WorkList *WorkList::MakeBFS() { return new BFS(); } + +namespace { + class BFSBlockDFSContents : public WorkList { + std::deque Queue; + llvm::SmallVector Stack; + public: + virtual bool hasWork() const { + return !Queue.empty() || !Stack.empty(); + } + + virtual void Enqueue(const WorkListUnit& U) { + if (isa(U.getNode()->getLocation())) + Queue.push_front(U); + else + Stack.push_back(U); + } + + virtual WorkListUnit Dequeue() { + // 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; + } + virtual bool VisitItemsInWorkList(Visitor &V) { + for (llvm::SmallVectorImpl::iterator + I = Stack.begin(), E = Stack.end(); I != E; ++I) { + if (V.Visit(*I)) + return true; + } + for (std::deque::iterator + I = Queue.begin(), E = Queue.end(); I != E; ++I) { + if (V.Visit(*I)) + return true; + } + return false; + } + + }; +} // end anonymous namespace + +WorkList* WorkList::MakeBFSBlockDFSContents() { + return new BFSBlockDFSContents(); +} + +//===----------------------------------------------------------------------===// +// Core analysis engine. +//===----------------------------------------------------------------------===// + +/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. +bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, + const GRState *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."); + + // 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) + // Generate the root. + generateNode(StartLoc, getInitialState(L), 0); + else + generateNode(StartLoc, InitState, 0); + } + + // Check if we have a steps limit + bool UnlimitedSteps = Steps == 0; + + while (WList->hasWork()) { + if (!UnlimitedSteps) { + if (Steps == 0) + break; + --Steps; + } + + const WorkListUnit& WU = WList->Dequeue(); + + // Set the current block counter. + WList->setBlockCounter(WU.getBlockCounter()); + + // Retrieve the node. + ExplodedNode* Node = WU.getNode(); + + // Dispatch on the location type. + switch (Node->getLocation().getKind()) { + case ProgramPoint::BlockEdgeKind: + HandleBlockEdge(cast(Node->getLocation()), Node); + break; + + case ProgramPoint::BlockEntranceKind: + HandleBlockEntrance(cast(Node->getLocation()), Node); + break; + + case ProgramPoint::BlockExitKind: + assert (false && "BlockExit location never occur in forward analysis."); + break; + + case ProgramPoint::CallEnterKind: + HandleCallEnter(cast(Node->getLocation()), WU.getBlock(), + WU.getIndex(), Node); + break; + + case ProgramPoint::CallExitKind: + HandleCallExit(cast(Node->getLocation()), Node); + break; + + default: + assert(isa(Node->getLocation()) || + isa(Node->getLocation())); + HandlePostStmt(WU.getBlock(), WU.getIndex(), Node); + break; + } + } + + SubEng.ProcessEndWorklist(hasWorkRemaining()); + return WList->hasWork(); +} + +void CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, + unsigned Steps, + const GRState *InitState, + ExplodedNodeSet &Dst) { + ExecuteWorkList(L, Steps, InitState); + for (llvm::SmallVectorImpl::iterator I = G->EndNodes.begin(), + E = G->EndNodes.end(); I != E; ++I) { + Dst.Add(*I); + } +} + +void CoreEngine::HandleCallEnter(const CallEnter &L, const CFGBlock *Block, + unsigned Index, ExplodedNode *Pred) { + CallEnterNodeBuilder Builder(*this, Pred, L.getCallExpr(), + L.getCalleeContext(), Block, Index); + ProcessCallEnter(Builder); +} + +void CoreEngine::HandleCallExit(const CallExit &L, ExplodedNode *Pred) { + CallExitNodeBuilder Builder(*this, Pred); + ProcessCallExit(Builder); +} + +void CoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { + + const CFGBlock* Blk = L.getDst(); + + // Check if we are entering the EXIT block. + if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { + + assert (L.getLocationContext()->getCFG()->getExit().size() == 0 + && "EXIT block cannot contain Stmts."); + + // Process the final state transition. + EndPathNodeBuilder Builder(Blk, Pred, this); + ProcessEndPath(Builder); + + // This path is done. Don't enqueue any more nodes. + return; + } + + // FIXME: Should we allow ProcessBlockEntrance to also manipulate state? + + if (ProcessBlockEntrance(Blk, Pred, WList->getBlockCounter())) + generateNode(BlockEntrance(Blk, Pred->getLocationContext()), + Pred->State, Pred); + else { + blocksAborted.push_back(std::make_pair(L, Pred)); + } +} + +void CoreEngine::HandleBlockEntrance(const BlockEntrance& L, + ExplodedNode* Pred) { + + // Increment the block counter. + BlockCounter Counter = WList->getBlockCounter(); + Counter = BCounterFactory.IncrementCount(Counter, + Pred->getLocationContext()->getCurrentStackFrame(), + L.getBlock()->getBlockID()); + WList->setBlockCounter(Counter); + + // Process the entrance of the block. + if (CFGElement E = L.getFirstElement()) { + StmtNodeBuilder Builder(L.getBlock(), 0, Pred, this, + SubEng.getStateManager()); + ProcessElement(E, Builder); + } + else + HandleBlockExit(L.getBlock(), Pred); +} + +void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) { + + if (const Stmt* Term = B->getTerminator()) { + switch (Term->getStmtClass()) { + default: + assert(false && "Analysis for this terminator not implemented."); + break; + + case Stmt::BinaryOperatorClass: // '&&' and '||' + HandleBranch(cast(Term)->getLHS(), Term, B, Pred); + return; + + case Stmt::ConditionalOperatorClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + // FIXME: Use constant-folding in CFG construction to simplify this + // case. + + case Stmt::ChooseExprClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::DoStmtClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::ForStmtClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::ContinueStmtClass: + case Stmt::BreakStmtClass: + case Stmt::GotoStmtClass: + break; + + case Stmt::IfStmtClass: + HandleBranch(cast(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(Term)->getTarget(), + *(B->succ_begin()), this); + + 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(Term)->getCond(), + this); + + ProcessSwitch(builder); + return; + } + + case Stmt::WhileStmtClass: + HandleBranch(cast(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::HandleBranch(const Stmt* Cond, const Stmt* Term, + const CFGBlock * B, ExplodedNode* Pred) { + assert (B->succ_size() == 2); + + BranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), + Pred, this); + + ProcessBranch(Cond, Term, Builder); +} + +void CoreEngine::HandlePostStmt(const CFGBlock* B, unsigned StmtIdx, + ExplodedNode* Pred) { + assert (!B->empty()); + + if (StmtIdx == B->size()) + HandleBlockExit(B, Pred); + else { + StmtNodeBuilder Builder(B, StmtIdx, Pred, this, + SubEng.getStateManager()); + ProcessElement((*B)[StmtIdx], Builder); + } +} + +/// generateNode - Utility method to generate nodes, hook up successors, +/// and add nodes to the worklist. +void CoreEngine::generateNode(const ProgramPoint& Loc, + const GRState* State, ExplodedNode* Pred) { + + bool IsNew; + ExplodedNode* Node = G->getNode(Loc, State, &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); +} + +StmtNodeBuilder::StmtNodeBuilder(const CFGBlock* b, unsigned idx, + ExplodedNode* N, CoreEngine* e, + GRStateManager &mgr) + : Eng(*e), B(*b), Idx(idx), Pred(N), Mgr(mgr), + PurgingDeadSymbols(false), BuildSinks(false), HasGeneratedNode(false), + PointKind(ProgramPoint::PostStmtKind), Tag(0) { + Deferred.insert(N); + CleanedState = Pred->getState(); +} + +StmtNodeBuilder::~StmtNodeBuilder() { + for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) + if (!(*I)->isSink()) + GenerateAutoTransition(*I); +} + +void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { + assert (!N->isSink()); + + // Check if this node entered a callee. + if (isa(N->getLocation())) { + // Still use the index of the CallExpr. It's needed to create the callee + // StackFrameContext. + Eng.WList->Enqueue(N, &B, Idx); + return; + } + + // Do not create extra nodes. Move to the next CFG element. + if (isa(N->getLocation())) { + Eng.WList->Enqueue(N, &B, Idx+1); + return; + } + + PostStmt Loc(getStmt(), N->getLocationContext()); + + if (Loc == N->getLocation()) { + // Note: 'N' should be a fresh node because otherwise it shouldn't be + // a member of Deferred. + Eng.WList->Enqueue(N, &B, Idx+1); + return; + } + + bool IsNew; + ExplodedNode* Succ = Eng.G->getNode(Loc, N->State, &IsNew); + Succ->addPredecessor(N, *Eng.G); + + if (IsNew) + Eng.WList->Enqueue(Succ, &B, Idx+1); +} + +ExplodedNode* StmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, + ExplodedNode* Pred, const GRState* St, + ProgramPoint::Kind K) { + + ExplodedNode* N = generateNode(S, St, Pred, K); + + if (N) { + if (BuildSinks) + N->markAsSink(); + else + Dst.Add(N); + } + + return N; +} + +static ProgramPoint GetProgramPoint(const Stmt *S, ProgramPoint::Kind K, + const LocationContext *LC, const void *tag){ + switch (K) { + default: + assert(false && "Unhandled ProgramPoint kind"); + case ProgramPoint::PreStmtKind: + return PreStmt(S, LC, tag); + case ProgramPoint::PostStmtKind: + return PostStmt(S, LC, tag); + case ProgramPoint::PreLoadKind: + return PreLoad(S, LC, tag); + case ProgramPoint::PostLoadKind: + return PostLoad(S, LC, tag); + case ProgramPoint::PreStoreKind: + return PreStore(S, LC, tag); + case ProgramPoint::PostStoreKind: + return PostStore(S, LC, tag); + case ProgramPoint::PostLValueKind: + return PostLValue(S, LC, tag); + case ProgramPoint::PostPurgeDeadSymbolsKind: + return PostPurgeDeadSymbols(S, LC, tag); + } +} + +ExplodedNode* +StmtNodeBuilder::generateNodeInternal(const Stmt* S, const GRState* state, + ExplodedNode* Pred, + ProgramPoint::Kind K, + const void *tag) { + + const ProgramPoint &L = GetProgramPoint(S, K, Pred->getLocationContext(),tag); + return generateNodeInternal(L, state, Pred); +} + +ExplodedNode* +StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, + const GRState* State, + ExplodedNode* Pred) { + bool IsNew; + ExplodedNode* N = Eng.G->getNode(Loc, State, &IsNew); + N->addPredecessor(Pred, *Eng.G); + Deferred.erase(Pred); + + if (IsNew) { + Deferred.insert(N); + return N; + } + + return NULL; +} + +ExplodedNode* BranchNodeBuilder::generateNode(const GRState* State, + bool branch) { + + // If the branch has been marked infeasible we should not generate a node. + if (!isFeasible(branch)) + return NULL; + + bool IsNew; + + ExplodedNode* Succ = + Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()), + State, &IsNew); + + Succ->addPredecessor(Pred, *Eng.G); + + if (branch) + GeneratedTrue = true; + else + GeneratedFalse = true; + + if (IsNew) { + Deferred.push_back(Succ); + return Succ; + } + + return NULL; +} + +BranchNodeBuilder::~BranchNodeBuilder() { + if (!GeneratedTrue) generateNode(Pred->State, true); + if (!GeneratedFalse) generateNode(Pred->State, false); + + for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) + if (!(*I)->isSink()) Eng.WList->Enqueue(*I); +} + + +ExplodedNode* +IndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St, + bool isSink) { + bool IsNew; + + ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), + Pred->getLocationContext()), St, &IsNew); + + Succ->addPredecessor(Pred, *Eng.G); + + if (IsNew) { + + if (isSink) + Succ->markAsSink(); + else + Eng.WList->Enqueue(Succ); + + return Succ; + } + + return NULL; +} + + +ExplodedNode* +SwitchNodeBuilder::generateCaseStmtNode(const iterator& I, const GRState* St){ + + bool IsNew; + + ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), + Pred->getLocationContext()), St, &IsNew); + Succ->addPredecessor(Pred, *Eng.G); + + if (IsNew) { + Eng.WList->Enqueue(Succ); + return Succ; + } + + return NULL; +} + + +ExplodedNode* +SwitchNodeBuilder::generateDefaultCaseNode(const GRState* St, bool isSink) { + + // Get the block for the default case. + assert (Src->succ_rbegin() != Src->succ_rend()); + CFGBlock* DefaultBlock = *Src->succ_rbegin(); + + bool IsNew; + + ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, + Pred->getLocationContext()), St, &IsNew); + Succ->addPredecessor(Pred, *Eng.G); + + if (IsNew) { + if (isSink) + Succ->markAsSink(); + else + Eng.WList->Enqueue(Succ); + + return Succ; + } + + return NULL; +} + +EndPathNodeBuilder::~EndPathNodeBuilder() { + // Auto-generate an EOP node if one has not been generated. + if (!HasGeneratedNode) { + // If we are in an inlined call, generate CallExit node. + if (Pred->getLocationContext()->getParent()) + GenerateCallExitNode(Pred->State); + else + generateNode(Pred->State); + } +} + +ExplodedNode* +EndPathNodeBuilder::generateNode(const GRState* State, const void *tag, + ExplodedNode* P) { + HasGeneratedNode = true; + bool IsNew; + + ExplodedNode* Node = Eng.G->getNode(BlockEntrance(&B, + Pred->getLocationContext(), tag), State, &IsNew); + + Node->addPredecessor(P ? P : Pred, *Eng.G); + + if (IsNew) { + Eng.G->addEndOfPath(Node); + return Node; + } + + return NULL; +} + +void EndPathNodeBuilder::GenerateCallExitNode(const GRState *state) { + HasGeneratedNode = true; + // Create a CallExit node and enqueue it. + const StackFrameContext *LocCtx + = cast(Pred->getLocationContext()); + const Stmt *CE = LocCtx->getCallSite(); + + // Use the the callee location context. + CallExit Loc(CE, LocCtx); + + bool isNew; + ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); + Node->addPredecessor(Pred, *Eng.G); + + if (isNew) + Eng.WList->Enqueue(Node); +} + + +void CallEnterNodeBuilder::generateNode(const GRState *state) { + // Check if the callee is in the same translation unit. + if (CalleeCtx->getTranslationUnit() != + Pred->getLocationContext()->getTranslationUnit()) { + // Create a new engine. We must be careful that the new engine should not + // reference data structures owned by the old engine. + + AnalysisManager &OldMgr = Eng.SubEng.getAnalysisManager(); + + // Get the callee's translation unit. + idx::TranslationUnit *TU = CalleeCtx->getTranslationUnit(); + + // Create a new AnalysisManager with components of the callee's + // TranslationUnit. + // The Diagnostic is actually shared when we create ASTUnits from AST files. + AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(), + OldMgr.getLangOptions(), + OldMgr.getPathDiagnosticClient(), + OldMgr.getStoreManagerCreator(), + OldMgr.getConstraintManagerCreator(), + OldMgr.getIndexer(), + OldMgr.getMaxNodes(), OldMgr.getMaxVisit(), + OldMgr.shouldVisualizeGraphviz(), + OldMgr.shouldVisualizeUbigraph(), + OldMgr.shouldPurgeDead(), + OldMgr.shouldEagerlyAssume(), + OldMgr.shouldTrimGraph(), + OldMgr.shouldInlineCall(), + OldMgr.getAnalysisContextManager().getUseUnoptimizedCFG(), + OldMgr.getAnalysisContextManager().getAddImplicitDtors(), + OldMgr.getAnalysisContextManager().getAddInitializers()); + llvm::OwningPtr TF(MakeCFRefCountTF(AMgr.getASTContext(), + /* GCEnabled */ false, + AMgr.getLangOptions())); + // Create the new engine. + ExprEngine NewEng(AMgr, TF.take()); + + // Create the new LocationContext. + AnalysisContext *NewAnaCtx = AMgr.getAnalysisContext(CalleeCtx->getDecl(), + CalleeCtx->getTranslationUnit()); + const StackFrameContext *OldLocCtx = CalleeCtx; + const StackFrameContext *NewLocCtx = AMgr.getStackFrame(NewAnaCtx, + OldLocCtx->getParent(), + OldLocCtx->getCallSite(), + OldLocCtx->getCallSiteBlock(), + OldLocCtx->getIndex()); + + // Now create an initial state for the new engine. + const GRState *NewState = NewEng.getStateManager().MarshalState(state, + NewLocCtx); + ExplodedNodeSet ReturnNodes; + NewEng.ExecuteWorkListWithInitialState(NewLocCtx, AMgr.getMaxNodes(), + NewState, ReturnNodes); + return; + } + + // Get the callee entry block. + const CFGBlock *Entry = &(CalleeCtx->getCFG()->getEntry()); + assert(Entry->empty()); + assert(Entry->succ_size() == 1); + + // Get the solitary successor. + const CFGBlock *SuccB = *(Entry->succ_begin()); + + // Construct an edge representing the starting location in the callee. + BlockEdge Loc(Entry, SuccB, CalleeCtx); + + bool isNew; + ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); + Node->addPredecessor(const_cast(Pred), *Eng.G); + + if (isNew) + Eng.WList->Enqueue(Node); +} + +void CallExitNodeBuilder::generateNode(const GRState *state) { + // Get the callee's location context. + const StackFrameContext *LocCtx + = cast(Pred->getLocationContext()); + // When exiting an implicit automatic obj dtor call, the callsite is the Stmt + // that triggers the dtor. + PostStmt Loc(LocCtx->getCallSite(), LocCtx->getParent()); + bool isNew; + ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); + Node->addPredecessor(const_cast(Pred), *Eng.G); + if (isNew) + Eng.WList->Enqueue(Node, LocCtx->getCallSiteBlock(), + LocCtx->getIndex() + 1); +} diff --git a/lib/EntoSA/Environment.cpp b/lib/StaticAnalyzer/EntoSA/Environment.cpp similarity index 99% rename from lib/EntoSA/Environment.cpp rename to lib/StaticAnalyzer/EntoSA/Environment.cpp index eecbabbb2e..1c74352d2c 100644 --- a/lib/EntoSA/Environment.cpp +++ b/lib/StaticAnalyzer/EntoSA/Environment.cpp @@ -13,7 +13,7 @@ #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/CFG.h" -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/ExplodedGraph.cpp b/lib/StaticAnalyzer/EntoSA/ExplodedGraph.cpp similarity index 98% rename from lib/EntoSA/ExplodedGraph.cpp rename to lib/StaticAnalyzer/EntoSA/ExplodedGraph.cpp index ec66ba6028..4c4612fab5 100644 --- a/lib/EntoSA/ExplodedGraph.cpp +++ b/lib/StaticAnalyzer/EntoSA/ExplodedGraph.cpp @@ -12,8 +12,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/ExplodedGraph.h" -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" #include "clang/AST/Stmt.h" #include "llvm/ADT/DenseSet.h" #include "llvm/ADT/DenseMap.h" diff --git a/lib/EntoSA/FlatStore.cpp b/lib/StaticAnalyzer/EntoSA/FlatStore.cpp similarity index 99% rename from lib/EntoSA/FlatStore.cpp rename to lib/StaticAnalyzer/EntoSA/FlatStore.cpp index 8ded4d16b6..1558db7483 100644 --- a/lib/EntoSA/FlatStore.cpp +++ b/lib/StaticAnalyzer/EntoSA/FlatStore.cpp @@ -7,7 +7,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" #include "llvm/ADT/ImmutableIntervalMap.h" #include "llvm/Support/ErrorHandling.h" diff --git a/lib/EntoSA/GRState.cpp b/lib/StaticAnalyzer/EntoSA/GRState.cpp similarity index 98% rename from lib/EntoSA/GRState.cpp rename to lib/StaticAnalyzer/EntoSA/GRState.cpp index 62396230ff..12c8968367 100644 --- a/lib/EntoSA/GRState.cpp +++ b/lib/StaticAnalyzer/EntoSA/GRState.cpp @@ -12,10 +12,10 @@ //===----------------------------------------------------------------------===// #include "clang/Analysis/CFG.h" -#include "clang/EntoSA/PathSensitive/GRStateTrait.h" -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/PathSensitive/SubEngine.h" -#include "clang/EntoSA/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" #include "llvm/Support/raw_ostream.h" using namespace clang; diff --git a/lib/EntoSA/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/EntoSA/HTMLDiagnostics.cpp similarity index 99% rename from lib/EntoSA/HTMLDiagnostics.cpp rename to lib/StaticAnalyzer/EntoSA/HTMLDiagnostics.cpp index a0d338ae1e..659c32d58d 100644 --- a/lib/EntoSA/HTMLDiagnostics.cpp +++ b/lib/StaticAnalyzer/EntoSA/HTMLDiagnostics.cpp @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathDiagnosticClients.h" -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/PathDiagnosticClients.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/Basic/SourceManager.h" diff --git a/lib/EntoSA/Makefile b/lib/StaticAnalyzer/EntoSA/Makefile similarity index 93% rename from lib/EntoSA/Makefile rename to lib/StaticAnalyzer/EntoSA/Makefile index 9678138695..08ace28e35 100644 --- a/lib/EntoSA/Makefile +++ b/lib/StaticAnalyzer/EntoSA/Makefile @@ -12,7 +12,7 @@ ##===----------------------------------------------------------------------===## CLANG_LEVEL := ../.. -LIBRARYNAME := clangEntoCore +LIBRARYNAME := clangStaticAnalyzerCore PARALLEL_DIRS := Checkers include $(CLANG_LEVEL)/Makefile diff --git a/lib/EntoSA/ManagerRegistry.cpp b/lib/StaticAnalyzer/EntoSA/ManagerRegistry.cpp similarity index 93% rename from lib/EntoSA/ManagerRegistry.cpp rename to lib/StaticAnalyzer/EntoSA/ManagerRegistry.cpp index 99b7a70748..3f9ed61db7 100644 --- a/lib/EntoSA/ManagerRegistry.cpp +++ b/lib/StaticAnalyzer/EntoSA/ManagerRegistry.cpp @@ -11,7 +11,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/ManagerRegistry.h" +#include "clang/StaticAnalyzer/ManagerRegistry.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/MemRegion.cpp b/lib/StaticAnalyzer/EntoSA/MemRegion.cpp similarity index 99% rename from lib/EntoSA/MemRegion.cpp rename to lib/StaticAnalyzer/EntoSA/MemRegion.cpp index ff0f34044e..e465224f42 100644 --- a/lib/EntoSA/MemRegion.cpp +++ b/lib/StaticAnalyzer/EntoSA/MemRegion.cpp @@ -13,8 +13,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/MemRegion.h" -#include "clang/EntoSA/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/PathSensitive/SValBuilder.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Analysis/Support/BumpVector.h" #include "clang/AST/CharUnits.h" diff --git a/lib/EntoSA/PathDiagnostic.cpp b/lib/StaticAnalyzer/EntoSA/PathDiagnostic.cpp similarity index 99% rename from lib/EntoSA/PathDiagnostic.cpp rename to lib/StaticAnalyzer/EntoSA/PathDiagnostic.cpp index bf8c8b0d3d..f05dec820d 100644 --- a/lib/EntoSA/PathDiagnostic.cpp +++ b/lib/StaticAnalyzer/EntoSA/PathDiagnostic.cpp @@ -11,7 +11,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" #include "clang/AST/Expr.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclObjC.h" diff --git a/lib/EntoSA/PlistDiagnostics.cpp b/lib/StaticAnalyzer/EntoSA/PlistDiagnostics.cpp similarity index 99% rename from lib/EntoSA/PlistDiagnostics.cpp rename to lib/StaticAnalyzer/EntoSA/PlistDiagnostics.cpp index 6e54d494c5..ecc0cff5c3 100644 --- a/lib/EntoSA/PlistDiagnostics.cpp +++ b/lib/StaticAnalyzer/EntoSA/PlistDiagnostics.cpp @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathDiagnosticClients.h" -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/PathDiagnosticClients.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" #include "clang/Basic/SourceManager.h" #include "clang/Basic/FileManager.h" #include "clang/Lex/Preprocessor.h" diff --git a/lib/EntoSA/README.txt b/lib/StaticAnalyzer/EntoSA/README.txt similarity index 100% rename from lib/EntoSA/README.txt rename to lib/StaticAnalyzer/EntoSA/README.txt diff --git a/lib/EntoSA/RangeConstraintManager.cpp b/lib/StaticAnalyzer/EntoSA/RangeConstraintManager.cpp similarity index 98% rename from lib/EntoSA/RangeConstraintManager.cpp rename to lib/StaticAnalyzer/EntoSA/RangeConstraintManager.cpp index 5c2d674515..c89fa50b16 100644 --- a/lib/EntoSA/RangeConstraintManager.cpp +++ b/lib/StaticAnalyzer/EntoSA/RangeConstraintManager.cpp @@ -13,10 +13,10 @@ //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/PathSensitive/GRStateTrait.h" -#include "clang/EntoSA/PathSensitive/TransferFuncs.h" -#include "clang/EntoSA/ManagerRegistry.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/ManagerRegistry.h" #include "llvm/Support/Debug.h" #include "llvm/ADT/FoldingSet.h" #include "llvm/ADT/ImmutableSet.h" diff --git a/lib/EntoSA/RegionStore.cpp b/lib/StaticAnalyzer/EntoSA/RegionStore.cpp similarity index 99% rename from lib/EntoSA/RegionStore.cpp rename to lib/StaticAnalyzer/EntoSA/RegionStore.cpp index 49795c6892..def1b43f34 100644 --- a/lib/EntoSA/RegionStore.cpp +++ b/lib/StaticAnalyzer/EntoSA/RegionStore.cpp @@ -20,9 +20,9 @@ #include "clang/Analysis/Analyses/LiveVariables.h" #include "clang/Analysis/AnalysisContext.h" #include "clang/Basic/TargetInfo.h" -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/PathSensitive/GRStateTrait.h" -#include "clang/EntoSA/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" #include "llvm/ADT/ImmutableList.h" #include "llvm/ADT/ImmutableMap.h" #include "llvm/ADT/Optional.h" diff --git a/lib/EntoSA/SValBuilder.cpp b/lib/StaticAnalyzer/EntoSA/SValBuilder.cpp similarity index 97% rename from lib/EntoSA/SValBuilder.cpp rename to lib/StaticAnalyzer/EntoSA/SValBuilder.cpp index c4efaae906..f87fb7ee1e 100644 --- a/lib/EntoSA/SValBuilder.cpp +++ b/lib/StaticAnalyzer/EntoSA/SValBuilder.cpp @@ -12,11 +12,11 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/MemRegion.h" -#include "clang/EntoSA/PathSensitive/SVals.h" -#include "clang/EntoSA/PathSensitive/SValBuilder.h" -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/PathSensitive/BasicValueFactory.h" +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/BasicValueFactory.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/SVals.cpp b/lib/StaticAnalyzer/EntoSA/SVals.cpp similarity index 99% rename from lib/EntoSA/SVals.cpp rename to lib/StaticAnalyzer/EntoSA/SVals.cpp index ea23293924..dd8508a50b 100644 --- a/lib/EntoSA/SVals.cpp +++ b/lib/StaticAnalyzer/EntoSA/SVals.cpp @@ -12,7 +12,7 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" #include "clang/Basic/IdentifierTable.h" using namespace clang; diff --git a/lib/EntoSA/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.cpp similarity index 98% rename from lib/EntoSA/SimpleConstraintManager.cpp rename to lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.cpp index ef26fc5d93..e54d0ffe00 100644 --- a/lib/EntoSA/SimpleConstraintManager.cpp +++ b/lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.cpp @@ -13,9 +13,9 @@ //===----------------------------------------------------------------------===// #include "SimpleConstraintManager.h" -#include "clang/EntoSA/PathSensitive/ExprEngine.h" -#include "clang/EntoSA/PathSensitive/GRState.h" -#include "clang/EntoSA/PathSensitive/Checker.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" namespace clang { diff --git a/lib/EntoSA/SimpleConstraintManager.h b/lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.h similarity index 96% rename from lib/EntoSA/SimpleConstraintManager.h rename to lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.h index 83db8cc9ce..75f67f7f17 100644 --- a/lib/EntoSA/SimpleConstraintManager.h +++ b/lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.h @@ -14,8 +14,8 @@ #ifndef LLVM_CLANG_GR_SIMPLE_CONSTRAINT_MANAGER_H #define LLVM_CLANG_GR_SIMPLE_CONSTRAINT_MANAGER_H -#include "clang/EntoSA/PathSensitive/ConstraintManager.h" -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/ConstraintManager.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" namespace clang { diff --git a/lib/EntoSA/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/EntoSA/SimpleSValBuilder.cpp similarity index 99% rename from lib/EntoSA/SimpleSValBuilder.cpp rename to lib/StaticAnalyzer/EntoSA/SimpleSValBuilder.cpp index ac0bfa62ef..f1a9074b95 100644 --- a/lib/EntoSA/SimpleSValBuilder.cpp +++ b/lib/StaticAnalyzer/EntoSA/SimpleSValBuilder.cpp @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/SValBuilder.h" -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" using namespace clang; using namespace ento; diff --git a/lib/EntoSA/Store.cpp b/lib/StaticAnalyzer/EntoSA/Store.cpp similarity index 99% rename from lib/EntoSA/Store.cpp rename to lib/StaticAnalyzer/EntoSA/Store.cpp index 29b0ee1b5f..f1b83389ca 100644 --- a/lib/EntoSA/Store.cpp +++ b/lib/StaticAnalyzer/EntoSA/Store.cpp @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/Store.h" -#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" #include "clang/AST/CharUnits.h" using namespace clang; diff --git a/lib/EntoSA/SymbolManager.cpp b/lib/StaticAnalyzer/EntoSA/SymbolManager.cpp similarity index 98% rename from lib/EntoSA/SymbolManager.cpp rename to lib/StaticAnalyzer/EntoSA/SymbolManager.cpp index 75e0774837..08677dafcf 100644 --- a/lib/EntoSA/SymbolManager.cpp +++ b/lib/StaticAnalyzer/EntoSA/SymbolManager.cpp @@ -12,9 +12,9 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathSensitive/SymbolManager.h" +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" #include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/EntoSA/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" #include "llvm/Support/raw_ostream.h" using namespace clang; diff --git a/lib/EntoSA/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/EntoSA/TextPathDiagnostics.cpp similarity index 94% rename from lib/EntoSA/TextPathDiagnostics.cpp rename to lib/StaticAnalyzer/EntoSA/TextPathDiagnostics.cpp index bf98301dc5..9ca378f2d4 100644 --- a/lib/EntoSA/TextPathDiagnostics.cpp +++ b/lib/StaticAnalyzer/EntoSA/TextPathDiagnostics.cpp @@ -11,8 +11,8 @@ // //===----------------------------------------------------------------------===// -#include "clang/EntoSA/PathDiagnosticClients.h" -#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/StaticAnalyzer/PathDiagnosticClients.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" #include "clang/Lex/Preprocessor.h" #include "llvm/Support/raw_ostream.h" using namespace clang; diff --git a/lib/StaticAnalyzer/Environment.cpp b/lib/StaticAnalyzer/Environment.cpp new file mode 100644 index 0000000000..1c74352d2c --- /dev/null +++ b/lib/StaticAnalyzer/Environment.cpp @@ -0,0 +1,236 @@ +//== Environment.cpp - Map from Stmt* to Locations/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 defined the Environment and EnvironmentManager classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" + +using namespace clang; +using namespace ento; + +SVal Environment::lookupExpr(const Stmt* E) const { + const SVal* X = ExprBindings.lookup(E); + if (X) { + SVal V = *X; + return V; + } + return UnknownVal(); +} + +SVal Environment::getSVal(const Stmt *E, SValBuilder& svalBuilder) const { + for (;;) { + switch (E->getStmtClass()) { + case Stmt::AddrLabelExprClass: + return svalBuilder.makeLoc(cast(E)); + case Stmt::ParenExprClass: + // ParenExprs are no-ops. + E = cast(E)->getSubExpr(); + continue; + case Stmt::CharacterLiteralClass: { + const CharacterLiteral* C = cast(E); + return svalBuilder.makeIntVal(C->getValue(), C->getType()); + } + case Stmt::CXXBoolLiteralExprClass: { + const SVal *X = ExprBindings.lookup(E); + if (X) + return *X; + else + return svalBuilder.makeIntVal(cast(E)); + } + case Stmt::IntegerLiteralClass: { + // In C++, this expression may have been bound to a temporary object. + SVal const *X = ExprBindings.lookup(E); + if (X) + return *X; + else + return svalBuilder.makeIntVal(cast(E)); + } + case Stmt::ImplicitCastExprClass: + case Stmt::CStyleCastExprClass: { + // We blast through no-op casts to get the descendant + // subexpression that has a value. + const CastExpr* C = cast(E); + QualType CT = C->getType(); + if (CT->isVoidType()) + return UnknownVal(); + if (C->getCastKind() == CK_NoOp) { + E = C->getSubExpr(); + continue; + } + break; + } + case Stmt::ExprWithCleanupsClass: + E = cast(E)->getSubExpr(); + continue; + case Stmt::CXXBindTemporaryExprClass: + E = cast(E)->getSubExpr(); + continue; + case Stmt::CXXFunctionalCastExprClass: + E = cast(E)->getSubExpr(); + continue; + // Handle all other Stmt* using a lookup. + default: + break; + }; + break; + } + return lookupExpr(E); +} + +Environment EnvironmentManager::bindExpr(Environment Env, const Stmt *S, + SVal V, bool Invalidate) { + assert(S); + + if (V.isUnknown()) { + if (Invalidate) + return Environment(F.remove(Env.ExprBindings, S)); + else + return Env; + } + + return Environment(F.add(Env.ExprBindings, S, V)); +} + +static inline const Stmt *MakeLocation(const Stmt *S) { + return (const Stmt*) (((uintptr_t) S) | 0x1); +} + +Environment EnvironmentManager::bindExprAndLocation(Environment Env, + const Stmt *S, + SVal location, SVal V) { + return Environment(F.add(F.add(Env.ExprBindings, MakeLocation(S), location), + S, V)); +} + +namespace { +class MarkLiveCallback : public SymbolVisitor { + SymbolReaper &SymReaper; +public: + MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {} + bool VisitSymbol(SymbolRef sym) { SymReaper.markLive(sym); return true; } +}; +} // end anonymous namespace + +static bool isBlockExprInCallers(const Stmt *E, const LocationContext *LC) { + const LocationContext *ParentLC = LC->getParent(); + while (ParentLC) { + CFG &C = *ParentLC->getCFG(); + if (C.isBlkExpr(E)) + return true; + ParentLC = ParentLC->getParent(); + } + + return false; +} + +// In addition to mapping from Stmt * - > SVals in the Environment, we also +// maintain a mapping from Stmt * -> SVals (locations) that were used during +// a load and store. +static inline bool IsLocation(const Stmt *S) { + return (bool) (((uintptr_t) S) & 0x1); +} + +// 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, + const GRState *ST, + llvm::SmallVectorImpl &DRoots) { + + CFG &C = *SymReaper.getLocationContext()->getCFG(); + + // 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(); + + llvm::SmallVector, 10> deferredLocations; + + // Iterate over the block-expr bindings. + for (Environment::iterator I = Env.begin(), E = Env.end(); + I != E; ++I) { + + const Stmt *BlkExpr = I.getKey(); + + // For recorded locations (used when evaluating loads and stores), we + // consider them live only when their associated normal expression is + // also live. + // NOTE: This assumes that loads/stores that evaluated to UnknownVal + // still have an entry in the map. + if (IsLocation(BlkExpr)) { + deferredLocations.push_back(std::make_pair(BlkExpr, I.getData())); + continue; + } + + const SVal &X = I.getData(); + + // Block-level expressions in callers are assumed always live. + if (isBlockExprInCallers(BlkExpr, SymReaper.getLocationContext())) { + NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, BlkExpr, X); + + if (isa(X)) { + const MemRegion* R = cast(X).getRegion(); + DRoots.push_back(R); + } + + // Mark all symbols in the block expr's value live. + MarkLiveCallback cb(SymReaper); + ST->scanReachableSymbols(X, cb); + continue; + } + + // Not a block-level expression? + if (!C.isBlkExpr(BlkExpr)) + continue; + + if (SymReaper.isLive(BlkExpr)) { + // Copy the binding to the new map. + NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, BlkExpr, X); + + // If the block expr's value is a memory region, then mark that region. + if (isa(X)) { + const MemRegion* R = cast(X).getRegion(); + DRoots.push_back(R); + } + + // Mark all symbols in the block expr's value live. + MarkLiveCallback cb(SymReaper); + ST->scanReachableSymbols(X, cb); + continue; + } + + // Otherwise the expression is dead with a couple exceptions. + // Do not misclean LogicalExpr or ConditionalOperator. It is dead at the + // beginning of itself, but we need its UndefinedVal to determine its + // SVal. + if (X.isUndef() && cast(X).getData()) + NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, BlkExpr, X); + } + + // Go through he deferred locations and add them to the new environment if + // the correspond Stmt* is in the map as well. + for (llvm::SmallVectorImpl >::iterator + I = deferredLocations.begin(), E = deferredLocations.end(); I != E; ++I) { + const Stmt *S = (Stmt*) (((uintptr_t) I->first) & (uintptr_t) ~0x1); + if (NewEnv.ExprBindings.lookup(S)) + NewEnv.ExprBindings = F.add(NewEnv.ExprBindings, I->first, I->second); + } + + return NewEnv; +} diff --git a/lib/StaticAnalyzer/ExplodedGraph.cpp b/lib/StaticAnalyzer/ExplodedGraph.cpp new file mode 100644 index 0000000000..4c4612fab5 --- /dev/null +++ b/lib/StaticAnalyzer/ExplodedGraph.cpp @@ -0,0 +1,282 @@ +//=-- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -*- 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 template classes ExplodedNode and ExplodedGraph, +// which represent a path-sensitive, intra-procedural "exploded graph." +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathSensitive/ExplodedGraph.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/AST/Stmt.h" +#include "llvm/ADT/DenseSet.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +#include + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Node auditing. +//===----------------------------------------------------------------------===// + +// An out of line virtual method to provide a home for the class vtable. +ExplodedNode::Auditor::~Auditor() {} + +#ifndef NDEBUG +static ExplodedNode::Auditor* NodeAuditor = 0; +#endif + +void ExplodedNode::SetAuditor(ExplodedNode::Auditor* A) { +#ifndef NDEBUG + NodeAuditor = A; +#endif +} + +//===----------------------------------------------------------------------===// +// ExplodedNode. +//===----------------------------------------------------------------------===// + +static inline BumpVector& getVector(void* P) { + return *reinterpret_cast*>(P); +} + +void ExplodedNode::addPredecessor(ExplodedNode* V, ExplodedGraph &G) { + assert (!V->isSink()); + Preds.addNode(V, G); + V->Succs.addNode(this, G); +#ifndef NDEBUG + if (NodeAuditor) NodeAuditor->AddEdge(V, this); +#endif +} + +void ExplodedNode::NodeGroup::addNode(ExplodedNode* N, ExplodedGraph &G) { + assert((reinterpret_cast(N) & Mask) == 0x0); + assert(!getFlag()); + + if (getKind() == Size1) { + if (ExplodedNode* NOld = getNode()) { + BumpVectorContext &Ctx = G.getNodeAllocator(); + BumpVector *V = + G.getAllocator().Allocate >(); + new (V) BumpVector(Ctx, 4); + + assert((reinterpret_cast(V) & Mask) == 0x0); + V->push_back(NOld, Ctx); + V->push_back(N, Ctx); + P = reinterpret_cast(V) | SizeOther; + assert(getPtr() == (void*) V); + assert(getKind() == SizeOther); + } + else { + P = reinterpret_cast(N); + assert(getKind() == Size1); + } + } + else { + assert(getKind() == SizeOther); + getVector(getPtr()).push_back(N, G.getNodeAllocator()); + } +} + +unsigned ExplodedNode::NodeGroup::size() const { + if (getFlag()) + return 0; + + if (getKind() == Size1) + return getNode() ? 1 : 0; + else + return getVector(getPtr()).size(); +} + +ExplodedNode **ExplodedNode::NodeGroup::begin() const { + if (getFlag()) + return NULL; + + if (getKind() == Size1) + return (ExplodedNode**) (getPtr() ? &P : NULL); + else + return const_cast(&*(getVector(getPtr()).begin())); +} + +ExplodedNode** ExplodedNode::NodeGroup::end() const { + if (getFlag()) + return NULL; + + if (getKind() == Size1) + return (ExplodedNode**) (getPtr() ? &P+1 : NULL); + else { + // Dereferencing end() is undefined behaviour. The vector is not empty, so + // we can dereference the last elem and then add 1 to the result. + return const_cast(getVector(getPtr()).end()); + } +} + +ExplodedNode *ExplodedGraph::getNode(const ProgramPoint& L, + const GRState* State, bool* IsNew) { + // Profile 'State' to determine if we already have an existing node. + llvm::FoldingSetNodeID profile; + void* InsertPos = 0; + + NodeTy::Profile(profile, L, State); + NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos); + + if (!V) { + // Allocate a new node. + V = (NodeTy*) getAllocator().Allocate(); + new (V) NodeTy(L, State); + + // 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; +} + +std::pair +ExplodedGraph::Trim(const NodeTy* const* NBeg, const NodeTy* const* NEnd, + llvm::DenseMap *InverseMap) const { + + if (NBeg == NEnd) + return std::make_pair((ExplodedGraph*) 0, + (InterExplodedGraphMap*) 0); + + assert (NBeg < NEnd); + + llvm::OwningPtr M(new InterExplodedGraphMap()); + + ExplodedGraph* G = TrimInternal(NBeg, NEnd, M.get(), InverseMap); + + return std::make_pair(static_cast(G), M.take()); +} + +ExplodedGraph* +ExplodedGraph::TrimInternal(const ExplodedNode* const* BeginSources, + const ExplodedNode* const* EndSources, + InterExplodedGraphMap* M, + llvm::DenseMap *InverseMap) const { + + typedef llvm::DenseSet Pass1Ty; + Pass1Ty Pass1; + + typedef llvm::DenseMap Pass2Ty; + Pass2Ty& Pass2 = M->M; + + llvm::SmallVector WL1, WL2; + + // ===- Pass 1 (reverse DFS) -=== + for (const ExplodedNode* const* I = BeginSources; I != EndSources; ++I) { + assert(*I); + WL1.push_back(*I); + } + + // Process the first worklist until it is empty. Because it is a std::list + // it acts like a FIFO queue. + while (!WL1.empty()) { + const ExplodedNode *N = WL1.back(); + WL1.pop_back(); + + // Have we already visited this node? If so, continue to the next one. + if (Pass1.count(N)) + continue; + + // Otherwise, mark this node as visited. + Pass1.insert(N); + + // If this is a root enqueue it to the second worklist. + if (N->Preds.empty()) { + WL2.push_back(N); + continue; + } + + // Visit our predecessors and enqueue them. + for (ExplodedNode** I=N->Preds.begin(), **E=N->Preds.end(); I!=E; ++I) + WL1.push_back(*I); + } + + // We didn't hit a root? Return with a null pointer for the new graph. + if (WL2.empty()) + return 0; + + // Create an empty graph. + ExplodedGraph* G = MakeEmptyGraph(); + + // ===- Pass 2 (forward DFS to construct the new graph) -=== + while (!WL2.empty()) { + const ExplodedNode* N = WL2.back(); + WL2.pop_back(); + + // 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->getNode(N->getLocation(), N->State, NULL); + 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 **I=N->Preds.begin(), **E=N->Preds.end(); I!=E; ++I) { + Pass2Ty::iterator PI = Pass2.find(*I); + if (PI == Pass2.end()) + continue; + + NewN->addPredecessor(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 **I=N->Succs.begin(), **E=N->Succs.end(); I!=E; ++I) { + Pass2Ty::iterator PI = Pass2.find(*I); + if (PI != Pass2.end()) { + 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); + } + + // Finally, explictly mark all nodes without any successors as sinks. + if (N->isSink()) + NewN->markAsSink(); + } + + return G; +} + +ExplodedNode* +InterExplodedGraphMap::getMappedNode(const ExplodedNode* N) const { + llvm::DenseMap::const_iterator I = + M.find(N); + + return I == M.end() ? 0 : I->second; +} + diff --git a/lib/StaticAnalyzer/FlatStore.cpp b/lib/StaticAnalyzer/FlatStore.cpp new file mode 100644 index 0000000000..1558db7483 --- /dev/null +++ b/lib/StaticAnalyzer/FlatStore.cpp @@ -0,0 +1,203 @@ +//=== FlatStore.cpp - Flat region-based store model -------------*- 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/PathSensitive/GRState.h" +#include "llvm/ADT/ImmutableIntervalMap.h" +#include "llvm/Support/ErrorHandling.h" + +using namespace clang; +using namespace ento; +using llvm::Interval; + +// The actual store type. +typedef llvm::ImmutableIntervalMap BindingVal; +typedef llvm::ImmutableMap RegionBindings; + +namespace { +class FlatStoreManager : public StoreManager { + RegionBindings::Factory RBFactory; + BindingVal::Factory BVFactory; + +public: + FlatStoreManager(GRStateManager &mgr) + : StoreManager(mgr), + RBFactory(mgr.getAllocator()), + BVFactory(mgr.getAllocator()) {} + + SVal Retrieve(Store store, Loc L, QualType T); + Store Bind(Store store, Loc L, SVal val); + Store Remove(Store St, Loc L); + Store BindCompoundLiteral(Store store, const CompoundLiteralExpr* cl, + const LocationContext *LC, SVal v); + + Store getInitialStore(const LocationContext *InitLoc) { + return RBFactory.getEmptyMap().getRoot(); + } + + SubRegionMap *getSubRegionMap(Store store) { + return 0; + } + + SVal ArrayToPointer(Loc Array); + Store RemoveDeadBindings(Store store, const StackFrameContext *LCtx, + SymbolReaper& SymReaper, + llvm::SmallVectorImpl& RegionRoots){ + return store; + } + + Store BindDecl(Store store, const VarRegion *VR, SVal initVal); + + Store BindDeclWithNoInit(Store store, const VarRegion *VR); + + typedef llvm::DenseSet InvalidatedSymbols; + + Store InvalidateRegions(Store store, const MemRegion * const *I, + const MemRegion * const *E, const Expr *Ex, + unsigned Count, InvalidatedSymbols *IS, + bool invalidateGlobals, InvalidatedRegions *Regions); + + void print(Store store, llvm::raw_ostream& Out, const char* nl, + const char *sep); + void iterBindings(Store store, BindingsHandler& f); + +private: + static RegionBindings getRegionBindings(Store store) { + return RegionBindings(static_cast(store)); + } + + class RegionInterval { + public: + const MemRegion *R; + Interval I; + RegionInterval(const MemRegion *r, int64_t s, int64_t e) : R(r), I(s, e){} + }; + + RegionInterval RegionToInterval(const MemRegion *R); + + SVal RetrieveRegionWithNoBinding(const MemRegion *R, QualType T); +}; +} // end anonymous namespace + +StoreManager *ento::CreateFlatStoreManager(GRStateManager &StMgr) { + return new FlatStoreManager(StMgr); +} + +SVal FlatStoreManager::Retrieve(Store store, Loc L, QualType T) { + const MemRegion *R = cast(L).getRegion(); + RegionInterval RI = RegionToInterval(R); + // FIXME: FlatStore should handle regions with unknown intervals. + if (!RI.R) + return UnknownVal(); + + RegionBindings B = getRegionBindings(store); + const BindingVal *BV = B.lookup(RI.R); + if (BV) { + const SVal *V = BVFactory.lookup(*BV, RI.I); + if (V) + return *V; + else + return RetrieveRegionWithNoBinding(R, T); + } + return RetrieveRegionWithNoBinding(R, T); +} + +SVal FlatStoreManager::RetrieveRegionWithNoBinding(const MemRegion *R, + QualType T) { + if (R->hasStackNonParametersStorage()) + return UndefinedVal(); + else + return svalBuilder.getRegionValueSymbolVal(cast(R)); +} + +Store FlatStoreManager::Bind(Store store, Loc L, SVal val) { + const MemRegion *R = cast(L).getRegion(); + RegionBindings B = getRegionBindings(store); + const BindingVal *V = B.lookup(R); + + BindingVal BV = BVFactory.getEmptyMap(); + if (V) + BV = *V; + + RegionInterval RI = RegionToInterval(R); + // FIXME: FlatStore should handle regions with unknown intervals. + if (!RI.R) + return B.getRoot(); + BV = BVFactory.add(BV, RI.I, val); + B = RBFactory.add(B, RI.R, BV); + return B.getRoot(); +} + +Store FlatStoreManager::Remove(Store store, Loc L) { + return store; +} + +Store FlatStoreManager::BindCompoundLiteral(Store store, + const CompoundLiteralExpr* cl, + const LocationContext *LC, + SVal v) { + return store; +} + +SVal FlatStoreManager::ArrayToPointer(Loc Array) { + return Array; +} + +Store FlatStoreManager::BindDecl(Store store, const VarRegion *VR, + SVal initVal) { + return Bind(store, svalBuilder.makeLoc(VR), initVal); +} + +Store FlatStoreManager::BindDeclWithNoInit(Store store, const VarRegion *VR) { + return store; +} + +Store FlatStoreManager::InvalidateRegions(Store store, + const MemRegion * const *I, + const MemRegion * const *E, + const Expr *Ex, unsigned Count, + InvalidatedSymbols *IS, + bool invalidateGlobals, + InvalidatedRegions *Regions) { + assert(false && "Not implemented"); + return store; +} + +void FlatStoreManager::print(Store store, llvm::raw_ostream& Out, + const char* nl, const char *sep) { +} + +void FlatStoreManager::iterBindings(Store store, BindingsHandler& f) { +} + +FlatStoreManager::RegionInterval +FlatStoreManager::RegionToInterval(const MemRegion *R) { + switch (R->getKind()) { + case MemRegion::VarRegionKind: { + QualType T = cast(R)->getValueType(); + int64_t Size = Ctx.getTypeSize(T); + return RegionInterval(R, 0, Size-1); + } + + case MemRegion::ElementRegionKind: + case MemRegion::FieldRegionKind: { + RegionOffset Offset = R->getAsOffset(); + // We cannot compute offset for all regions, for example, elements + // with symbolic offsets. + if (!Offset.getRegion()) + return RegionInterval(0, 0, 0); + int64_t Start = Offset.getOffset(); + int64_t Size = Ctx.getTypeSize(cast(R)->getValueType()); + return RegionInterval(Offset.getRegion(), Start, Start+Size); + } + + default: + llvm_unreachable("Region kind unhandled."); + return RegionInterval(0, 0, 0); + } +} diff --git a/lib/StaticAnalyzer/GRState.cpp b/lib/StaticAnalyzer/GRState.cpp new file mode 100644 index 0000000000..12c8968367 --- /dev/null +++ b/lib/StaticAnalyzer/GRState.cpp @@ -0,0 +1,551 @@ +//= GRState.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 GRState and GRStateManager. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/CFG.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/SubEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +// Give the vtable for ConstraintManager somewhere to live. +// FIXME: Move this elsewhere. +ConstraintManager::~ConstraintManager() {} + +GRStateManager::~GRStateManager() { + for (std::vector::iterator I=Printers.begin(), + E=Printers.end(); I!=E; ++I) + delete *I; + + for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end(); + I!=E; ++I) + I->second.second(I->second.first); +} + +const GRState* +GRStateManager::RemoveDeadBindings(const GRState* 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. + llvm::SmallVector RegionRoots; + GRState NewState = *state; + + NewState.Env = EnvMgr.RemoveDeadBindings(NewState.Env, SymReaper, + state, RegionRoots); + + // Clean up the store. + NewState.St = StoreMgr->RemoveDeadBindings(NewState.St, LCtx, + SymReaper, RegionRoots); + state = getPersistentState(NewState); + return ConstraintMgr->RemoveDeadBindings(state, SymReaper); +} + +const GRState *GRStateManager::MarshalState(const GRState *state, + const StackFrameContext *InitLoc) { + // make up an empty state for now. + GRState State(this, + EnvMgr.getInitialEnvironment(), + StoreMgr->getInitialStore(InitLoc), + GDMFactory.getEmptyMap()); + + return getPersistentState(State); +} + +const GRState *GRState::bindCompoundLiteral(const CompoundLiteralExpr* CL, + const LocationContext *LC, + SVal V) const { + Store new_store = + getStateManager().StoreMgr->BindCompoundLiteral(St, CL, LC, V); + return makeWithStore(new_store); +} + +const GRState *GRState::bindDecl(const VarRegion* VR, SVal IVal) const { + Store new_store = getStateManager().StoreMgr->BindDecl(St, VR, IVal); + return makeWithStore(new_store); +} + +const GRState *GRState::bindDeclWithNoInit(const VarRegion* VR) const { + Store new_store = getStateManager().StoreMgr->BindDeclWithNoInit(St, VR); + return makeWithStore(new_store); +} + +const GRState *GRState::bindLoc(Loc LV, SVal V) const { + GRStateManager &Mgr = getStateManager(); + Store new_store = Mgr.StoreMgr->Bind(St, LV, V); + const GRState *new_state = makeWithStore(new_store); + + const MemRegion *MR = LV.getAsRegion(); + if (MR) + return Mgr.getOwningEngine().ProcessRegionChange(new_state, MR); + + return new_state; +} + +const GRState *GRState::bindDefault(SVal loc, SVal V) const { + GRStateManager &Mgr = getStateManager(); + const MemRegion *R = cast(loc).getRegion(); + Store new_store = Mgr.StoreMgr->BindDefault(St, R, V); + const GRState *new_state = makeWithStore(new_store); + return Mgr.getOwningEngine().ProcessRegionChange(new_state, R); +} + +const GRState *GRState::InvalidateRegions(const MemRegion * const *Begin, + const MemRegion * const *End, + const Expr *E, unsigned Count, + StoreManager::InvalidatedSymbols *IS, + bool invalidateGlobals) const { + GRStateManager &Mgr = getStateManager(); + SubEngine &Eng = Mgr.getOwningEngine(); + + if (Eng.WantsRegionChangeUpdate(this)) { + StoreManager::InvalidatedRegions Regions; + + Store new_store = Mgr.StoreMgr->InvalidateRegions(St, Begin, End, + E, Count, IS, + invalidateGlobals, + &Regions); + const GRState *new_state = makeWithStore(new_store); + + return Eng.ProcessRegionChanges(new_state, + &Regions.front(), + &Regions.back()+1); + } + + Store new_store = Mgr.StoreMgr->InvalidateRegions(St, Begin, End, + E, Count, IS, + invalidateGlobals, + NULL); + return makeWithStore(new_store); +} + +const GRState *GRState::unbindLoc(Loc LV) const { + assert(!isa(LV) && "Use InvalidateRegion instead."); + + Store OldStore = getStore(); + Store NewStore = getStateManager().StoreMgr->Remove(OldStore, LV); + + if (NewStore == OldStore) + return this; + + return makeWithStore(NewStore); +} + +const GRState *GRState::EnterStackFrame(const StackFrameContext *frame) const { + Store new_store = getStateManager().StoreMgr->EnterStackFrame(this, frame); + return makeWithStore(new_store); +} + +SVal GRState::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 TypedRegion *TR = dyn_cast(R)) { + QualType T = TR->getValueType(); + if (Loc::IsLocType(T) || T->isIntegerType()) + return getSVal(R); + } + + return UnknownVal(); +} + +SVal GRState::getSVal(Loc location, QualType T) const { + SVal V = getRawSVal(cast(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). + if (!T.isNull()) { + if (SymbolRef sym = V.getAsSymbol()) { + if (const llvm::APSInt *Int = getSymVal(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 (isa(V)) + return loc::ConcreteInt(NewV); + else + return nonloc::ConcreteInt(NewV); + } + } + } + + return V; +} + +const GRState *GRState::BindExpr(const Stmt* S, SVal V, bool Invalidate) const{ + Environment NewEnv = getStateManager().EnvMgr.bindExpr(Env, S, V, + Invalidate); + if (NewEnv == Env) + return this; + + GRState NewSt = *this; + NewSt.Env = NewEnv; + return getStateManager().getPersistentState(NewSt); +} + +const GRState *GRState::bindExprAndLocation(const Stmt *S, SVal location, + SVal V) const { + Environment NewEnv = + getStateManager().EnvMgr.bindExprAndLocation(Env, S, location, V); + + if (NewEnv == Env) + return this; + + GRState NewSt = *this; + NewSt.Env = NewEnv; + return getStateManager().getPersistentState(NewSt); +} + +const GRState *GRState::assumeInBound(DefinedOrUnknownSVal Idx, + DefinedOrUnknownSVal UpperBound, + bool Assumption) 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. + GRStateManager &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(); + // FIXME: This should be using ValueManager::ArrayindexTy...somehow. + QualType indexTy = Ctx.IntTy; + nonloc::ConcreteInt Min(BVF.getMinValue(indexTy)); + + // Adjust the index. + SVal newIdx = svalBuilder.evalBinOpNN(this, BO_Add, + cast(Idx), Min, indexTy); + if (newIdx.isUnknownOrUndef()) + return this; + + // Adjust the upper bound. + SVal newBound = + svalBuilder.evalBinOpNN(this, BO_Add, cast(UpperBound), + Min, indexTy); + + if (newBound.isUnknownOrUndef()) + return this; + + // Build the actual comparison. + SVal inBound = svalBuilder.evalBinOpNN(this, BO_LT, + cast(newIdx), cast(newBound), + Ctx.IntTy); + if (inBound.isUnknownOrUndef()) + return this; + + // Finally, let the constraint manager take care of it. + ConstraintManager &CM = SM.getConstraintManager(); + return CM.assume(this, cast(inBound), Assumption); +} + +const GRState* GRStateManager::getInitialState(const LocationContext *InitLoc) { + GRState State(this, + EnvMgr.getInitialEnvironment(), + StoreMgr->getInitialStore(InitLoc), + GDMFactory.getEmptyMap()); + + return getPersistentState(State); +} + +const GRState* GRStateManager::getPersistentState(GRState& State) { + + llvm::FoldingSetNodeID ID; + State.Profile(ID); + void* InsertPos; + + if (GRState* I = StateSet.FindNodeOrInsertPos(ID, InsertPos)) + return I; + + GRState* I = (GRState*) Alloc.Allocate(); + new (I) GRState(State); + StateSet.InsertNode(I, InsertPos); + return I; +} + +const GRState* GRState::makeWithStore(Store store) const { + GRState NewSt = *this; + NewSt.St = store; + return getStateManager().getPersistentState(NewSt); +} + +//===----------------------------------------------------------------------===// +// State pretty-printing. +//===----------------------------------------------------------------------===// + +static bool IsEnvLoc(const Stmt *S) { + // FIXME: This is a layering violation. Should be in environment. + return (bool) (((uintptr_t) S) & 0x1); +} + +void GRState::print(llvm::raw_ostream& Out, CFG &C, const char* nl, + const char* sep) const { + // Print the store. + GRStateManager &Mgr = getStateManager(); + Mgr.getStoreManager().print(getStore(), Out, nl, sep); + + // Print Subexpression bindings. + bool isFirst = true; + + // FIXME: All environment printing should be moved inside Environment. + for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { + if (C.isBlkExpr(I.getKey()) || IsEnvLoc(I.getKey())) + continue; + + if (isFirst) { + Out << nl << nl << "Sub-Expressions:" << nl; + isFirst = false; + } + else { Out << nl; } + + Out << " (" << (void*) I.getKey() << ") "; + LangOptions LO; // FIXME. + I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); + Out << " : " << I.getData(); + } + + // Print block-expression bindings. + isFirst = true; + + for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { + if (!C.isBlkExpr(I.getKey())) + continue; + + if (isFirst) { + Out << nl << nl << "Block-level Expressions:" << nl; + isFirst = false; + } + else { Out << nl; } + + Out << " (" << (void*) I.getKey() << ") "; + LangOptions LO; // FIXME. + I.getKey()->printPretty(Out, 0, PrintingPolicy(LO)); + Out << " : " << I.getData(); + } + + // Print locations. + isFirst = true; + + for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) { + if (!IsEnvLoc(I.getKey())) + continue; + + if (isFirst) { + Out << nl << nl << "Load/store locations:" << nl; + isFirst = false; + } + else { Out << nl; } + + const Stmt *S = (Stmt*) (((uintptr_t) I.getKey()) & ((uintptr_t) ~0x1)); + + Out << " (" << (void*) S << ") "; + LangOptions LO; // FIXME. + S->printPretty(Out, 0, PrintingPolicy(LO)); + Out << " : " << I.getData(); + } + + Mgr.getConstraintManager().print(this, Out, nl, sep); + + // Print checker-specific data. + for (std::vector::iterator I = Mgr.Printers.begin(), + E = Mgr.Printers.end(); I != E; ++I) { + (*I)->Print(Out, this, nl, sep); + } +} + +void GRState::printDOT(llvm::raw_ostream& Out, CFG &C) const { + print(Out, C, "\\l", "\\|"); +} + +void GRState::printStdErr(CFG &C) const { + print(llvm::errs(), C); +} + +//===----------------------------------------------------------------------===// +// Generic Data Map. +//===----------------------------------------------------------------------===// + +void* const* GRState::FindGDM(void* K) const { + return GDM.lookup(K); +} + +void* +GRStateManager::FindGDMContext(void* K, + void* (*CreateContext)(llvm::BumpPtrAllocator&), + void (*DeleteContext)(void*)) { + + std::pair& p = GDMContexts[K]; + if (!p.first) { + p.first = CreateContext(Alloc); + p.second = DeleteContext; + } + + return p.first; +} + +const GRState* GRStateManager::addGDM(const GRState* St, void* Key, void* Data){ + GRState::GenericDataMap M1 = St->getGDM(); + GRState::GenericDataMap M2 = GDMFactory.add(M1, Key, Data); + + if (M1 == M2) + return St; + + GRState NewSt = *St; + NewSt.GDM = M2; + return getPersistentState(NewSt); +} + +const GRState *GRStateManager::removeGDM(const GRState *state, void *Key) { + GRState::GenericDataMap OldM = state->getGDM(); + GRState::GenericDataMap NewM = GDMFactory.remove(OldM, Key); + + if (NewM == OldM) + return state; + + GRState NewState = *state; + NewState.GDM = NewM; + return getPersistentState(NewState); +} + +//===----------------------------------------------------------------------===// +// Utility. +//===----------------------------------------------------------------------===// + +namespace { +class ScanReachableSymbols : public SubRegionMap::Visitor { + typedef llvm::DenseSet VisitedRegionsTy; + + VisitedRegionsTy visited; + const GRState *state; + SymbolVisitor &visitor; + llvm::OwningPtr SRM; +public: + + ScanReachableSymbols(const GRState *st, SymbolVisitor& v) + : state(st), visitor(v) {} + + bool scan(nonloc::CompoundVal val); + bool scan(SVal val); + bool scan(const MemRegion *R); + + // From SubRegionMap::Visitor. + bool Visit(const MemRegion* Parent, const MemRegion* SubRegion) { + return scan(SubRegion); + } +}; +} + +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(SVal val) { + if (loc::MemRegionVal *X = dyn_cast(&val)) + return scan(X->getRegion()); + + if (nonloc::LocAsInteger *X = dyn_cast(&val)) + return scan(X->getLoc()); + + if (SymbolRef Sym = val.getAsSymbol()) + return visitor.VisitSymbol(Sym); + + if (nonloc::CompoundVal *X = dyn_cast(&val)) + return scan(*X); + + return true; +} + +bool ScanReachableSymbols::scan(const MemRegion *R) { + if (isa(R) || visited.count(R)) + return true; + + visited.insert(R); + + // If this is a symbolic region, visit the symbol for the region. + if (const SymbolicRegion *SR = dyn_cast(R)) + if (!visitor.VisitSymbol(SR->getSymbol())) + return false; + + // If this is a subregion, also visit the parent regions. + if (const SubRegion *SR = dyn_cast(R)) + if (!scan(SR->getSuperRegion())) + return false; + + // Now look at the binding to this region (if any). + if (!scan(state->getSValAsScalarOrLoc(R))) + return false; + + // Now look at the subregions. + if (!SRM.get()) + SRM.reset(state->getStateManager().getStoreManager(). + getSubRegionMap(state->getStore())); + + return SRM->iterSubRegions(R, *this); +} + +bool GRState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const { + ScanReachableSymbols S(this, visitor); + return S.scan(val); +} + +bool GRState::scanReachableSymbols(const SVal *I, const SVal *E, + SymbolVisitor &visitor) const { + ScanReachableSymbols S(this, visitor); + for ( ; I != E; ++I) { + if (!S.scan(*I)) + return false; + } + return true; +} + +bool GRState::scanReachableSymbols(const MemRegion * const *I, + const MemRegion * const *E, + SymbolVisitor &visitor) const { + ScanReachableSymbols S(this, visitor); + for ( ; I != E; ++I) { + if (!S.scan(*I)) + return false; + } + return true; +} diff --git a/lib/StaticAnalyzer/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/HTMLDiagnostics.cpp new file mode 100644 index 0000000000..659c32d58d --- /dev/null +++ b/lib/StaticAnalyzer/HTMLDiagnostics.cpp @@ -0,0 +1,578 @@ +//===--- HTMLDiagnostics.cpp - HTML 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 HTMLDiagnostics object. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathDiagnosticClients.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Decl.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/Rewrite/Rewriter.h" +#include "clang/Rewrite/HTMLRewrite.h" +#include "clang/Lex/Lexer.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Path.h" + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Boilerplate. +//===----------------------------------------------------------------------===// + +namespace { + +class HTMLDiagnostics : public PathDiagnosticClient { + llvm::sys::Path Directory, FilePrefix; + bool createdDir, noDir; + const Preprocessor &PP; + std::vector BatchedDiags; +public: + HTMLDiagnostics(const std::string& prefix, const Preprocessor &pp); + + virtual ~HTMLDiagnostics() { FlushDiagnostics(NULL); } + + virtual void FlushDiagnostics(llvm::SmallVectorImpl *FilesMade); + + virtual void HandlePathDiagnostic(const PathDiagnostic* D); + + virtual llvm::StringRef getName() const { + return "HTMLDiagnostics"; + } + + unsigned ProcessMacroPiece(llvm::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 = "", + const char *HighlightEnd = ""); + + void ReportDiag(const PathDiagnostic& D, + llvm::SmallVectorImpl *FilesMade); +}; + +} // end anonymous namespace + +HTMLDiagnostics::HTMLDiagnostics(const std::string& prefix, + const Preprocessor &pp) + : Directory(prefix), FilePrefix(prefix), createdDir(false), noDir(false), + PP(pp) { + // All html files begin with "report" + FilePrefix.appendComponent("report"); +} + +PathDiagnosticClient* +ento::createHTMLDiagnosticClient(const std::string& prefix, + const Preprocessor &PP) { + return new HTMLDiagnostics(prefix, PP); +} + +//===----------------------------------------------------------------------===// +// Report processing. +//===----------------------------------------------------------------------===// + +void HTMLDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) { + if (!D) + return; + + if (D->empty()) { + delete D; + return; + } + + const_cast(D)->flattenLocations(); + BatchedDiags.push_back(D); +} + +void +HTMLDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl *FilesMade) +{ + while (!BatchedDiags.empty()) { + const PathDiagnostic* D = BatchedDiags.back(); + BatchedDiags.pop_back(); + ReportDiag(*D, FilesMade); + delete D; + } + + BatchedDiags.clear(); +} + +void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D, + llvm::SmallVectorImpl *FilesMade){ + // Create the HTML directory if it is missing. + if (!createdDir) { + createdDir = true; + std::string ErrorMsg; + Directory.createDirectoryOnDisk(true, &ErrorMsg); + + if (!Directory.isDirectory()) { + llvm::errs() << "warning: could not create directory '" + << Directory.str() << "'\n" + << "reason: " << ErrorMsg << '\n'; + + noDir = true; + + return; + } + } + + if (noDir) + return; + + const SourceManager &SMgr = D.begin()->getLocation().getManager(); + FileID FID; + + // Verify that the entire path is from the same FileID. + for (PathDiagnostic::const_iterator I = D.begin(), E = D.end(); I != E; ++I) { + FullSourceLoc L = I->getLocation().asLocation().getInstantiationLoc(); + + if (FID.isInvalid()) { + FID = SMgr.getFileID(L); + } else if (SMgr.getFileID(L) != FID) + return; // FIXME: Emit a warning? + + // Check the source ranges. + for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(), + RE=I->ranges_end(); RI!=RE; ++RI) { + + SourceLocation L = SMgr.getInstantiationLoc(RI->getBegin()); + + if (!L.isFileID() || SMgr.getFileID(L) != FID) + return; // FIXME: Emit a warning? + + L = SMgr.getInstantiationLoc(RI->getEnd()); + + if (!L.isFileID() || SMgr.getFileID(L) != FID) + return; // FIXME: Emit a warning? + } + } + + if (FID.isInvalid()) + return; // FIXME: Emit a warning? + + // Create a new rewriter to generate HTML. + Rewriter R(const_cast(SMgr), PP.getLangOptions()); + + // Process the path. + unsigned n = D.size(); + unsigned max = n; + + for (PathDiagnostic::const_reverse_iterator I=D.rbegin(), E=D.rend(); + I!=E; ++I, --n) + HandlePiece(R, FID, *I, n, max); + + // Add line numbers, header, footer, etc. + + // unsigned FID = R.getSourceMgr().getMainFileID(); + 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); + + // Get the full directory name of the analyzed file. + + const FileEntry* Entry = SMgr.getFileEntryForID(FID); + + // 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. + + std::string DirName = ""; + + if (llvm::sys::path::is_relative(Entry->getName())) { + llvm::sys::Path P = llvm::sys::Path::GetCurrentDirectory(); + DirName = P.str() + "/"; + } + + // Add the name of the file as an

tag. + + { + std::string s; + llvm::raw_string_ostream os(s); + + os << "\n" + << "

Bug Summary

\n\n" + "\n\n" + "\n"; + + // Output any other meta data. + + for (PathDiagnostic::meta_iterator I=D.meta_begin(), E=D.meta_end(); + I!=E; ++I) { + os << "\n"; + } + + os << "
File:" + << html::EscapeText(DirName) + << html::EscapeText(Entry->getName()) + << "
Location:" + "line " + << (*D.rbegin()).getLocation().asLocation().getInstantiationLineNumber() + << ", column " + << (*D.rbegin()).getLocation().asLocation().getInstantiationColumnNumber() + << "
Description:" + << D.getDescription() << "
" << html::EscapeText(*I) << "
\n\n" + "

Annotated Source Code

\n"; + + R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str()); + } + + // Embed meta-data tags. + { + std::string s; + llvm::raw_string_ostream os(s); + + const std::string& BugDesc = D.getDescription(); + if (!BugDesc.empty()) + os << "\n\n"; + + const std::string& BugType = D.getBugType(); + if (!BugType.empty()) + os << "\n\n"; + + const std::string& BugCategory = D.getCategory(); + if (!BugCategory.empty()) + os << "\n\n"; + + os << "\n\n"; + + os << "\n\n"; + + os << "\n\n"; + + // Mark the end of the tags. + os << "\n\n"; + + // Insert the text. + R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str()); + } + + // Add CSS, header, and footer. + + html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName()); + + // Get the rewrite buffer. + const RewriteBuffer *Buf = R.getRewriteBufferFor(FID); + + if (!Buf) { + llvm::errs() << "warning: no diagnostics generated for main file.\n"; + return; + } + + // Create a path for the target HTML file. + llvm::sys::Path F(FilePrefix); + F.makeUnique(false, NULL); + + // Rename the file with an HTML extension. + llvm::sys::Path H(F); + H.appendSuffix("html"); + F.renamePathOnDisk(H, NULL); + + std::string ErrorMsg; + llvm::raw_fd_ostream os(H.c_str(), ErrorMsg); + + if (!ErrorMsg.empty()) { + llvm::errs() << "warning: could not create file '" << F.str() + << "'\n"; + return; + } + + if (FilesMade) + FilesMade->push_back(llvm::sys::path::filename(H.str())); + + // Emit the HTML to disk. + for (RewriteBuffer::iterator I = Buf->begin(), E = Buf->end(); I!=E; ++I) + os << *I; +} + +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 LPosInfo = SM.getDecomposedInstantiationLoc(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.getInstantiationLoc().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 = 0; + switch (P.getKind()) { + case PathDiagnosticPiece::Event: Kind = "Event"; break; + case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break; + // Setting Kind to "Control" is intentional. + case PathDiagnosticPiece::Macro: Kind = "Control"; break; + } + + std::string sbuf; + llvm::raw_string_ostream os(sbuf); + + os << "\n
(P)) { + // Get the string and determining its maximum substring. + const std::string& Msg = P.getString(); + unsigned max_token = 0; + unsigned cnt = 0; + unsigned len = Msg.size(); + + for (std::string::const_iterator I=Msg.begin(), E=Msg.end(); I!=E; ++I) + switch (*I) { + 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 (max > 1) { + os << "
"; + os << "
" << num << "
"; + os << "
"; + } + + if (const PathDiagnosticMacroPiece *MP = + dyn_cast(&P)) { + + os << "Within the expansion of the macro '"; + + // Get the name of the macro by relexing it. + { + FullSourceLoc L = MP->getLocation().asLocation().getInstantiationLoc(); + assert(L.isFileID()); + llvm::StringRef BufferInfo = L.getBufferData(); + const char* MacroName = L.getDecomposedLoc().second + BufferInfo.data(); + Lexer rawLexer(L, PP.getLangOptions(), 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 (max > 1) + os << "
"; + + // Within a macro piece. Write out each event. + ProcessMacroPiece(os, *MP, 0); + } + else { + os << html::EscapeText(P.getString()); + + if (max > 1) + os << ""; + } + + os << "
"; + + // Insert the new html. + unsigned DisplayPos = LineEnd - FileStart; + SourceLocation Loc = + SM.getLocForStartOfFile(LPosInfo.first).getFileLocWithOffset(DisplayPos); + + R.InsertTextBefore(Loc, os.str()); + + // Now highlight the ranges. + for (const SourceRange *I = P.ranges_begin(), *E = P.ranges_end(); + I != E; ++I) + HighlightRange(R, LPosInfo.first, *I); + +#if 0 + // If there is a code insertion hint, insert that code. + // FIXME: This code is disabled because it seems to mangle the HTML + // output. I'm leaving it here because it's generally the right idea, + // but needs some help from someone more familiar with the rewriter. + for (const FixItHint *Hint = P.fixit_begin(), *HintEnd = P.fixit_end(); + Hint != HintEnd; ++Hint) { + if (Hint->RemoveRange.isValid()) { + HighlightRange(R, LPosInfo.first, Hint->RemoveRange, + "", ""); + } + if (Hint->InsertionLoc.isValid()) { + std::string EscapedCode = html::EscapeText(Hint->CodeToInsert, true); + EscapedCode = "" + EscapedCode + + ""; + R.InsertTextBefore(Hint->InsertionLoc, EscapedCode); + } + } +#endif +} + +static void EmitAlphaCounter(llvm::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(llvm::raw_ostream& os, + const PathDiagnosticMacroPiece& P, + unsigned num) { + + for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end(); + I!=E; ++I) { + + if (const PathDiagnosticMacroPiece *MP = + dyn_cast(*I)) { + num = ProcessMacroPiece(os, *MP, num); + continue; + } + + if (PathDiagnosticEventPiece *EP = dyn_cast(*I)) { + os << "
" + "" + "
"; + EmitAlphaCounter(os, num++); + os << "
" + << html::EscapeText(EP->getString()) + << "
\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.getInstantiationLoc(Range.getBegin()); + unsigned StartLineNo = SM.getInstantiationLineNumber(InstantiationStart); + + SourceLocation InstantiationEnd = SM.getInstantiationLoc(Range.getEnd()); + unsigned EndLineNo = SM.getInstantiationLineNumber(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.getInstantiationColumnNumber(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.getFileLocWithOffset(EndColNo - OldEndColNo); + + html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd); +} diff --git a/lib/StaticAnalyzer/Makefile b/lib/StaticAnalyzer/Makefile new file mode 100644 index 0000000000..08ace28e35 --- /dev/null +++ b/lib/StaticAnalyzer/Makefile @@ -0,0 +1,19 @@ +##===- clang/lib/Checker/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements analyses built on top of source-level CFGs. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../.. +LIBRARYNAME := clangStaticAnalyzerCore +PARALLEL_DIRS := Checkers + +include $(CLANG_LEVEL)/Makefile + diff --git a/lib/StaticAnalyzer/ManagerRegistry.cpp b/lib/StaticAnalyzer/ManagerRegistry.cpp new file mode 100644 index 0000000000..3f9ed61db7 --- /dev/null +++ b/lib/StaticAnalyzer/ManagerRegistry.cpp @@ -0,0 +1,21 @@ +//===- ManagerRegistry.cpp - Pluggble Analyzer module creators --*- 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 pluggable analyzer module creators. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/ManagerRegistry.h" + +using namespace clang; +using namespace ento; + +StoreManagerCreator ManagerRegistry::StoreMgrCreator = 0; + +ConstraintManagerCreator ManagerRegistry::ConstraintMgrCreator = 0; diff --git a/lib/StaticAnalyzer/MemRegion.cpp b/lib/StaticAnalyzer/MemRegion.cpp new file mode 100644 index 0000000000..e465224f42 --- /dev/null +++ b/lib/StaticAnalyzer/MemRegion.cpp @@ -0,0 +1,987 @@ +//== MemRegion.cpp - Abstract memory regions for static analysis --*- 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 MemRegion and its subclasses. MemRegion defines a +// partially-typed abstraction of memory useful for path-sensitive dataflow +// analyses. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/PathSensitive/SValBuilder.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/Support/BumpVector.h" +#include "clang/AST/CharUnits.h" +#include "clang/AST/RecordLayout.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// MemRegion Construction. +//===----------------------------------------------------------------------===// + +template struct MemRegionManagerTrait; + +template +RegionTy* MemRegionManager::getRegion(const A1 a1) { + + const typename MemRegionManagerTrait::SuperRegionTy *superRegion = + MemRegionManagerTrait::getSuperRegion(*this, a1); + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate(); + new (R) RegionTy(a1, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template +RegionTy* MemRegionManager::getSubRegion(const A1 a1, + const MemRegion *superRegion) { + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate(); + new (R) RegionTy(a1, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template +RegionTy* MemRegionManager::getRegion(const A1 a1, const A2 a2) { + + const typename MemRegionManagerTrait::SuperRegionTy *superRegion = + MemRegionManagerTrait::getSuperRegion(*this, a1, a2); + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, a2, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate(); + new (R) RegionTy(a1, a2, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template +RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, + const MemRegion *superRegion) { + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, a2, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate(); + new (R) RegionTy(a1, a2, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +template +RegionTy* MemRegionManager::getSubRegion(const A1 a1, const A2 a2, const A3 a3, + const MemRegion *superRegion) { + + llvm::FoldingSetNodeID ID; + RegionTy::ProfileRegion(ID, a1, a2, a3, superRegion); + void* InsertPos; + RegionTy* R = cast_or_null(Regions.FindNodeOrInsertPos(ID, + InsertPos)); + + if (!R) { + R = (RegionTy*) A.Allocate(); + new (R) RegionTy(a1, a2, a3, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +//===----------------------------------------------------------------------===// +// Object destruction. +//===----------------------------------------------------------------------===// + +MemRegion::~MemRegion() {} + +MemRegionManager::~MemRegionManager() { + // All regions and their data are BumpPtrAllocated. No need to call + // their destructors. +} + +//===----------------------------------------------------------------------===// +// Basic methods. +//===----------------------------------------------------------------------===// + +bool SubRegion::isSubRegionOf(const MemRegion* R) const { + const MemRegion* r = getSuperRegion(); + while (r != 0) { + if (r == R) + return true; + if (const SubRegion* sr = dyn_cast(r)) + r = sr->getSuperRegion(); + else + break; + } + return false; +} + +MemRegionManager* SubRegion::getMemRegionManager() const { + const SubRegion* r = this; + do { + const MemRegion *superRegion = r->getSuperRegion(); + if (const SubRegion *sr = dyn_cast(superRegion)) { + r = sr; + continue; + } + return superRegion->getMemRegionManager(); + } while (1); +} + +const StackFrameContext *VarRegion::getStackFrame() const { + const StackSpaceRegion *SSR = dyn_cast(getMemorySpace()); + return SSR ? SSR->getStackFrame() : NULL; +} + +//===----------------------------------------------------------------------===// +// Region extents. +//===----------------------------------------------------------------------===// + +DefinedOrUnknownSVal DeclRegion::getExtent(SValBuilder &svalBuilder) const { + ASTContext& Ctx = svalBuilder.getContext(); + QualType T = getDesugaredValueType(Ctx); + + if (isa(T)) + return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this)); + if (isa(T)) + return UnknownVal(); + + CharUnits size = Ctx.getTypeSizeInChars(T); + QualType sizeTy = svalBuilder.getArrayIndexType(); + return svalBuilder.makeIntVal(size.getQuantity(), sizeTy); +} + +DefinedOrUnknownSVal FieldRegion::getExtent(SValBuilder &svalBuilder) const { + 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(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()); +} + +QualType CXXBaseObjectRegion::getValueType() const { + return QualType(decl->getTypeForDecl(), 0); +} + +//===----------------------------------------------------------------------===// +// FoldingSet profiling. +//===----------------------------------------------------------------------===// + +void MemSpaceRegion::Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddInteger((unsigned)getKind()); +} + +void StackSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger((unsigned)getKind()); + ID.AddPointer(getStackFrame()); +} + +void StaticGlobalSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger((unsigned)getKind()); + ID.AddPointer(getCodeRegion()); +} + +void StringRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const StringLiteral* Str, + const MemRegion* superRegion) { + ID.AddInteger((unsigned) StringRegionKind); + ID.AddPointer(Str); + ID.AddPointer(superRegion); +} + +void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const Expr* Ex, unsigned cnt, + const MemRegion *) { + ID.AddInteger((unsigned) AllocaRegionKind); + ID.AddPointer(Ex); + ID.AddInteger(cnt); +} + +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((unsigned) CompoundLiteralRegionKind); + ID.AddPointer(CL); + ID.AddPointer(superRegion); +} + +void CXXThisRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + const PointerType *PT, + const MemRegion *sRegion) { + ID.AddInteger((unsigned) CXXThisRegionKind); + ID.AddPointer(PT); + ID.AddPointer(sRegion); +} + +void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const { + CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion); +} + +void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl* D, + const MemRegion* superRegion, Kind k) { + ID.AddInteger((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((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 FunctionTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const FunctionDecl *FD, + const MemRegion*) { + ID.AddInteger(MemRegion::FunctionTextRegionKind); + ID.AddPointer(FD); +} + +void FunctionTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { + FunctionTextRegion::ProfileRegion(ID, FD, superRegion); +} + +void BlockTextRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const BlockDecl *BD, CanQualType, + const AnalysisContext *AC, + const MemRegion*) { + ID.AddInteger(MemRegion::BlockTextRegionKind); + ID.AddPointer(BD); +} + +void BlockTextRegion::Profile(llvm::FoldingSetNodeID& ID) const { + BlockTextRegion::ProfileRegion(ID, BD, locTy, AC, superRegion); +} + +void BlockDataRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, + const BlockTextRegion *BC, + const LocationContext *LC, + const MemRegion *sReg) { + ID.AddInteger(MemRegion::BlockDataRegionKind); + ID.AddPointer(BC); + ID.AddPointer(LC); + ID.AddPointer(sReg); +} + +void BlockDataRegion::Profile(llvm::FoldingSetNodeID& ID) const { + BlockDataRegion::ProfileRegion(ID, BC, LC, 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 *decl, + const MemRegion *sReg) { + ID.AddPointer(decl); + ID.AddPointer(sReg); +} + +void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const { + ProfileRegion(ID, decl, superRegion); +} + +//===----------------------------------------------------------------------===// +// Region pretty-printing. +//===----------------------------------------------------------------------===// + +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(llvm::raw_ostream& os) const { + os << ""; +} + +void AllocaRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "alloca{" << (void*) Ex << ',' << Cnt << '}'; +} + +void FunctionTextRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "code{" << getDecl()->getDeclName().getAsString() << '}'; +} + +void BlockTextRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "block_code{" << (void*) this << '}'; +} + +void BlockDataRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "block_data{" << BC << '}'; +} + +void CompoundLiteralRegion::dumpToStream(llvm::raw_ostream& os) const { + // FIXME: More elaborate pretty-printing. + os << "{ " << (void*) CL << " }"; +} + +void CXXTempObjectRegion::dumpToStream(llvm::raw_ostream &os) const { + os << "temp_object"; +} + +void CXXBaseObjectRegion::dumpToStream(llvm::raw_ostream &os) const { + os << "base " << decl->getName(); +} + +void CXXThisRegion::dumpToStream(llvm::raw_ostream &os) const { + os << "this"; +} + +void ElementRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "element{" << superRegion << ',' + << Index << ',' << getElementType().getAsString() << '}'; +} + +void FieldRegion::dumpToStream(llvm::raw_ostream& os) const { + os << superRegion << "->" << getDecl(); +} + +void NonStaticGlobalSpaceRegion::dumpToStream(llvm::raw_ostream &os) const { + os << "NonStaticGlobalSpaceRegion"; +} + +void ObjCIvarRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "ivar{" << superRegion << ',' << getDecl() << '}'; +} + +void StringRegion::dumpToStream(llvm::raw_ostream& os) const { + Str->printPretty(os, 0, PrintingPolicy(getContext().getLangOptions())); +} + +void SymbolicRegion::dumpToStream(llvm::raw_ostream& os) const { + os << "SymRegion{" << sym << '}'; +} + +void VarRegion::dumpToStream(llvm::raw_ostream& os) const { + os << cast(D); +} + +void RegionRawOffset::dump() const { + dumpToStream(llvm::errs()); +} + +void RegionRawOffset::dumpToStream(llvm::raw_ostream& os) const { + os << "raw_offset{" << getRegion() << ',' << getByteOffset() << '}'; +} + +void StaticGlobalSpaceRegion::dumpToStream(llvm::raw_ostream &os) const { + os << "StaticGlobalsMemSpace{" << CR << '}'; +} + +//===----------------------------------------------------------------------===// +// MemRegionManager methods. +//===----------------------------------------------------------------------===// + +template +const REG *MemRegionManager::LazyAllocate(REG*& region) { + if (!region) { + region = (REG*) A.Allocate(); + new (region) REG(this); + } + + return region; +} + +template +const REG *MemRegionManager::LazyAllocate(REG*& region, ARG a) { + if (!region) { + region = (REG*) A.Allocate(); + 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(); + 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(); + new (R) StackArgumentsSpaceRegion(this, STC); + return R; +} + +const GlobalsSpaceRegion +*MemRegionManager::getGlobalsRegion(const CodeTextRegion *CR) { + if (!CR) + return LazyAllocate(globals); + + StaticGlobalSpaceRegion *&R = StaticsGlobalSpaceRegions[CR]; + if (R) + return R; + + R = A.Allocate(); + new (R) StaticGlobalSpaceRegion(this, CR); + return R; +} + +const HeapSpaceRegion *MemRegionManager::getHeapRegion() { + return LazyAllocate(heap); +} + +const MemSpaceRegion *MemRegionManager::getUnknownRegion() { + return LazyAllocate(unknown); +} + +const MemSpaceRegion *MemRegionManager::getCodeRegion() { + return LazyAllocate(code); +} + +//===----------------------------------------------------------------------===// +// Constructing regions. +//===----------------------------------------------------------------------===// + +const StringRegion* MemRegionManager::getStringRegion(const StringLiteral* Str){ + return getSubRegion(Str, getGlobalsRegion()); +} + +const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D, + const LocationContext *LC) { + const MemRegion *sReg = 0; + + if (D->hasGlobalStorage() && !D->isStaticLocal()) + sReg = getGlobalsRegion(); + else { + // FIXME: Once we implement scope handling, we will need to properly lookup + // 'D' to the proper LocationContext. + const DeclContext *DC = D->getDeclContext(); + const StackFrameContext *STC = LC->getStackFrameForDeclContext(DC); + + if (!STC) + sReg = getUnknownRegion(); + else { + if (D->hasLocalStorage()) { + sReg = isa(D) || isa(D) + ? static_cast(getStackArgumentsRegion(STC)) + : static_cast(getStackLocalsRegion(STC)); + } + else { + assert(D->isStaticLocal()); + const Decl *D = STC->getDecl(); + if (const FunctionDecl *FD = dyn_cast(D)) + sReg = getGlobalsRegion(getFunctionTextRegion(FD)); + else if (const BlockDecl *BD = dyn_cast(D)) { + const BlockTextRegion *BTR = + getBlockTextRegion(BD, + C.getCanonicalType(BD->getSignatureAsWritten()->getType()), + STC->getAnalysisContext()); + sReg = getGlobalsRegion(BTR); + } + else { + // FIXME: For ObjC-methods, we need a new CodeTextRegion. For now + // just use the main global memspace. + sReg = getGlobalsRegion(); + } + } + } + } + + return getSubRegion(D, sReg); +} + +const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D, + const MemRegion *superR) { + return getSubRegion(D, superR); +} + +const BlockDataRegion * +MemRegionManager::getBlockDataRegion(const BlockTextRegion *BC, + const LocationContext *LC) { + const MemRegion *sReg = 0; + + if (LC) { + // FIXME: Once we implement scope handling, we want the parent region + // to be the scope. + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + sReg = getStackLocalsRegion(STC); + } + else { + // We allow 'LC' to be NULL for cases where want BlockDataRegions + // without context-sensitivity. + sReg = getUnknownRegion(); + } + + return getSubRegion(BC, LC, sReg); +} + +const CompoundLiteralRegion* +MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr* CL, + const LocationContext *LC) { + + const MemRegion *sReg = 0; + + if (CL->isFileScope()) + sReg = getGlobalsRegion(); + else { + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + sReg = getStackLocalsRegion(STC); + } + + return getSubRegion(CL, sReg); +} + +const ElementRegion* +MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx, + const MemRegion* 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); + ElementRegion* R = cast_or_null(data); + + if (!R) { + R = (ElementRegion*) A.Allocate(); + new (R) ElementRegion(T, Idx, superRegion); + Regions.InsertNode(R, InsertPos); + } + + return R; +} + +const FunctionTextRegion * +MemRegionManager::getFunctionTextRegion(const FunctionDecl *FD) { + return getSubRegion(FD, getCodeRegion()); +} + +const BlockTextRegion * +MemRegionManager::getBlockTextRegion(const BlockDecl *BD, CanQualType locTy, + AnalysisContext *AC) { + return getSubRegion(BD, locTy, AC, getCodeRegion()); +} + + +/// getSymbolicRegion - Retrieve or create a "symbolic" memory region. +const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) { + return getSubRegion(sym, getUnknownRegion()); +} + +const FieldRegion* +MemRegionManager::getFieldRegion(const FieldDecl* d, + const MemRegion* superRegion){ + return getSubRegion(d, superRegion); +} + +const ObjCIvarRegion* +MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl* d, + const MemRegion* superRegion) { + return getSubRegion(d, superRegion); +} + +const CXXTempObjectRegion* +MemRegionManager::getCXXTempObjectRegion(Expr const *E, + LocationContext const *LC) { + const StackFrameContext *SFC = LC->getCurrentStackFrame(); + assert(SFC); + return getSubRegion(E, getStackLocalsRegion(SFC)); +} + +const CXXBaseObjectRegion * +MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *decl, + const MemRegion *superRegion) { + return getSubRegion(decl, superRegion); +} + +const CXXThisRegion* +MemRegionManager::getCXXThisRegion(QualType thisPointerTy, + const LocationContext *LC) { + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + const PointerType *PT = thisPointerTy->getAs(); + assert(PT); + return getSubRegion(PT, getStackArgumentsRegion(STC)); +} + +const AllocaRegion* +MemRegionManager::getAllocaRegion(const Expr* E, unsigned cnt, + const LocationContext *LC) { + const StackFrameContext *STC = LC->getCurrentStackFrame(); + assert(STC); + return getSubRegion(E, cnt, getStackLocalsRegion(STC)); +} + +const MemSpaceRegion *MemRegion::getMemorySpace() const { + const MemRegion *R = this; + const SubRegion* SR = dyn_cast(this); + + while (SR) { + R = SR->getSuperRegion(); + SR = dyn_cast(R); + } + + return dyn_cast(R); +} + +bool MemRegion::hasStackStorage() const { + return isa(getMemorySpace()); +} + +bool MemRegion::hasStackNonParametersStorage() const { + return isa(getMemorySpace()); +} + +bool MemRegion::hasStackParametersStorage() const { + return isa(getMemorySpace()); +} + +bool MemRegion::hasGlobalsOrParametersStorage() const { + const MemSpaceRegion *MS = getMemorySpace(); + return isa(MS) || + isa(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: + R = cast(R)->getSuperRegion(); + continue; + default: + break; + } + break; + } + return R; +} + +//===----------------------------------------------------------------------===// +// View handling. +//===----------------------------------------------------------------------===// + +const MemRegion *MemRegion::StripCasts() const { + const MemRegion *R = this; + while (true) { + if (const ElementRegion *ER = dyn_cast(R)) { + // FIXME: generalize. Essentially we want to strip away ElementRegions + // that were layered on a symbolic region because of casts. We only + // want to strip away ElementRegions, however, where the index is 0. + SVal index = ER->getIndex(); + if (nonloc::ConcreteInt *CI = dyn_cast(&index)) { + if (CI->getValue().getSExtValue() == 0) { + R = ER->getSuperRegion(); + continue; + } + } + } + break; + } + return R; +} + +// FIXME: Merge with the implementation of the same method in Store.cpp +static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { + if (const RecordType *RT = Ty->getAs()) { + const RecordDecl *D = RT->getDecl(); + if (!D->getDefinition()) + return false; + } + + return true; +} + +RegionRawOffset ElementRegion::getAsArrayOffset() const { + CharUnits offset = CharUnits::Zero(); + const ElementRegion *ER = this; + const MemRegion *superR = NULL; + ASTContext &C = getContext(); + + // FIXME: Handle multi-dimensional arrays. + + while (ER) { + superR = ER->getSuperRegion(); + + // FIXME: generalize to symbolic offsets. + SVal index = ER->getIndex(); + if (nonloc::ConcreteInt *CI = dyn_cast(&index)) { + // 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 (!IsCompleteType(C, elemType)) { + superR = ER; + break; + } + + CharUnits size = C.getTypeSizeInChars(elemType); + offset += (i * size); + } + + // Go to the next ElementRegion (if any). + ER = dyn_cast(superR); + continue; + } + + return NULL; + } + + assert(superR && "super region cannot be NULL"); + return RegionRawOffset(superR, offset.getQuantity()); +} + +RegionOffset MemRegion::getAsOffset() const { + const MemRegion *R = this; + int64_t Offset = 0; + + while (1) { + switch (R->getKind()) { + default: + return RegionOffset(0); + case SymbolicRegionKind: + case AllocaRegionKind: + case CompoundLiteralRegionKind: + case CXXThisRegionKind: + case StringRegionKind: + case VarRegionKind: + case CXXTempObjectRegionKind: + goto Finish; + case ElementRegionKind: { + const ElementRegion *ER = cast(R); + QualType EleTy = ER->getValueType(); + + if (!IsCompleteType(getContext(), EleTy)) + return RegionOffset(0); + + SVal Index = ER->getIndex(); + if (const nonloc::ConcreteInt *CI=dyn_cast(&Index)) { + int64_t i = CI->getValue().getSExtValue(); + CharUnits Size = getContext().getTypeSizeInChars(EleTy); + Offset += i * Size.getQuantity() * 8; + } else { + // We cannot compute offset for non-concrete index. + return RegionOffset(0); + } + R = ER->getSuperRegion(); + break; + } + case FieldRegionKind: { + const FieldRegion *FR = cast(R); + const RecordDecl *RD = FR->getDecl()->getParent(); + if (!RD->isDefinition()) + // We cannot compute offset for incomplete type. + return RegionOffset(0); + // 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 = getContext().getASTRecordLayout(RD); + // This is offset in bits. + Offset += Layout.getFieldOffset(idx); + R = FR->getSuperRegion(); + break; + } + } + } + + Finish: + return RegionOffset(R, Offset); +} + +//===----------------------------------------------------------------------===// +// BlockDataRegion +//===----------------------------------------------------------------------===// + +void BlockDataRegion::LazyInitializeReferencedVars() { + if (ReferencedVars) + return; + + AnalysisContext *AC = getCodeRegion()->getAnalysisContext(); + AnalysisContext::referenced_decls_iterator I, E; + llvm::tie(I, E) = AC->getReferencedBlockVars(BC->getDecl()); + + if (I == E) { + ReferencedVars = (void*) 0x1; + return; + } + + MemRegionManager &MemMgr = *getMemRegionManager(); + llvm::BumpPtrAllocator &A = MemMgr.getAllocator(); + BumpVectorContext BC(A); + + typedef BumpVector VarVec; + VarVec *BV = (VarVec*) A.Allocate(); + new (BV) VarVec(BC, E - I); + + for ( ; I != E; ++I) { + const VarDecl *VD = *I; + const VarRegion *VR = 0; + + if (!VD->getAttr() && VD->hasLocalStorage()) + VR = MemMgr.getVarRegion(VD, this); + else { + if (LC) + VR = MemMgr.getVarRegion(VD, LC); + else { + VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion()); + } + } + + assert(VR); + BV->push_back(VR, BC); + } + + ReferencedVars = BV; +} + +BlockDataRegion::referenced_vars_iterator +BlockDataRegion::referenced_vars_begin() const { + const_cast(this)->LazyInitializeReferencedVars(); + + BumpVector *Vec = + static_cast*>(ReferencedVars); + + return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? + NULL : Vec->begin()); +} + +BlockDataRegion::referenced_vars_iterator +BlockDataRegion::referenced_vars_end() const { + const_cast(this)->LazyInitializeReferencedVars(); + + BumpVector *Vec = + static_cast*>(ReferencedVars); + + return BlockDataRegion::referenced_vars_iterator(Vec == (void*) 0x1 ? + NULL : Vec->end()); +} diff --git a/lib/StaticAnalyzer/PathDiagnostic.cpp b/lib/StaticAnalyzer/PathDiagnostic.cpp new file mode 100644 index 0000000000..f05dec820d --- /dev/null +++ b/lib/StaticAnalyzer/PathDiagnostic.cpp @@ -0,0 +1,279 @@ +//===--- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -*- 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 PathDiagnostic-related interfaces. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/AST/Expr.h" +#include "clang/AST/Decl.h" +#include "clang/AST/DeclObjC.h" +#include "clang/AST/StmtCXX.h" +#include "llvm/ADT/SmallString.h" +#include "llvm/Support/Casting.h" + +using namespace clang; +using namespace ento; +using llvm::dyn_cast; +using llvm::isa; + +bool PathDiagnosticMacroPiece::containsEvent() const { + for (const_iterator I = begin(), E = end(); I!=E; ++I) { + if (isa(*I)) + return true; + + if (PathDiagnosticMacroPiece *MP = dyn_cast(*I)) + if (MP->containsEvent()) + return true; + } + + return false; +} + +static llvm::StringRef StripTrailingDots(llvm::StringRef s) { + for (llvm::StringRef::size_type i = s.size(); i != 0; --i) + if (s[i - 1] != '.') + return s.substr(0, i); + return ""; +} + +PathDiagnosticPiece::PathDiagnosticPiece(llvm::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() {} +PathDiagnosticEventPiece::~PathDiagnosticEventPiece() {} +PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() {} + +PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() { + for (iterator I = begin(), E = end(); I != E; ++I) delete *I; +} + +PathDiagnostic::PathDiagnostic() : Size(0) {} + +PathDiagnostic::~PathDiagnostic() { + for (iterator I = begin(), E = end(); I != E; ++I) delete &*I; +} + +void PathDiagnostic::resetPath(bool deletePieces) { + Size = 0; + + if (deletePieces) + for (iterator I=begin(), E=end(); I!=E; ++I) + delete &*I; + + path.clear(); +} + + +PathDiagnostic::PathDiagnostic(llvm::StringRef bugtype, llvm::StringRef desc, + llvm::StringRef category) + : Size(0), + BugType(StripTrailingDots(bugtype)), + Desc(StripTrailingDots(desc)), + Category(StripTrailingDots(category)) {} + +void PathDiagnosticClient::HandleDiagnostic(Diagnostic::Level DiagLevel, + const DiagnosticInfo &Info) { + // Default implementation (Warnings/errors count). + DiagnosticClient::HandleDiagnostic(DiagLevel, Info); + + // Create a PathDiagnostic with a single piece. + + PathDiagnostic* D = new PathDiagnostic(); + + const char *LevelStr; + switch (DiagLevel) { + default: + case Diagnostic::Ignored: assert(0 && "Invalid diagnostic type"); + case Diagnostic::Note: LevelStr = "note: "; break; + case Diagnostic::Warning: LevelStr = "warning: "; break; + case Diagnostic::Error: LevelStr = "error: "; break; + case Diagnostic::Fatal: LevelStr = "fatal error: "; break; + } + + llvm::SmallString<100> StrC; + StrC += LevelStr; + Info.FormatDiagnostic(StrC); + + PathDiagnosticPiece *P = + new PathDiagnosticEventPiece(FullSourceLoc(Info.getLocation(), + Info.getSourceManager()), + StrC.str()); + + for (unsigned i = 0, e = Info.getNumRanges(); i != e; ++i) + P->addRange(Info.getRange(i).getAsRange()); + for (unsigned i = 0, e = Info.getNumFixItHints(); i != e; ++i) + P->addFixItHint(Info.getFixItHint(i)); + D->push_front(P); + + HandlePathDiagnostic(D); +} + +//===----------------------------------------------------------------------===// +// PathDiagnosticLocation methods. +//===----------------------------------------------------------------------===// + +FullSourceLoc PathDiagnosticLocation::asLocation() 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: + return FullSourceLoc(S->getLocStart(), const_cast(*SM)); + case DeclK: + return FullSourceLoc(D->getLocation(), const_cast(*SM)); + } + + return FullSourceLoc(R.getBegin(), const_cast(*SM)); +} + +PathDiagnosticRange PathDiagnosticLocation::asRange() 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(R, true); + case RangeK: + break; + case StmtK: { + const Stmt *S = asStmt(); + switch (S->getStmtClass()) { + default: + break; + case Stmt::DeclStmtClass: { + const DeclStmt *DS = cast(S); + if (DS->isSingleDecl()) { + // Should always be the case, but we'll be defensive. + return SourceRange(DS->getLocStart(), + 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::ConditionalOperatorClass: + case Stmt::ObjCForCollectionStmtClass: { + SourceLocation L = S->getLocStart(); + return SourceRange(L, L); + } + } + + return S->getSourceRange(); + } + case DeclK: + if (const ObjCMethodDecl *MD = dyn_cast(D)) + return MD->getSourceRange(); + if (const FunctionDecl *FD = dyn_cast(D)) { + if (Stmt *Body = FD->getBody()) + return Body->getSourceRange(); + } + else { + SourceLocation L = D->getLocation(); + return PathDiagnosticRange(SourceRange(L, L), true); + } + } + + return R; +} + +void PathDiagnosticLocation::flatten() { + if (K == StmtK) { + R = asRange(); + K = RangeK; + S = 0; + D = 0; + } + else if (K == DeclK) { + SourceLocation L = D->getLocation(); + R = SourceRange(L, L); + K = SingleLocK; + S = 0; + D = 0; + } +} + +//===----------------------------------------------------------------------===// +// FoldingSet profiling methods. +//===----------------------------------------------------------------------===// + +void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger((unsigned) K); + switch (K) { + case RangeK: + ID.AddInteger(R.getBegin().getRawEncoding()); + ID.AddInteger(R.getEnd().getRawEncoding()); + break; + case SingleLocK: + ID.AddInteger(R.getBegin().getRawEncoding()); + break; + case StmtK: + ID.Add(S); + break; + case DeclK: + ID.Add(D); + break; + } + return; +} + +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()); + for (range_iterator I = ranges_begin(), E = ranges_end(); I != E; ++I) { + ID.AddInteger(I->getBegin().getRawEncoding()); + ID.AddInteger(I->getEnd().getRawEncoding()); + } +} + +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_iterator I = begin(), E = end(); I != E; ++I) + ID.Add(*I); +} + +void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const { + PathDiagnosticSpotPiece::Profile(ID); + for (const_iterator I = begin(), E = end(); I != E; ++I) + ID.Add(**I); +} + +void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddInteger(Size); + ID.AddString(BugType); + ID.AddString(Desc); + ID.AddString(Category); + for (const_iterator I = begin(), E = end(); I != E; ++I) + ID.Add(*I); + + for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I) + ID.AddString(*I); +} diff --git a/lib/StaticAnalyzer/PlistDiagnostics.cpp b/lib/StaticAnalyzer/PlistDiagnostics.cpp new file mode 100644 index 0000000000..ecc0cff5c3 --- /dev/null +++ b/lib/StaticAnalyzer/PlistDiagnostics.cpp @@ -0,0 +1,472 @@ +//===--- 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/StaticAnalyzer/PathDiagnosticClients.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Basic/FileManager.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/Casting.h" +#include "llvm/ADT/DenseMap.h" +#include "llvm/ADT/SmallVector.h" +using namespace clang; +using namespace ento; +using llvm::cast; + +typedef llvm::DenseMap FIDMap; + +namespace clang { + class Preprocessor; +} + +namespace { +struct CompareDiagnostics { + // Compare if 'X' is "<" than 'Y'. + bool operator()(const PathDiagnostic *X, const PathDiagnostic *Y) const { + // First compare by location + const FullSourceLoc &XLoc = X->getLocation().asLocation(); + const FullSourceLoc &YLoc = Y->getLocation().asLocation(); + if (XLoc < YLoc) + return true; + if (XLoc != YLoc) + return false; + + // Next, compare by bug type. + llvm::StringRef XBugType = X->getBugType(); + llvm::StringRef YBugType = Y->getBugType(); + if (XBugType < YBugType) + return true; + if (XBugType != YBugType) + return false; + + // Next, compare by bug description. + llvm::StringRef XDesc = X->getDescription(); + llvm::StringRef YDesc = Y->getDescription(); + if (XDesc < YDesc) + return true; + if (XDesc != YDesc) + return false; + + // FIXME: Further refine by comparing PathDiagnosticPieces? + return false; + } +}; +} + +namespace { + class PlistDiagnostics : public PathDiagnosticClient { + std::vector BatchedDiags; + const std::string OutputFile; + const LangOptions &LangOpts; + llvm::OwningPtr SubPD; + bool flushed; + public: + PlistDiagnostics(const std::string& prefix, const LangOptions &LangOpts, + PathDiagnosticClient *subPD); + + ~PlistDiagnostics() { FlushDiagnostics(NULL); } + + void FlushDiagnostics(llvm::SmallVectorImpl *FilesMade); + + void HandlePathDiagnostic(const PathDiagnostic* D); + + virtual llvm::StringRef getName() const { + return "PlistDiagnostics"; + } + + PathGenerationScheme getGenerationScheme() const; + bool supportsLogicalOpControlFlow() const { return true; } + bool supportsAllBlockEdges() const { return true; } + virtual bool useVerboseDescription() const { return false; } + }; +} // end anonymous namespace + +PlistDiagnostics::PlistDiagnostics(const std::string& output, + const LangOptions &LO, + PathDiagnosticClient *subPD) + : OutputFile(output), LangOpts(LO), SubPD(subPD), flushed(false) {} + +PathDiagnosticClient* +ento::createPlistDiagnosticClient(const std::string& s, const Preprocessor &PP, + PathDiagnosticClient *subPD) { + return new PlistDiagnostics(s, PP.getLangOptions(), subPD); +} + +PathDiagnosticClient::PathGenerationScheme +PlistDiagnostics::getGenerationScheme() const { + if (const PathDiagnosticClient *PD = SubPD.get()) + return PD->getGenerationScheme(); + + return Extensive; +} + +static void AddFID(FIDMap &FIDs, llvm::SmallVectorImpl &V, + const SourceManager* SM, SourceLocation L) { + + FileID FID = SM->getFileID(SM->getInstantiationLoc(L)); + FIDMap::iterator I = FIDs.find(FID); + if (I != FIDs.end()) return; + FIDs[FID] = V.size(); + V.push_back(FID); +} + +static unsigned GetFID(const FIDMap& FIDs, const SourceManager &SM, + SourceLocation L) { + FileID FID = SM.getFileID(SM.getInstantiationLoc(L)); + FIDMap::const_iterator I = FIDs.find(FID); + assert(I != FIDs.end()); + return I->second; +} + +static llvm::raw_ostream& Indent(llvm::raw_ostream& o, const unsigned indent) { + for (unsigned i = 0; i < indent; ++i) o << ' '; + return o; +} + +static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM, + const LangOptions &LangOpts, + SourceLocation L, const FIDMap &FM, + unsigned indent, bool extend = false) { + + FullSourceLoc Loc(SM.getInstantiationLoc(L), const_cast(SM)); + + // Add in the length of the token, so that we cover multi-char tokens. + unsigned offset = + extend ? Lexer::MeasureTokenLength(Loc, SM, LangOpts) - 1 : 0; + + Indent(o, indent) << "\n"; + Indent(o, indent) << " line" + << Loc.getInstantiationLineNumber() << "\n"; + Indent(o, indent) << " col" + << Loc.getInstantiationColumnNumber() + offset << "\n"; + Indent(o, indent) << " file" + << GetFID(FM, SM, Loc) << "\n"; + Indent(o, indent) << "\n"; +} + +static void EmitLocation(llvm::raw_ostream& o, const SourceManager &SM, + const LangOptions &LangOpts, + const PathDiagnosticLocation &L, const FIDMap& FM, + unsigned indent, bool extend = false) { + EmitLocation(o, SM, LangOpts, L.asLocation(), FM, indent, extend); +} + +static void EmitRange(llvm::raw_ostream& o, const SourceManager &SM, + const LangOptions &LangOpts, + PathDiagnosticRange R, const FIDMap &FM, + unsigned indent) { + Indent(o, indent) << "\n"; + EmitLocation(o, SM, LangOpts, R.getBegin(), FM, indent+1); + EmitLocation(o, SM, LangOpts, R.getEnd(), FM, indent+1, !R.isPoint); + Indent(o, indent) << "\n"; +} + +static llvm::raw_ostream& EmitString(llvm::raw_ostream& o, + const std::string& s) { + o << ""; + for (std::string::const_iterator I=s.begin(), E=s.end(); I!=E; ++I) { + char c = *I; + switch (c) { + default: o << c; break; + case '&': o << "&"; break; + case '<': o << "<"; break; + case '>': o << ">"; break; + case '\'': o << "'"; break; + case '\"': o << """; break; + } + } + o << ""; + return o; +} + +static void ReportControlFlow(llvm::raw_ostream& o, + const PathDiagnosticControlFlowPiece& P, + const FIDMap& FM, + const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent) { + + Indent(o, indent) << "\n"; + ++indent; + + Indent(o, indent) << "kindcontrol\n"; + + // Emit edges. + Indent(o, indent) << "edges\n"; + ++indent; + Indent(o, indent) << "\n"; + ++indent; + for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end(); + I!=E; ++I) { + Indent(o, indent) << "\n"; + ++indent; + Indent(o, indent) << "start\n"; + EmitRange(o, SM, LangOpts, I->getStart().asRange(), FM, indent+1); + Indent(o, indent) << "end\n"; + EmitRange(o, SM, LangOpts, I->getEnd().asRange(), FM, indent+1); + --indent; + Indent(o, indent) << "\n"; + } + --indent; + Indent(o, indent) << "\n"; + --indent; + + // Output any helper text. + const std::string& s = P.getString(); + if (!s.empty()) { + Indent(o, indent) << "alternate"; + EmitString(o, s) << '\n'; + } + + --indent; + Indent(o, indent) << "\n"; +} + +static void ReportEvent(llvm::raw_ostream& o, const PathDiagnosticPiece& P, + const FIDMap& FM, + const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent) { + + Indent(o, indent) << "\n"; + ++indent; + + Indent(o, indent) << "kindevent\n"; + + // Output the location. + FullSourceLoc L = P.getLocation().asLocation(); + + Indent(o, indent) << "location\n"; + EmitLocation(o, SM, LangOpts, L, FM, indent); + + // Output the ranges (if any). + PathDiagnosticPiece::range_iterator RI = P.ranges_begin(), + RE = P.ranges_end(); + + if (RI != RE) { + Indent(o, indent) << "ranges\n"; + Indent(o, indent) << "\n"; + ++indent; + for (; RI != RE; ++RI) + EmitRange(o, SM, LangOpts, *RI, FM, indent+1); + --indent; + Indent(o, indent) << "\n"; + } + + // Output the text. + assert(!P.getString().empty()); + Indent(o, indent) << "extended_message\n"; + Indent(o, indent); + EmitString(o, P.getString()) << '\n'; + + // Output the short text. + // FIXME: Really use a short string. + Indent(o, indent) << "message\n"; + EmitString(o, P.getString()) << '\n'; + + // Finish up. + --indent; + Indent(o, indent); o << "\n"; +} + +static void ReportMacro(llvm::raw_ostream& o, + const PathDiagnosticMacroPiece& P, + const FIDMap& FM, const SourceManager &SM, + const LangOptions &LangOpts, + unsigned indent) { + + for (PathDiagnosticMacroPiece::const_iterator I=P.begin(), E=P.end(); + I!=E; ++I) { + + switch ((*I)->getKind()) { + default: + break; + case PathDiagnosticPiece::Event: + ReportEvent(o, cast(**I), FM, SM, LangOpts, + indent); + break; + case PathDiagnosticPiece::Macro: + ReportMacro(o, cast(**I), FM, SM, LangOpts, + indent); + break; + } + } +} + +static void ReportDiag(llvm::raw_ostream& o, const PathDiagnosticPiece& P, + const FIDMap& FM, const SourceManager &SM, + const LangOptions &LangOpts) { + + unsigned indent = 4; + + switch (P.getKind()) { + case PathDiagnosticPiece::ControlFlow: + ReportControlFlow(o, cast(P), FM, SM, + LangOpts, indent); + break; + case PathDiagnosticPiece::Event: + ReportEvent(o, cast(P), FM, SM, LangOpts, + indent); + break; + case PathDiagnosticPiece::Macro: + ReportMacro(o, cast(P), FM, SM, LangOpts, + indent); + break; + } +} + +void PlistDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) { + if (!D) + return; + + if (D->empty()) { + delete D; + 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. + const_cast(D)->flattenLocations(); + BatchedDiags.push_back(D); +} + +void PlistDiagnostics::FlushDiagnostics(llvm::SmallVectorImpl + *FilesMade) { + + if (flushed) + return; + + flushed = true; + + // Sort the diagnostics so that they are always emitted in a deterministic + // order. + if (!BatchedDiags.empty()) + std::sort(BatchedDiags.begin(), BatchedDiags.end(), CompareDiagnostics()); + + // Build up a set of FIDs that we use by scanning the locations and + // ranges of the diagnostics. + FIDMap FM; + llvm::SmallVector Fids; + const SourceManager* SM = 0; + + if (!BatchedDiags.empty()) + SM = &(*BatchedDiags.begin())->begin()->getLocation().getManager(); + + for (std::vector::iterator DI = BatchedDiags.begin(), + DE = BatchedDiags.end(); DI != DE; ++DI) { + + const PathDiagnostic *D = *DI; + + for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I!=E; ++I) { + AddFID(FM, Fids, SM, I->getLocation().asLocation()); + + for (PathDiagnosticPiece::range_iterator RI=I->ranges_begin(), + RE=I->ranges_end(); RI!=RE; ++RI) { + AddFID(FM, Fids, SM, RI->getBegin()); + AddFID(FM, Fids, SM, RI->getEnd()); + } + } + } + + // Open the file. + std::string ErrMsg; + llvm::raw_fd_ostream o(OutputFile.c_str(), ErrMsg); + if (!ErrMsg.empty()) { + llvm::errs() << "warning: could not creat file: " << OutputFile << '\n'; + return; + } + + // Write the plist header. + o << "\n" + "\n" + "\n"; + + // Write the root object: a containing... + // - "files", an mapping from FIDs to file names + // - "diagnostics", an containing the path diagnostics + o << "\n" + " files\n" + " \n"; + + for (llvm::SmallVectorImpl::iterator I=Fids.begin(), E=Fids.end(); + I!=E; ++I) { + o << " "; + EmitString(o, SM->getFileEntryForID(*I)->getName()) << '\n'; + } + + o << " \n" + " diagnostics\n" + " \n"; + + for (std::vector::iterator DI=BatchedDiags.begin(), + DE = BatchedDiags.end(); DI!=DE; ++DI) { + + o << " \n" + " path\n"; + + const PathDiagnostic *D = *DI; + // Create an owning smart pointer for 'D' just so that we auto-free it + // when we exit this method. + llvm::OwningPtr OwnedD(const_cast(D)); + + o << " \n"; + + for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I) + ReportDiag(o, *I, FM, *SM, LangOpts); + + o << " \n"; + + // Output the bug type and bug category. + o << " description"; + EmitString(o, D->getDescription()) << '\n'; + o << " category"; + EmitString(o, D->getCategory()) << '\n'; + o << " type"; + EmitString(o, D->getBugType()) << '\n'; + + // Output the location of the bug. + o << " location\n"; + EmitLocation(o, *SM, LangOpts, D->getLocation(), FM, 2); + + // Output the diagnostic to the sub-diagnostic client, if any. + if (SubPD) { + SubPD->HandlePathDiagnostic(OwnedD.take()); + llvm::SmallVector SubFilesMade; + SubPD->FlushDiagnostics(SubFilesMade); + + if (!SubFilesMade.empty()) { + o << " " << SubPD->getName() << "_files\n"; + o << " \n"; + for (size_t i = 0, n = SubFilesMade.size(); i < n ; ++i) + o << " " << SubFilesMade[i] << "\n"; + o << " \n"; + } + } + + // Close up the entry. + o << " \n"; + } + + o << " \n"; + + // Finish. + o << "\n"; + + if (FilesMade) + FilesMade->push_back(OutputFile); + + BatchedDiags.clear(); +} diff --git a/lib/StaticAnalyzer/README.txt b/lib/StaticAnalyzer/README.txt new file mode 100644 index 0000000000..dd16ccc980 --- /dev/null +++ b/lib/StaticAnalyzer/README.txt @@ -0,0 +1,117 @@ +//===----------------------------------------------------------------------===// +// Clang Static Analyzer +//===----------------------------------------------------------------------===// + += Library Structure = + +The analyzer library has two layers: a (low-level) static analysis +engine (GRExprEngine.cpp and friends), and some static checkers +(*Checker.cpp). The latter are built on top of the former via the +Checker and CheckerVisitor interfaces (Checker.h and +CheckerVisitor.h). The Checker interface is designed to be minimal +and simple for checker writers, and attempts to isolate them from much +of the gore of the internal analysis engine. + += How It Works = + +The analyzer is inspired by several foundational research papers ([1], +[2]). (FIXME: kremenek to add more links) + +In a nutshell, the analyzer is basically a source code simulator that +traces out possible paths of execution. The state of the program +(values of variables and expressions) is encapsulated by the state +(GRState). A location in the program is called a program point +(ProgramPoint), and the combination of state and program point is a +node in an exploded graph (ExplodedGraph). The term "exploded" comes +from exploding the control-flow edges in the control-flow graph (CFG). + +Conceptually the analyzer does a reachability analysis through the +ExplodedGraph. We start at a root node, which has the entry program +point and initial state, and then simulate transitions by analyzing +individual expressions. The analysis of an expression can cause the +state to change, resulting in a new node in the ExplodedGraph with an +updated program point and an updated state. A bug is found by hitting +a node that satisfies some "bug condition" (basically a violation of a +checking invariant). + +The analyzer traces out multiple paths by reasoning about branches and +then bifurcating the state: on the true branch the conditions of the +branch are assumed to be true and on the false branch the conditions +of the branch are assumed to be false. Such "assumptions" create +constraints on the values of the program, and those constraints are +recorded in the GRState object (and are manipulated by the +ConstraintManager). If assuming the conditions of a branch would +cause the constraints to be unsatisfiable, the branch is considered +infeasible and that path is not taken. This is how we get +path-sensitivity. We reduce exponential blow-up by caching nodes. If +a new node with the same state and program point as an existing node +would get generated, the path "caches out" and we simply reuse the +existing node. Thus the ExplodedGraph is not a DAG; it can contain +cycles as paths loop back onto each other and cache out. + +GRState and ExplodedNodes are basically immutable once created. Once +one creates a GRState, you need to create a new one to get a new +GRState. This immutability is key since the ExplodedGraph represents +the behavior of the analyzed program from the entry point. To +represent these efficiently, we use functional data structures (e.g., +ImmutableMaps) which share data between instances. + +Finally, individual Checkers work by also manipulating the analysis +state. The analyzer engine talks to them via a visitor interface. +For example, the PreVisitCallExpr() method is called by GRExprEngine +to tell the Checker that we are about to analyze a CallExpr, and the +checker is asked to check for any preconditions that might not be +satisfied. The checker can do nothing, or it can generate a new +GRState and ExplodedNode which contains updated checker state. If it +finds a bug, it can tell the BugReporter object about the bug, +providing it an ExplodedNode which is the last node in the path that +triggered the problem. + += Working on the Analyzer = + +If you are interested in bringing up support for C++ expressions, the +best place to look is the visitation logic in GRExprEngine, which +handles the simulation of individual expressions. There are plenty of +examples there of how other expressions are handled. + +If you are interested in writing checkers, look at the Checker and +CheckerVisitor interfaces (Checker.h and CheckerVisitor.h). Also look +at the files named *Checker.cpp for examples on how you can implement +these interfaces. + += Debugging the Analyzer = + +There are some useful command-line options for debugging. For example: + +$ clang -cc1 -help | grep analyze + -analyze-function + -analyzer-display-progress + -analyzer-viz-egraph-graphviz + ... + +The first allows you to specify only analyzing a specific function. +The second prints to the console what function is being analyzed. The +third generates a graphviz dot file of the ExplodedGraph. This is +extremely useful when debugging the analyzer and viewing the +simulation results. + +Of course, viewing the CFG (Control-Flow Graph) is also useful: + +$ clang -cc1 -help | grep cfg + -cfg-add-implicit-dtors Add C++ implicit destructors to CFGs for all analyses + -cfg-add-initializers Add C++ initializers to CFGs for all analyses + -cfg-dump Display Control-Flow Graphs + -cfg-view View Control-Flow Graphs using GraphViz + -unoptimized-cfg Generate unoptimized CFGs for all analyses + +-cfg-dump dumps a textual representation of the CFG to the console, +and -cfg-view creates a GraphViz representation. + += References = + +[1] Precise interprocedural dataflow analysis via graph reachability, + T Reps, S Horwitz, and M Sagiv, POPL '95, + http://portal.acm.org/citation.cfm?id=199462 + +[2] A memory model for static analysis of C programs, Z Xu, T + Kremenek, and J Zhang, http://lcs.ios.ac.cn/~xzx/memmodel.pdf diff --git a/lib/StaticAnalyzer/RangeConstraintManager.cpp b/lib/StaticAnalyzer/RangeConstraintManager.cpp new file mode 100644 index 0000000000..c89fa50b16 --- /dev/null +++ b/lib/StaticAnalyzer/RangeConstraintManager.cpp @@ -0,0 +1,442 @@ +//== 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 GRState. +// +//===----------------------------------------------------------------------===// + +#include "SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/TransferFuncs.h" +#include "clang/StaticAnalyzer/ManagerRegistry.h" +#include "llvm/Support/Debug.h" +#include "llvm/ADT/FoldingSet.h" +#include "llvm/ADT/ImmutableSet.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +namespace { class ConstraintRange {}; } +static int ConstraintRangeIndex = 0; + +/// A Range represents the closed range [from, to]. The caller must +/// guarantee that from <= to. Note that Range is immutable, so as not +/// to subvert RangeSet's immutability. +namespace { +class Range : public std::pair { +public: + Range(const llvm::APSInt &from, const llvm::APSInt &to) + : std::pair(&from, &to) { + assert(from <= to); + } + bool Includes(const llvm::APSInt &v) const { + return *first <= v && v <= *second; + } + const llvm::APSInt &From() const { + return *first; + } + const llvm::APSInt &To() const { + return *second; + } + const llvm::APSInt *getConcreteValue() const { + return &From() == &To() ? &From() : NULL; + } + + void Profile(llvm::FoldingSetNodeID &ID) const { + ID.AddPointer(&From()); + ID.AddPointer(&To()); + } +}; + + +class RangeTrait : public llvm::ImutContainerInfo { +public: + // When comparing if one Range is less than another, we should compare + // the actual APSInt values instead of their pointers. This keeps the order + // consistent (instead of comparing by pointer values) and can potentially + // be used to speed up some of the operations in RangeSet. + static inline bool isLess(key_type_ref lhs, key_type_ref rhs) { + return *lhs.first < *rhs.first || (!(*rhs.first < *lhs.first) && + *lhs.second < *rhs.second); + } +}; + +/// RangeSet contains a set of ranges. If the set is empty, then +/// there the value of a symbol is overly constrained and there are no +/// possible values for that symbol. +class RangeSet { + typedef llvm::ImmutableSet PrimRangeSet; + PrimRangeSet ranges; // no need to make const, since it is an + // ImmutableSet - this allows default operator= + // to work. +public: + typedef PrimRangeSet::Factory Factory; + typedef PrimRangeSet::iterator iterator; + + RangeSet(PrimRangeSet RS) : ranges(RS) {} + + iterator begin() const { return ranges.begin(); } + iterator end() const { return ranges.end(); } + + bool isEmpty() const { return ranges.isEmpty(); } + + /// Construct a new RangeSet representing '{ [from, to] }'. + RangeSet(Factory &F, const llvm::APSInt &from, const llvm::APSInt &to) + : ranges(F.add(F.getEmptySet(), Range(from, to))) {} + + /// Profile - Generates a hash profile of this RangeSet for use + /// by FoldingSet. + void Profile(llvm::FoldingSetNodeID &ID) const { ranges.Profile(ID); } + + /// getConcreteValue - If a symbol is contrained to equal a specific integer + /// constant then this method returns that value. Otherwise, it returns + /// NULL. + const llvm::APSInt* getConcreteValue() const { + return ranges.isSingleton() ? ranges.begin()->getConcreteValue() : 0; + } + +private: + void 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); + } + } + } + +public: + // 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 Intersect(BasicValueFactory &BV, Factory &F, + const llvm::APSInt &Lower, + const llvm::APSInt &Upper) const { + 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; + } + + void print(llvm::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 << " }"; + } + + bool operator==(const RangeSet &other) const { + return ranges == other.ranges; + } +}; +} // end anonymous namespace + +typedef llvm::ImmutableMap ConstraintRangeTy; + +namespace clang { +namespace ento { +template<> +struct GRStateTrait + : public GRStatePartialTrait { + static inline void* GDMIndex() { return &ConstraintRangeIndex; } +}; +} +} + +namespace { +class RangeConstraintManager : public SimpleConstraintManager{ + RangeSet GetRange(const GRState *state, SymbolRef sym); +public: + RangeConstraintManager(SubEngine &subengine) + : SimpleConstraintManager(subengine) {} + + const GRState *assumeSymNE(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment); + + const GRState *assumeSymEQ(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment); + + const GRState *assumeSymLT(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment); + + const GRState *assumeSymGT(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment); + + const GRState *assumeSymGE(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment); + + const GRState *assumeSymLE(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment); + + const llvm::APSInt* getSymVal(const GRState* St, SymbolRef sym) const; + + // FIXME: Refactor into SimpleConstraintManager? + bool isEqual(const GRState* St, SymbolRef sym, const llvm::APSInt& V) const { + const llvm::APSInt *i = getSymVal(St, sym); + return i ? *i == V : false; + } + + const GRState* RemoveDeadBindings(const GRState* St, SymbolReaper& SymReaper); + + void print(const GRState* St, llvm::raw_ostream& Out, + const char* nl, const char *sep); + +private: + RangeSet::Factory F; +}; + +} // end anonymous namespace + +ConstraintManager* ento::CreateRangeConstraintManager(GRStateManager&, + SubEngine &subeng) { + return new RangeConstraintManager(subeng); +} + +const llvm::APSInt* RangeConstraintManager::getSymVal(const GRState* St, + SymbolRef sym) const { + const ConstraintRangeTy::data_type *T = St->get(sym); + return T ? T->getConcreteValue() : NULL; +} + +/// Scan all symbols referenced by the constraints. If the symbol is not alive +/// as marked in LSymbols, mark it as dead in DSymbols. +const GRState* +RangeConstraintManager::RemoveDeadBindings(const GRState* state, + SymbolReaper& SymReaper) { + + ConstraintRangeTy CR = state->get(); + ConstraintRangeTy::Factory& CRFactory = state->get_context(); + + for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) { + SymbolRef sym = I.getKey(); + if (SymReaper.maybeDead(sym)) + CR = CRFactory.remove(CR, sym); + } + + return state->set(CR); +} + +RangeSet +RangeConstraintManager::GetRange(const GRState *state, SymbolRef sym) { + if (ConstraintRangeTy::data_type* V = state->get(sym)) + return *V; + + // Lazily generate a new RangeSet representing all possible values for the + // given symbol type. + QualType T = state->getSymbolManager().getType(sym); + BasicValueFactory& BV = state->getBasicVals(); + return RangeSet(F, BV.getMinValue(T), BV.getMaxValue(T)); +} + +//===------------------------------------------------------------------------=== +// assumeSymX methods: public 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. + +const GRState* +RangeConstraintManager::assumeSymNE(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment) { + BasicValueFactory &BV = state->getBasicVals(); + + llvm::APSInt Lower = 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(state, sym).Intersect(BV, F, Upper, Lower); + return New.isEmpty() ? NULL : state->set(sym, New); +} + +const GRState* +RangeConstraintManager::assumeSymEQ(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment) { + // [Int-Adjustment, Int-Adjustment] + BasicValueFactory &BV = state->getBasicVals(); + llvm::APSInt AdjInt = Int-Adjustment; + RangeSet New = GetRange(state, sym).Intersect(BV, F, AdjInt, AdjInt); + return New.isEmpty() ? NULL : state->set(sym, New); +} + +const GRState* +RangeConstraintManager::assumeSymLT(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment) { + BasicValueFactory &BV = state->getBasicVals(); + + QualType T = state->getSymbolManager().getType(sym); + const llvm::APSInt &Min = BV.getMinValue(T); + + // Special case for Int == Min. This is always false. + if (Int == Min) + return NULL; + + llvm::APSInt Lower = Min-Adjustment; + llvm::APSInt Upper = Int-Adjustment; + --Upper; + + RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); + return New.isEmpty() ? NULL : state->set(sym, New); +} + +const GRState* +RangeConstraintManager::assumeSymGT(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment) { + BasicValueFactory &BV = state->getBasicVals(); + + QualType T = state->getSymbolManager().getType(sym); + const llvm::APSInt &Max = BV.getMaxValue(T); + + // Special case for Int == Max. This is always false. + if (Int == Max) + return NULL; + + llvm::APSInt Lower = Int-Adjustment; + llvm::APSInt Upper = Max-Adjustment; + ++Lower; + + RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); + return New.isEmpty() ? NULL : state->set(sym, New); +} + +const GRState* +RangeConstraintManager::assumeSymGE(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment) { + BasicValueFactory &BV = state->getBasicVals(); + + QualType T = state->getSymbolManager().getType(sym); + const llvm::APSInt &Min = BV.getMinValue(T); + + // Special case for Int == Min. This is always feasible. + if (Int == Min) + return state; + + const llvm::APSInt &Max = BV.getMaxValue(T); + + llvm::APSInt Lower = Int-Adjustment; + llvm::APSInt Upper = Max-Adjustment; + + RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); + return New.isEmpty() ? NULL : state->set(sym, New); +} + +const GRState* +RangeConstraintManager::assumeSymLE(const GRState* state, SymbolRef sym, + const llvm::APSInt& Int, + const llvm::APSInt& Adjustment) { + BasicValueFactory &BV = state->getBasicVals(); + + QualType T = state->getSymbolManager().getType(sym); + const llvm::APSInt &Max = BV.getMaxValue(T); + + // Special case for Int == Max. This is always feasible. + if (Int == Max) + return state; + + const llvm::APSInt &Min = BV.getMinValue(T); + + llvm::APSInt Lower = Min-Adjustment; + llvm::APSInt Upper = Int-Adjustment; + + RangeSet New = GetRange(state, sym).Intersect(BV, F, Lower, Upper); + return New.isEmpty() ? NULL : state->set(sym, New); +} + +//===------------------------------------------------------------------------=== +// Pretty-printing. +//===------------------------------------------------------------------------===/ + +void RangeConstraintManager::print(const GRState* St, llvm::raw_ostream& Out, + const char* nl, const char *sep) { + + ConstraintRangeTy Ranges = St->get(); + + if (Ranges.isEmpty()) + 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); + } +} diff --git a/lib/StaticAnalyzer/RegionStore.cpp b/lib/StaticAnalyzer/RegionStore.cpp new file mode 100644 index 0000000000..def1b43f34 --- /dev/null +++ b/lib/StaticAnalyzer/RegionStore.cpp @@ -0,0 +1,1875 @@ +//== 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/CharUnits.h" +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprCXX.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/GRStateTrait.h" +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" +#include "llvm/ADT/ImmutableList.h" +#include "llvm/ADT/ImmutableMap.h" +#include "llvm/ADT/Optional.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; +using llvm::Optional; + +//===----------------------------------------------------------------------===// +// Representation of binding keys. +//===----------------------------------------------------------------------===// + +namespace { +class BindingKey { +public: + enum Kind { Direct = 0x0, Default = 0x1 }; +private: + llvm ::PointerIntPair P; + uint64_t Offset; + + explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k) + : P(r, (unsigned) k), Offset(offset) {} +public: + + bool isDirect() const { return P.getInt() == Direct; } + + const MemRegion *getRegion() const { return P.getPointer(); } + uint64_t getOffset() const { return Offset; } + + void Profile(llvm::FoldingSetNodeID& ID) const { + ID.AddPointer(P.getOpaqueValue()); + ID.AddInteger(Offset); + } + + 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 Offset < X.Offset; + } + + bool operator==(const BindingKey &X) const { + return P.getOpaqueValue() == X.P.getOpaqueValue() && + Offset == X.Offset; + } + + bool isValid() const { + return getRegion() != NULL; + } +}; +} // end anonymous namespace + +BindingKey BindingKey::Make(const MemRegion *R, Kind k) { + if (const ElementRegion *ER = dyn_cast(R)) { + const RegionRawOffset &O = ER->getAsArrayOffset(); + + // FIXME: There are some ElementRegions for which we cannot compute + // raw offsets yet, including regions with symbolic offsets. These will be + // ignored by the store. + return BindingKey(O.getRegion(), O.getByteOffset(), k); + } + + return BindingKey(R, 0, k); +} + +namespace llvm { + static inline + llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BindingKey K) { + os << '(' << K.getRegion() << ',' << K.getOffset() + << ',' << (K.isDirect() ? "direct" : "default") + << ')'; + return os; + } +} // end llvm namespace + +//===----------------------------------------------------------------------===// +// Actual Store type. +//===----------------------------------------------------------------------===// + +typedef llvm::ImmutableMap RegionBindings; + +//===----------------------------------------------------------------------===// +// 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 RegionStoreSubRegionMap : public SubRegionMap { +public: + typedef llvm::ImmutableSet Set; + typedef llvm::DenseMap Map; +private: + Set::Factory F; + Map M; +public: + bool add(const MemRegion* Parent, const MemRegion* SubRegion) { + Map::iterator I = M.find(Parent); + + if (I == M.end()) { + M.insert(std::make_pair(Parent, F.add(F.getEmptySet(), SubRegion))); + return true; + } + + I->second = F.add(I->second, SubRegion); + return false; + } + + void process(llvm::SmallVectorImpl &WL, const SubRegion *R); + + ~RegionStoreSubRegionMap() {} + + const Set *getSubRegions(const MemRegion *Parent) const { + Map::const_iterator I = M.find(Parent); + return I == M.end() ? NULL : &I->second; + } + + bool iterSubRegions(const MemRegion* Parent, Visitor& V) const { + Map::const_iterator I = M.find(Parent); + + if (I == M.end()) + return true; + + Set S = I->second; + for (Set::iterator SI=S.begin(),SE=S.end(); SI != SE; ++SI) { + if (!V.Visit(Parent, *SI)) + return false; + } + + return true; + } +}; + +void +RegionStoreSubRegionMap::process(llvm::SmallVectorImpl &WL, + const SubRegion *R) { + const MemRegion *superR = R->getSuperRegion(); + if (add(superR, R)) + if (const SubRegion *sr = dyn_cast(superR)) + WL.push_back(sr); +} + +class RegionStoreManager : public StoreManager { + const RegionStoreFeatures Features; + RegionBindings::Factory RBFactory; + +public: + RegionStoreManager(GRStateManager& mgr, const RegionStoreFeatures &f) + : StoreManager(mgr), + Features(f), + RBFactory(mgr.getAllocator()) {} + + SubRegionMap *getSubRegionMap(Store store) { + return getRegionStoreSubRegionMap(store); + } + + RegionStoreSubRegionMap *getRegionStoreSubRegionMap(Store store); + + Optional getDirectBinding(RegionBindings B, const MemRegion *R); + /// getDefaultBinding - Returns an SVal* representing an optional default + /// binding associated with a region and its subregions. + Optional getDefaultBinding(RegionBindings B, const MemRegion *R); + + /// setImplicitDefaultValue - Set the default binding for the provided + /// MemRegion to the value implicitly defined for compound literals when + /// the value is not specified. + Store setImplicitDefaultValue(Store store, 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); + + /// For DerivedToBase casts, create a CXXBaseObjectRegion and return it. + virtual SVal evalDerivedToBase(SVal derived, QualType basePtrType); + + SVal evalBinOp(BinaryOperator::Opcode Op,Loc L, NonLoc R, QualType resultTy); + + Store getInitialStore(const LocationContext *InitLoc) { + return RBFactory.getEmptyMap().getRoot(); + } + + //===-------------------------------------------------------------------===// + // Binding values to regions. + //===-------------------------------------------------------------------===// + + Store InvalidateRegions(Store store, + const MemRegion * const *Begin, + const MemRegion * const *End, + const Expr *E, unsigned Count, + InvalidatedSymbols *IS, + bool invalidateGlobals, + InvalidatedRegions *Regions); + +public: // Made public for helper classes. + + void RemoveSubRegionBindings(RegionBindings &B, const MemRegion *R, + RegionStoreSubRegionMap &M); + + RegionBindings addBinding(RegionBindings B, BindingKey K, SVal V); + + RegionBindings addBinding(RegionBindings B, const MemRegion *R, + BindingKey::Kind k, SVal V); + + const SVal *lookup(RegionBindings B, BindingKey K); + const SVal *lookup(RegionBindings B, const MemRegion *R, BindingKey::Kind k); + + RegionBindings removeBinding(RegionBindings B, BindingKey K); + RegionBindings removeBinding(RegionBindings B, const MemRegion *R, + BindingKey::Kind k); + + RegionBindings removeBinding(RegionBindings B, const MemRegion *R) { + return removeBinding(removeBinding(B, R, BindingKey::Direct), R, + BindingKey::Default); + } + +public: // Part of public interface to class. + + Store Bind(Store store, Loc LV, SVal V); + + // BindDefault is only used to initialize a region with a default value. + Store BindDefault(Store store, const MemRegion *R, SVal V) { + RegionBindings B = GetRegionBindings(store); + assert(!lookup(B, R, BindingKey::Default)); + assert(!lookup(B, R, BindingKey::Direct)); + return addBinding(B, R, BindingKey::Default, V).getRoot(); + } + + Store BindCompoundLiteral(Store store, const CompoundLiteralExpr* CL, + const LocationContext *LC, SVal V); + + Store BindDecl(Store store, const VarRegion *VR, SVal InitVal); + + Store BindDeclWithNoInit(Store store, const VarRegion *) { + return store; + } + + /// BindStruct - Bind a compound value to a structure. + Store BindStruct(Store store, const TypedRegion* R, SVal V); + + Store BindArray(Store store, const TypedRegion* R, SVal V); + + /// KillStruct - Set the entire struct to unknown. + Store KillStruct(Store store, const TypedRegion* R, SVal DefaultVal); + + Store Remove(Store store, Loc LV); + + + //===------------------------------------------------------------------===// + // Loading values from regions. + //===------------------------------------------------------------------===// + + /// The high level logic for this method is this: + /// Retrieve (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 Retrieve(Store store, Loc L, QualType T = QualType()); + + SVal RetrieveElement(Store store, const ElementRegion *R); + + SVal RetrieveField(Store store, const FieldRegion *R); + + SVal RetrieveObjCIvar(Store store, const ObjCIvarRegion *R); + + SVal RetrieveVar(Store store, const VarRegion *R); + + SVal RetrieveLazySymbol(const TypedRegion *R); + + SVal RetrieveFieldOrElementCommon(Store store, const TypedRegion *R, + QualType Ty, const MemRegion *superR); + + /// Retrieve 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 RetrieveStruct(Store store, const TypedRegion* R); + + SVal RetrieveArray(Store store, const TypedRegion* R); + + /// Used to lazily generate derived symbols for bindings that are defined + /// implicitly by default bindings in a super region. + Optional RetrieveDerivedDefaultValue(RegionBindings B, + const MemRegion *superR, + const TypedRegion *R, QualType Ty); + + /// Get the state and region whose binding this region R corresponds to. + std::pair + GetLazyBinding(RegionBindings B, const MemRegion *R); + + Store CopyLazyBindings(nonloc::LazyCompoundVal V, Store store, + const TypedRegion *R); + + //===------------------------------------------------------------------===// + // State pruning. + //===------------------------------------------------------------------===// + + /// RemoveDeadBindings - Scans the RegionStore of 'state' for dead values. + /// It returns a new Store with these values removed. + Store RemoveDeadBindings(Store store, const StackFrameContext *LCtx, + SymbolReaper& SymReaper, + llvm::SmallVectorImpl& RegionRoots); + + Store EnterStackFrame(const GRState *state, const StackFrameContext *frame); + + //===------------------------------------------------------------------===// + // Region "extents". + //===------------------------------------------------------------------===// + + // FIXME: This method will soon be eliminated; see the note in Store.h. + DefinedOrUnknownSVal getSizeInElements(const GRState *state, + const MemRegion* R, QualType EleTy); + + //===------------------------------------------------------------------===// + // Utility methods. + //===------------------------------------------------------------------===// + + static inline RegionBindings GetRegionBindings(Store store) { + return RegionBindings(static_cast(store)); + } + + void print(Store store, llvm::raw_ostream& Out, const char* nl, + const char *sep); + + void iterBindings(Store store, BindingsHandler& f) { + RegionBindings B = GetRegionBindings(store); + for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) { + const BindingKey &K = I.getKey(); + if (!K.isDirect()) + continue; + if (const SubRegion *R = dyn_cast(I.getKey().getRegion())) { + // FIXME: Possibly incorporate the offset? + if (!f.HandleBinding(*this, store, R, I.getData())) + return; + } + } + } +}; + +} // end anonymous namespace + +//===----------------------------------------------------------------------===// +// RegionStore creation. +//===----------------------------------------------------------------------===// + +StoreManager *ento::CreateRegionStoreManager(GRStateManager& StMgr) { + RegionStoreFeatures F = maximal_features_tag(); + return new RegionStoreManager(StMgr, F); +} + +StoreManager *ento::CreateFieldsOnlyRegionStoreManager(GRStateManager &StMgr) { + RegionStoreFeatures F = minimal_features_tag(); + F.enableFields(true); + return new RegionStoreManager(StMgr, F); +} + + +RegionStoreSubRegionMap* +RegionStoreManager::getRegionStoreSubRegionMap(Store store) { + RegionBindings B = GetRegionBindings(store); + RegionStoreSubRegionMap *M = new RegionStoreSubRegionMap(); + + llvm::SmallVector WL; + + for (RegionBindings::iterator I=B.begin(), E=B.end(); I!=E; ++I) + if (const SubRegion *R = dyn_cast(I.getKey().getRegion())) + M->process(WL, R); + + // We also need to record in the subregion map "intermediate" regions that + // don't have direct bindings but are super regions of those that do. + while (!WL.empty()) { + const SubRegion *R = WL.back(); + WL.pop_back(); + M->process(WL, R); + } + + return M; +} + +//===----------------------------------------------------------------------===// +// Region Cluster analysis. +//===----------------------------------------------------------------------===// + +namespace { +template +class ClusterAnalysis { +protected: + typedef BumpVector RegionCluster; + typedef llvm::DenseMap ClusterMap; + llvm::DenseMap Visited; + typedef llvm::SmallVector, 10> + WorkList; + + BumpVectorContext BVC; + ClusterMap ClusterM; + WorkList WL; + + RegionStoreManager &RM; + ASTContext &Ctx; + SValBuilder &svalBuilder; + + RegionBindings B; + + const bool includeGlobals; + +public: + ClusterAnalysis(RegionStoreManager &rm, GRStateManager &StateMgr, + RegionBindings b, const bool includeGlobals) + : RM(rm), Ctx(StateMgr.getContext()), + svalBuilder(StateMgr.getSValBuilder()), + B(b), includeGlobals(includeGlobals) {} + + RegionBindings getRegionBindings() const { return B; } + + RegionCluster &AddToCluster(BindingKey K) { + const MemRegion *R = K.getRegion(); + const MemRegion *baseR = R->getBaseRegion(); + RegionCluster &C = getCluster(baseR); + C.push_back(K, BVC); + static_cast(this)->VisitAddedToCluster(baseR, C); + return C; + } + + bool isVisited(const MemRegion *R) { + return (bool) Visited[&getCluster(R->getBaseRegion())]; + } + + RegionCluster& getCluster(const MemRegion *R) { + RegionCluster *&CRef = ClusterM[R]; + if (!CRef) { + void *Mem = BVC.getAllocator().template Allocate(); + CRef = new (Mem) RegionCluster(BVC, 10); + } + return *CRef; + } + + void GenerateClusters() { + // Scan the entire set of bindings and make the region clusters. + for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ + RegionCluster &C = AddToCluster(RI.getKey()); + if (const MemRegion *R = RI.getData().getAsRegion()) { + // Generate a cluster, but don't add the region to the cluster + // if there aren't any bindings. + getCluster(R->getBaseRegion()); + } + if (includeGlobals) { + const MemRegion *R = RI.getKey().getRegion(); + if (isa(R->getMemorySpace())) + AddToWorkList(R, C); + } + } + } + + bool AddToWorkList(const MemRegion *R, RegionCluster &C) { + if (unsigned &visited = Visited[&C]) + return false; + else + visited = 1; + + WL.push_back(std::make_pair(R, &C)); + return true; + } + + bool AddToWorkList(BindingKey K) { + return AddToWorkList(K.getRegion()); + } + + bool AddToWorkList(const MemRegion *R) { + const MemRegion *baseR = R->getBaseRegion(); + return AddToWorkList(baseR, getCluster(baseR)); + } + + void RunWorkList() { + while (!WL.empty()) { + const MemRegion *baseR; + RegionCluster *C; + llvm::tie(baseR, C) = WL.back(); + WL.pop_back(); + + // First visit the cluster. + static_cast(this)->VisitCluster(baseR, C->begin(), C->end()); + + // Next, visit the base region. + static_cast(this)->VisitBaseRegion(baseR); + } + } + +public: + void VisitAddedToCluster(const MemRegion *baseR, RegionCluster &C) {} + void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E) {} + void VisitBaseRegion(const MemRegion *baseR) {} +}; +} + +//===----------------------------------------------------------------------===// +// Binding invalidation. +//===----------------------------------------------------------------------===// + +void RegionStoreManager::RemoveSubRegionBindings(RegionBindings &B, + const MemRegion *R, + RegionStoreSubRegionMap &M) { + + if (const RegionStoreSubRegionMap::Set *S = M.getSubRegions(R)) + for (RegionStoreSubRegionMap::Set::iterator I = S->begin(), E = S->end(); + I != E; ++I) + RemoveSubRegionBindings(B, *I, M); + + B = removeBinding(B, R); +} + +namespace { +class InvalidateRegionsWorker : public ClusterAnalysis +{ + const Expr *Ex; + unsigned Count; + StoreManager::InvalidatedSymbols *IS; + StoreManager::InvalidatedRegions *Regions; +public: + InvalidateRegionsWorker(RegionStoreManager &rm, + GRStateManager &stateMgr, + RegionBindings b, + const Expr *ex, unsigned count, + StoreManager::InvalidatedSymbols *is, + StoreManager::InvalidatedRegions *r, + bool includeGlobals) + : ClusterAnalysis(rm, stateMgr, b, includeGlobals), + Ex(ex), Count(count), IS(is), Regions(r) {} + + void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E); + void VisitBaseRegion(const MemRegion *baseR); + +private: + void VisitBinding(SVal V); +}; +} + +void InvalidateRegionsWorker::VisitBinding(SVal V) { + // A symbol? Mark it touched by the invalidation. + if (IS) + 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 (const nonloc::LazyCompoundVal *LCS = + dyn_cast(&V)) { + + const MemRegion *LazyR = LCS->getRegion(); + RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore()); + + for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ + const SubRegion *baseR = dyn_cast(RI.getKey().getRegion()); + if (baseR && baseR->isSubRegionOf(LazyR)) + VisitBinding(RI.getData()); + } + + return; + } +} + +void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR, + BindingKey *I, BindingKey *E) { + for ( ; I != E; ++I) { + // Get the old binding. Is it a region? If so, add it to the worklist. + const BindingKey &K = *I; + if (const SVal *V = RM.lookup(B, K)) + VisitBinding(*V); + + B = RM.removeBinding(B, K); + } +} + +void InvalidateRegionsWorker::VisitBaseRegion(const MemRegion *baseR) { + if (IS) { + // Symbolic region? Mark that symbol touched by the invalidation. + if (const SymbolicRegion *SR = dyn_cast(baseR)) + IS->insert(SR->getSymbol()); + } + + // BlockDataRegion? If so, invalidate captured variables that are passed + // by reference. + if (const BlockDataRegion *BR = dyn_cast(baseR)) { + for (BlockDataRegion::referenced_vars_iterator + BI = BR->referenced_vars_begin(), BE = BR->referenced_vars_end() ; + BI != BE; ++BI) { + const VarRegion *VR = *BI; + const VarDecl *VD = VR->getDecl(); + if (VD->getAttr() || !VD->hasLocalStorage()) + AddToWorkList(VR); + } + return; + } + + // Otherwise, we have a normal data region. Record that we touched the region. + if (Regions) + Regions->push_back(baseR); + + if (isa(baseR) || isa(baseR)) { + // Invalidate the region by setting its default value to + // conjured symbol. The type of the symbol is irrelavant. + DefinedOrUnknownSVal V = + svalBuilder.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy, Count); + B = RM.addBinding(B, baseR, BindingKey::Default, V); + return; + } + + if (!baseR->isBoundable()) + return; + + const TypedRegion *TR = cast(baseR); + QualType T = TR->getValueType(); + + // Invalidate the binding. + if (T->isStructureType()) { + // Invalidate the region by setting its default value to + // conjured symbol. The type of the symbol is irrelavant. + DefinedOrUnknownSVal V = svalBuilder.getConjuredSymbolVal(baseR, Ex, Ctx.IntTy, + Count); + B = RM.addBinding(B, baseR, BindingKey::Default, V); + return; + } + + if (const ArrayType *AT = Ctx.getAsArrayType(T)) { + // Set the default value of the array to conjured symbol. + DefinedOrUnknownSVal V = + svalBuilder.getConjuredSymbolVal(baseR, Ex, AT->getElementType(), Count); + B = RM.addBinding(B, baseR, BindingKey::Default, V); + return; + } + + if (includeGlobals && + isa(baseR->getMemorySpace())) { + // If the region is a global and we are invalidating all globals, + // just erase the entry. This causes all globals to be lazily + // symbolicated from the same base symbol. + B = RM.removeBinding(B, baseR); + return; + } + + + DefinedOrUnknownSVal V = svalBuilder.getConjuredSymbolVal(baseR, Ex, T, Count); + assert(SymbolManager::canSymbolicate(T) || V.isUnknown()); + B = RM.addBinding(B, baseR, BindingKey::Direct, V); +} + +Store RegionStoreManager::InvalidateRegions(Store store, + const MemRegion * const *I, + const MemRegion * const *E, + const Expr *Ex, unsigned Count, + InvalidatedSymbols *IS, + bool invalidateGlobals, + InvalidatedRegions *Regions) { + InvalidateRegionsWorker W(*this, StateMgr, + RegionStoreManager::GetRegionBindings(store), + Ex, Count, IS, Regions, invalidateGlobals); + + // Scan the bindings and generate the clusters. + W.GenerateClusters(); + + // Add I .. E to the worklist. + for ( ; I != E; ++I) + W.AddToWorkList(*I); + + W.RunWorkList(); + + // Return the new bindings. + RegionBindings B = W.getRegionBindings(); + + if (invalidateGlobals) { + // Bind the non-static globals memory space to a new symbol that we will + // use to derive the bindings for all non-static globals. + const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(); + SVal V = + svalBuilder.getConjuredSymbolVal(/* SymbolTag = */ (void*) GS, Ex, + /* symbol type, doesn't matter */ Ctx.IntTy, + Count); + B = addBinding(B, 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 (Regions) + Regions->push_back(GS); + } + + return B.getRoot(); +} + +//===----------------------------------------------------------------------===// +// Extents for regions. +//===----------------------------------------------------------------------===// + +DefinedOrUnknownSVal RegionStoreManager::getSizeInElements(const GRState *state, + const MemRegion *R, + QualType EleTy) { + SVal Size = cast(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, false); +} + +//===----------------------------------------------------------------------===// +// 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) { + if (!isa(Array)) + return UnknownVal(); + + const MemRegion* R = cast(&Array)->getRegion(); + const TypedRegion* ArrayR = dyn_cast(R); + + if (!ArrayR) + return UnknownVal(); + + // Strip off typedefs from the ArrayRegion's ValueType. + QualType T = ArrayR->getValueType().getDesugaredType(Ctx); + ArrayType *AT = cast(T); + T = AT->getElementType(); + + NonLoc ZeroIdx = svalBuilder.makeZeroArrayIndex(); + return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, ArrayR, Ctx)); +} + +SVal RegionStoreManager::evalDerivedToBase(SVal derived, QualType baseType) { + const CXXRecordDecl *baseDecl; + if (baseType->isPointerType()) + baseDecl = baseType->getCXXRecordDeclForPointerType(); + else + baseDecl = baseType->getAsCXXRecordDecl(); + + assert(baseDecl && "not a CXXRecordDecl?"); + + loc::MemRegionVal *derivedRegVal = dyn_cast(&derived); + if (!derivedRegVal) + return derived; + + const MemRegion *baseReg = + MRMgr.getCXXBaseObjectRegion(baseDecl, derivedRegVal->getRegion()); + return loc::MemRegionVal(baseReg); +} +//===----------------------------------------------------------------------===// +// Pointer arithmetic. +//===----------------------------------------------------------------------===// + +SVal RegionStoreManager::evalBinOp(BinaryOperator::Opcode Op, Loc L, NonLoc R, + QualType resultTy) { + // Assume the base location is MemRegionVal. + if (!isa(L)) + return UnknownVal(); + + // Special case for zero RHS. + if (R.isZeroConstant()) { + switch (Op) { + default: + // Handle it normally. + break; + case BO_Add: + case BO_Sub: + // FIXME: does this need to be casted to match resultTy? + return L; + } + } + + const MemRegion* MR = cast(L).getRegion(); + const ElementRegion *ER = 0; + + switch (MR->getKind()) { + case MemRegion::SymbolicRegionKind: { + const SymbolicRegion *SR = cast(MR); + SymbolRef Sym = SR->getSymbol(); + QualType T = Sym->getType(Ctx); + QualType EleTy; + + if (const PointerType *PT = T->getAs()) + EleTy = PT->getPointeeType(); + else + EleTy = T->getAs()->getPointeeType(); + + const NonLoc &ZeroIdx = svalBuilder.makeZeroArrayIndex(); + ER = MRMgr.getElementRegion(EleTy, ZeroIdx, SR, Ctx); + break; + } + case MemRegion::AllocaRegionKind: { + const AllocaRegion *AR = cast(MR); + QualType EleTy = Ctx.CharTy; // Create an ElementRegion of bytes. + NonLoc ZeroIdx = svalBuilder.makeZeroArrayIndex(); + ER = MRMgr.getElementRegion(EleTy, ZeroIdx, AR, Ctx); + break; + } + + case MemRegion::ElementRegionKind: { + ER = cast(MR); + break; + } + + // Not yet handled. + case MemRegion::VarRegionKind: + case MemRegion::StringRegionKind: { + + } + // Fall-through. + case MemRegion::CompoundLiteralRegionKind: + case MemRegion::FieldRegionKind: + case MemRegion::ObjCIvarRegionKind: + case MemRegion::CXXTempObjectRegionKind: + case MemRegion::CXXBaseObjectRegionKind: + return UnknownVal(); + + case MemRegion::FunctionTextRegionKind: + case MemRegion::BlockTextRegionKind: + case MemRegion::BlockDataRegionKind: + // Technically this can happen if people do funny things with casts. + return UnknownVal(); + + case MemRegion::CXXThisRegionKind: + assert(0 && + "Cannot perform pointer arithmetic on implicit argument 'this'"); + case MemRegion::GenericMemSpaceRegionKind: + case MemRegion::StackLocalsSpaceRegionKind: + case MemRegion::StackArgumentsSpaceRegionKind: + case MemRegion::HeapSpaceRegionKind: + case MemRegion::NonStaticGlobalSpaceRegionKind: + case MemRegion::StaticGlobalSpaceRegionKind: + case MemRegion::UnknownSpaceRegionKind: + assert(0 && "Cannot perform pointer arithmetic on a MemSpace"); + return UnknownVal(); + } + + SVal Idx = ER->getIndex(); + nonloc::ConcreteInt* Base = dyn_cast(&Idx); + + // For now, only support: + // (a) concrete integer indices that can easily be resolved + // (b) 0 + symbolic index + if (Base) { + if (nonloc::ConcreteInt *Offset = dyn_cast(&R)) { + // FIXME: Should use SValBuilder here. + SVal NewIdx = + Base->evalBinOp(svalBuilder, Op, + cast(svalBuilder.convertToArrayIndex(*Offset))); + + if (!isa(NewIdx)) + return UnknownVal(); + + const MemRegion* NewER = + MRMgr.getElementRegion(ER->getElementType(), cast(NewIdx), + ER->getSuperRegion(), Ctx); + return svalBuilder.makeLoc(NewER); + } + if (0 == Base->getValue()) { + const MemRegion* NewER = + MRMgr.getElementRegion(ER->getElementType(), R, + ER->getSuperRegion(), Ctx); + return svalBuilder.makeLoc(NewER); + } + } + + return UnknownVal(); +} + +//===----------------------------------------------------------------------===// +// Loading values from regions. +//===----------------------------------------------------------------------===// + +Optional RegionStoreManager::getDirectBinding(RegionBindings B, + const MemRegion *R) { + + if (const SVal *V = lookup(B, R, BindingKey::Direct)) + return *V; + + return Optional(); +} + +Optional RegionStoreManager::getDefaultBinding(RegionBindings B, + const MemRegion *R) { + if (R->isBoundable()) + if (const TypedRegion *TR = dyn_cast(R)) + if (TR->getValueType()->isUnionType()) + return UnknownVal(); + + if (const SVal *V = lookup(B, R, BindingKey::Default)) + return *V; + + return Optional(); +} + +SVal RegionStoreManager::Retrieve(Store store, Loc L, QualType T) { + assert(!isa(L) && "location unknown"); + assert(!isa(L) && "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 (isa(L)) { + return UnknownVal(); + } + + const MemRegion *MR = cast(L).getRegion(); + + if (isa(MR) || isa(MR)) { + if (T.isNull()) { + const SymbolicRegion *SR = cast(MR); + T = SR->getSymbol()->getType(Ctx); + } + MR = GetElementZeroRegion(MR, T); + } + + if (isa(MR)) { + assert(0 && "Why load from a code text region?"); + return UnknownVal(); + } + + // 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 TypedRegion *R = cast(MR); + QualType RTy = R->getValueType(); + + // 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 RetrieveStruct(store, R); + + // FIXME: Handle unions. + if (RTy->isUnionType()) + return UnknownVal(); + + if (RTy->isArrayType()) + return RetrieveArray(store, R); + + // FIXME: handle Vector types. + if (RTy->isVectorType()) + return UnknownVal(); + + if (const FieldRegion* FR = dyn_cast(R)) + return CastRetrievedVal(RetrieveField(store, FR), FR, T, false); + + if (const ElementRegion* ER = dyn_cast(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(RetrieveElement(store, ER), ER, T, false); + } + + if (const ObjCIvarRegion *IVR = dyn_cast(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(RetrieveObjCIvar(store, IVR), IVR, T, false); + } + + if (const VarRegion *VR = dyn_cast(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(RetrieveVar(store, VR), VR, T, false); + } + + RegionBindings B = GetRegionBindings(store); + const SVal *V = lookup(B, 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); +} + +std::pair +RegionStoreManager::GetLazyBinding(RegionBindings B, const MemRegion *R) { + if (Optional OV = getDirectBinding(B, R)) + if (const nonloc::LazyCompoundVal *V = + dyn_cast(OV.getPointer())) + return std::make_pair(V->getStore(), V->getRegion()); + + if (const ElementRegion *ER = dyn_cast(R)) { + const std::pair &X = + GetLazyBinding(B, ER->getSuperRegion()); + + if (X.second) + return std::make_pair(X.first, + MRMgr.getElementRegionWithSuper(ER, X.second)); + } + else if (const FieldRegion *FR = dyn_cast(R)) { + const std::pair &X = + GetLazyBinding(B, FR->getSuperRegion()); + + if (X.second) + return std::make_pair(X.first, + MRMgr.getFieldRegionWithSuper(FR, X.second)); + } + // The NULL MemRegion indicates an non-existent lazy binding. A NULL Store is + // possible for a valid lazy binding. + return std::make_pair((Store) 0, (const MemRegion *) 0); +} + +SVal RegionStoreManager::RetrieveElement(Store store, + const ElementRegion* R) { + // Check if the region has a binding. + RegionBindings B = GetRegionBindings(store); + if (const Optional &V = getDirectBinding(B, 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(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 (T != Ctx.getCanonicalType(R->getElementType())) + return UnknownVal(); + + const StringLiteral *Str = StrR->getStringLiteral(); + SVal Idx = R->getIndex(); + if (nonloc::ConcreteInt *CI = dyn_cast(&Idx)) { + int64_t i = CI->getValue().getSExtValue(); + int64_t byteLength = Str->getByteLength(); + // Technically, only i == byteLength 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 >= byteLength) ? '\0' : Str->getString()[i]; + return svalBuilder.makeIntVal(c, T); + } + } + + // Check for loads from a code text region. For such loads, just give up. + if (isa(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 (const TypedRegion *baseR = dyn_cast_or_null(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 &V = getDirectBinding(B, 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 RetrieveFieldOrElementCommon(store, R, R->getElementType(), superR); +} + +SVal RegionStoreManager::RetrieveField(Store store, + const FieldRegion* R) { + + // Check if the region has a binding. + RegionBindings B = GetRegionBindings(store); + if (const Optional &V = getDirectBinding(B, R)) + return *V; + + QualType Ty = R->getValueType(); + return RetrieveFieldOrElementCommon(store, R, Ty, R->getSuperRegion()); +} + +Optional +RegionStoreManager::RetrieveDerivedDefaultValue(RegionBindings B, + const MemRegion *superR, + const TypedRegion *R, + QualType Ty) { + + if (const Optional &D = getDefaultBinding(B, superR)) { + if (SymbolRef parentSym = D->getAsSymbol()) + return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); + + if (D->isZeroConstant()) + return svalBuilder.makeZeroVal(Ty); + + if (D->isUnknownOrUndef()) + return *D; + + assert(0 && "Unknown default value"); + } + + return Optional(); +} + +SVal RegionStoreManager::RetrieveFieldOrElementCommon(Store store, + const TypedRegion *R, + QualType Ty, + const MemRegion *superR) { + + // At this point we have already checked in either RetrieveElement or + // RetrieveField if 'R' has a direct binding. + + RegionBindings B = GetRegionBindings(store); + + while (superR) { + if (const Optional &D = + RetrieveDerivedDefaultValue(B, superR, R, Ty)) + return *D; + + // 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. + if (const SubRegion *SR = dyn_cast(superR)) { + superR = SR->getSuperRegion(); + continue; + } + break; + } + + // Lazy binding? + Store lazyBindingStore = NULL; + const MemRegion *lazyBindingRegion = NULL; + llvm::tie(lazyBindingStore, lazyBindingRegion) = GetLazyBinding(B, R); + + if (lazyBindingRegion) { + if (const ElementRegion *ER = dyn_cast(lazyBindingRegion)) + return RetrieveElement(lazyBindingStore, ER); + return RetrieveField(lazyBindingStore, + cast(lazyBindingRegion)); + } + + if (R->hasStackNonParametersStorage()) { + if (const ElementRegion *ER = dyn_cast(R)) { + // Currently we don't reason specially about Clang-style vectors. Check + // if superR is a vector and if so return Unknown. + if (const TypedRegion *typedSuperR = dyn_cast(superR)) { + if (typedSuperR->getValueType()->isVectorType()) + return UnknownVal(); + } + + // FIXME: We also need to take ElementRegions with symbolic indexes into + // account. + if (!ER->getIndex().isConstant()) + return UnknownVal(); + } + + return UndefinedVal(); + } + + // All other values are symbolic. + return svalBuilder.getRegionValueSymbolVal(R); +} + +SVal RegionStoreManager::RetrieveObjCIvar(Store store, const ObjCIvarRegion* R){ + + // Check if the region has a binding. + RegionBindings B = GetRegionBindings(store); + + if (const Optional &V = getDirectBinding(B, R)) + return *V; + + const MemRegion *superR = R->getSuperRegion(); + + // Check if the super region has a default binding. + if (const Optional &V = getDefaultBinding(B, superR)) { + if (SymbolRef parentSym = V->getAsSymbol()) + return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R); + + // Other cases: give up. + return UnknownVal(); + } + + return RetrieveLazySymbol(R); +} + +SVal RegionStoreManager::RetrieveVar(Store store, const VarRegion *R) { + + // Check if the region has a binding. + RegionBindings B = GetRegionBindings(store); + + if (const Optional &V = getDirectBinding(B, R)) + return *V; + + // Lazily derive a value for the VarRegion. + const VarDecl *VD = R->getDecl(); + QualType T = VD->getType(); + const MemSpaceRegion *MS = R->getMemorySpace(); + + if (isa(MS) || + isa(MS)) + return svalBuilder.getRegionValueSymbolVal(R); + + if (isa(MS)) { + if (isa(MS)) { + // Is 'VD' declared constant? If so, retrieve the constant value. + QualType CT = Ctx.getCanonicalType(T); + if (CT.isConstQualified()) { + const Expr *Init = VD->getInit(); + // Do the null check first, as we want to call 'IgnoreParenCasts'. + if (Init) + if (const IntegerLiteral *IL = + dyn_cast(Init->IgnoreParenCasts())) { + const nonloc::ConcreteInt &V = svalBuilder.makeIntVal(IL); + return svalBuilder.evalCast(V, Init->getType(), IL->getType()); + } + } + + if (const Optional &V = RetrieveDerivedDefaultValue(B, MS, R, CT)) + return V.getValue(); + + return svalBuilder.getRegionValueSymbolVal(R); + } + + if (T->isIntegerType()) + return svalBuilder.makeIntVal(0, T); + if (T->isPointerType()) + return svalBuilder.makeNull(); + + return UnknownVal(); + } + + return UndefinedVal(); +} + +SVal RegionStoreManager::RetrieveLazySymbol(const TypedRegion *R) { + // All other values are symbolic. + return svalBuilder.getRegionValueSymbolVal(R); +} + +SVal RegionStoreManager::RetrieveStruct(Store store, const TypedRegion* R) { + QualType T = R->getValueType(); + assert(T->isStructureOrClassType()); + return svalBuilder.makeLazyCompoundVal(store, R); +} + +SVal RegionStoreManager::RetrieveArray(Store store, const TypedRegion * R) { + assert(Ctx.getAsConstantArrayType(R->getValueType())); + return svalBuilder.makeLazyCompoundVal(store, R); +} + +//===----------------------------------------------------------------------===// +// Binding values to regions. +//===----------------------------------------------------------------------===// + +Store RegionStoreManager::Remove(Store store, Loc L) { + if (isa(L)) + if (const MemRegion* R = cast(L).getRegion()) + return removeBinding(GetRegionBindings(store), R).getRoot(); + + return store; +} + +Store RegionStoreManager::Bind(Store store, Loc L, SVal V) { + if (isa(L)) + return store; + + // If we get here, the location should be a region. + const MemRegion *R = cast(L).getRegion(); + + // Check if the region is a struct region. + if (const TypedRegion* TR = dyn_cast(R)) + if (TR->getValueType()->isStructureOrClassType()) + return BindStruct(store, TR, V); + + if (const ElementRegion *ER = dyn_cast(R)) { + if (ER->getIndex().isZeroConstant()) { + if (const TypedRegion *superR = + dyn_cast(ER->getSuperRegion())) { + QualType superTy = superR->getValueType(); + // For now, just invalidate the fields of the struct/union/class. + // This is for test rdar_test_7185607 in misc-ps-region-store.m. + // FIXME: Precisely handle the fields of the record. + if (superTy->isStructureOrClassType()) + return KillStruct(store, superR, UnknownVal()); + } + } + } + else if (const SymbolicRegion *SR = dyn_cast(R)) { + // Binding directly to a symbolic region should be treated as binding + // to element 0. + QualType T = SR->getSymbol()->getType(Ctx); + + // FIXME: Is this the right way to handle symbols that are references? + if (const PointerType *PT = T->getAs()) + T = PT->getPointeeType(); + else + T = T->getAs()->getPointeeType(); + + R = GetElementZeroRegion(SR, T); + } + + // Perform the binding. + RegionBindings B = GetRegionBindings(store); + return addBinding(B, R, BindingKey::Direct, V).getRoot(); +} + +Store RegionStoreManager::BindDecl(Store store, const VarRegion *VR, + SVal InitVal) { + + QualType T = VR->getDecl()->getType(); + + if (T->isArrayType()) + return BindArray(store, VR, InitVal); + if (T->isStructureOrClassType()) + return BindStruct(store, VR, InitVal); + + return Bind(store, svalBuilder.makeLoc(VR), InitVal); +} + +// FIXME: this method should be merged into Bind(). +Store RegionStoreManager::BindCompoundLiteral(Store store, + const CompoundLiteralExpr *CL, + const LocationContext *LC, + SVal V) { + return Bind(store, loc::MemRegionVal(MRMgr.getCompoundLiteralRegion(CL, LC)), + V); +} + + +Store RegionStoreManager::setImplicitDefaultValue(Store store, + const MemRegion *R, + QualType T) { + RegionBindings B = GetRegionBindings(store); + SVal V; + + if (Loc::IsLocType(T)) + V = svalBuilder.makeNull(); + else if (T->isIntegerType()) + 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 { + return store; + } + + return addBinding(B, R, BindingKey::Default, V).getRoot(); +} + +Store RegionStoreManager::BindArray(Store store, const TypedRegion* R, + SVal Init) { + + const ArrayType *AT =cast(Ctx.getCanonicalType(R->getValueType())); + QualType ElementTy = AT->getElementType(); + Optional Size; + + if (const ConstantArrayType* CAT = dyn_cast(AT)) + Size = CAT->getSize().getZExtValue(); + + // Check if the init expr is a string literal. + if (loc::MemRegionVal *MRV = dyn_cast(&Init)) { + const StringRegion *S = cast(MRV->getRegion()); + + // Treat the string as a lazy compound value. + nonloc::LazyCompoundVal LCV = + cast(svalBuilder.makeLazyCompoundVal(store, S)); + return CopyLazyBindings(LCV, store, R); + } + + // Handle lazy compound values. + if (nonloc::LazyCompoundVal *LCV = dyn_cast(&Init)) + return CopyLazyBindings(*LCV, store, R); + + // Remaining case: explicit compound values. + + if (Init.isUnknown()) + return setImplicitDefaultValue(store, R, ElementTy); + + nonloc::CompoundVal& CV = cast(Init); + nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); + uint64_t i = 0; + + 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()) + store = BindStruct(store, ER, *VI); + else if (ElementTy->isArrayType()) + store = BindArray(store, ER, *VI); + else + store = Bind(store, svalBuilder.makeLoc(ER), *VI); + } + + // If the init list is shorter than the array length, set the + // array default value. + if (Size.hasValue() && i < Size.getValue()) + store = setImplicitDefaultValue(store, R, ElementTy); + + return store; +} + +Store RegionStoreManager::BindStruct(Store store, const TypedRegion* R, + SVal V) { + + if (!Features.supportsFields()) + return store; + + QualType T = R->getValueType(); + assert(T->isStructureOrClassType()); + + const RecordType* RT = T->getAs(); + RecordDecl* RD = RT->getDecl(); + + if (!RD->isDefinition()) + return store; + + // Handle lazy compound values. + if (const nonloc::LazyCompoundVal *LCV=dyn_cast(&V)) + return CopyLazyBindings(*LCV, store, R); + + // 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() || !isa(V)) { + SVal SV = isa(V) ? V : UnknownVal(); + return KillStruct(store, R, SV); + } + + nonloc::CompoundVal& CV = cast(V); + nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end(); + + RecordDecl::field_iterator FI, FE; + + for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI, ++VI) { + + if (VI == VE) + break; + + QualType FTy = (*FI)->getType(); + const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R); + + if (FTy->isArrayType()) + store = BindArray(store, FR, *VI); + else if (FTy->isStructureOrClassType()) + store = BindStruct(store, FR, *VI); + else + store = Bind(store, svalBuilder.makeLoc(FR), *VI); + } + + // There may be fewer values in the initialize list than the fields of struct. + if (FI != FE) { + RegionBindings B = GetRegionBindings(store); + B = addBinding(B, R, BindingKey::Default, svalBuilder.makeIntVal(0, false)); + store = B.getRoot(); + } + + return store; +} + +Store RegionStoreManager::KillStruct(Store store, const TypedRegion* R, + SVal DefaultVal) { + RegionBindings B = GetRegionBindings(store); + llvm::OwningPtr + SubRegions(getRegionStoreSubRegionMap(store)); + RemoveSubRegionBindings(B, R, *SubRegions); + + // Set the default value of the struct region to "unknown". + return addBinding(B, R, BindingKey::Default, DefaultVal).getRoot(); +} + +Store RegionStoreManager::CopyLazyBindings(nonloc::LazyCompoundVal V, + Store store, const TypedRegion *R) { + + // Nuke the old bindings stemming from R. + RegionBindings B = GetRegionBindings(store); + + llvm::OwningPtr + SubRegions(getRegionStoreSubRegionMap(store)); + + // B and DVM are updated after the call to RemoveSubRegionBindings. + RemoveSubRegionBindings(B, R, *SubRegions.get()); + + // Now copy the bindings. This amounts to just binding 'V' to 'R'. This + // results in a zero-copy algorithm. + return addBinding(B, R, BindingKey::Direct, V).getRoot(); +} + +//===----------------------------------------------------------------------===// +// "Raw" retrievals and bindings. +//===----------------------------------------------------------------------===// + + +RegionBindings RegionStoreManager::addBinding(RegionBindings B, BindingKey K, + SVal V) { + if (!K.isValid()) + return B; + return RBFactory.add(B, K, V); +} + +RegionBindings RegionStoreManager::addBinding(RegionBindings B, + const MemRegion *R, + BindingKey::Kind k, SVal V) { + return addBinding(B, BindingKey::Make(R, k), V); +} + +const SVal *RegionStoreManager::lookup(RegionBindings B, BindingKey K) { + if (!K.isValid()) + return NULL; + return B.lookup(K); +} + +const SVal *RegionStoreManager::lookup(RegionBindings B, + const MemRegion *R, + BindingKey::Kind k) { + return lookup(B, BindingKey::Make(R, k)); +} + +RegionBindings RegionStoreManager::removeBinding(RegionBindings B, + BindingKey K) { + if (!K.isValid()) + return B; + return RBFactory.remove(B, K); +} + +RegionBindings RegionStoreManager::removeBinding(RegionBindings B, + const MemRegion *R, + BindingKey::Kind k){ + return removeBinding(B, BindingKey::Make(R, k)); +} + +//===----------------------------------------------------------------------===// +// State pruning. +//===----------------------------------------------------------------------===// + +namespace { +class RemoveDeadBindingsWorker : + public ClusterAnalysis { + llvm::SmallVector Postponed; + SymbolReaper &SymReaper; + const StackFrameContext *CurrentLCtx; + +public: + RemoveDeadBindingsWorker(RegionStoreManager &rm, GRStateManager &stateMgr, + RegionBindings b, SymbolReaper &symReaper, + const StackFrameContext *LCtx) + : ClusterAnalysis(rm, stateMgr, b, + /* includeGlobals = */ false), + SymReaper(symReaper), CurrentLCtx(LCtx) {} + + // Called by ClusterAnalysis. + void VisitAddedToCluster(const MemRegion *baseR, RegionCluster &C); + void VisitCluster(const MemRegion *baseR, BindingKey *I, BindingKey *E); + + void VisitBindingKey(BindingKey K); + bool UpdatePostponed(); + void VisitBinding(SVal V); +}; +} + +void RemoveDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR, + RegionCluster &C) { + + if (const VarRegion *VR = dyn_cast(baseR)) { + if (SymReaper.isLive(VR)) + AddToWorkList(baseR, C); + + return; + } + + if (const SymbolicRegion *SR = dyn_cast(baseR)) { + if (SymReaper.isLive(SR->getSymbol())) + AddToWorkList(SR, C); + else + Postponed.push_back(SR); + + return; + } + + if (isa(baseR)) { + AddToWorkList(baseR, C); + return; + } + + // CXXThisRegion in the current or parent location context is live. + if (const CXXThisRegion *TR = dyn_cast(baseR)) { + const StackArgumentsSpaceRegion *StackReg = + cast(TR->getSuperRegion()); + const StackFrameContext *RegCtx = StackReg->getStackFrame(); + if (RegCtx == CurrentLCtx || RegCtx->isParentOf(CurrentLCtx)) + AddToWorkList(TR, C); + } +} + +void RemoveDeadBindingsWorker::VisitCluster(const MemRegion *baseR, + BindingKey *I, BindingKey *E) { + for ( ; I != E; ++I) + VisitBindingKey(*I); +} + +void RemoveDeadBindingsWorker::VisitBinding(SVal V) { + // Is it a LazyCompoundVal? All referenced regions are live as well. + if (const nonloc::LazyCompoundVal *LCS = + dyn_cast(&V)) { + + const MemRegion *LazyR = LCS->getRegion(); + RegionBindings B = RegionStoreManager::GetRegionBindings(LCS->getStore()); + for (RegionBindings::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI){ + const SubRegion *baseR = dyn_cast(RI.getKey().getRegion()); + if (baseR && baseR->isSubRegionOf(LazyR)) + VisitBinding(RI.getData()); + } + return; + } + + // If V is a region, then add it to the worklist. + if (const MemRegion *R = V.getAsRegion()) + AddToWorkList(R); + + // Update the set of live symbols. + for (SVal::symbol_iterator SI=V.symbol_begin(), SE=V.symbol_end(); + SI!=SE;++SI) + SymReaper.markLive(*SI); +} + +void RemoveDeadBindingsWorker::VisitBindingKey(BindingKey K) { + const MemRegion *R = K.getRegion(); + + // Mark this region "live" by adding it to the worklist. This will cause + // use to visit all regions in the cluster (if we haven't visited them + // already). + if (AddToWorkList(R)) { + // Mark the symbol for any live SymbolicRegion as "live". This means we + // should continue to track that symbol. + if (const SymbolicRegion *SymR = dyn_cast(R)) + SymReaper.markLive(SymR->getSymbol()); + + // For BlockDataRegions, enqueue the VarRegions for variables marked + // with __block (passed-by-reference). + // via BlockDeclRefExprs. + if (const BlockDataRegion *BD = dyn_cast(R)) { + for (BlockDataRegion::referenced_vars_iterator + RI = BD->referenced_vars_begin(), RE = BD->referenced_vars_end(); + RI != RE; ++RI) { + if ((*RI)->getDecl()->getAttr()) + AddToWorkList(*RI); + } + + // No possible data bindings on a BlockDataRegion. + return; + } + } + + // Visit the data binding for K. + if (const SVal *V = RM.lookup(B, K)) + VisitBinding(*V); +} + +bool RemoveDeadBindingsWorker::UpdatePostponed() { + // See if any postponed SymbolicRegions are actually live now, after + // having done a scan. + bool changed = false; + + for (llvm::SmallVectorImpl::iterator + I = Postponed.begin(), E = Postponed.end() ; I != E ; ++I) { + if (const SymbolicRegion *SR = cast_or_null(*I)) { + if (SymReaper.isLive(SR->getSymbol())) { + changed |= AddToWorkList(SR); + *I = NULL; + } + } + } + + return changed; +} + +Store RegionStoreManager::RemoveDeadBindings(Store store, + const StackFrameContext *LCtx, + SymbolReaper& SymReaper, + llvm::SmallVectorImpl& RegionRoots) +{ + RegionBindings B = GetRegionBindings(store); + RemoveDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx); + W.GenerateClusters(); + + // Enqueue the region roots onto the worklist. + for (llvm::SmallVectorImpl::iterator I=RegionRoots.begin(), + E=RegionRoots.end(); I!=E; ++I) + W.AddToWorkList(*I); + + do W.RunWorkList(); while (W.UpdatePostponed()); + + // We have now scanned the store, marking reachable regions and symbols + // as live. We now remove all the regions that are dead from the store + // as well as update DSymbols with the set symbols that are now dead. + for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) { + const BindingKey &K = I.getKey(); + + // If the cluster has been visited, we know the region has been marked. + if (W.isVisited(K.getRegion())) + continue; + + // Remove the dead entry. + B = removeBinding(B, K); + + // Mark all non-live symbols that this binding references as dead. + if (const SymbolicRegion* SymR = dyn_cast(K.getRegion())) + SymReaper.maybeDead(SymR->getSymbol()); + + SVal X = I.getData(); + SVal::symbol_iterator SI = X.symbol_begin(), SE = X.symbol_end(); + for (; SI != SE; ++SI) + SymReaper.maybeDead(*SI); + } + + return B.getRoot(); +} + + +Store RegionStoreManager::EnterStackFrame(const GRState *state, + const StackFrameContext *frame) { + FunctionDecl const *FD = cast(frame->getDecl()); + FunctionDecl::param_const_iterator PI = FD->param_begin(); + Store store = state->getStore(); + + if (CallExpr const *CE = dyn_cast(frame->getCallSite())) { + CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end(); + + // Copy the arg expression value to the arg variables. + for (; AI != AE; ++AI, ++PI) { + SVal ArgVal = state->getSVal(*AI); + store = Bind(store, + svalBuilder.makeLoc(MRMgr.getVarRegion(*PI,frame)), ArgVal); + } + } else if (const CXXConstructExpr *CE = + dyn_cast(frame->getCallSite())) { + CXXConstructExpr::const_arg_iterator AI = CE->arg_begin(), + AE = CE->arg_end(); + + // Copy the arg expression value to the arg variables. + for (; AI != AE; ++AI, ++PI) { + SVal ArgVal = state->getSVal(*AI); + store = Bind(store, + svalBuilder.makeLoc(MRMgr.getVarRegion(*PI,frame)), ArgVal); + } + } else + assert(isa(frame->getDecl())); + + return store; +} + +//===----------------------------------------------------------------------===// +// Utility methods. +//===----------------------------------------------------------------------===// + +void RegionStoreManager::print(Store store, llvm::raw_ostream& OS, + const char* nl, const char *sep) { + RegionBindings B = GetRegionBindings(store); + OS << "Store (direct and default bindings):" << nl; + + for (RegionBindings::iterator I = B.begin(), E = B.end(); I != E; ++I) + OS << ' ' << I.getKey() << " : " << I.getData() << nl; +} diff --git a/lib/StaticAnalyzer/SValBuilder.cpp b/lib/StaticAnalyzer/SValBuilder.cpp new file mode 100644 index 0000000000..f87fb7ee1e --- /dev/null +++ b/lib/StaticAnalyzer/SValBuilder.cpp @@ -0,0 +1,310 @@ +// SValBuilder.cpp - Basic class for all SValBuilder implementations -*- 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 SValBuilder, the base class for all (complete) SValBuilder +// implementations. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" +#include "clang/StaticAnalyzer/PathSensitive/SVals.h" +#include "clang/StaticAnalyzer/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/BasicValueFactory.h" + +using namespace clang; +using namespace ento; + +//===----------------------------------------------------------------------===// +// Basic SVal creation. +//===----------------------------------------------------------------------===// + +DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType T) { + if (Loc::IsLocType(T)) + return makeNull(); + + if (T->isIntegerType()) + return makeIntVal(0, T); + + // FIXME: Handle floats. + // FIXME: Handle structs. + return UnknownVal(); +} + + +NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const llvm::APSInt& v, QualType T) { + // The Environment ensures we always get a persistent APSInt in + // BasicValueFactory, so we don't need to get the APSInt from + // BasicValueFactory again. + assert(!Loc::IsLocType(T)); + return nonloc::SymExprVal(SymMgr.getSymIntExpr(lhs, op, v, T)); +} + +NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op, + const SymExpr *rhs, QualType T) { + assert(SymMgr.getType(lhs) == SymMgr.getType(rhs)); + assert(!Loc::IsLocType(T)); + return nonloc::SymExprVal(SymMgr.getSymSymExpr(lhs, op, rhs, T)); +} + + +SVal SValBuilder::convertToArrayIndex(SVal V) { + if (V.isUnknownOrUndef()) + return V; + + // Common case: we have an appropriately sized integer. + if (nonloc::ConcreteInt* CI = dyn_cast(&V)) { + const llvm::APSInt& I = CI->getValue(); + if (I.getBitWidth() == ArrayIndexWidth && I.isSigned()) + return V; + } + + return evalCastNL(cast(V), ArrayIndexTy); +} + +DefinedOrUnknownSVal +SValBuilder::getRegionValueSymbolVal(const TypedRegion* R) { + QualType T = R->getValueType(); + + if (!SymbolManager::canSymbolicate(T)) + return UnknownVal(); + + SymbolRef sym = SymMgr.getRegionValueSymbol(R); + + if (Loc::IsLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedOrUnknownSVal SValBuilder::getConjuredSymbolVal(const void *SymbolTag, + const Expr *E, + unsigned Count) { + QualType T = E->getType(); + + if (!SymbolManager::canSymbolicate(T)) + return UnknownVal(); + + SymbolRef sym = SymMgr.getConjuredSymbol(E, Count, SymbolTag); + + if (Loc::IsLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedOrUnknownSVal SValBuilder::getConjuredSymbolVal(const void *SymbolTag, + const Expr *E, + QualType T, + unsigned Count) { + + if (!SymbolManager::canSymbolicate(T)) + return UnknownVal(); + + SymbolRef sym = SymMgr.getConjuredSymbol(E, T, Count, SymbolTag); + + if (Loc::IsLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedSVal SValBuilder::getMetadataSymbolVal(const void *SymbolTag, + const MemRegion *MR, + const Expr *E, QualType T, + unsigned Count) { + assert(SymbolManager::canSymbolicate(T) && "Invalid metadata symbol type"); + + SymbolRef sym = SymMgr.getMetadataSymbol(MR, E, T, Count, SymbolTag); + + if (Loc::IsLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedOrUnknownSVal +SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol, + const TypedRegion *R) { + QualType T = R->getValueType(); + + if (!SymbolManager::canSymbolicate(T)) + return UnknownVal(); + + SymbolRef sym = SymMgr.getDerivedSymbol(parentSymbol, R); + + if (Loc::IsLocType(T)) + return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym)); + + return nonloc::SymbolVal(sym); +} + +DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl* FD) { + return loc::MemRegionVal(MemMgr.getFunctionTextRegion(FD)); +} + +DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *D, + CanQualType locTy, + const LocationContext *LC) { + const BlockTextRegion *BC = + MemMgr.getBlockTextRegion(D, locTy, LC->getAnalysisContext()); + const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, LC); + return loc::MemRegionVal(BD); +} + +//===----------------------------------------------------------------------===// + +SVal SValBuilder::evalBinOp(const GRState *ST, BinaryOperator::Opcode Op, + SVal L, SVal R, QualType T) { + + if (L.isUndef() || R.isUndef()) + return UndefinedVal(); + + if (L.isUnknown() || R.isUnknown()) + return UnknownVal(); + + if (isa(L)) { + if (isa(R)) + return evalBinOpLL(ST, Op, cast(L), cast(R), T); + + return evalBinOpLN(ST, Op, cast(L), cast(R), T); + } + + if (isa(R)) { + // 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(ST, Op, cast(R), cast(L), T); + } + + return evalBinOpNN(ST, Op, cast(L), cast(R), T); +} + +DefinedOrUnknownSVal SValBuilder::evalEQ(const GRState *ST, + DefinedOrUnknownSVal L, + DefinedOrUnknownSVal R) { + return cast(evalBinOp(ST, BO_EQ, L, R, + Context.IntTy)); +} + +// FIXME: should rewrite according to the cast kind. +SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) { + if (val.isUnknownOrUndef() || castTy == originalTy) + return val; + + // For const casts, just propagate the value. + if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType()) + if (Context.hasSameUnqualifiedType(castTy, originalTy)) + return val; + + // Check for casts to real or complex numbers. We don't handle these at all + // right now. + if (castTy->isFloatingType() || castTy->isAnyComplexType()) + return UnknownVal(); + + // Check for casts from integers to integers. + if (castTy->isIntegerType() && originalTy->isIntegerType()) + return evalCastNL(cast(val), castTy); + + // Check for casts from pointers to integers. + if (castTy->isIntegerType() && Loc::IsLocType(originalTy)) + return evalCastL(cast(val), castTy); + + // Check for casts from integers to pointers. + if (Loc::IsLocType(castTy) && originalTy->isIntegerType()) { + if (nonloc::LocAsInteger *LV = dyn_cast(&val)) { + 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(); + } + goto DispatchCast; + } + + // 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 (originalTy->isArrayType()) { + // We will always decay to a pointer. + val = StateMgr.ArrayToPointer(cast(val)); + + // Are we casting from an array to a pointer? If so just pass on + // the decayed value. + if (castTy->isPointerType()) + return val; + + // Are we casting from an array to an integer? If so, cast the decayed + // pointer value to an integer. + assert(castTy->isIntegerType()); + + // FIXME: Keep these here for now in case we decide soon that we + // need the original decayed type. + // QualType elemTy = cast(originalTy)->getElementType(); + // QualType pointerTy = C.getPointerType(elemTy); + return evalCastL(cast(val), castTy); + } + + // Check for casts from a region to a specific type. + if (const MemRegion *R = val.getAsRegion()) { + // 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()); + + 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(); + } + +DispatchCast: + // All other cases. + return isa(val) ? evalCastL(cast(val), castTy) + : evalCastNL(cast(val), castTy); +} diff --git a/lib/StaticAnalyzer/SVals.cpp b/lib/StaticAnalyzer/SVals.cpp new file mode 100644 index 0000000000..dd8508a50b --- /dev/null +++ b/lib/StaticAnalyzer/SVals.cpp @@ -0,0 +1,361 @@ +//= RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -*- 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 SVal, Loc, and NonLoc, classes that represent +// abstract r-values for use with path-sensitive value tracking. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/Basic/IdentifierTable.h" + +using namespace clang; +using namespace ento; +using llvm::dyn_cast; +using llvm::cast; +using llvm::APSInt; + +//===----------------------------------------------------------------------===// +// Symbol iteration within an SVal. +//===----------------------------------------------------------------------===// + + +//===----------------------------------------------------------------------===// +// Utility methods. +//===----------------------------------------------------------------------===// + +bool SVal::hasConjuredSymbol() const { + if (const nonloc::SymbolVal* SV = dyn_cast(this)) { + SymbolRef sym = SV->getSymbol(); + if (isa(sym)) + return true; + } + + if (const loc::MemRegionVal *RV = dyn_cast(this)) { + const MemRegion *R = RV->getRegion(); + if (const SymbolicRegion *SR = dyn_cast(R)) { + SymbolRef sym = SR->getSymbol(); + if (isa(sym)) + return true; + } + } + + return false; +} + +const FunctionDecl *SVal::getAsFunctionDecl() const { + if (const loc::MemRegionVal* X = dyn_cast(this)) { + const MemRegion* R = X->getRegion(); + if (const FunctionTextRegion *CTR = R->getAs()) + return CTR->getDecl(); + } + + return NULL; +} + +/// getAsLocSymbol - If this SVal is a location (subclasses Loc) and +/// wraps a symbol, return that SymbolRef. Otherwise return 0. +// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? +SymbolRef SVal::getAsLocSymbol() const { + if (const nonloc::LocAsInteger *X = dyn_cast(this)) + return X->getLoc().getAsLocSymbol(); + + if (const loc::MemRegionVal *X = dyn_cast(this)) { + const MemRegion *R = X->StripCasts(); + if (const SymbolicRegion *SymR = dyn_cast(R)) + return SymR->getSymbol(); + } + return NULL; +} + +/// Get the symbol in the SVal or its base region. +SymbolRef SVal::getLocSymbolInBase() const { + const loc::MemRegionVal *X = dyn_cast(this); + + if (!X) + return 0; + + const MemRegion *R = X->getRegion(); + + while (const SubRegion *SR = dyn_cast(R)) { + if (const SymbolicRegion *SymR = dyn_cast(SR)) + return SymR->getSymbol(); + else + R = SR->getSuperRegion(); + } + + return 0; +} + +/// getAsSymbol - If this Sval wraps a symbol return that SymbolRef. +/// Otherwise return 0. +// FIXME: should we consider SymbolRef wrapped in CodeTextRegion? +SymbolRef SVal::getAsSymbol() const { + if (const nonloc::SymbolVal *X = dyn_cast(this)) + return X->getSymbol(); + + if (const nonloc::SymExprVal *X = dyn_cast(this)) + if (SymbolRef Y = dyn_cast(X->getSymbolicExpression())) + return Y; + + return getAsLocSymbol(); +} + +/// getAsSymbolicExpression - If this Sval wraps a symbolic expression then +/// return that expression. Otherwise return NULL. +const SymExpr *SVal::getAsSymbolicExpression() const { + if (const nonloc::SymExprVal *X = dyn_cast(this)) + return X->getSymbolicExpression(); + + return getAsSymbol(); +} + +const MemRegion *SVal::getAsRegion() const { + if (const loc::MemRegionVal *X = dyn_cast(this)) + return X->getRegion(); + + if (const nonloc::LocAsInteger *X = dyn_cast(this)) { + return X->getLoc().getAsRegion(); + } + + return 0; +} + +const MemRegion *loc::MemRegionVal::StripCasts() const { + const MemRegion *R = getRegion(); + return R ? R->StripCasts() : NULL; +} + +bool SVal::symbol_iterator::operator==(const symbol_iterator &X) const { + return itr == X.itr; +} + +bool SVal::symbol_iterator::operator!=(const symbol_iterator &X) const { + return itr != X.itr; +} + +SVal::symbol_iterator::symbol_iterator(const SymExpr *SE) { + itr.push_back(SE); + while (!isa(itr.back())) expand(); +} + +SVal::symbol_iterator& SVal::symbol_iterator::operator++() { + assert(!itr.empty() && "attempting to iterate on an 'end' iterator"); + assert(isa(itr.back())); + itr.pop_back(); + if (!itr.empty()) + while (!isa(itr.back())) expand(); + return *this; +} + +SymbolRef SVal::symbol_iterator::operator*() { + assert(!itr.empty() && "attempting to dereference an 'end' iterator"); + return cast(itr.back()); +} + +void SVal::symbol_iterator::expand() { + const SymExpr *SE = itr.back(); + itr.pop_back(); + + if (const SymIntExpr *SIE = dyn_cast(SE)) { + itr.push_back(SIE->getLHS()); + return; + } + else if (const SymSymExpr *SSE = dyn_cast(SE)) { + itr.push_back(SSE->getLHS()); + itr.push_back(SSE->getRHS()); + return; + } + + assert(false && "unhandled expansion case"); +} + +const void *nonloc::LazyCompoundVal::getStore() const { + return static_cast(Data)->getStore(); +} + +const TypedRegion *nonloc::LazyCompoundVal::getRegion() const { + return static_cast(Data)->getRegion(); +} + +//===----------------------------------------------------------------------===// +// Other Iterators. +//===----------------------------------------------------------------------===// + +nonloc::CompoundVal::iterator nonloc::CompoundVal::begin() const { + return getValue()->begin(); +} + +nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const { + return getValue()->end(); +} + +//===----------------------------------------------------------------------===// +// Useful predicates. +//===----------------------------------------------------------------------===// + +bool SVal::isConstant() const { + return isa(this) || isa(this); +} + +bool SVal::isConstant(int I) const { + if (isa(*this)) + return cast(*this).getValue() == I; + else if (isa(*this)) + return cast(*this).getValue() == I; + else + 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 (Op == BO_Add || Op == BO_Sub || + (Op >= BO_LT && Op <= BO_NE)); + + const llvm::APSInt* X = BasicVals.evalAPSInt(Op, getValue(), R.getValue()); + + if (X) + return loc::ConcreteInt(*X); + else + return UndefinedVal(); +} + +//===----------------------------------------------------------------------===// +// Pretty-Printing. +//===----------------------------------------------------------------------===// + +void SVal::dump() const { dumpToStream(llvm::errs()); } + +void SVal::dumpToStream(llvm::raw_ostream& os) const { + switch (getBaseKind()) { + case UnknownKind: + os << "Unknown"; + break; + case NonLocKind: + cast(this)->dumpToStream(os); + break; + case LocKind: + cast(this)->dumpToStream(os); + break; + case UndefinedKind: + os << "Undefined"; + break; + default: + assert (false && "Invalid SVal."); + } +} + +void NonLoc::dumpToStream(llvm::raw_ostream& os) const { + switch (getSubKind()) { + case nonloc::ConcreteIntKind: { + const nonloc::ConcreteInt& C = *cast(this); + if (C.getValue().isUnsigned()) + os << C.getValue().getZExtValue(); + else + os << C.getValue().getSExtValue(); + os << ' ' << (C.getValue().isUnsigned() ? 'U' : 'S') + << C.getValue().getBitWidth() << 'b'; + break; + } + case nonloc::SymbolValKind: + os << '$' << cast(this)->getSymbol(); + break; + case nonloc::SymExprValKind: { + const nonloc::SymExprVal& C = *cast(this); + const SymExpr *SE = C.getSymbolicExpression(); + os << SE; + break; + } + case nonloc::LocAsIntegerKind: { + const nonloc::LocAsInteger& C = *cast(this); + os << C.getLoc() << " [as " << C.getNumBits() << " bit integer]"; + break; + } + case nonloc::CompoundValKind: { + const nonloc::CompoundVal& C = *cast(this); + os << "compoundVal{"; + bool first = true; + for (nonloc::CompoundVal::iterator I=C.begin(), E=C.end(); I!=E; ++I) { + if (first) { + os << ' '; first = false; + } + else + os << ", "; + + (*I).dumpToStream(os); + } + os << "}"; + break; + } + case nonloc::LazyCompoundValKind: { + const nonloc::LazyCompoundVal &C = *cast(this); + os << "lazyCompoundVal{" << const_cast(C.getStore()) + << ',' << C.getRegion() + << '}'; + break; + } + default: + assert (false && "Pretty-printed not implemented for this NonLoc."); + break; + } +} + +void Loc::dumpToStream(llvm::raw_ostream& os) const { + switch (getSubKind()) { + case loc::ConcreteIntKind: + os << cast(this)->getValue().getZExtValue() << " (Loc)"; + break; + case loc::GotoLabelKind: + os << "&&" << cast(this)->getLabel()->getID()->getName(); + break; + case loc::MemRegionKind: + os << '&' << cast(this)->getRegion()->getString(); + break; + default: + assert(false && "Pretty-printing not implemented for this Loc."); + break; + } +} diff --git a/lib/StaticAnalyzer/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/SimpleConstraintManager.cpp new file mode 100644 index 0000000000..e54d0ffe00 --- /dev/null +++ b/lib/StaticAnalyzer/SimpleConstraintManager.cpp @@ -0,0 +1,303 @@ +//== 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 holds code shared +// between BasicConstraintManager and RangeConstraintManager. +// +//===----------------------------------------------------------------------===// + +#include "SimpleConstraintManager.h" +#include "clang/StaticAnalyzer/PathSensitive/ExprEngine.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/StaticAnalyzer/PathSensitive/Checker.h" + +namespace clang { + +namespace ento { + +SimpleConstraintManager::~SimpleConstraintManager() {} + +bool SimpleConstraintManager::canReasonAbout(SVal X) const { + if (nonloc::SymExprVal *SymVal = dyn_cast(&X)) { + const SymExpr *SE = SymVal->getSymbolicExpression(); + + if (isa(SE)) + return true; + + if (const SymIntExpr *SIE = dyn_cast(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; + } + } + + return false; + } + + return true; +} + +const GRState *SimpleConstraintManager::assume(const GRState *state, + DefinedSVal Cond, + bool Assumption) { + if (isa(Cond)) + return assume(state, cast(Cond), Assumption); + else + return assume(state, cast(Cond), Assumption); +} + +const GRState *SimpleConstraintManager::assume(const GRState *state, Loc cond, + bool assumption) { + state = assumeAux(state, cond, assumption); + return SU.ProcessAssume(state, cond, assumption); +} + +const GRState *SimpleConstraintManager::assumeAux(const GRState *state, + Loc Cond, bool Assumption) { + + BasicValueFactory &BasicVals = state->getBasicVals(); + + switch (Cond.getSubKind()) { + default: + assert (false && "'Assume' not implemented for this Loc."); + return state; + + case loc::MemRegionKind: { + // FIXME: Should this go into the storemanager? + + const MemRegion *R = cast(Cond).getRegion(); + const SubRegion *SubR = dyn_cast(R); + + while (SubR) { + // FIXME: now we only find the first symbolic region. + if (const SymbolicRegion *SymR = dyn_cast(SubR)) { + const llvm::APSInt &zero = BasicVals.getZeroWithPtrWidth(); + if (Assumption) + return assumeSymNE(state, SymR->getSymbol(), zero, zero); + else + return assumeSymEQ(state, SymR->getSymbol(), zero, zero); + } + SubR = dyn_cast(SubR->getSuperRegion()); + } + + // FALL-THROUGH. + } + + case loc::GotoLabelKind: + return Assumption ? state : NULL; + + case loc::ConcreteIntKind: { + bool b = cast(Cond).getValue() != 0; + bool isFeasible = b ? Assumption : !Assumption; + return isFeasible ? state : NULL; + } + } // end switch +} + +const GRState *SimpleConstraintManager::assume(const GRState *state, + NonLoc cond, + bool assumption) { + state = assumeAux(state, cond, assumption); + return SU.ProcessAssume(state, cond, assumption); +} + +static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { + // FIXME: This should probably be part of BinaryOperator, since this isn't + // the only place it's used. (This code was copied from SimpleSValBuilder.cpp.) + switch (op) { + default: + assert(false && "Invalid opcode."); + case BO_LT: return BO_GE; + case BO_GT: return BO_LE; + case BO_LE: return BO_GT; + case BO_GE: return BO_LT; + case BO_EQ: return BO_NE; + case BO_NE: return BO_EQ; + } +} + +const GRState *SimpleConstraintManager::assumeAux(const GRState *state, + NonLoc Cond, + bool Assumption) { + + // We cannot reason about SymSymExprs, + // and can only reason about some SymIntExprs. + if (!canReasonAbout(Cond)) { + // Just return the current state indicating that the path is feasible. + // This may be an over-approximation of what is possible. + return state; + } + + BasicValueFactory &BasicVals = state->getBasicVals(); + SymbolManager &SymMgr = state->getSymbolManager(); + + switch (Cond.getSubKind()) { + default: + assert(false && "'Assume' not implemented for this NonLoc"); + + case nonloc::SymbolValKind: { + nonloc::SymbolVal& SV = cast(Cond); + SymbolRef sym = SV.getSymbol(); + QualType T = SymMgr.getType(sym); + const llvm::APSInt &zero = BasicVals.getValue(0, T); + if (Assumption) + return assumeSymNE(state, sym, zero, zero); + else + return assumeSymEQ(state, sym, zero, zero); + } + + case nonloc::SymExprValKind: { + nonloc::SymExprVal V = cast(Cond); + + // For now, we only handle expressions whose RHS is an integer. + // All other expressions are assumed to be feasible. + const SymIntExpr *SE = dyn_cast(V.getSymbolicExpression()); + if (!SE) + return state; + + BinaryOperator::Opcode op = SE->getOpcode(); + // Implicitly compare non-comparison expressions to 0. + if (!BinaryOperator::isComparisonOp(op)) { + QualType T = SymMgr.getType(SE); + const llvm::APSInt &zero = BasicVals.getValue(0, T); + op = (Assumption ? BO_NE : BO_EQ); + return assumeSymRel(state, SE, op, zero); + } + + // From here on out, op is the real comparison we'll be testing. + if (!Assumption) + op = NegateComparison(op); + + return assumeSymRel(state, SE->getLHS(), op, SE->getRHS()); + } + + case nonloc::ConcreteIntKind: { + bool b = cast(Cond).getValue() != 0; + bool isFeasible = b ? Assumption : !Assumption; + return isFeasible ? state : NULL; + } + + case nonloc::LocAsIntegerKind: + return assumeAux(state, cast(Cond).getLoc(), + Assumption); + } // end switch +} + +const GRState *SimpleConstraintManager::assumeSymRel(const GRState *state, + const SymExpr *LHS, + BinaryOperator::Opcode op, + const llvm::APSInt& Int) { + assert(BinaryOperator::isComparisonOp(op) && + "Non-comparison ops should be rewritten as comparisons to zero."); + + // 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; + + // First check if the LHS is a simple symbol reference. + SymbolRef Sym = dyn_cast(LHS); + if (Sym) { + Adjustment = 0; + } else { + // Next, see if it's a "($sym+constant1)" expression. + const SymIntExpr *SE = dyn_cast(LHS); + + // We don't handle "($sym1+$sym2)". + // Give up and assume the constraint is feasible. + if (!SE) + return state; + + // We don't handle "(+constant1)". + // Give up and assume the constraint is feasible. + Sym = dyn_cast(SE->getLHS()); + if (!Sym) + return state; + + // Get the constant out of the expression "($sym+constant1)". + switch (SE->getOpcode()) { + case BO_Add: + Adjustment = SE->getRHS(); + break; + case BO_Sub: + Adjustment = -SE->getRHS(); + break; + default: + // We don't handle non-additive operators. + // Give up and assume the constraint is feasible. + return state; + } + } + + // FIXME: This next section is a hack. It silently converts the integers to + // be of the same type as the symbol, which is not always correct. Really the + // comparisons should be performed using the Int's type, then mapped back to + // the symbol's range of values. + GRStateManager &StateMgr = state->getStateManager(); + ASTContext &Ctx = StateMgr.getContext(); + + QualType T = Sym->getType(Ctx); + assert(T->isIntegerType() || Loc::IsLocType(T)); + unsigned bitwidth = Ctx.getTypeSize(T); + bool isSymUnsigned = T->isUnsignedIntegerType() || Loc::IsLocType(T); + + // Convert the adjustment. + Adjustment.setIsUnsigned(isSymUnsigned); + Adjustment = Adjustment.extOrTrunc(bitwidth); + + // Convert the right-hand side integer. + llvm::APSInt ConvertedInt(Int, isSymUnsigned); + ConvertedInt = ConvertedInt.extOrTrunc(bitwidth); + + switch (op) { + default: + // No logic yet for other operators. assume the constraint is feasible. + return state; + + 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 +} + +} // end of namespace ento + +} // end of namespace clang diff --git a/lib/StaticAnalyzer/SimpleConstraintManager.h b/lib/StaticAnalyzer/SimpleConstraintManager.h new file mode 100644 index 0000000000..75f67f7f17 --- /dev/null +++ b/lib/StaticAnalyzer/SimpleConstraintManager.h @@ -0,0 +1,93 @@ +//== SimpleConstraintManager.h ----------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Code shared between BasicConstraintManager and RangeConstraintManager. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_SIMPLE_CONSTRAINT_MANAGER_H +#define LLVM_CLANG_GR_SIMPLE_CONSTRAINT_MANAGER_H + +#include "clang/StaticAnalyzer/PathSensitive/ConstraintManager.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" + +namespace clang { + +namespace ento { + +class SimpleConstraintManager : public ConstraintManager { + SubEngine &SU; +public: + SimpleConstraintManager(SubEngine &subengine) : SU(subengine) {} + virtual ~SimpleConstraintManager(); + + //===------------------------------------------------------------------===// + // Common implementation for the interface provided by ConstraintManager. + //===------------------------------------------------------------------===// + + bool canReasonAbout(SVal X) const; + + const GRState *assume(const GRState *state, DefinedSVal Cond, + bool Assumption); + + const GRState *assume(const GRState *state, Loc Cond, bool Assumption); + + const GRState *assume(const GRState *state, NonLoc Cond, bool Assumption); + + const GRState *assumeSymRel(const GRState *state, + const SymExpr *LHS, + BinaryOperator::Opcode op, + const llvm::APSInt& Int); + +protected: + + //===------------------------------------------------------------------===// + // Interface that subclasses must implement. + //===------------------------------------------------------------------===// + + // Each of these is of the form "$sym+Adj <> V", where "<>" is the comparison + // operation for the method being invoked. + virtual const GRState *assumeSymNE(const GRState *state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment) = 0; + + virtual const GRState *assumeSymEQ(const GRState *state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment) = 0; + + virtual const GRState *assumeSymLT(const GRState *state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment) = 0; + + virtual const GRState *assumeSymGT(const GRState *state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment) = 0; + + virtual const GRState *assumeSymLE(const GRState *state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment) = 0; + + virtual const GRState *assumeSymGE(const GRState *state, SymbolRef sym, + const llvm::APSInt& V, + const llvm::APSInt& Adjustment) = 0; + + //===------------------------------------------------------------------===// + // Internal implementation. + //===------------------------------------------------------------------===// + + const GRState *assumeAux(const GRState *state, Loc Cond,bool Assumption); + + const GRState *assumeAux(const GRState *state, NonLoc Cond, bool Assumption); +}; + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/lib/StaticAnalyzer/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/SimpleSValBuilder.cpp new file mode 100644 index 0000000000..f1a9074b95 --- /dev/null +++ b/lib/StaticAnalyzer/SimpleSValBuilder.cpp @@ -0,0 +1,884 @@ +// 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/PathSensitive/SValBuilder.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" + +using namespace clang; +using namespace ento; + +namespace { +class SimpleSValBuilder : public SValBuilder { +protected: + virtual SVal evalCastNL(NonLoc val, QualType castTy); + virtual SVal evalCastL(Loc val, QualType castTy); + +public: + SimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context, + GRStateManager &stateMgr) + : SValBuilder(alloc, context, stateMgr) {} + virtual ~SimpleSValBuilder() {} + + virtual SVal evalMinus(NonLoc val); + virtual SVal evalComplement(NonLoc val); + virtual SVal evalBinOpNN(const GRState *state, BinaryOperator::Opcode op, + NonLoc lhs, NonLoc rhs, QualType resultTy); + virtual SVal evalBinOpLL(const GRState *state, BinaryOperator::Opcode op, + Loc lhs, Loc rhs, QualType resultTy); + virtual SVal evalBinOpLN(const GRState *state, BinaryOperator::Opcode op, + Loc lhs, NonLoc rhs, QualType resultTy); + + /// getKnownValue - evaluates a given SVal. If the SVal has only one possible + /// (integer) value, that value is returned. Otherwise, returns NULL. + virtual const llvm::APSInt *getKnownValue(const GRState *state, SVal V); + + 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, + GRStateManager &stateMgr) { + return new SimpleSValBuilder(alloc, context, stateMgr); +} + +//===----------------------------------------------------------------------===// +// Transfer function for Casts. +//===----------------------------------------------------------------------===// + +SVal SimpleSValBuilder::evalCastNL(NonLoc val, QualType castTy) { + + bool isLocType = Loc::IsLocType(castTy); + + if (nonloc::LocAsInteger *LI = dyn_cast(&val)) { + if (isLocType) + return LI->getLoc(); + + // FIXME: Correctly support promotions/truncations. + unsigned castSize = Context.getTypeSize(castTy); + if (castSize == LI->getNumBits()) + return val; + return makeLocAsInteger(LI->getLoc(), castSize); + } + + if (const SymExpr *se = val.getAsSymbolicExpression()) { + QualType T = Context.getCanonicalType(se->getType(Context)); + if (T == Context.getCanonicalType(castTy)) + return val; + + // 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 (T->isIntegerType() && castTy->isIntegerType()) + return val; + + return UnknownVal(); + } + + if (!isa(val)) + return UnknownVal(); + + // Only handle casts from integers to integers. + if (!isLocType && !castTy->isIntegerType()) + return UnknownVal(); + + llvm::APSInt i = cast(val).getValue(); + i.setIsUnsigned(castTy->isUnsignedIntegerType() || Loc::IsLocType(castTy)); + i = i.extOrTrunc(Context.getTypeSize(castTy)); + + if (isLocType) + return makeIntLocVal(i); + else + return makeIntVal(i); +} + +SVal SimpleSValBuilder::evalCastL(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(); + + if (castTy->isIntegerType()) { + unsigned BitWidth = Context.getTypeSize(castTy); + + if (!isa(val)) + return makeLocAsInteger(val, BitWidth); + + llvm::APSInt i = cast(val).getValue(); + i.setIsUnsigned(castTy->isUnsignedIntegerType() || Loc::IsLocType(castTy)); + i = i.extOrTrunc(BitWidth); + 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 cast(val).evalMinus(*this); + default: + return UnknownVal(); + } +} + +SVal SimpleSValBuilder::evalComplement(NonLoc X) { + switch (X.getSubKind()) { + case nonloc::ConcreteIntKind: + return cast(X).evalComplement(*this); + default: + return UnknownVal(); + } +} + +//===----------------------------------------------------------------------===// +// Transfer function for binary operators. +//===----------------------------------------------------------------------===// + +static BinaryOperator::Opcode NegateComparison(BinaryOperator::Opcode op) { + switch (op) { + default: + assert(false && "Invalid opcode."); + case BO_LT: return BO_GE; + case BO_GT: return BO_LE; + case BO_LE: return BO_GT; + case BO_GE: return BO_LT; + case BO_EQ: return BO_NE; + case BO_NE: return BO_EQ; + } +} + +static BinaryOperator::Opcode ReverseComparison(BinaryOperator::Opcode op) { + switch (op) { + default: + assert(false && "Invalid opcode."); + case BO_LT: return BO_GT; + case BO_GT: return BO_LT; + case BO_LE: return BO_GE; + case BO_GE: return BO_LE; + case BO_EQ: + case BO_NE: + return op; + } +} + +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 evalCastNL do the dirty work. + if (isIdempotent) { + if (SymbolRef LHSSym = dyn_cast(LHS)) + return evalCastNL(nonloc::SymbolVal(LHSSym), resultTy); + return evalCastNL(nonloc::SymExprVal(LHS), resultTy); + } + + // If we reach this point, the expression cannot be simplified. + // Make a SymExprVal for the entire thing. + return makeNonLoc(LHS, op, RHS, resultTy); +} + +SVal SimpleSValBuilder::evalBinOpNN(const GRState *state, + BinaryOperator::Opcode op, + NonLoc lhs, NonLoc rhs, + QualType resultTy) { + // 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: + return makeIntVal(0, resultTy); + case BO_Or: + case BO_And: + return evalCastNL(lhs, resultTy); + } + + while (1) { + switch (lhs.getSubKind()) { + default: + return UnknownVal(); + case nonloc::LocAsIntegerKind: { + Loc lhsL = cast(lhs).getLoc(); + switch (rhs.getSubKind()) { + case nonloc::LocAsIntegerKind: + return evalBinOpLL(state, op, lhsL, + cast(rhs).getLoc(), + resultTy); + case nonloc::ConcreteIntKind: { + // Transform the integer into a location and compare. + llvm::APSInt i = cast(rhs).getValue(); + i.setIsUnsigned(true); + i = i.extOrTrunc(Context.getTypeSize(Context.VoidPtrTy)); + 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 UnknownVal(); + } + } + } + case nonloc::SymExprValKind: { + nonloc::SymExprVal *selhs = cast(&lhs); + + // Only handle LHS of the form "$sym op constant", at least for now. + const SymIntExpr *symIntExpr = + dyn_cast(selhs->getSymbolicExpression()); + + if (!symIntExpr) + return UnknownVal(); + + // 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: + assert(false && "Logical operators handled by branching logic."); + return UnknownVal(); + 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: + assert(false && "'=' and ',' operators handled by ExprEngine."); + return UnknownVal(); + case BO_PtrMemD: + case BO_PtrMemI: + assert(false && "Pointer arithmetic not handled here."); + return UnknownVal(); + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + case BO_EQ: + case BO_NE: + // Negate the comparison and make a value. + opc = NegateComparison(opc); + assert(symIntExpr->getType(Context) == resultTy); + return makeNonLoc(symIntExpr->getLHS(), opc, + symIntExpr->getRHS(), resultTy); + } + } + + // For now, only handle expressions whose RHS is a constant. + const nonloc::ConcreteInt *rhsInt = dyn_cast(&rhs); + if (!rhsInt) + return UnknownVal(); + + // If both the LHS and the current expression are additive, + // fold their constants. + if (BinaryOperator::isAdditiveOp(op)) { + BinaryOperator::Opcode lop = symIntExpr->getOpcode(); + if (BinaryOperator::isAdditiveOp(lop)) { + // 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. + const llvm::APSInt &first = + BasicVals.Convert(resultTy, symIntExpr->getRHS()); + const llvm::APSInt &second = + BasicVals.Convert(resultTy, rhsInt->getValue()); + const llvm::APSInt *newRHS; + if (lop == op) + newRHS = BasicVals.evalAPSInt(BO_Add, first, second); + else + newRHS = BasicVals.evalAPSInt(BO_Sub, first, second); + return MakeSymIntVal(symIntExpr->getLHS(), lop, *newRHS, resultTy); + } + } + + // Otherwise, make a SymExprVal out of the expression. + return MakeSymIntVal(symIntExpr, op, rhsInt->getValue(), resultTy); + } + case nonloc::ConcreteIntKind: { + const nonloc::ConcreteInt& lhsInt = cast(lhs); + + if (isa(rhs)) { + return lhsInt.evalBinOp(*this, op, cast(rhs)); + } else { + const llvm::APSInt& lhsValue = lhsInt.getValue(); + + // 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. + NonLoc tmp = rhs; + rhs = lhs; + lhs = tmp; + + switch (op) { + case BO_LT: + case BO_GT: + case BO_LE: + case BO_GE: + op = ReverseComparison(op); + continue; + case BO_EQ: + case BO_NE: + case BO_Add: + case BO_Mul: + case BO_And: + case BO_Xor: + case BO_Or: + continue; + case BO_Shr: + if (lhsValue.isAllOnesValue() && lhsValue.isSigned()) + // At this point lhs and rhs have been swapped. + return rhs; + // FALL-THROUGH + case BO_Shl: + if (lhsValue == 0) + // At this point lhs and rhs have been swapped. + return rhs; + return UnknownVal(); + default: + return UnknownVal(); + } + } + } + case nonloc::SymbolValKind: { + nonloc::SymbolVal *slhs = cast(&lhs); + SymbolRef Sym = slhs->getSymbol(); + // Does the symbol simplify to a constant? If so, "fold" the constant + // by setting 'lhs' to a ConcreteInt and try again. + if (Sym->getType(Context)->isIntegerType()) + if (const llvm::APSInt *Constant = state->getSymVal(Sym)) { + // The symbol evaluates to a constant. If necessary, promote the + // folded constant (LHS) to the result type. + const llvm::APSInt &lhs_I = BasicVals.Convert(resultTy, *Constant); + lhs = nonloc::ConcreteInt(lhs_I); + + // Also promote the RHS (if necessary). + + // For shifts, it is not necessary to promote the RHS. + if (BinaryOperator::isShiftOp(op)) + continue; + + // Other operators: do an implicit conversion. This shouldn't be + // necessary once we support truncation/extension of symbolic values. + if (nonloc::ConcreteInt *rhs_I = dyn_cast(&rhs)){ + rhs = nonloc::ConcreteInt(BasicVals.Convert(resultTy, + rhs_I->getValue())); + } + + continue; + } + + // Is the RHS a symbol we can simplify? + if (const nonloc::SymbolVal *srhs = dyn_cast(&rhs)) { + SymbolRef RSym = srhs->getSymbol(); + if (RSym->getType(Context)->isIntegerType()) { + if (const llvm::APSInt *Constant = state->getSymVal(RSym)) { + // The symbol evaluates to a constant. + const llvm::APSInt &rhs_I = BasicVals.Convert(resultTy, *Constant); + rhs = nonloc::ConcreteInt(rhs_I); + } + } + } + + if (isa(rhs)) { + return MakeSymIntVal(slhs->getSymbol(), op, + cast(rhs).getValue(), + resultTy); + } + + return UnknownVal(); + } + } + } +} + +// FIXME: all this logic will change if/when we have MemRegion::getLocation(). +SVal SimpleSValBuilder::evalBinOpLL(const GRState *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: + assert(false && "Unimplemented operation for two identical values"); + return UnknownVal(); + 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: + assert(false && "Ordering not implemented for this Loc."); + return UnknownVal(); + + 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 evalCastL(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)) + return UnknownVal(); + + const llvm::APSInt &lVal = cast(lhs).getValue(); + return makeNonLoc(rSym, ReverseComparison(op), lVal, resultTy); + } + + // If both operands are constants, just perform the operation. + if (loc::ConcreteInt *rInt = dyn_cast(&rhs)) { + SVal ResultVal = cast(lhs).evalBinOp(BasicVals, op, + *rInt); + if (Loc *Result = dyn_cast(&ResultVal)) + return evalCastL(*Result, resultTy); + else + 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(isa(rhs) || isa(rhs)); + 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::MemRegionKind: { + if (loc::ConcreteInt *rInt = dyn_cast(&rhs)) { + // 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()) + return MakeSymIntVal(lSym, op, rInt->getValue(), resultTy); + + // 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()) { + switch (op) { + default: + break; + case BO_Sub: + return evalCastL(lhs, resultTy); + case BO_EQ: + case BO_LT: + case BO_LE: + return makeTruthVal(false, resultTy); + case BO_NE: + case BO_GT: + case BO_GE: + return makeTruthVal(true, 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 && "MemRegionKind 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(); + + // If both values wrap regions, see if they're from different base regions. + const MemRegion *LeftBase = LeftMR->getBaseRegion(); + const MemRegion *RightBase = RightMR->getBaseRegion(); + if (LeftBase != RightBase && + !isa(LeftBase) && !isa(RightBase)) { + switch (op) { + default: + return UnknownVal(); + case BO_EQ: + return makeTruthVal(false, resultTy); + case BO_NE: + return makeTruthVal(true, resultTy); + } + } + + // The two regions are from the same base region. See if they're both a + // type of region we know how to compare. + + // FIXME: If/when there is a getAsRawOffset() for FieldRegions, this + // ElementRegion path and the FieldRegion path below should be unified. + if (const ElementRegion *LeftER = dyn_cast(LeftMR)) { + // First see if the right region is also an ElementRegion. + const ElementRegion *RightER = dyn_cast(RightMR); + if (!RightER) + return UnknownVal(); + + // 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(); + NonLoc *LeftIndex = dyn_cast(&LeftIndexVal); + if (!LeftIndex) + return UnknownVal(); + LeftIndexVal = evalCastNL(*LeftIndex, resultTy); + LeftIndex = dyn_cast(&LeftIndexVal); + if (!LeftIndex) + return UnknownVal(); + + // Do the same for the right index. + SVal RightIndexVal = RightER->getIndex(); + NonLoc *RightIndex = dyn_cast(&RightIndexVal); + if (!RightIndex) + return UnknownVal(); + RightIndexVal = evalCastNL(*RightIndex, resultTy); + RightIndex = dyn_cast(&RightIndexVal); + 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); + } + + // If the element indexes aren't comparable, see if the raw offsets are. + RegionRawOffset LeftOffset = LeftER->getAsArrayOffset(); + RegionRawOffset RightOffset = RightER->getAsArrayOffset(); + + if (LeftOffset.getRegion() != NULL && + LeftOffset.getRegion() == RightOffset.getRegion()) { + int64_t left = LeftOffset.getByteOffset(); + int64_t right = RightOffset.getByteOffset(); + + 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); + } + } + + // If we get here, we have no way of comparing the ElementRegions. + return UnknownVal(); + } + + // See if both regions are fields of the same structure. + // FIXME: This doesn't handle nesting, inheritance, or Objective-C ivars. + if (const FieldRegion *LeftFR = dyn_cast(LeftMR)) { + // Only comparisons are meaningful here! + if (!BinaryOperator::isComparisonOp(op)) + return UnknownVal(); + + // First see if the right region is also a FieldRegion. + const FieldRegion *RightFR = dyn_cast(RightMR); + if (!RightFR) + 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 makeTruthVal(false, resultTy); + if (op == BO_NE) + return 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 (RecordDecl::field_iterator I = RD->field_begin(), + E = RD->field_end(); I!=E; ++I) { + if (*I == LeftFD) + return makeTruthVal(leftFirst, resultTy); + if (*I == RightFD) + return makeTruthVal(!leftFirst, resultTy); + } + + assert(false && "Fields not found in parent record's definition"); + } + + // If we get here, we have no way of comparing the regions. + return UnknownVal(); + } + } +} + +SVal SimpleSValBuilder::evalBinOpLN(const GRState *state, + BinaryOperator::Opcode op, + Loc lhs, NonLoc rhs, QualType resultTy) { + // Special case: 'rhs' is an integer that has the same width as a pointer and + // we are using the integer location in a comparison. Normally this cannot be + // triggered, but transfer functions like those for OSCommpareAndSwapBarrier32 + // can generate comparisons that trigger this code. + // FIXME: Are all locations guaranteed to have pointer width? + if (BinaryOperator::isComparisonOp(op)) { + if (nonloc::ConcreteInt *rhsInt = dyn_cast(&rhs)) { + const llvm::APSInt *x = &rhsInt->getValue(); + ASTContext &ctx = Context; + if (ctx.getTypeSize(ctx.VoidPtrTy) == x->getBitWidth()) { + // Convert the signedness of the integer (if necessary). + if (x->isSigned()) + x = &getBasicValueFactory().getValue(*x, true); + + return evalBinOpLL(state, op, lhs, loc::ConcreteInt(*x), resultTy); + } + } + } + + // We are dealing with pointer arithmetic. + + // Handle pointer arithmetic on constant values. + if (nonloc::ConcreteInt *rhsInt = dyn_cast(&rhs)) { + if (loc::ConcreteInt *lhsInt = dyn_cast(&lhs)) { + 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); + 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)); + } + } + + + // Delegate remaining pointer arithmetic to the StoreManager. + return state->getStateManager().getStoreManager().evalBinOp(op, lhs, + rhs, resultTy); +} + +const llvm::APSInt *SimpleSValBuilder::getKnownValue(const GRState *state, + SVal V) { + if (V.isUnknownOrUndef()) + return NULL; + + if (loc::ConcreteInt* X = dyn_cast(&V)) + return &X->getValue(); + + if (nonloc::ConcreteInt* X = dyn_cast(&V)) + return &X->getValue(); + + if (SymbolRef Sym = V.getAsSymbol()) + return state->getSymVal(Sym); + + // FIXME: Add support for SymExprs. + return NULL; +} diff --git a/lib/StaticAnalyzer/Store.cpp b/lib/StaticAnalyzer/Store.cpp new file mode 100644 index 0000000000..f1b83389ca --- /dev/null +++ b/lib/StaticAnalyzer/Store.cpp @@ -0,0 +1,334 @@ +//== Store.cpp - Interface for maps from Locations to 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 defined the types Store and StoreManager. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathSensitive/Store.h" +#include "clang/StaticAnalyzer/PathSensitive/GRState.h" +#include "clang/AST/CharUnits.h" + +using namespace clang; +using namespace ento; + +StoreManager::StoreManager(GRStateManager &stateMgr) + : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr), + MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {} + +Store StoreManager::EnterStackFrame(const GRState *state, + const StackFrameContext *frame) { + return state->getStore(); +} + +const MemRegion *StoreManager::MakeElementRegion(const MemRegion *Base, + QualType EleTy, uint64_t index) { + NonLoc idx = svalBuilder.makeArrayIndex(index); + return MRMgr.getElementRegion(EleTy, idx, Base, svalBuilder.getContext()); +} + +// FIXME: Merge with the implementation of the same method in MemRegion.cpp +static bool IsCompleteType(ASTContext &Ctx, QualType Ty) { + if (const RecordType *RT = Ty->getAs()) { + const RecordDecl *D = RT->getDecl(); + if (!D->getDefinition()) + return false; + } + + return true; +} + +const ElementRegion *StoreManager::GetElementZeroRegion(const MemRegion *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(R) || isa(R)) + return R; + + // We don't know what to make of it. Return a NULL region, which + // will be interpretted as UnknownVal. + return NULL; + } + + // Now assume we are casting from pointer to pointer. Other cases should + // already be handled. + QualType PointeeTy = CastToTy->getAs()->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 TypedRegion *TR = dyn_cast(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::GenericMemSpaceRegionKind: + case MemRegion::StackLocalsSpaceRegionKind: + case MemRegion::StackArgumentsSpaceRegionKind: + case MemRegion::HeapSpaceRegionKind: + case MemRegion::UnknownSpaceRegionKind: + case MemRegion::NonStaticGlobalSpaceRegionKind: + case MemRegion::StaticGlobalSpaceRegionKind: { + assert(0 && "Invalid region cast"); + break; + } + + case MemRegion::FunctionTextRegionKind: + case MemRegion::BlockTextRegionKind: + 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::VarRegionKind: + case MemRegion::CXXTempObjectRegionKind: + case MemRegion::CXXBaseObjectRegionKind: + return MakeElementRegion(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(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 NULL; + + CharUnits off = CharUnits::fromQuantity(rawOff.getByteOffset()); + + 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 TypedRegion *TR = dyn_cast(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(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 = 0; + + // We can only compute sizeof(PointeeTy) if it is a complete type. + if (IsCompleteType(Ctx, PointeeTy)) { + // 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(baseR, Ctx.CharTy, off.getQuantity()); + } + + return MakeElementRegion(newSuperR, PointeeTy, newIndex); + } + } + + assert(0 && "unreachable"); + return 0; +} + + +/// 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 TypedRegion *R, + QualType castTy, bool performTestOnly) { + + if (castTy.isNull()) + return V; + + ASTContext &Ctx = svalBuilder.getContext(); + + if (performTestOnly) { + // Automatically translate references to pointers. + QualType T = R->getValueType(); + if (const ReferenceType *RT = T->getAs()) + T = Ctx.getPointerType(RT->getPointeeType()); + + assert(svalBuilder.getContext().hasSameUnqualifiedType(castTy, T)); + return V; + } + + if (const Loc *L = dyn_cast(&V)) + return svalBuilder.evalCastL(*L, castTy); + else if (const NonLoc *NL = dyn_cast(&V)) + return svalBuilder.evalCastNL(*NL, castTy); + + return V; +} + +SVal StoreManager::getLValueFieldOrIvar(const Decl* D, SVal Base) { + if (Base.isUnknownOrUndef()) + return Base; + + Loc BaseL = cast(Base); + const MemRegion* BaseR = 0; + + switch (BaseL.getSubKind()) { + case loc::MemRegionKind: + BaseR = cast(BaseL).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. For example, + // add the field offset to the integer value. That way funny things + // like this work properly: &(((struct foo *) 0xa)->f) + return Base; + + default: + assert(0 && "Unhandled Base."); + return Base; + } + + // NOTE: We must have this check first because ObjCIvarDecl is a subclass + // of FieldDecl. + if (const ObjCIvarDecl *ID = dyn_cast(D)) + return loc::MemRegionVal(MRMgr.getObjCIvarRegion(ID, BaseR)); + + return loc::MemRegionVal(MRMgr.getFieldRegion(cast(D), BaseR)); +} + +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. + if (Base.isUnknownOrUndef() || isa(Base)) + return Base; + + const MemRegion* BaseRegion = cast(Base).getRegion(); + + // Pointer of any type can be cast and used as array base. + const ElementRegion *ElemR = dyn_cast(BaseRegion); + + // Convert the offset to the appropriate size and signedness. + Offset = cast(svalBuilder.convertToArrayIndex(Offset)); + + 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 (!isa(BaseIdx)) + return UnknownVal(); + + const llvm::APSInt& BaseIdxI = cast(BaseIdx).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 (!isa(Offset)) { + if (isa(BaseRegion->StripCasts())) + return UnknownVal(); + + return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset, + ElemR->getSuperRegion(), + Ctx)); + } + + const llvm::APSInt& OffI = cast(Offset).getValue(); + assert(BaseIdxI.isSigned()); + + // Compute the new index. + nonloc::ConcreteInt NewIdx(svalBuilder.getBasicValueFactory().getValue(BaseIdxI + + OffI)); + + // Construct the new ElementRegion. + const MemRegion *ArrayR = ElemR->getSuperRegion(); + return loc::MemRegionVal(MRMgr.getElementRegion(elementType, NewIdx, ArrayR, + Ctx)); +} diff --git a/lib/StaticAnalyzer/SymbolManager.cpp b/lib/StaticAnalyzer/SymbolManager.cpp new file mode 100644 index 0000000000..08677dafcf --- /dev/null +++ b/lib/StaticAnalyzer/SymbolManager.cpp @@ -0,0 +1,343 @@ +//== SymbolManager.h - Management of Symbolic 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 defines SymbolManager, a class that manages symbolic values +// created for use by ExprEngine and related classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathSensitive/SymbolManager.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/StaticAnalyzer/PathSensitive/MemRegion.h" +#include "llvm/Support/raw_ostream.h" + +using namespace clang; +using namespace ento; + +void SymExpr::dump() const { + dumpToStream(llvm::errs()); +} + +static void print(llvm::raw_ostream& os, BinaryOperator::Opcode Op) { + switch (Op) { + default: + assert(false && "operator printing not implemented"); + break; + case BO_Mul: os << '*' ; break; + case BO_Div: os << '/' ; break; + case BO_Rem: os << '%' ; break; + case BO_Add: os << '+' ; break; + case BO_Sub: os << '-' ; break; + case BO_Shl: os << "<<" ; break; + case BO_Shr: os << ">>" ; break; + case BO_LT: os << "<" ; break; + case BO_GT: os << '>' ; break; + case BO_LE: os << "<=" ; break; + case BO_GE: os << ">=" ; break; + case BO_EQ: os << "==" ; break; + case BO_NE: os << "!=" ; break; + case BO_And: os << '&' ; break; + case BO_Xor: os << '^' ; break; + case BO_Or: os << '|' ; break; + } +} + +void SymIntExpr::dumpToStream(llvm::raw_ostream& os) const { + os << '('; + getLHS()->dumpToStream(os); + os << ") "; + print(os, getOpcode()); + os << ' ' << getRHS().getZExtValue(); + if (getRHS().isUnsigned()) os << 'U'; +} + +void SymSymExpr::dumpToStream(llvm::raw_ostream& os) const { + os << '('; + getLHS()->dumpToStream(os); + os << ") "; + os << '('; + getRHS()->dumpToStream(os); + os << ')'; +} + +void SymbolConjured::dumpToStream(llvm::raw_ostream& os) const { + os << "conj_$" << getSymbolID() << '{' << T.getAsString() << '}'; +} + +void SymbolDerived::dumpToStream(llvm::raw_ostream& os) const { + os << "derived_$" << getSymbolID() << '{' + << getParentSymbol() << ',' << getRegion() << '}'; +} + +void SymbolExtent::dumpToStream(llvm::raw_ostream& os) const { + os << "extent_$" << getSymbolID() << '{' << getRegion() << '}'; +} + +void SymbolMetadata::dumpToStream(llvm::raw_ostream& os) const { + os << "meta_$" << getSymbolID() << '{' + << getRegion() << ',' << T.getAsString() << '}'; +} + +void SymbolRegionValue::dumpToStream(llvm::raw_ostream& os) const { + os << "reg_$" << getSymbolID() << "<" << R << ">"; +} + +const SymbolRegionValue* +SymbolManager::getRegionValueSymbol(const TypedRegion* R) { + llvm::FoldingSetNodeID profile; + SymbolRegionValue::Profile(profile, R); + void* InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate(); + new (SD) SymbolRegionValue(SymbolCounter, R); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast(SD); +} + +const SymbolConjured* +SymbolManager::getConjuredSymbol(const Stmt* E, QualType T, unsigned Count, + const void* SymbolTag) { + + llvm::FoldingSetNodeID profile; + SymbolConjured::Profile(profile, E, T, Count, SymbolTag); + void* InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate(); + new (SD) SymbolConjured(SymbolCounter, E, T, Count, SymbolTag); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast(SD); +} + +const SymbolDerived* +SymbolManager::getDerivedSymbol(SymbolRef parentSymbol, + const TypedRegion *R) { + + llvm::FoldingSetNodeID profile; + SymbolDerived::Profile(profile, parentSymbol, R); + void* InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate(); + new (SD) SymbolDerived(SymbolCounter, parentSymbol, R); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast(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(); + new (SD) SymbolExtent(SymbolCounter, R); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast(SD); +} + +const SymbolMetadata* +SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt* S, QualType T, + unsigned Count, const void* SymbolTag) { + + llvm::FoldingSetNodeID profile; + SymbolMetadata::Profile(profile, R, S, T, Count, SymbolTag); + void* InsertPos; + SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos); + if (!SD) { + SD = (SymExpr*) BPAlloc.Allocate(); + new (SD) SymbolMetadata(SymbolCounter, R, S, T, Count, SymbolTag); + DataSet.InsertNode(SD, InsertPos); + ++SymbolCounter; + } + + return cast(SD); +} + +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(); + new (data) SymIntExpr(lhs, op, v, t); + DataSet.InsertNode(data, InsertPos); + } + + return cast(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(); + new (data) SymSymExpr(lhs, op, rhs, t); + DataSet.InsertNode(data, InsertPos); + } + + return cast(data); +} + +QualType SymbolConjured::getType(ASTContext&) const { + return T; +} + +QualType SymbolDerived::getType(ASTContext& Ctx) const { + return R->getValueType(); +} + +QualType SymbolExtent::getType(ASTContext& Ctx) const { + return Ctx.getSizeType(); +} + +QualType SymbolMetadata::getType(ASTContext&) const { + return T; +} + +QualType SymbolRegionValue::getType(ASTContext& C) const { + return R->getValueType(); +} + +SymbolManager::~SymbolManager() {} + +bool SymbolManager::canSymbolicate(QualType T) { + if (Loc::IsLocType(T)) + return true; + + if (T->isIntegerType()) + return T->isScalarType(); + + if (T->isRecordType()) + return true; + + return false; +} + +void SymbolReaper::markLive(SymbolRef sym) { + TheLiving.insert(sym); + TheDead.erase(sym); +} + +void SymbolReaper::markInUse(SymbolRef sym) { + if (isa(sym)) + MetadataInUse.insert(sym); +} + +bool SymbolReaper::maybeDead(SymbolRef sym) { + if (isLive(sym)) + return false; + + TheDead.insert(sym); + return true; +} + +static bool IsLiveRegion(SymbolReaper &Reaper, const MemRegion *MR) { + MR = MR->getBaseRegion(); + + if (const SymbolicRegion *SR = dyn_cast(MR)) + return Reaper.isLive(SR->getSymbol()); + + if (const VarRegion *VR = dyn_cast(MR)) + return Reaper.isLive(VR); + + // 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(MR)) + return true; + + if (isa(MR)) + return true; + + if (isa(MR)) + return true; + + return false; +} + +bool SymbolReaper::isLive(SymbolRef sym) { + if (TheLiving.count(sym)) + return true; + + if (const SymbolDerived *derived = dyn_cast(sym)) { + if (isLive(derived->getParentSymbol())) { + markLive(sym); + return true; + } + return false; + } + + if (const SymbolExtent *extent = dyn_cast(sym)) { + if (IsLiveRegion(*this, extent->getRegion())) { + markLive(sym); + return true; + } + return false; + } + + if (const SymbolMetadata *metadata = dyn_cast(sym)) { + if (MetadataInUse.count(sym)) { + if (IsLiveRegion(*this, metadata->getRegion())) { + markLive(sym); + MetadataInUse.erase(sym); + return true; + } + } + return false; + } + + // Interogate the symbol. It may derive from an input value to + // the analyzed function/method. + return isa(sym); +} + +bool SymbolReaper::isLive(const Stmt* ExprVal) const { + return LCtx->getAnalysisContext()->getRelaxedLiveVariables()-> + isLive(Loc, ExprVal); +} + +bool SymbolReaper::isLive(const VarRegion *VR) const { + const StackFrameContext *VarContext = VR->getStackFrame(); + const StackFrameContext *CurrentContext = LCtx->getCurrentStackFrame(); + + if (VarContext == CurrentContext) + return LCtx->getAnalysisContext()->getRelaxedLiveVariables()-> + isLive(Loc, VR->getDecl()); + + return VarContext->isParentOf(CurrentContext); +} + +SymbolVisitor::~SymbolVisitor() {} diff --git a/lib/StaticAnalyzer/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/TextPathDiagnostics.cpp new file mode 100644 index 0000000000..9ca378f2d4 --- /dev/null +++ b/lib/StaticAnalyzer/TextPathDiagnostics.cpp @@ -0,0 +1,70 @@ +//===--- TextPathDiagnostics.cpp - Text 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 TextPathDiagnostics object. +// +//===----------------------------------------------------------------------===// + +#include "clang/StaticAnalyzer/PathDiagnosticClients.h" +#include "clang/StaticAnalyzer/BugReporter/PathDiagnostic.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; +using namespace ento; +using namespace llvm; + +namespace { + +/// \brief Simple path diagnostic client used for outputting as diagnostic notes +/// the sequence of events. +class TextPathDiagnostics : public PathDiagnosticClient { + const std::string OutputFile; + Diagnostic &Diag; + +public: + TextPathDiagnostics(const std::string& output, Diagnostic &diag) + : OutputFile(output), Diag(diag) {} + + void HandlePathDiagnostic(const PathDiagnostic* D); + + void FlushDiagnostics(llvm::SmallVectorImpl *FilesMade) { } + + virtual llvm::StringRef getName() const { + return "TextPathDiagnostics"; + } + + PathGenerationScheme getGenerationScheme() const { return Minimal; } + bool supportsLogicalOpControlFlow() const { return true; } + bool supportsAllBlockEdges() const { return true; } + virtual bool useVerboseDescription() const { return true; } +}; + +} // end anonymous namespace + +PathDiagnosticClient* +ento::createTextPathDiagnosticClient(const std::string& out, + const Preprocessor &PP) { + return new TextPathDiagnostics(out, PP.getDiagnostics()); +} + +void TextPathDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) { + if (!D) + return; + + if (D->empty()) { + delete D; + return; + } + + for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I) { + unsigned diagID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, I->getString()); + Diag.Report(I->getLocation().asLocation(), diagID); + } +} diff --git a/tools/driver/CMakeLists.txt b/tools/driver/CMakeLists.txt index 6e02e85e5a..14c06c098f 100644 --- a/tools/driver/CMakeLists.txt +++ b/tools/driver/CMakeLists.txt @@ -8,8 +8,8 @@ set( LLVM_USED_LIBS clangCodeGen clangParse clangSema - clangEntoCheckers - clangEntoCore + clangStaticAnalyzerCheckers + clangStaticAnalyzerCore clangAnalysis clangIndex clangRewrite diff --git a/tools/driver/Makefile b/tools/driver/Makefile index e97bfeb443..9942310e4f 100644 --- a/tools/driver/Makefile +++ b/tools/driver/Makefile @@ -39,7 +39,7 @@ LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader bitwriter codegen \ ipo selectiondag USEDLIBS = clangFrontendTool.a clangFrontend.a clangDriver.a \ clangSerialization.a clangCodeGen.a clangParse.a clangSema.a \ - clangEntoCheckers.a clangEntoCore.a clangAnalysis.a clangIndex.a clangRewrite.a \ + clangStaticAnalyzerCheckers.a clangStaticAnalyzerCore.a clangAnalysis.a clangIndex.a clangRewrite.a \ clangAST.a clangLex.a clangBasic.a include $(CLANG_LEVEL)/Makefile