From 1994756114cf42546cba9748ffc087edaee42df8 Mon Sep 17 00:00:00 2001 From: Argiris Kirtzidis Date: Fri, 24 Dec 2010 06:19:58 +0000 Subject: [PATCH] Remove the EntoSA directories. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@122543 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../clang/StaticAnalyzer/EntoSA/AnalysisConsumer.h | 39 - .../EntoSA/BugReporter/BugReporter.h | 486 --- .../StaticAnalyzer/EntoSA/BugReporter/BugType.h | 76 - .../EntoSA/BugReporter/PathDiagnostic.h | 500 --- .../EntoSA/Checkers/DereferenceChecker.h | 35 - .../StaticAnalyzer/EntoSA/Checkers/LocalCheckers.h | 67 - .../clang/StaticAnalyzer/EntoSA/FrontendActions.h | 33 - .../clang/StaticAnalyzer/EntoSA/ManagerRegistry.h | 58 - .../StaticAnalyzer/EntoSA/PathDiagnosticClients.h | 42 - .../EntoSA/PathSensitive/AnalysisManager.h | 208 -- .../EntoSA/PathSensitive/BasicValueFactory.h | 201 -- .../EntoSA/PathSensitive/BlockCounter.h | 59 - .../StaticAnalyzer/EntoSA/PathSensitive/Checker.h | 309 -- .../EntoSA/PathSensitive/CheckerHelpers.h | 44 - .../EntoSA/PathSensitive/CheckerVisitor.def | 43 - .../EntoSA/PathSensitive/CheckerVisitor.h | 111 - .../EntoSA/PathSensitive/ConstraintManager.h | 76 - .../EntoSA/PathSensitive/CoreEngine.h | 542 --- .../EntoSA/PathSensitive/Environment.h | 106 - .../EntoSA/PathSensitive/ExplodedGraph.h | 435 --- .../EntoSA/PathSensitive/ExprEngine.h | 544 --- .../EntoSA/PathSensitive/ExprEngineBuilders.h | 81 - .../StaticAnalyzer/EntoSA/PathSensitive/GRState.h | 760 ----- .../EntoSA/PathSensitive/GRStateTrait.h | 152 - .../EntoSA/PathSensitive/MemRegion.h | 1077 ------ .../EntoSA/PathSensitive/SValBuilder.h | 259 -- .../StaticAnalyzer/EntoSA/PathSensitive/SVals.h | 522 --- .../StaticAnalyzer/EntoSA/PathSensitive/Store.h | 259 -- .../EntoSA/PathSensitive/SubEngine.h | 113 - .../EntoSA/PathSensitive/SummaryManager.h | 61 - .../EntoSA/PathSensitive/SymbolManager.h | 489 --- .../EntoSA/PathSensitive/TransferFuncs.h | 92 - .../StaticAnalyzer/EntoSA/PathSensitive/WorkList.h | 94 - lib/StaticAnalyzer/EntoSA/AggExprVisitor.cpp | 63 - lib/StaticAnalyzer/EntoSA/AnalysisManager.cpp | 32 - lib/StaticAnalyzer/EntoSA/AnalyzerStatsChecker.cpp | 123 - .../EntoSA/BasicConstraintManager.cpp | 338 -- lib/StaticAnalyzer/EntoSA/BasicStore.cpp | 594 ---- lib/StaticAnalyzer/EntoSA/BasicValueFactory.cpp | 290 -- lib/StaticAnalyzer/EntoSA/BlockCounter.cpp | 86 - lib/StaticAnalyzer/EntoSA/BugReporter.cpp | 1892 ----------- lib/StaticAnalyzer/EntoSA/BugReporterVisitors.cpp | 457 --- lib/StaticAnalyzer/EntoSA/CFRefCount.cpp | 3500 ------------------- lib/StaticAnalyzer/EntoSA/CMakeLists.txt | 41 - lib/StaticAnalyzer/EntoSA/CXXExprEngine.cpp | 328 -- lib/StaticAnalyzer/EntoSA/Checker.cpp | 36 - lib/StaticAnalyzer/EntoSA/CheckerHelpers.cpp | 80 - .../EntoSA/Checkers/AdjustedReturnValueChecker.cpp | 96 - .../EntoSA/Checkers/AnalysisConsumer.cpp | 610 ---- .../EntoSA/Checkers/ArrayBoundChecker.cpp | 91 - .../EntoSA/Checkers/ArrayBoundCheckerV2.cpp | 277 -- .../EntoSA/Checkers/AttrNonNullChecker.cpp | 136 - .../EntoSA/Checkers/BasicObjCFoundationChecks.cpp | 521 --- .../EntoSA/Checkers/BasicObjCFoundationChecks.h | 36 - .../EntoSA/Checkers/BuiltinFunctionChecker.cpp | 83 - lib/StaticAnalyzer/EntoSA/Checkers/CMakeLists.txt | 53 - .../EntoSA/Checkers/CStringChecker.cpp | 1048 ------ .../EntoSA/Checkers/CallAndMessageChecker.cpp | 350 -- .../EntoSA/Checkers/CastSizeChecker.cpp | 91 - .../EntoSA/Checkers/CastToStructChecker.cpp | 79 - .../EntoSA/Checkers/CheckDeadStores.cpp | 290 -- .../EntoSA/Checkers/CheckObjCDealloc.cpp | 262 -- .../EntoSA/Checkers/CheckObjCInstMethSignature.cpp | 120 - .../EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp | 503 --- .../EntoSA/Checkers/CheckSizeofPointer.cpp | 72 - .../EntoSA/Checkers/ChrootChecker.cpp | 162 - .../EntoSA/Checkers/DereferenceChecker.cpp | 204 -- .../EntoSA/Checkers/DivZeroChecker.cpp | 86 - lib/StaticAnalyzer/EntoSA/Checkers/ExprEngine.cpp | 3513 -------------------- .../Checkers/ExprEngineExperimentalChecks.cpp | 46 - .../EntoSA/Checkers/ExprEngineExperimentalChecks.h | 37 - .../EntoSA/Checkers/ExprEngineInternalChecks.h | 59 - .../EntoSA/Checkers/FixedAddressChecker.cpp | 72 - .../EntoSA/Checkers/FrontendActions.cpp | 22 - .../EntoSA/Checkers/IdempotentOperationChecker.cpp | 834 ----- .../EntoSA/Checkers/LLVMConventionsChecker.cpp | 313 -- .../EntoSA/Checkers/MacOSXAPIChecker.cpp | 142 - lib/StaticAnalyzer/EntoSA/Checkers/Makefile | 17 - .../EntoSA/Checkers/MallocChecker.cpp | 733 ---- .../EntoSA/Checkers/NSAutoreleasePoolChecker.cpp | 87 - .../EntoSA/Checkers/NSErrorChecker.cpp | 238 -- .../EntoSA/Checkers/NoReturnFunctionChecker.cpp | 80 - .../EntoSA/Checkers/OSAtomicChecker.cpp | 203 -- .../EntoSA/Checkers/ObjCAtSyncChecker.cpp | 95 - .../EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp | 164 - .../EntoSA/Checkers/PointerArithChecker.cpp | 72 - .../EntoSA/Checkers/PointerSubChecker.cpp | 79 - .../EntoSA/Checkers/PthreadLockChecker.cpp | 147 - .../EntoSA/Checkers/ReturnPointerRangeChecker.cpp | 95 - .../EntoSA/Checkers/ReturnUndefChecker.cpp | 69 - .../EntoSA/Checkers/StackAddrLeakChecker.cpp | 205 -- .../EntoSA/Checkers/StreamChecker.cpp | 466 --- .../EntoSA/Checkers/UndefBranchChecker.cpp | 120 - .../Checkers/UndefCapturedBlockVarChecker.cpp | 102 - .../EntoSA/Checkers/UndefResultChecker.cpp | 87 - .../Checkers/UndefinedArraySubscriptChecker.cpp | 57 - .../EntoSA/Checkers/UndefinedAssignmentChecker.cpp | 94 - .../EntoSA/Checkers/UnixAPIChecker.cpp | 277 -- .../EntoSA/Checkers/UnreachableCodeChecker.cpp | 223 -- .../EntoSA/Checkers/VLASizeChecker.cpp | 138 - lib/StaticAnalyzer/EntoSA/CoreEngine.cpp | 809 ----- lib/StaticAnalyzer/EntoSA/Environment.cpp | 236 -- lib/StaticAnalyzer/EntoSA/ExplodedGraph.cpp | 282 -- lib/StaticAnalyzer/EntoSA/FlatStore.cpp | 203 -- lib/StaticAnalyzer/EntoSA/GRState.cpp | 551 --- lib/StaticAnalyzer/EntoSA/HTMLDiagnostics.cpp | 578 ---- lib/StaticAnalyzer/EntoSA/Makefile | 19 - lib/StaticAnalyzer/EntoSA/ManagerRegistry.cpp | 21 - lib/StaticAnalyzer/EntoSA/MemRegion.cpp | 987 ------ lib/StaticAnalyzer/EntoSA/PathDiagnostic.cpp | 279 -- lib/StaticAnalyzer/EntoSA/PlistDiagnostics.cpp | 472 --- lib/StaticAnalyzer/EntoSA/README.txt | 117 - .../EntoSA/RangeConstraintManager.cpp | 442 --- lib/StaticAnalyzer/EntoSA/RegionStore.cpp | 1875 ----------- lib/StaticAnalyzer/EntoSA/SValBuilder.cpp | 310 -- lib/StaticAnalyzer/EntoSA/SVals.cpp | 361 -- .../EntoSA/SimpleConstraintManager.cpp | 303 -- .../EntoSA/SimpleConstraintManager.h | 93 - lib/StaticAnalyzer/EntoSA/SimpleSValBuilder.cpp | 884 ----- lib/StaticAnalyzer/EntoSA/Store.cpp | 334 -- lib/StaticAnalyzer/EntoSA/SymbolManager.cpp | 343 -- lib/StaticAnalyzer/EntoSA/TextPathDiagnostics.cpp | 70 - 122 files changed, 39458 deletions(-) delete mode 100644 include/clang/StaticAnalyzer/EntoSA/AnalysisConsumer.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/BugReporter/BugReporter.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/BugReporter/BugType.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/BugReporter/PathDiagnostic.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/Checkers/DereferenceChecker.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/Checkers/LocalCheckers.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/FrontendActions.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/ManagerRegistry.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathDiagnosticClients.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/AnalysisManager.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/BasicValueFactory.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/BlockCounter.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/Checker.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerHelpers.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerVisitor.def delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerVisitor.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/ConstraintManager.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/CoreEngine.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/Environment.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExplodedGraph.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngine.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngineBuilders.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/GRState.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/GRStateTrait.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/MemRegion.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/SValBuilder.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/SVals.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/Store.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/SubEngine.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/SummaryManager.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/SymbolManager.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/TransferFuncs.h delete mode 100644 include/clang/StaticAnalyzer/EntoSA/PathSensitive/WorkList.h delete mode 100644 lib/StaticAnalyzer/EntoSA/AggExprVisitor.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/AnalysisManager.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/AnalyzerStatsChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/BasicConstraintManager.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/BasicStore.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/BasicValueFactory.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/BlockCounter.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/BugReporter.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/BugReporterVisitors.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/CFRefCount.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/CMakeLists.txt delete mode 100644 lib/StaticAnalyzer/EntoSA/CXXExprEngine.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/CheckerHelpers.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/AdjustedReturnValueChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/AnalysisConsumer.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/ArrayBoundChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/ArrayBoundCheckerV2.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/AttrNonNullChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/BasicObjCFoundationChecks.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/BasicObjCFoundationChecks.h delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/BuiltinFunctionChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/CMakeLists.txt delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/CStringChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/CallAndMessageChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/CastSizeChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/CastToStructChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/CheckDeadStores.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/CheckObjCDealloc.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/CheckObjCInstMethSignature.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/CheckSizeofPointer.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/ChrootChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/DereferenceChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/DivZeroChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/ExprEngine.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineExperimentalChecks.h delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineInternalChecks.h delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/FixedAddressChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/FrontendActions.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/IdempotentOperationChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/LLVMConventionsChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/MacOSXAPIChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/Makefile delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/MallocChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/NSErrorChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/NoReturnFunctionChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/OSAtomicChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/ObjCAtSyncChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/PointerArithChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/PointerSubChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/PthreadLockChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/ReturnPointerRangeChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/ReturnUndefChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/StackAddrLeakChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/StreamChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/UndefBranchChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/UndefCapturedBlockVarChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/UndefResultChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/UndefinedAssignmentChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/UnixAPIChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/UnreachableCodeChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Checkers/VLASizeChecker.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/CoreEngine.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Environment.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/ExplodedGraph.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/FlatStore.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/GRState.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/HTMLDiagnostics.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Makefile delete mode 100644 lib/StaticAnalyzer/EntoSA/ManagerRegistry.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/MemRegion.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/PathDiagnostic.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/PlistDiagnostics.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/README.txt delete mode 100644 lib/StaticAnalyzer/EntoSA/RangeConstraintManager.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/RegionStore.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/SValBuilder.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/SVals.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.h delete mode 100644 lib/StaticAnalyzer/EntoSA/SimpleSValBuilder.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/Store.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/SymbolManager.cpp delete mode 100644 lib/StaticAnalyzer/EntoSA/TextPathDiagnostics.cpp diff --git a/include/clang/StaticAnalyzer/EntoSA/AnalysisConsumer.h b/include/clang/StaticAnalyzer/EntoSA/AnalysisConsumer.h deleted file mode 100644 index 86f6c727a..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/AnalysisConsumer.h +++ /dev/null @@ -1,39 +0,0 @@ -//===--- 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 deleted file mode 100644 index a5b1980d1..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/BugReporter/BugReporter.h +++ /dev/null @@ -1,486 +0,0 @@ -//===--- 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 deleted file mode 100644 index 6e6ec5491..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/BugReporter/BugType.h +++ /dev/null @@ -1,76 +0,0 @@ -//===--- 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 deleted file mode 100644 index 6d53c097d..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/BugReporter/PathDiagnostic.h +++ /dev/null @@ -1,500 +0,0 @@ -//===--- 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 deleted file mode 100644 index f9cce9ca9..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/Checkers/DereferenceChecker.h +++ /dev/null @@ -1,35 +0,0 @@ -//== 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 deleted file mode 100644 index e0f002b6d..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/Checkers/LocalCheckers.h +++ /dev/null @@ -1,67 +0,0 @@ -//==- 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/StaticAnalyzer/EntoSA/FrontendActions.h b/include/clang/StaticAnalyzer/EntoSA/FrontendActions.h deleted file mode 100644 index e3867a2a2..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/FrontendActions.h +++ /dev/null @@ -1,33 +0,0 @@ -//===-- 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/EntoSA/ManagerRegistry.h b/include/clang/StaticAnalyzer/EntoSA/ManagerRegistry.h deleted file mode 100644 index 1b8498042..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/ManagerRegistry.h +++ /dev/null @@ -1,58 +0,0 @@ -//===-- 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/EntoSA/PathDiagnosticClients.h b/include/clang/StaticAnalyzer/EntoSA/PathDiagnosticClients.h deleted file mode 100644 index 2713e31fc..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathDiagnosticClients.h +++ /dev/null @@ -1,42 +0,0 @@ -//===--- 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/EntoSA/PathSensitive/AnalysisManager.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/AnalysisManager.h deleted file mode 100644 index ba86ed8ba..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/AnalysisManager.h +++ /dev/null @@ -1,208 +0,0 @@ -//== 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/EntoSA/PathSensitive/BasicValueFactory.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/BasicValueFactory.h deleted file mode 100644 index 4cac8b641..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/BasicValueFactory.h +++ /dev/null @@ -1,201 +0,0 @@ -//=== 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/EntoSA/PathSensitive/BlockCounter.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/BlockCounter.h deleted file mode 100644 index 7d0fdfb5f..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/BlockCounter.h +++ /dev/null @@ -1,59 +0,0 @@ -//==- 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/EntoSA/PathSensitive/Checker.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Checker.h deleted file mode 100644 index b2103a242..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Checker.h +++ /dev/null @@ -1,309 +0,0 @@ -//== 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/EntoSA/PathSensitive/CheckerHelpers.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerHelpers.h deleted file mode 100644 index 005e0e586..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerHelpers.h +++ /dev/null @@ -1,44 +0,0 @@ -//== 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/EntoSA/PathSensitive/CheckerVisitor.def b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerVisitor.def deleted file mode 100644 index 3ee807198..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerVisitor.def +++ /dev/null @@ -1,43 +0,0 @@ -//===-- 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/EntoSA/PathSensitive/CheckerVisitor.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerVisitor.h deleted file mode 100644 index 68677f550..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CheckerVisitor.h +++ /dev/null @@ -1,111 +0,0 @@ -//== 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/EntoSA/PathSensitive/ConstraintManager.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ConstraintManager.h deleted file mode 100644 index 81f4558e6..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ConstraintManager.h +++ /dev/null @@ -1,76 +0,0 @@ -//== 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/EntoSA/PathSensitive/CoreEngine.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CoreEngine.h deleted file mode 100644 index 3d63982d7..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/CoreEngine.h +++ /dev/null @@ -1,542 +0,0 @@ -//==- 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/EntoSA/PathSensitive/Environment.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Environment.h deleted file mode 100644 index 8f2ebce67..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Environment.h +++ /dev/null @@ -1,106 +0,0 @@ -//== 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/EntoSA/PathSensitive/ExplodedGraph.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExplodedGraph.h deleted file mode 100644 index 7f8c4c60c..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExplodedGraph.h +++ /dev/null @@ -1,435 +0,0 @@ -//=-- 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/EntoSA/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngine.h deleted file mode 100644 index 407691a20..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngine.h +++ /dev/null @@ -1,544 +0,0 @@ -//===-- 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/EntoSA/PathSensitive/ExprEngineBuilders.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngineBuilders.h deleted file mode 100644 index 965ec3e1c..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/ExprEngineBuilders.h +++ /dev/null @@ -1,81 +0,0 @@ -//===-- 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/EntoSA/PathSensitive/GRState.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/GRState.h deleted file mode 100644 index dcdb6e26c..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/GRState.h +++ /dev/null @@ -1,760 +0,0 @@ -//== 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/EntoSA/PathSensitive/GRStateTrait.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/GRStateTrait.h deleted file mode 100644 index 914625a28..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/GRStateTrait.h +++ /dev/null @@ -1,152 +0,0 @@ -//==- 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/EntoSA/PathSensitive/MemRegion.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/MemRegion.h deleted file mode 100644 index 33779a9fd..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/MemRegion.h +++ /dev/null @@ -1,1077 +0,0 @@ -//== 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/EntoSA/PathSensitive/SValBuilder.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SValBuilder.h deleted file mode 100644 index 0705510cc..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SValBuilder.h +++ /dev/null @@ -1,259 +0,0 @@ -// 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/EntoSA/PathSensitive/SVals.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SVals.h deleted file mode 100644 index d4d846954..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SVals.h +++ /dev/null @@ -1,522 +0,0 @@ -//== 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/EntoSA/PathSensitive/Store.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Store.h deleted file mode 100644 index c48f129fe..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/Store.h +++ /dev/null @@ -1,259 +0,0 @@ -//== 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/EntoSA/PathSensitive/SubEngine.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SubEngine.h deleted file mode 100644 index 8672bd1a0..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SubEngine.h +++ /dev/null @@ -1,113 +0,0 @@ -//== 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/EntoSA/PathSensitive/SummaryManager.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SummaryManager.h deleted file mode 100644 index ed8785159..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SummaryManager.h +++ /dev/null @@ -1,61 +0,0 @@ -//== 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/EntoSA/PathSensitive/SymbolManager.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SymbolManager.h deleted file mode 100644 index ad173bb43..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/SymbolManager.h +++ /dev/null @@ -1,489 +0,0 @@ -//== 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/EntoSA/PathSensitive/TransferFuncs.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/TransferFuncs.h deleted file mode 100644 index 22e3a4d3c..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/TransferFuncs.h +++ /dev/null @@ -1,92 +0,0 @@ -//== 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/EntoSA/PathSensitive/WorkList.h b/include/clang/StaticAnalyzer/EntoSA/PathSensitive/WorkList.h deleted file mode 100644 index be909061c..000000000 --- a/include/clang/StaticAnalyzer/EntoSA/PathSensitive/WorkList.h +++ /dev/null @@ -1,94 +0,0 @@ -//==- 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/StaticAnalyzer/EntoSA/AggExprVisitor.cpp b/lib/StaticAnalyzer/EntoSA/AggExprVisitor.cpp deleted file mode 100644 index 0cc23900e..000000000 --- a/lib/StaticAnalyzer/EntoSA/AggExprVisitor.cpp +++ /dev/null @@ -1,63 +0,0 @@ -//=-- 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 deleted file mode 100644 index cc5e27139..000000000 --- a/lib/StaticAnalyzer/EntoSA/AnalysisManager.cpp +++ /dev/null @@ -1,32 +0,0 @@ -//===-- 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 deleted file mode 100644 index 8521c18c5..000000000 --- a/lib/StaticAnalyzer/EntoSA/AnalyzerStatsChecker.cpp +++ /dev/null @@ -1,123 +0,0 @@ -//==--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 deleted file mode 100644 index 8bdc4fa00..000000000 --- a/lib/StaticAnalyzer/EntoSA/BasicConstraintManager.cpp +++ /dev/null @@ -1,338 +0,0 @@ -//== 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 deleted file mode 100644 index 35bcf945f..000000000 --- a/lib/StaticAnalyzer/EntoSA/BasicStore.cpp +++ /dev/null @@ -1,594 +0,0 @@ -//== 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 deleted file mode 100644 index d8b501bc7..000000000 --- a/lib/StaticAnalyzer/EntoSA/BasicValueFactory.cpp +++ /dev/null @@ -1,290 +0,0 @@ -//=== 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 deleted file mode 100644 index 47f6088a4..000000000 --- a/lib/StaticAnalyzer/EntoSA/BlockCounter.cpp +++ /dev/null @@ -1,86 +0,0 @@ -//==- 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 deleted file mode 100644 index 8edbe1487..000000000 --- a/lib/StaticAnalyzer/EntoSA/BugReporter.cpp +++ /dev/null @@ -1,1892 +0,0 @@ -// 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 deleted file mode 100644 index 91d727592..000000000 --- a/lib/StaticAnalyzer/EntoSA/BugReporterVisitors.cpp +++ /dev/null @@ -1,457 +0,0 @@ -// 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 deleted file mode 100644 index 5fd223c47..000000000 --- a/lib/StaticAnalyzer/EntoSA/CFRefCount.cpp +++ /dev/null @@ -1,3500 +0,0 @@ -// 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 deleted file mode 100644 index cf81e1d5a..000000000 --- a/lib/StaticAnalyzer/EntoSA/CMakeLists.txt +++ /dev/null @@ -1,41 +0,0 @@ -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 deleted file mode 100644 index 47b3a0bd0..000000000 --- a/lib/StaticAnalyzer/EntoSA/CXXExprEngine.cpp +++ /dev/null @@ -1,328 +0,0 @@ -//===- 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 deleted file mode 100644 index 6867ff5b6..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checker.cpp +++ /dev/null @@ -1,36 +0,0 @@ -//== 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 deleted file mode 100644 index be9fee548..000000000 --- a/lib/StaticAnalyzer/EntoSA/CheckerHelpers.cpp +++ /dev/null @@ -1,80 +0,0 @@ -//===---- 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 deleted file mode 100644 index 46e2de5c2..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/AdjustedReturnValueChecker.cpp +++ /dev/null @@ -1,96 +0,0 @@ -//== 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 deleted file mode 100644 index ee4eae2eb..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/AnalysisConsumer.cpp +++ /dev/null @@ -1,610 +0,0 @@ -//===--- 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 deleted file mode 100644 index 13e628bcb..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/ArrayBoundChecker.cpp +++ /dev/null @@ -1,91 +0,0 @@ -//== 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 deleted file mode 100644 index b55fadebf..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/ArrayBoundCheckerV2.cpp +++ /dev/null @@ -1,277 +0,0 @@ -//== 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 deleted file mode 100644 index 646b30bc3..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/AttrNonNullChecker.cpp +++ /dev/null @@ -1,136 +0,0 @@ -//===--- 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 deleted file mode 100644 index b4e295915..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/BasicObjCFoundationChecks.cpp +++ /dev/null @@ -1,521 +0,0 @@ -//== 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 deleted file mode 100644 index f4966e8ae..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/BasicObjCFoundationChecks.h +++ /dev/null @@ -1,36 +0,0 @@ -//== 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 deleted file mode 100644 index 2e6f1b90e..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/BuiltinFunctionChecker.cpp +++ /dev/null @@ -1,83 +0,0 @@ -//=== 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 deleted file mode 100644 index e4b9541c2..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/CMakeLists.txt +++ /dev/null @@ -1,53 +0,0 @@ -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 deleted file mode 100644 index b7513c39c..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/CStringChecker.cpp +++ /dev/null @@ -1,1048 +0,0 @@ -//= 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 deleted file mode 100644 index 2998406da..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/CallAndMessageChecker.cpp +++ /dev/null @@ -1,350 +0,0 @@ -//===--- 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 deleted file mode 100644 index 9329ea39d..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/CastSizeChecker.cpp +++ /dev/null @@ -1,91 +0,0 @@ -//=== 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 deleted file mode 100644 index dd6ac7ea8..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/CastToStructChecker.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//=== 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 deleted file mode 100644 index 1fe40c56e..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/CheckDeadStores.cpp +++ /dev/null @@ -1,290 +0,0 @@ -//==- 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 deleted file mode 100644 index 932a6f594..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/CheckObjCDealloc.cpp +++ /dev/null @@ -1,262 +0,0 @@ -//==- 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 deleted file mode 100644 index 57e388f44..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/CheckObjCInstMethSignature.cpp +++ /dev/null @@ -1,120 +0,0 @@ -//=- 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 deleted file mode 100644 index b30e985f3..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp +++ /dev/null @@ -1,503 +0,0 @@ -//==- 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 deleted file mode 100644 index ed060b27f..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/CheckSizeofPointer.cpp +++ /dev/null @@ -1,72 +0,0 @@ -//==- 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 deleted file mode 100644 index a3f4daa3b..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/ChrootChecker.cpp +++ /dev/null @@ -1,162 +0,0 @@ -//===- 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 deleted file mode 100644 index 7dc8ec84d..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/DereferenceChecker.cpp +++ /dev/null @@ -1,204 +0,0 @@ -//== 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 deleted file mode 100644 index 8332af8f5..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/DivZeroChecker.cpp +++ /dev/null @@ -1,86 +0,0 @@ -//== 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 deleted file mode 100644 index 17bc339f7..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngine.cpp +++ /dev/null @@ -1,3513 +0,0 @@ -//=-- 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 deleted file mode 100644 index 52a3aced2..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp +++ /dev/null @@ -1,46 +0,0 @@ -//=-- 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 deleted file mode 100644 index f8359d1c5..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineExperimentalChecks.h +++ /dev/null @@ -1,37 +0,0 @@ -//=-- 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 deleted file mode 100644 index f67371da0..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/ExprEngineInternalChecks.h +++ /dev/null @@ -1,59 +0,0 @@ -//=-- 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 deleted file mode 100644 index 77e9876d4..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/FixedAddressChecker.cpp +++ /dev/null @@ -1,72 +0,0 @@ -//=== 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 deleted file mode 100644 index b0e8e5d9b..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/FrontendActions.cpp +++ /dev/null @@ -1,22 +0,0 @@ -//===--- 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 deleted file mode 100644 index 435d3d4a4..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/IdempotentOperationChecker.cpp +++ /dev/null @@ -1,834 +0,0 @@ -//==- 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 deleted file mode 100644 index eefad95f2..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/LLVMConventionsChecker.cpp +++ /dev/null @@ -1,313 +0,0 @@ -//=== 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 deleted file mode 100644 index 44887fa76..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/MacOSXAPIChecker.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// 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 deleted file mode 100644 index d4de35c1f..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -##===- 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 deleted file mode 100644 index 42243cb52..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/MallocChecker.cpp +++ /dev/null @@ -1,733 +0,0 @@ -//=== 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 deleted file mode 100644 index 6ef242ba4..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp +++ /dev/null @@ -1,87 +0,0 @@ -//=- 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 deleted file mode 100644 index 54e611889..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/NSErrorChecker.cpp +++ /dev/null @@ -1,238 +0,0 @@ -//=- 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 deleted file mode 100644 index 8cdc04f9f..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/NoReturnFunctionChecker.cpp +++ /dev/null @@ -1,80 +0,0 @@ -//=== 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 deleted file mode 100644 index 993173484..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/OSAtomicChecker.cpp +++ /dev/null @@ -1,203 +0,0 @@ -//=== 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 deleted file mode 100644 index 2d94e8ce9..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/ObjCAtSyncChecker.cpp +++ /dev/null @@ -1,95 +0,0 @@ -//== 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 deleted file mode 100644 index 799da332b..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp +++ /dev/null @@ -1,164 +0,0 @@ -//==- 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 deleted file mode 100644 index 2b03dbcd9..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/PointerArithChecker.cpp +++ /dev/null @@ -1,72 +0,0 @@ -//=== 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 deleted file mode 100644 index 1ba60f754..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/PointerSubChecker.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//=== 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 deleted file mode 100644 index 7a91696c9..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/PthreadLockChecker.cpp +++ /dev/null @@ -1,147 +0,0 @@ -//===--- 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 deleted file mode 100644 index a56c1b658..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/ReturnPointerRangeChecker.cpp +++ /dev/null @@ -1,95 +0,0 @@ -//== 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 deleted file mode 100644 index 50ffd322d..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/ReturnUndefChecker.cpp +++ /dev/null @@ -1,69 +0,0 @@ -//== 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 deleted file mode 100644 index 1ec5c32af..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/StackAddrLeakChecker.cpp +++ /dev/null @@ -1,205 +0,0 @@ -//=== 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 deleted file mode 100644 index a6d1e079c..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/StreamChecker.cpp +++ /dev/null @@ -1,466 +0,0 @@ -//===-- 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 deleted file mode 100644 index c0b3b7afe..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/UndefBranchChecker.cpp +++ /dev/null @@ -1,120 +0,0 @@ -//=== 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 deleted file mode 100644 index 57e698bb7..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/UndefCapturedBlockVarChecker.cpp +++ /dev/null @@ -1,102 +0,0 @@ -// 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 deleted file mode 100644 index 91e489bd2..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/UndefResultChecker.cpp +++ /dev/null @@ -1,87 +0,0 @@ -//=== 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 deleted file mode 100644 index 2cd293b2f..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp +++ /dev/null @@ -1,57 +0,0 @@ -//===--- 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 deleted file mode 100644 index 3146bbda4..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/UndefinedAssignmentChecker.cpp +++ /dev/null @@ -1,94 +0,0 @@ -//===--- 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 deleted file mode 100644 index 9092f93ba..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/UnixAPIChecker.cpp +++ /dev/null @@ -1,277 +0,0 @@ -//= 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 deleted file mode 100644 index 98834485e..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/UnreachableCodeChecker.cpp +++ /dev/null @@ -1,223 +0,0 @@ -//==- 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 deleted file mode 100644 index e24521af5..000000000 --- a/lib/StaticAnalyzer/EntoSA/Checkers/VLASizeChecker.cpp +++ /dev/null @@ -1,138 +0,0 @@ -//=== 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 deleted file mode 100644 index a23c7f9d2..000000000 --- a/lib/StaticAnalyzer/EntoSA/CoreEngine.cpp +++ /dev/null @@ -1,809 +0,0 @@ -//==- 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/StaticAnalyzer/EntoSA/Environment.cpp b/lib/StaticAnalyzer/EntoSA/Environment.cpp deleted file mode 100644 index 1c74352d2..000000000 --- a/lib/StaticAnalyzer/EntoSA/Environment.cpp +++ /dev/null @@ -1,236 +0,0 @@ -//== 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/EntoSA/ExplodedGraph.cpp b/lib/StaticAnalyzer/EntoSA/ExplodedGraph.cpp deleted file mode 100644 index 4c4612fab..000000000 --- a/lib/StaticAnalyzer/EntoSA/ExplodedGraph.cpp +++ /dev/null @@ -1,282 +0,0 @@ -//=-- 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/EntoSA/FlatStore.cpp b/lib/StaticAnalyzer/EntoSA/FlatStore.cpp deleted file mode 100644 index 1558db748..000000000 --- a/lib/StaticAnalyzer/EntoSA/FlatStore.cpp +++ /dev/null @@ -1,203 +0,0 @@ -//=== 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/EntoSA/GRState.cpp b/lib/StaticAnalyzer/EntoSA/GRState.cpp deleted file mode 100644 index 12c896836..000000000 --- a/lib/StaticAnalyzer/EntoSA/GRState.cpp +++ /dev/null @@ -1,551 +0,0 @@ -//= 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/EntoSA/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/EntoSA/HTMLDiagnostics.cpp deleted file mode 100644 index 659c32d58..000000000 --- a/lib/StaticAnalyzer/EntoSA/HTMLDiagnostics.cpp +++ /dev/null @@ -1,578 +0,0 @@ -//===--- 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/EntoSA/Makefile b/lib/StaticAnalyzer/EntoSA/Makefile deleted file mode 100644 index 08ace28e3..000000000 --- a/lib/StaticAnalyzer/EntoSA/Makefile +++ /dev/null @@ -1,19 +0,0 @@ -##===- 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/EntoSA/ManagerRegistry.cpp b/lib/StaticAnalyzer/EntoSA/ManagerRegistry.cpp deleted file mode 100644 index 3f9ed61db..000000000 --- a/lib/StaticAnalyzer/EntoSA/ManagerRegistry.cpp +++ /dev/null @@ -1,21 +0,0 @@ -//===- 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/EntoSA/MemRegion.cpp b/lib/StaticAnalyzer/EntoSA/MemRegion.cpp deleted file mode 100644 index e465224f4..000000000 --- a/lib/StaticAnalyzer/EntoSA/MemRegion.cpp +++ /dev/null @@ -1,987 +0,0 @@ -//== 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/EntoSA/PathDiagnostic.cpp b/lib/StaticAnalyzer/EntoSA/PathDiagnostic.cpp deleted file mode 100644 index f05dec820..000000000 --- a/lib/StaticAnalyzer/EntoSA/PathDiagnostic.cpp +++ /dev/null @@ -1,279 +0,0 @@ -//===--- 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/EntoSA/PlistDiagnostics.cpp b/lib/StaticAnalyzer/EntoSA/PlistDiagnostics.cpp deleted file mode 100644 index ecc0cff5c..000000000 --- a/lib/StaticAnalyzer/EntoSA/PlistDiagnostics.cpp +++ /dev/null @@ -1,472 +0,0 @@ -//===--- 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/EntoSA/README.txt b/lib/StaticAnalyzer/EntoSA/README.txt deleted file mode 100644 index dd16ccc98..000000000 --- a/lib/StaticAnalyzer/EntoSA/README.txt +++ /dev/null @@ -1,117 +0,0 @@ -//===----------------------------------------------------------------------===// -// 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/EntoSA/RangeConstraintManager.cpp b/lib/StaticAnalyzer/EntoSA/RangeConstraintManager.cpp deleted file mode 100644 index c89fa50b1..000000000 --- a/lib/StaticAnalyzer/EntoSA/RangeConstraintManager.cpp +++ /dev/null @@ -1,442 +0,0 @@ -//== 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/EntoSA/RegionStore.cpp b/lib/StaticAnalyzer/EntoSA/RegionStore.cpp deleted file mode 100644 index def1b43f3..000000000 --- a/lib/StaticAnalyzer/EntoSA/RegionStore.cpp +++ /dev/null @@ -1,1875 +0,0 @@ -//== 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/EntoSA/SValBuilder.cpp b/lib/StaticAnalyzer/EntoSA/SValBuilder.cpp deleted file mode 100644 index f87fb7ee1..000000000 --- a/lib/StaticAnalyzer/EntoSA/SValBuilder.cpp +++ /dev/null @@ -1,310 +0,0 @@ -// 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/EntoSA/SVals.cpp b/lib/StaticAnalyzer/EntoSA/SVals.cpp deleted file mode 100644 index dd8508a50..000000000 --- a/lib/StaticAnalyzer/EntoSA/SVals.cpp +++ /dev/null @@ -1,361 +0,0 @@ -//= 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/EntoSA/SimpleConstraintManager.cpp b/lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.cpp deleted file mode 100644 index e54d0ffe0..000000000 --- a/lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.cpp +++ /dev/null @@ -1,303 +0,0 @@ -//== 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/EntoSA/SimpleConstraintManager.h b/lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.h deleted file mode 100644 index 75f67f7f1..000000000 --- a/lib/StaticAnalyzer/EntoSA/SimpleConstraintManager.h +++ /dev/null @@ -1,93 +0,0 @@ -//== 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/EntoSA/SimpleSValBuilder.cpp b/lib/StaticAnalyzer/EntoSA/SimpleSValBuilder.cpp deleted file mode 100644 index f1a9074b9..000000000 --- a/lib/StaticAnalyzer/EntoSA/SimpleSValBuilder.cpp +++ /dev/null @@ -1,884 +0,0 @@ -// 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/EntoSA/Store.cpp b/lib/StaticAnalyzer/EntoSA/Store.cpp deleted file mode 100644 index f1b83389c..000000000 --- a/lib/StaticAnalyzer/EntoSA/Store.cpp +++ /dev/null @@ -1,334 +0,0 @@ -//== 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/EntoSA/SymbolManager.cpp b/lib/StaticAnalyzer/EntoSA/SymbolManager.cpp deleted file mode 100644 index 08677dafc..000000000 --- a/lib/StaticAnalyzer/EntoSA/SymbolManager.cpp +++ /dev/null @@ -1,343 +0,0 @@ -//== 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/EntoSA/TextPathDiagnostics.cpp b/lib/StaticAnalyzer/EntoSA/TextPathDiagnostics.cpp deleted file mode 100644 index 9ca378f2d..000000000 --- a/lib/StaticAnalyzer/EntoSA/TextPathDiagnostics.cpp +++ /dev/null @@ -1,70 +0,0 @@ -//===--- 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); - } -} -- 2.11.4.GIT