From 3a8f40ed5e86a97e07d255976a95d2f3ad792b6d Mon Sep 17 00:00:00 2001 From: Ted Kremenek Date: Thu, 23 Dec 2010 07:22:02 +0000 Subject: Rename headers: 'clang/GR' 'clang/EntoSA' and update Makefile. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@122493 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CMakeLists.txt | 4 +- lib/EntoSA/AggExprVisitor.cpp | 63 + lib/EntoSA/AnalysisManager.cpp | 32 + lib/EntoSA/AnalyzerStatsChecker.cpp | 123 + lib/EntoSA/BasicConstraintManager.cpp | 338 ++ lib/EntoSA/BasicStore.cpp | 594 ++++ lib/EntoSA/BasicValueFactory.cpp | 290 ++ lib/EntoSA/BlockCounter.cpp | 86 + lib/EntoSA/BugReporter.cpp | 1892 +++++++++++ lib/EntoSA/BugReporterVisitors.cpp | 457 +++ lib/EntoSA/CFRefCount.cpp | 3500 +++++++++++++++++++ lib/EntoSA/CMakeLists.txt | 41 + lib/EntoSA/CXXExprEngine.cpp | 328 ++ lib/EntoSA/Checker.cpp | 36 + lib/EntoSA/CheckerHelpers.cpp | 80 + lib/EntoSA/Checkers/AdjustedReturnValueChecker.cpp | 96 + lib/EntoSA/Checkers/AnalysisConsumer.cpp | 610 ++++ lib/EntoSA/Checkers/ArrayBoundChecker.cpp | 91 + lib/EntoSA/Checkers/ArrayBoundCheckerV2.cpp | 277 ++ lib/EntoSA/Checkers/AttrNonNullChecker.cpp | 136 + lib/EntoSA/Checkers/BasicObjCFoundationChecks.cpp | 521 +++ lib/EntoSA/Checkers/BasicObjCFoundationChecks.h | 36 + lib/EntoSA/Checkers/BuiltinFunctionChecker.cpp | 83 + lib/EntoSA/Checkers/CMakeLists.txt | 53 + lib/EntoSA/Checkers/CStringChecker.cpp | 1048 ++++++ lib/EntoSA/Checkers/CallAndMessageChecker.cpp | 350 ++ lib/EntoSA/Checkers/CastSizeChecker.cpp | 91 + lib/EntoSA/Checkers/CastToStructChecker.cpp | 79 + lib/EntoSA/Checkers/CheckDeadStores.cpp | 290 ++ lib/EntoSA/Checkers/CheckObjCDealloc.cpp | 262 ++ lib/EntoSA/Checkers/CheckObjCInstMethSignature.cpp | 120 + lib/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp | 503 +++ lib/EntoSA/Checkers/CheckSizeofPointer.cpp | 72 + lib/EntoSA/Checkers/ChrootChecker.cpp | 162 + lib/EntoSA/Checkers/DereferenceChecker.cpp | 204 ++ lib/EntoSA/Checkers/DivZeroChecker.cpp | 86 + lib/EntoSA/Checkers/ExprEngine.cpp | 3513 ++++++++++++++++++++ .../Checkers/ExprEngineExperimentalChecks.cpp | 46 + lib/EntoSA/Checkers/ExprEngineExperimentalChecks.h | 37 + lib/EntoSA/Checkers/ExprEngineInternalChecks.h | 59 + lib/EntoSA/Checkers/FixedAddressChecker.cpp | 72 + lib/EntoSA/Checkers/FrontendActions.cpp | 22 + lib/EntoSA/Checkers/IdempotentOperationChecker.cpp | 834 +++++ lib/EntoSA/Checkers/LLVMConventionsChecker.cpp | 313 ++ lib/EntoSA/Checkers/MacOSXAPIChecker.cpp | 142 + lib/EntoSA/Checkers/Makefile | 17 + lib/EntoSA/Checkers/MallocChecker.cpp | 733 ++++ lib/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp | 87 + lib/EntoSA/Checkers/NSErrorChecker.cpp | 238 ++ lib/EntoSA/Checkers/NoReturnFunctionChecker.cpp | 80 + lib/EntoSA/Checkers/OSAtomicChecker.cpp | 203 ++ lib/EntoSA/Checkers/ObjCAtSyncChecker.cpp | 95 + lib/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp | 164 + lib/EntoSA/Checkers/PointerArithChecker.cpp | 72 + lib/EntoSA/Checkers/PointerSubChecker.cpp | 79 + lib/EntoSA/Checkers/PthreadLockChecker.cpp | 147 + lib/EntoSA/Checkers/ReturnPointerRangeChecker.cpp | 95 + lib/EntoSA/Checkers/ReturnUndefChecker.cpp | 69 + lib/EntoSA/Checkers/StackAddrLeakChecker.cpp | 205 ++ lib/EntoSA/Checkers/StreamChecker.cpp | 466 +++ lib/EntoSA/Checkers/UndefBranchChecker.cpp | 120 + .../Checkers/UndefCapturedBlockVarChecker.cpp | 102 + lib/EntoSA/Checkers/UndefResultChecker.cpp | 87 + .../Checkers/UndefinedArraySubscriptChecker.cpp | 57 + lib/EntoSA/Checkers/UndefinedAssignmentChecker.cpp | 94 + lib/EntoSA/Checkers/UnixAPIChecker.cpp | 277 ++ lib/EntoSA/Checkers/UnreachableCodeChecker.cpp | 223 ++ lib/EntoSA/Checkers/VLASizeChecker.cpp | 138 + lib/EntoSA/CoreEngine.cpp | 809 +++++ lib/EntoSA/Environment.cpp | 236 ++ lib/EntoSA/ExplodedGraph.cpp | 282 ++ lib/EntoSA/FlatStore.cpp | 203 ++ lib/EntoSA/GRState.cpp | 551 +++ lib/EntoSA/HTMLDiagnostics.cpp | 578 ++++ lib/EntoSA/Makefile | 19 + lib/EntoSA/ManagerRegistry.cpp | 21 + lib/EntoSA/MemRegion.cpp | 987 ++++++ lib/EntoSA/PathDiagnostic.cpp | 279 ++ lib/EntoSA/PlistDiagnostics.cpp | 472 +++ lib/EntoSA/README.txt | 117 + lib/EntoSA/RangeConstraintManager.cpp | 442 +++ lib/EntoSA/RegionStore.cpp | 1872 +++++++++++ lib/EntoSA/SValBuilder.cpp | 310 ++ lib/EntoSA/SVals.cpp | 361 ++ lib/EntoSA/SimpleConstraintManager.cpp | 303 ++ lib/EntoSA/SimpleConstraintManager.h | 93 + lib/EntoSA/SimpleSValBuilder.cpp | 884 +++++ lib/EntoSA/Store.cpp | 334 ++ lib/EntoSA/SymbolManager.cpp | 343 ++ lib/EntoSA/TextPathDiagnostics.cpp | 70 + lib/FrontendTool/CMakeLists.txt | 2 +- lib/FrontendTool/ExecuteCompilerInvocation.cpp | 2 +- lib/GR/AggExprVisitor.cpp | 63 - lib/GR/AnalysisManager.cpp | 32 - lib/GR/AnalyzerStatsChecker.cpp | 123 - lib/GR/BasicConstraintManager.cpp | 338 -- lib/GR/BasicStore.cpp | 594 ---- lib/GR/BasicValueFactory.cpp | 290 -- lib/GR/BlockCounter.cpp | 86 - lib/GR/BugReporter.cpp | 1892 ----------- lib/GR/BugReporterVisitors.cpp | 457 --- lib/GR/CFRefCount.cpp | 3500 ------------------- lib/GR/CMakeLists.txt | 41 - lib/GR/CXXExprEngine.cpp | 328 -- lib/GR/Checker.cpp | 36 - lib/GR/CheckerHelpers.cpp | 80 - lib/GR/Checkers/AdjustedReturnValueChecker.cpp | 96 - lib/GR/Checkers/AnalysisConsumer.cpp | 610 ---- lib/GR/Checkers/ArrayBoundChecker.cpp | 91 - lib/GR/Checkers/ArrayBoundCheckerV2.cpp | 277 -- lib/GR/Checkers/AttrNonNullChecker.cpp | 136 - lib/GR/Checkers/BasicObjCFoundationChecks.cpp | 521 --- lib/GR/Checkers/BasicObjCFoundationChecks.h | 36 - lib/GR/Checkers/BuiltinFunctionChecker.cpp | 83 - lib/GR/Checkers/CMakeLists.txt | 53 - lib/GR/Checkers/CStringChecker.cpp | 1048 ------ lib/GR/Checkers/CallAndMessageChecker.cpp | 350 -- lib/GR/Checkers/CastSizeChecker.cpp | 91 - lib/GR/Checkers/CastToStructChecker.cpp | 79 - lib/GR/Checkers/CheckDeadStores.cpp | 290 -- lib/GR/Checkers/CheckObjCDealloc.cpp | 262 -- lib/GR/Checkers/CheckObjCInstMethSignature.cpp | 120 - lib/GR/Checkers/CheckSecuritySyntaxOnly.cpp | 503 --- lib/GR/Checkers/CheckSizeofPointer.cpp | 72 - lib/GR/Checkers/ChrootChecker.cpp | 162 - lib/GR/Checkers/DereferenceChecker.cpp | 204 -- lib/GR/Checkers/DivZeroChecker.cpp | 86 - lib/GR/Checkers/ExprEngine.cpp | 3513 -------------------- lib/GR/Checkers/ExprEngineExperimentalChecks.cpp | 46 - lib/GR/Checkers/ExprEngineExperimentalChecks.h | 37 - lib/GR/Checkers/ExprEngineInternalChecks.h | 59 - lib/GR/Checkers/FixedAddressChecker.cpp | 72 - lib/GR/Checkers/FrontendActions.cpp | 22 - lib/GR/Checkers/IdempotentOperationChecker.cpp | 834 ----- lib/GR/Checkers/LLVMConventionsChecker.cpp | 313 -- lib/GR/Checkers/MacOSXAPIChecker.cpp | 142 - lib/GR/Checkers/Makefile | 17 - lib/GR/Checkers/MallocChecker.cpp | 733 ---- lib/GR/Checkers/NSAutoreleasePoolChecker.cpp | 87 - lib/GR/Checkers/NSErrorChecker.cpp | 238 -- lib/GR/Checkers/NoReturnFunctionChecker.cpp | 80 - lib/GR/Checkers/OSAtomicChecker.cpp | 203 -- lib/GR/Checkers/ObjCAtSyncChecker.cpp | 95 - lib/GR/Checkers/ObjCUnusedIVarsChecker.cpp | 164 - lib/GR/Checkers/PointerArithChecker.cpp | 72 - lib/GR/Checkers/PointerSubChecker.cpp | 79 - lib/GR/Checkers/PthreadLockChecker.cpp | 147 - lib/GR/Checkers/ReturnPointerRangeChecker.cpp | 95 - lib/GR/Checkers/ReturnUndefChecker.cpp | 69 - lib/GR/Checkers/StackAddrLeakChecker.cpp | 205 -- lib/GR/Checkers/StreamChecker.cpp | 466 --- lib/GR/Checkers/UndefBranchChecker.cpp | 120 - lib/GR/Checkers/UndefCapturedBlockVarChecker.cpp | 102 - lib/GR/Checkers/UndefResultChecker.cpp | 87 - lib/GR/Checkers/UndefinedArraySubscriptChecker.cpp | 57 - lib/GR/Checkers/UndefinedAssignmentChecker.cpp | 94 - lib/GR/Checkers/UnixAPIChecker.cpp | 277 -- lib/GR/Checkers/UnreachableCodeChecker.cpp | 223 -- lib/GR/Checkers/VLASizeChecker.cpp | 138 - lib/GR/CoreEngine.cpp | 809 ----- lib/GR/Environment.cpp | 236 -- lib/GR/ExplodedGraph.cpp | 282 -- lib/GR/FlatStore.cpp | 203 -- lib/GR/GRState.cpp | 551 --- lib/GR/HTMLDiagnostics.cpp | 578 ---- lib/GR/Makefile | 19 - lib/GR/ManagerRegistry.cpp | 21 - lib/GR/MemRegion.cpp | 987 ------ lib/GR/PathDiagnostic.cpp | 279 -- lib/GR/PlistDiagnostics.cpp | 472 --- lib/GR/README.txt | 117 - lib/GR/RangeConstraintManager.cpp | 442 --- lib/GR/RegionStore.cpp | 1872 ----------- lib/GR/SValBuilder.cpp | 310 -- lib/GR/SVals.cpp | 361 -- lib/GR/SimpleConstraintManager.cpp | 303 -- lib/GR/SimpleConstraintManager.h | 93 - lib/GR/SimpleSValBuilder.cpp | 884 ----- lib/GR/Store.cpp | 334 -- lib/GR/SymbolManager.cpp | 343 -- lib/GR/TextPathDiagnostics.cpp | 70 - lib/Makefile | 2 +- 182 files changed, 31487 insertions(+), 31487 deletions(-) create mode 100644 lib/EntoSA/AggExprVisitor.cpp create mode 100644 lib/EntoSA/AnalysisManager.cpp create mode 100644 lib/EntoSA/AnalyzerStatsChecker.cpp create mode 100644 lib/EntoSA/BasicConstraintManager.cpp create mode 100644 lib/EntoSA/BasicStore.cpp create mode 100644 lib/EntoSA/BasicValueFactory.cpp create mode 100644 lib/EntoSA/BlockCounter.cpp create mode 100644 lib/EntoSA/BugReporter.cpp create mode 100644 lib/EntoSA/BugReporterVisitors.cpp create mode 100644 lib/EntoSA/CFRefCount.cpp create mode 100644 lib/EntoSA/CMakeLists.txt create mode 100644 lib/EntoSA/CXXExprEngine.cpp create mode 100644 lib/EntoSA/Checker.cpp create mode 100644 lib/EntoSA/CheckerHelpers.cpp create mode 100644 lib/EntoSA/Checkers/AdjustedReturnValueChecker.cpp create mode 100644 lib/EntoSA/Checkers/AnalysisConsumer.cpp create mode 100644 lib/EntoSA/Checkers/ArrayBoundChecker.cpp create mode 100644 lib/EntoSA/Checkers/ArrayBoundCheckerV2.cpp create mode 100644 lib/EntoSA/Checkers/AttrNonNullChecker.cpp create mode 100644 lib/EntoSA/Checkers/BasicObjCFoundationChecks.cpp create mode 100644 lib/EntoSA/Checkers/BasicObjCFoundationChecks.h create mode 100644 lib/EntoSA/Checkers/BuiltinFunctionChecker.cpp create mode 100644 lib/EntoSA/Checkers/CMakeLists.txt create mode 100644 lib/EntoSA/Checkers/CStringChecker.cpp create mode 100644 lib/EntoSA/Checkers/CallAndMessageChecker.cpp create mode 100644 lib/EntoSA/Checkers/CastSizeChecker.cpp create mode 100644 lib/EntoSA/Checkers/CastToStructChecker.cpp create mode 100644 lib/EntoSA/Checkers/CheckDeadStores.cpp create mode 100644 lib/EntoSA/Checkers/CheckObjCDealloc.cpp create mode 100644 lib/EntoSA/Checkers/CheckObjCInstMethSignature.cpp create mode 100644 lib/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp create mode 100644 lib/EntoSA/Checkers/CheckSizeofPointer.cpp create mode 100644 lib/EntoSA/Checkers/ChrootChecker.cpp create mode 100644 lib/EntoSA/Checkers/DereferenceChecker.cpp create mode 100644 lib/EntoSA/Checkers/DivZeroChecker.cpp create mode 100644 lib/EntoSA/Checkers/ExprEngine.cpp create mode 100644 lib/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp create mode 100644 lib/EntoSA/Checkers/ExprEngineExperimentalChecks.h create mode 100644 lib/EntoSA/Checkers/ExprEngineInternalChecks.h create mode 100644 lib/EntoSA/Checkers/FixedAddressChecker.cpp create mode 100644 lib/EntoSA/Checkers/FrontendActions.cpp create mode 100644 lib/EntoSA/Checkers/IdempotentOperationChecker.cpp create mode 100644 lib/EntoSA/Checkers/LLVMConventionsChecker.cpp create mode 100644 lib/EntoSA/Checkers/MacOSXAPIChecker.cpp create mode 100644 lib/EntoSA/Checkers/Makefile create mode 100644 lib/EntoSA/Checkers/MallocChecker.cpp create mode 100644 lib/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp create mode 100644 lib/EntoSA/Checkers/NSErrorChecker.cpp create mode 100644 lib/EntoSA/Checkers/NoReturnFunctionChecker.cpp create mode 100644 lib/EntoSA/Checkers/OSAtomicChecker.cpp create mode 100644 lib/EntoSA/Checkers/ObjCAtSyncChecker.cpp create mode 100644 lib/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp create mode 100644 lib/EntoSA/Checkers/PointerArithChecker.cpp create mode 100644 lib/EntoSA/Checkers/PointerSubChecker.cpp create mode 100644 lib/EntoSA/Checkers/PthreadLockChecker.cpp create mode 100644 lib/EntoSA/Checkers/ReturnPointerRangeChecker.cpp create mode 100644 lib/EntoSA/Checkers/ReturnUndefChecker.cpp create mode 100644 lib/EntoSA/Checkers/StackAddrLeakChecker.cpp create mode 100644 lib/EntoSA/Checkers/StreamChecker.cpp create mode 100644 lib/EntoSA/Checkers/UndefBranchChecker.cpp create mode 100644 lib/EntoSA/Checkers/UndefCapturedBlockVarChecker.cpp create mode 100644 lib/EntoSA/Checkers/UndefResultChecker.cpp create mode 100644 lib/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp create mode 100644 lib/EntoSA/Checkers/UndefinedAssignmentChecker.cpp create mode 100644 lib/EntoSA/Checkers/UnixAPIChecker.cpp create mode 100644 lib/EntoSA/Checkers/UnreachableCodeChecker.cpp create mode 100644 lib/EntoSA/Checkers/VLASizeChecker.cpp create mode 100644 lib/EntoSA/CoreEngine.cpp create mode 100644 lib/EntoSA/Environment.cpp create mode 100644 lib/EntoSA/ExplodedGraph.cpp create mode 100644 lib/EntoSA/FlatStore.cpp create mode 100644 lib/EntoSA/GRState.cpp create mode 100644 lib/EntoSA/HTMLDiagnostics.cpp create mode 100644 lib/EntoSA/Makefile create mode 100644 lib/EntoSA/ManagerRegistry.cpp create mode 100644 lib/EntoSA/MemRegion.cpp create mode 100644 lib/EntoSA/PathDiagnostic.cpp create mode 100644 lib/EntoSA/PlistDiagnostics.cpp create mode 100644 lib/EntoSA/README.txt create mode 100644 lib/EntoSA/RangeConstraintManager.cpp create mode 100644 lib/EntoSA/RegionStore.cpp create mode 100644 lib/EntoSA/SValBuilder.cpp create mode 100644 lib/EntoSA/SVals.cpp create mode 100644 lib/EntoSA/SimpleConstraintManager.cpp create mode 100644 lib/EntoSA/SimpleConstraintManager.h create mode 100644 lib/EntoSA/SimpleSValBuilder.cpp create mode 100644 lib/EntoSA/Store.cpp create mode 100644 lib/EntoSA/SymbolManager.cpp create mode 100644 lib/EntoSA/TextPathDiagnostics.cpp delete mode 100644 lib/GR/AggExprVisitor.cpp delete mode 100644 lib/GR/AnalysisManager.cpp delete mode 100644 lib/GR/AnalyzerStatsChecker.cpp delete mode 100644 lib/GR/BasicConstraintManager.cpp delete mode 100644 lib/GR/BasicStore.cpp delete mode 100644 lib/GR/BasicValueFactory.cpp delete mode 100644 lib/GR/BlockCounter.cpp delete mode 100644 lib/GR/BugReporter.cpp delete mode 100644 lib/GR/BugReporterVisitors.cpp delete mode 100644 lib/GR/CFRefCount.cpp delete mode 100644 lib/GR/CMakeLists.txt delete mode 100644 lib/GR/CXXExprEngine.cpp delete mode 100644 lib/GR/Checker.cpp delete mode 100644 lib/GR/CheckerHelpers.cpp delete mode 100644 lib/GR/Checkers/AdjustedReturnValueChecker.cpp delete mode 100644 lib/GR/Checkers/AnalysisConsumer.cpp delete mode 100644 lib/GR/Checkers/ArrayBoundChecker.cpp delete mode 100644 lib/GR/Checkers/ArrayBoundCheckerV2.cpp delete mode 100644 lib/GR/Checkers/AttrNonNullChecker.cpp delete mode 100644 lib/GR/Checkers/BasicObjCFoundationChecks.cpp delete mode 100644 lib/GR/Checkers/BasicObjCFoundationChecks.h delete mode 100644 lib/GR/Checkers/BuiltinFunctionChecker.cpp delete mode 100644 lib/GR/Checkers/CMakeLists.txt delete mode 100644 lib/GR/Checkers/CStringChecker.cpp delete mode 100644 lib/GR/Checkers/CallAndMessageChecker.cpp delete mode 100644 lib/GR/Checkers/CastSizeChecker.cpp delete mode 100644 lib/GR/Checkers/CastToStructChecker.cpp delete mode 100644 lib/GR/Checkers/CheckDeadStores.cpp delete mode 100644 lib/GR/Checkers/CheckObjCDealloc.cpp delete mode 100644 lib/GR/Checkers/CheckObjCInstMethSignature.cpp delete mode 100644 lib/GR/Checkers/CheckSecuritySyntaxOnly.cpp delete mode 100644 lib/GR/Checkers/CheckSizeofPointer.cpp delete mode 100644 lib/GR/Checkers/ChrootChecker.cpp delete mode 100644 lib/GR/Checkers/DereferenceChecker.cpp delete mode 100644 lib/GR/Checkers/DivZeroChecker.cpp delete mode 100644 lib/GR/Checkers/ExprEngine.cpp delete mode 100644 lib/GR/Checkers/ExprEngineExperimentalChecks.cpp delete mode 100644 lib/GR/Checkers/ExprEngineExperimentalChecks.h delete mode 100644 lib/GR/Checkers/ExprEngineInternalChecks.h delete mode 100644 lib/GR/Checkers/FixedAddressChecker.cpp delete mode 100644 lib/GR/Checkers/FrontendActions.cpp delete mode 100644 lib/GR/Checkers/IdempotentOperationChecker.cpp delete mode 100644 lib/GR/Checkers/LLVMConventionsChecker.cpp delete mode 100644 lib/GR/Checkers/MacOSXAPIChecker.cpp delete mode 100644 lib/GR/Checkers/Makefile delete mode 100644 lib/GR/Checkers/MallocChecker.cpp delete mode 100644 lib/GR/Checkers/NSAutoreleasePoolChecker.cpp delete mode 100644 lib/GR/Checkers/NSErrorChecker.cpp delete mode 100644 lib/GR/Checkers/NoReturnFunctionChecker.cpp delete mode 100644 lib/GR/Checkers/OSAtomicChecker.cpp delete mode 100644 lib/GR/Checkers/ObjCAtSyncChecker.cpp delete mode 100644 lib/GR/Checkers/ObjCUnusedIVarsChecker.cpp delete mode 100644 lib/GR/Checkers/PointerArithChecker.cpp delete mode 100644 lib/GR/Checkers/PointerSubChecker.cpp delete mode 100644 lib/GR/Checkers/PthreadLockChecker.cpp delete mode 100644 lib/GR/Checkers/ReturnPointerRangeChecker.cpp delete mode 100644 lib/GR/Checkers/ReturnUndefChecker.cpp delete mode 100644 lib/GR/Checkers/StackAddrLeakChecker.cpp delete mode 100644 lib/GR/Checkers/StreamChecker.cpp delete mode 100644 lib/GR/Checkers/UndefBranchChecker.cpp delete mode 100644 lib/GR/Checkers/UndefCapturedBlockVarChecker.cpp delete mode 100644 lib/GR/Checkers/UndefResultChecker.cpp delete mode 100644 lib/GR/Checkers/UndefinedArraySubscriptChecker.cpp delete mode 100644 lib/GR/Checkers/UndefinedAssignmentChecker.cpp delete mode 100644 lib/GR/Checkers/UnixAPIChecker.cpp delete mode 100644 lib/GR/Checkers/UnreachableCodeChecker.cpp delete mode 100644 lib/GR/Checkers/VLASizeChecker.cpp delete mode 100644 lib/GR/CoreEngine.cpp delete mode 100644 lib/GR/Environment.cpp delete mode 100644 lib/GR/ExplodedGraph.cpp delete mode 100644 lib/GR/FlatStore.cpp delete mode 100644 lib/GR/GRState.cpp delete mode 100644 lib/GR/HTMLDiagnostics.cpp delete mode 100644 lib/GR/Makefile delete mode 100644 lib/GR/ManagerRegistry.cpp delete mode 100644 lib/GR/MemRegion.cpp delete mode 100644 lib/GR/PathDiagnostic.cpp delete mode 100644 lib/GR/PlistDiagnostics.cpp delete mode 100644 lib/GR/README.txt delete mode 100644 lib/GR/RangeConstraintManager.cpp delete mode 100644 lib/GR/RegionStore.cpp delete mode 100644 lib/GR/SValBuilder.cpp delete mode 100644 lib/GR/SVals.cpp delete mode 100644 lib/GR/SimpleConstraintManager.cpp delete mode 100644 lib/GR/SimpleConstraintManager.h delete mode 100644 lib/GR/SimpleSValBuilder.cpp delete mode 100644 lib/GR/Store.cpp delete mode 100644 lib/GR/SymbolManager.cpp delete mode 100644 lib/GR/TextPathDiagnostics.cpp (limited to 'lib') diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 2066563c33..6197dc72d0 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -12,5 +12,5 @@ add_subdirectory(Serialization) add_subdirectory(Frontend) add_subdirectory(FrontendTool) add_subdirectory(Index) -add_subdirectory(GR) -add_subdirectory(GR/Checkers) +add_subdirectory(EntoSA) +add_subdirectory(EntoSA/Checkers) diff --git a/lib/EntoSA/AggExprVisitor.cpp b/lib/EntoSA/AggExprVisitor.cpp new file mode 100644 index 0000000000..c356186622 --- /dev/null +++ b/lib/EntoSA/AggExprVisitor.cpp @@ -0,0 +1,63 @@ +//=-- AggExprVisitor.cpp - evaluating expressions of C++ class type -*- C++ -*-= +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines AggExprVisitor class, which contains lots of boiler +// plate code for evaluating expressions of C++ class type. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/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/EntoSA/AnalysisManager.cpp b/lib/EntoSA/AnalysisManager.cpp new file mode 100644 index 0000000000..fa64f472f7 --- /dev/null +++ b/lib/EntoSA/AnalysisManager.cpp @@ -0,0 +1,32 @@ +//===-- AnalysisManager.cpp -------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/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/EntoSA/AnalyzerStatsChecker.cpp b/lib/EntoSA/AnalyzerStatsChecker.cpp new file mode 100644 index 0000000000..fb0e74b4b5 --- /dev/null +++ b/lib/EntoSA/AnalyzerStatsChecker.cpp @@ -0,0 +1,123 @@ +//==--AnalyzerStatsChecker.cpp - Analyzer visitation statistics --*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This file reports various statistics about analyzer visitation. +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/PathSensitive/ExplodedGraph.h" +#include "clang/EntoSA/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/EntoSA/BasicConstraintManager.cpp b/lib/EntoSA/BasicConstraintManager.cpp new file mode 100644 index 0000000000..92837d2ff4 --- /dev/null +++ b/lib/EntoSA/BasicConstraintManager.cpp @@ -0,0 +1,338 @@ +//== BasicConstraintManager.cpp - Manage basic constraints.------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicConstraintManager, a class that tracks simple +// equality and inequality constraints on symbolic values of GRState. +// +//===----------------------------------------------------------------------===// + +#include "SimpleConstraintManager.h" +#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/EntoSA/PathSensitive/GRStateTrait.h" +#include "clang/EntoSA/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/EntoSA/BasicStore.cpp b/lib/EntoSA/BasicStore.cpp new file mode 100644 index 0000000000..8c4a732aa3 --- /dev/null +++ b/lib/EntoSA/BasicStore.cpp @@ -0,0 +1,594 @@ +//== BasicStore.cpp - Basic map from Locations to Values --------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defined the BasicStore and BasicStoreManager classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclCXX.h" +#include "clang/AST/ExprObjC.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/AnalysisContext.h" +#include "clang/EntoSA/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/EntoSA/BasicValueFactory.cpp b/lib/EntoSA/BasicValueFactory.cpp new file mode 100644 index 0000000000..0d1cb4a913 --- /dev/null +++ b/lib/EntoSA/BasicValueFactory.cpp @@ -0,0 +1,290 @@ +//=== BasicValueFactory.cpp - Basic values for Path Sens analysis --*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicValueFactory, a class that manages the lifetime +// of APSInt objects and symbolic constraints used by ExprEngine +// and related classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/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/EntoSA/BlockCounter.cpp b/lib/EntoSA/BlockCounter.cpp new file mode 100644 index 0000000000..b4e6eeee3f --- /dev/null +++ b/lib/EntoSA/BlockCounter.cpp @@ -0,0 +1,86 @@ +//==- BlockCounter.h - ADT for counting block visits -------------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines BlockCounter, an abstract data type used to count +// the number of times a given block has been visited along a path +// analyzed by CoreEngine. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/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/EntoSA/BugReporter.cpp b/lib/EntoSA/BugReporter.cpp new file mode 100644 index 0000000000..ff35024499 --- /dev/null +++ b/lib/EntoSA/BugReporter.cpp @@ -0,0 +1,1892 @@ +// BugReporter.cpp - Generate PathDiagnostics for Bugs ------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines BugReporter, a utility class for generating +// PathDiagnostics. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/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/EntoSA/BugReporterVisitors.cpp b/lib/EntoSA/BugReporterVisitors.cpp new file mode 100644 index 0000000000..d264505b23 --- /dev/null +++ b/lib/EntoSA/BugReporterVisitors.cpp @@ -0,0 +1,457 @@ +// BugReporterVisitors.cpp - Helpers for reporting bugs -----------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a set of BugReporter "visitors" which can be used to +// enhance the diagnostics reported for a bug. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/Expr.h" +#include "clang/AST/ExprObjC.h" +#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/EntoSA/PathSensitive/ExplodedGraph.h" +#include "clang/EntoSA/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/EntoSA/CFRefCount.cpp b/lib/EntoSA/CFRefCount.cpp new file mode 100644 index 0000000000..9c9637eb82 --- /dev/null +++ b/lib/EntoSA/CFRefCount.cpp @@ -0,0 +1,3500 @@ +// CFRefCount.cpp - Transfer functions for tracking simple values -*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the methods for CFRefCount, which implements +// a reference count checker for Core Foundation (Mac OS X). +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclObjC.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/Basic/LangOptions.h" +#include "clang/Basic/SourceManager.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/Analysis/DomainSpecific/CocoaConventions.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/PathSensitive/ExprEngineBuilders.h" +#include "clang/EntoSA/PathSensitive/GRStateTrait.h" +#include "clang/EntoSA/PathSensitive/TransferFuncs.h" +#include "clang/EntoSA/PathSensitive/SymbolManager.h" +#include "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/EntoSA/CMakeLists.txt b/lib/EntoSA/CMakeLists.txt new file mode 100644 index 0000000000..09a6c2f364 --- /dev/null +++ b/lib/EntoSA/CMakeLists.txt @@ -0,0 +1,41 @@ +set(LLVM_NO_RTTI 1) + +set(LLVM_USED_LIBS clangBasic clangLex clangAST clangFrontend clangRewrite) + +add_clang_library(clangEntoCore + 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(clangEntoCore ClangAttrClasses ClangAttrList ClangDeclNodes + ClangStmtNodes) diff --git a/lib/EntoSA/CXXExprEngine.cpp b/lib/EntoSA/CXXExprEngine.cpp new file mode 100644 index 0000000000..3ecd439d0a --- /dev/null +++ b/lib/EntoSA/CXXExprEngine.cpp @@ -0,0 +1,328 @@ +//===- GRCXXExprEngine.cpp - C++ expr evaluation engine ---------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the C++ expression evaluation engine. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathSensitive/AnalysisManager.h" +#include "clang/EntoSA/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/EntoSA/Checker.cpp b/lib/EntoSA/Checker.cpp new file mode 100644 index 0000000000..943be6b931 --- /dev/null +++ b/lib/EntoSA/Checker.cpp @@ -0,0 +1,36 @@ +//== Checker.h - Abstract interface for checkers -----------------*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines Checker and CheckerVisitor, classes used for creating +// domain-specific checks. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/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/EntoSA/CheckerHelpers.cpp b/lib/EntoSA/CheckerHelpers.cpp new file mode 100644 index 0000000000..7c7f707afd --- /dev/null +++ b/lib/EntoSA/CheckerHelpers.cpp @@ -0,0 +1,80 @@ +//===---- CheckerHelpers.cpp - Helper functions for checkers ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines several static functions for use in checkers. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/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/EntoSA/Checkers/AdjustedReturnValueChecker.cpp b/lib/EntoSA/Checkers/AdjustedReturnValueChecker.cpp new file mode 100644 index 0000000000..914b1e4fbb --- /dev/null +++ b/lib/EntoSA/Checkers/AdjustedReturnValueChecker.cpp @@ -0,0 +1,96 @@ +//== AdjustedReturnValueChecker.cpp -----------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines AdjustedReturnValueChecker, a simple check to see if the +// return value of a function call is different than the one the caller thinks +// it is. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/EntoSA/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/EntoSA/Checkers/AnalysisConsumer.cpp b/lib/EntoSA/Checkers/AnalysisConsumer.cpp new file mode 100644 index 0000000000..043a24a2ef --- /dev/null +++ b/lib/EntoSA/Checkers/AnalysisConsumer.cpp @@ -0,0 +1,610 @@ +//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// "Meta" ASTConsumer for running different source analyses. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/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/EntoSA/Checkers/LocalCheckers.h" +#include "clang/EntoSA/ManagerRegistry.h" +#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/EntoSA/PathSensitive/AnalysisManager.h" +#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/EntoSA/PathSensitive/TransferFuncs.h" +#include "clang/EntoSA/PathDiagnosticClients.h" + +// 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/EntoSA/Checkers/ArrayBoundChecker.cpp b/lib/EntoSA/Checkers/ArrayBoundChecker.cpp new file mode 100644 index 0000000000..c72a4668ab --- /dev/null +++ b/lib/EntoSA/Checkers/ArrayBoundChecker.cpp @@ -0,0 +1,91 @@ +//== ArrayBoundChecker.cpp ------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ArrayBoundChecker, which is a path-sensitive check +// which looks for an out-of-bound array element access. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/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/EntoSA/Checkers/ArrayBoundCheckerV2.cpp b/lib/EntoSA/Checkers/ArrayBoundCheckerV2.cpp new file mode 100644 index 0000000000..33683bac32 --- /dev/null +++ b/lib/EntoSA/Checkers/ArrayBoundCheckerV2.cpp @@ -0,0 +1,277 @@ +//== ArrayBoundCheckerV2.cpp ------------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ArrayBoundCheckerV2, which is a path-sensitive check +// which looks for an out-of-bound array element access. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/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/EntoSA/Checkers/AttrNonNullChecker.cpp b/lib/EntoSA/Checkers/AttrNonNullChecker.cpp new file mode 100644 index 0000000000..f887e24d16 --- /dev/null +++ b/lib/EntoSA/Checkers/AttrNonNullChecker.cpp @@ -0,0 +1,136 @@ +//===--- AttrNonNullChecker.h - Undefined arguments checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines AttrNonNullChecker, a builtin check in ExprEngine that +// performs checks for arguments declared to have nonnull attribute. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/BasicObjCFoundationChecks.cpp b/lib/EntoSA/Checkers/BasicObjCFoundationChecks.cpp new file mode 100644 index 0000000000..8e83160a20 --- /dev/null +++ b/lib/EntoSA/Checkers/BasicObjCFoundationChecks.cpp @@ -0,0 +1,521 @@ +//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*-- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicObjCFoundationChecks, a class that encapsulates +// a set of simple checks to run on Objective-C code using Apple's Foundation +// classes. +// +//===----------------------------------------------------------------------===// + +#include "BasicObjCFoundationChecks.h" + +#include "clang/EntoSA/PathSensitive/ExplodedGraph.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/MemRegion.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/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/EntoSA/Checkers/BasicObjCFoundationChecks.h b/lib/EntoSA/Checkers/BasicObjCFoundationChecks.h new file mode 100644 index 0000000000..f4966e8ae8 --- /dev/null +++ b/lib/EntoSA/Checkers/BasicObjCFoundationChecks.h @@ -0,0 +1,36 @@ +//== BasicObjCFoundationChecks.h - Simple Apple-Foundation checks -*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines BasicObjCFoundationChecks, a class that encapsulates +// a set of simple checks to run on Objective-C code using Apple's Foundation +// classes. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_BASICOBJCFOUNDATIONCHECKS +#define LLVM_CLANG_GR_BASICOBJCFOUNDATIONCHECKS + +namespace clang { + +class ASTContext; +class Decl; + +namespace ento { + +class BugReporter; +class ExprEngine; + +void RegisterNSErrorChecks(BugReporter& BR, ExprEngine &Eng, const Decl &D); +void RegisterNSAutoreleasePoolChecks(ExprEngine &Eng); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/lib/EntoSA/Checkers/BuiltinFunctionChecker.cpp b/lib/EntoSA/Checkers/BuiltinFunctionChecker.cpp new file mode 100644 index 0000000000..1db8aac93f --- /dev/null +++ b/lib/EntoSA/Checkers/BuiltinFunctionChecker.cpp @@ -0,0 +1,83 @@ +//=== BuiltinFunctionChecker.cpp --------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker evaluates clang builtin functions. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/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/EntoSA/Checkers/CMakeLists.txt b/lib/EntoSA/Checkers/CMakeLists.txt new file mode 100644 index 0000000000..224a515d9a --- /dev/null +++ b/lib/EntoSA/Checkers/CMakeLists.txt @@ -0,0 +1,53 @@ +add_clang_library(clangEntoCheckers + 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(clangEntoCore ClangAttrClasses ClangAttrList ClangDeclNodes + ClangStmtNodes) diff --git a/lib/EntoSA/Checkers/CStringChecker.cpp b/lib/EntoSA/Checkers/CStringChecker.cpp new file mode 100644 index 0000000000..199d6b2f5e --- /dev/null +++ b/lib/EntoSA/Checkers/CStringChecker.cpp @@ -0,0 +1,1048 @@ +//= CStringChecker.h - Checks calls to C string functions ----------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines CStringChecker, which is an assortment of checks on calls +// to functions in . +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineExperimentalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/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/EntoSA/Checkers/CallAndMessageChecker.cpp b/lib/EntoSA/Checkers/CallAndMessageChecker.cpp new file mode 100644 index 0000000000..2fa37e3fa5 --- /dev/null +++ b/lib/EntoSA/Checkers/CallAndMessageChecker.cpp @@ -0,0 +1,350 @@ +//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines CallAndMessageChecker, a builtin checker that checks for various +// errors of call and objc message expressions. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/AST/ParentMap.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/CastSizeChecker.cpp b/lib/EntoSA/Checkers/CastSizeChecker.cpp new file mode 100644 index 0000000000..41a72ef32d --- /dev/null +++ b/lib/EntoSA/Checkers/CastSizeChecker.cpp @@ -0,0 +1,91 @@ +//=== CastSizeChecker.cpp ---------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// CastSizeChecker checks when casting a malloc'ed symbolic region to type T, +// whether the size of the symbolic region is a multiple of the size of T. +// +//===----------------------------------------------------------------------===// +#include "clang/AST/CharUnits.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/CastToStructChecker.cpp b/lib/EntoSA/Checkers/CastToStructChecker.cpp new file mode 100644 index 0000000000..83393b9268 --- /dev/null +++ b/lib/EntoSA/Checkers/CastToStructChecker.cpp @@ -0,0 +1,79 @@ +//=== CastToStructChecker.cpp - Fixed address usage checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines CastToStructChecker, a builtin checker that checks for +// cast from non-struct pointer to struct pointer. +// This check corresponds to CWE-588. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/CheckDeadStores.cpp b/lib/EntoSA/Checkers/CheckDeadStores.cpp new file mode 100644 index 0000000000..047c83abc6 --- /dev/null +++ b/lib/EntoSA/Checkers/CheckDeadStores.cpp @@ -0,0 +1,290 @@ +//==- DeadStores.cpp - Check for stores to dead variables --------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a DeadStores, a flow-sensitive checker that looks for +// stores to variables that are no longer live. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/Analysis/Visitors/CFGRecStmtVisitor.h" +#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/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/EntoSA/Checkers/CheckObjCDealloc.cpp b/lib/EntoSA/Checkers/CheckObjCDealloc.cpp new file mode 100644 index 0000000000..391e30f61e --- /dev/null +++ b/lib/EntoSA/Checkers/CheckObjCDealloc.cpp @@ -0,0 +1,262 @@ +//==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a CheckObjCDealloc, a checker that +// analyzes an Objective-C class's implementation to determine if it +// correctly implements -dealloc. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/EntoSA/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/EntoSA/Checkers/CheckObjCInstMethSignature.cpp b/lib/EntoSA/Checkers/CheckObjCInstMethSignature.cpp new file mode 100644 index 0000000000..f908e69cc7 --- /dev/null +++ b/lib/EntoSA/Checkers/CheckObjCInstMethSignature.cpp @@ -0,0 +1,120 @@ +//=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a CheckObjCInstMethSignature, a flow-insenstive check +// that determines if an Objective-C class interface incorrectly redefines +// the method signature in a subclass. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/EntoSA/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/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp new file mode 100644 index 0000000000..98da69231c --- /dev/null +++ b/lib/EntoSA/Checkers/CheckSecuritySyntaxOnly.cpp @@ -0,0 +1,503 @@ +//==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a set of flow-insensitive security checks. +// +//===----------------------------------------------------------------------===// + +#include "clang/Basic/TargetInfo.h" +#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/EntoSA/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/EntoSA/Checkers/CheckSizeofPointer.cpp b/lib/EntoSA/Checkers/CheckSizeofPointer.cpp new file mode 100644 index 0000000000..f7ab9514e9 --- /dev/null +++ b/lib/EntoSA/Checkers/CheckSizeofPointer.cpp @@ -0,0 +1,72 @@ +//==- CheckSizeofPointer.cpp - Check for sizeof on pointers ------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a check for unintended use of sizeof() on pointer +// expressions. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/EntoSA/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/EntoSA/Checkers/ChrootChecker.cpp b/lib/EntoSA/Checkers/ChrootChecker.cpp new file mode 100644 index 0000000000..e951c29d1a --- /dev/null +++ b/lib/EntoSA/Checkers/ChrootChecker.cpp @@ -0,0 +1,162 @@ +//===- Chrootchecker.cpp -------- Basic security checks ----------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines chroot checker, which checks improper use of chroot. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineExperimentalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/EntoSA/PathSensitive/GRStateTrait.h" +#include "clang/EntoSA/PathSensitive/SymbolManager.h" +#include "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/EntoSA/Checkers/DereferenceChecker.cpp b/lib/EntoSA/Checkers/DereferenceChecker.cpp new file mode 100644 index 0000000000..35fae40935 --- /dev/null +++ b/lib/EntoSA/Checkers/DereferenceChecker.cpp @@ -0,0 +1,204 @@ +//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines NullDerefChecker, a builtin check in ExprEngine that performs +// checks for null pointers at loads and stores. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/Checkers/DereferenceChecker.h" +#include "clang/EntoSA/PathSensitive/Checker.h" +#include "clang/EntoSA/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/EntoSA/Checkers/DivZeroChecker.cpp b/lib/EntoSA/Checkers/DivZeroChecker.cpp new file mode 100644 index 0000000000..24c743a8d0 --- /dev/null +++ b/lib/EntoSA/Checkers/DivZeroChecker.cpp @@ -0,0 +1,86 @@ +//== DivZeroChecker.cpp - Division by zero checker --------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines DivZeroChecker, a builtin check in ExprEngine that performs +// checks for division by zeros. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/ExprEngine.cpp b/lib/EntoSA/Checkers/ExprEngine.cpp new file mode 100644 index 0000000000..afd6318acc --- /dev/null +++ b/lib/EntoSA/Checkers/ExprEngine.cpp @@ -0,0 +1,3513 @@ +//=-- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ---*- C++ -*-= +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a meta-engine for path-sensitive dataflow analysis that +// is built on GREngine, but provides the boilerplate to execute transfer +// functions and build the ExplodedGraph at the expression level. +// +//===----------------------------------------------------------------------===// + +// FIXME: Restructure checker registration. +#include "ExprEngineInternalChecks.h" + +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/AnalysisManager.h" +#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/EntoSA/PathSensitive/ExprEngineBuilders.h" +#include "clang/EntoSA/PathSensitive/Checker.h" +#include "clang/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/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp b/lib/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp new file mode 100644 index 0000000000..6ab5a4d5da --- /dev/null +++ b/lib/EntoSA/Checkers/ExprEngineExperimentalChecks.cpp @@ -0,0 +1,46 @@ +//=-- ExprEngineExperimentalChecks.h ------------------------------*- C++ -*-= +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines functions to instantiate and register experimental +// checks in ExprEngine. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "ExprEngineExperimentalChecks.h" +#include "clang/EntoSA/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/EntoSA/Checkers/ExprEngineExperimentalChecks.h b/lib/EntoSA/Checkers/ExprEngineExperimentalChecks.h new file mode 100644 index 0000000000..f8359d1c5c --- /dev/null +++ b/lib/EntoSA/Checkers/ExprEngineExperimentalChecks.h @@ -0,0 +1,37 @@ +//=-- ExprEngineExperimentalChecks.h ------------------------------*- C++ -*-= +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines functions to instantiate and register experimental +// checks in ExprEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_ExprEngine_EXPERIMENTAL_CHECKS +#define LLVM_CLANG_GR_ExprEngine_EXPERIMENTAL_CHECKS + +namespace clang { + +namespace ento { + +class ExprEngine; + +void RegisterAnalyzerStatsChecker(ExprEngine &Eng); +void RegisterChrootChecker(ExprEngine &Eng); +void RegisterCStringChecker(ExprEngine &Eng); +void RegisterIdempotentOperationChecker(ExprEngine &Eng); +void RegisterMallocChecker(ExprEngine &Eng); +void RegisterPthreadLockChecker(ExprEngine &Eng); +void RegisterStreamChecker(ExprEngine &Eng); +void RegisterUnreachableCodeChecker(ExprEngine &Eng); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/lib/EntoSA/Checkers/ExprEngineInternalChecks.h b/lib/EntoSA/Checkers/ExprEngineInternalChecks.h new file mode 100644 index 0000000000..f67371da0f --- /dev/null +++ b/lib/EntoSA/Checkers/ExprEngineInternalChecks.h @@ -0,0 +1,59 @@ +//=-- ExprEngineInternalChecks.h- Builtin ExprEngine Checks -----*- C++ -*-= +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines functions to instantiate and register the "built-in" +// checks in ExprEngine. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_ExprEngine_INTERNAL_CHECKS +#define LLVM_CLANG_GR_ExprEngine_INTERNAL_CHECKS + +namespace clang { + +namespace ento { + +class ExprEngine; + +// Foundational checks that handle basic semantics. +void RegisterAdjustedReturnValueChecker(ExprEngine &Eng); +void RegisterArrayBoundChecker(ExprEngine &Eng); +void RegisterArrayBoundCheckerV2(ExprEngine &Eng); +void RegisterAttrNonNullChecker(ExprEngine &Eng); +void RegisterBuiltinFunctionChecker(ExprEngine &Eng); +void RegisterCallAndMessageChecker(ExprEngine &Eng); +void RegisterCastToStructChecker(ExprEngine &Eng); +void RegisterCastSizeChecker(ExprEngine &Eng); +void RegisterDereferenceChecker(ExprEngine &Eng); +void RegisterDivZeroChecker(ExprEngine &Eng); +void RegisterFixedAddressChecker(ExprEngine &Eng); +void RegisterNoReturnFunctionChecker(ExprEngine &Eng); +void RegisterObjCAtSyncChecker(ExprEngine &Eng); +void RegisterPointerArithChecker(ExprEngine &Eng); +void RegisterPointerSubChecker(ExprEngine &Eng); +void RegisterReturnPointerRangeChecker(ExprEngine &Eng); +void RegisterReturnUndefChecker(ExprEngine &Eng); +void RegisterStackAddrLeakChecker(ExprEngine &Eng); +void RegisterUndefBranchChecker(ExprEngine &Eng); +void RegisterUndefCapturedBlockVarChecker(ExprEngine &Eng); +void RegisterUndefResultChecker(ExprEngine &Eng); +void RegisterUndefinedArraySubscriptChecker(ExprEngine &Eng); +void RegisterUndefinedAssignmentChecker(ExprEngine &Eng); +void RegisterVLASizeChecker(ExprEngine &Eng); + +// API checks. +void RegisterMacOSXAPIChecker(ExprEngine &Eng); +void RegisterOSAtomicChecker(ExprEngine &Eng); +void RegisterUnixAPIChecker(ExprEngine &Eng); + +} // end GR namespace + +} // end clang namespace + +#endif diff --git a/lib/EntoSA/Checkers/FixedAddressChecker.cpp b/lib/EntoSA/Checkers/FixedAddressChecker.cpp new file mode 100644 index 0000000000..ea736435d6 --- /dev/null +++ b/lib/EntoSA/Checkers/FixedAddressChecker.cpp @@ -0,0 +1,72 @@ +//=== FixedAddressChecker.cpp - Fixed address usage checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines FixedAddressChecker, a builtin checker that checks for +// assignment of a fixed address to a pointer. +// This check corresponds to CWE-587. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/FrontendActions.cpp b/lib/EntoSA/Checkers/FrontendActions.cpp new file mode 100644 index 0000000000..b901afc5e1 --- /dev/null +++ b/lib/EntoSA/Checkers/FrontendActions.cpp @@ -0,0 +1,22 @@ +//===--- FrontendActions.cpp ----------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/FrontendActions.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/EntoSA/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/EntoSA/Checkers/IdempotentOperationChecker.cpp b/lib/EntoSA/Checkers/IdempotentOperationChecker.cpp new file mode 100644 index 0000000000..04f652cdcb --- /dev/null +++ b/lib/EntoSA/Checkers/IdempotentOperationChecker.cpp @@ -0,0 +1,834 @@ +//==- IdempotentOperationChecker.cpp - Idempotent Operations ----*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a set of path-sensitive checks for idempotent and/or +// tautological operations. Each potential operation is checked along all paths +// to see if every path results in a pointless operation. +// +-------------------------------------------+ +// |Table of idempotent/tautological operations| +// +-------------------------------------------+ +//+--------------------------------------------------------------------------+ +//|Operator | x op x | x op 1 | 1 op x | x op 0 | 0 op x | x op ~0 | ~0 op x | +//+--------------------------------------------------------------------------+ +// +, += | | | | x | x | | +// -, -= | | | | x | -x | | +// *, *= | | x | x | 0 | 0 | | +// /, /= | 1 | x | | N/A | 0 | | +// &, &= | x | | | 0 | 0 | x | x +// |, |= | x | | | x | x | ~0 | ~0 +// ^, ^= | 0 | | | x | x | | +// <<, <<= | | | | x | 0 | | +// >>, >>= | | | | x | 0 | | +// || | 1 | 1 | 1 | x | x | 1 | 1 +// && | 1 | x | x | 0 | 0 | x | x +// = | x | | | | | | +// == | 1 | | | | | | +// >= | 1 | | | | | | +// <= | 1 | | | | | | +// > | 0 | | | | | | +// < | 0 | | | | | | +// != | 0 | | | | | | +//===----------------------------------------------------------------------===// +// +// Things TODO: +// - Improved error messages +// - Handle mixed assumptions (which assumptions can belong together?) +// - Finer grained false positive control (levels) +// - Handling ~0 values + +#include "ExprEngineExperimentalChecks.h" +#include "clang/Analysis/CFGStmtMap.h" +#include "clang/Analysis/Analyses/PseudoConstantAnalysis.h" +#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerHelpers.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/PathSensitive/CoreEngine.h" +#include "clang/EntoSA/PathSensitive/SVals.h" +#include "clang/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/EntoSA/Checkers/LLVMConventionsChecker.cpp b/lib/EntoSA/Checkers/LLVMConventionsChecker.cpp new file mode 100644 index 0000000000..e88997f79c --- /dev/null +++ b/lib/EntoSA/Checkers/LLVMConventionsChecker.cpp @@ -0,0 +1,313 @@ +//=== LLVMConventionsChecker.cpp - Check LLVM codebase conventions ---*- C++ -*- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines LLVMConventionsChecker, a bunch of small little checks +// for checking specific coding conventions in the LLVM/Clang codebase. +// +//===----------------------------------------------------------------------===// + +#include "clang/AST/DeclTemplate.h" +#include "clang/AST/StmtVisitor.h" +#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/EntoSA/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/EntoSA/Checkers/MacOSXAPIChecker.cpp b/lib/EntoSA/Checkers/MacOSXAPIChecker.cpp new file mode 100644 index 0000000000..c2f5f4b83c --- /dev/null +++ b/lib/EntoSA/Checkers/MacOSXAPIChecker.cpp @@ -0,0 +1,142 @@ +// MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines MacOSXAPIChecker, which is an assortment of checks on calls +// to various, widely used Mac OS X functions. +// +// FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated +// to here, using the new Checker interface. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/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/EntoSA/Checkers/Makefile b/lib/EntoSA/Checkers/Makefile new file mode 100644 index 0000000000..9ba4e282d2 --- /dev/null +++ b/lib/EntoSA/Checkers/Makefile @@ -0,0 +1,17 @@ +##===- clang/lib/Checker/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements analyses built on top of source-level CFGs. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../../.. +LIBRARYNAME := clangEntoCheckers + +include $(CLANG_LEVEL)/Makefile diff --git a/lib/EntoSA/Checkers/MallocChecker.cpp b/lib/EntoSA/Checkers/MallocChecker.cpp new file mode 100644 index 0000000000..2b0ae82ccd --- /dev/null +++ b/lib/EntoSA/Checkers/MallocChecker.cpp @@ -0,0 +1,733 @@ +//=== MallocChecker.cpp - A malloc/free checker -------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines malloc/free checker, which checks for potential memory +// leaks, double free, and use-after-free problems. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineExperimentalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/EntoSA/PathSensitive/GRStateTrait.h" +#include "clang/EntoSA/PathSensitive/SymbolManager.h" +#include "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/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp b/lib/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp new file mode 100644 index 0000000000..acad46c352 --- /dev/null +++ b/lib/EntoSA/Checkers/NSAutoreleasePoolChecker.cpp @@ -0,0 +1,87 @@ +//=- NSAutoreleasePoolChecker.cpp --------------------------------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a NSAutoreleasePoolChecker, a small checker that warns +// about subpar uses of NSAutoreleasePool. Note that while the check itself +// (in it's current form) could be written as a flow-insensitive check, in +// can be potentially enhanced in the future with flow-sensitive information. +// It is also a good example of the CheckerVisitor interface. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/EntoSA/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/EntoSA/Checkers/NSErrorChecker.cpp b/lib/EntoSA/Checkers/NSErrorChecker.cpp new file mode 100644 index 0000000000..d5c395f0a1 --- /dev/null +++ b/lib/EntoSA/Checkers/NSErrorChecker.cpp @@ -0,0 +1,238 @@ +//=- NSErrorCheckerer.cpp - Coding conventions for uses of NSError -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a CheckNSError, a flow-insenstive check +// that determines if an Objective-C class interface correctly returns +// a non-void return type. +// +// File under feature request PR 2600. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/EntoSA/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/EntoSA/Checkers/NoReturnFunctionChecker.cpp b/lib/EntoSA/Checkers/NoReturnFunctionChecker.cpp new file mode 100644 index 0000000000..4b96ddba39 --- /dev/null +++ b/lib/EntoSA/Checkers/NoReturnFunctionChecker.cpp @@ -0,0 +1,80 @@ +//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines NoReturnFunctionChecker, which evaluates functions that do not +// return to the caller. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/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/EntoSA/Checkers/OSAtomicChecker.cpp b/lib/EntoSA/Checkers/OSAtomicChecker.cpp new file mode 100644 index 0000000000..cc67c3df7a --- /dev/null +++ b/lib/EntoSA/Checkers/OSAtomicChecker.cpp @@ -0,0 +1,203 @@ +//=== OSAtomicChecker.cpp - OSAtomic functions evaluator --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker evaluates OSAtomic functions. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/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/EntoSA/Checkers/ObjCAtSyncChecker.cpp b/lib/EntoSA/Checkers/ObjCAtSyncChecker.cpp new file mode 100644 index 0000000000..3e901b6007 --- /dev/null +++ b/lib/EntoSA/Checkers/ObjCAtSyncChecker.cpp @@ -0,0 +1,95 @@ +//== ObjCAtSyncChecker.cpp - nil mutex checker for @synchronized -*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines ObjCAtSyncChecker, a builtin check that checks for null pointers +// used as mutexes for @synchronized. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/Checkers/DereferenceChecker.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/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/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp new file mode 100644 index 0000000000..a2e2475f28 --- /dev/null +++ b/lib/EntoSA/Checkers/ObjCUnusedIVarsChecker.cpp @@ -0,0 +1,164 @@ +//==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a CheckObjCUnusedIvars, a checker that +// analyzes an Objective-C class's interface/implementation to determine if it +// has any ivars that are never accessed. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/Checkers/LocalCheckers.h" +#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/EntoSA/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/EntoSA/Checkers/PointerArithChecker.cpp b/lib/EntoSA/Checkers/PointerArithChecker.cpp new file mode 100644 index 0000000000..30009a2560 --- /dev/null +++ b/lib/EntoSA/Checkers/PointerArithChecker.cpp @@ -0,0 +1,72 @@ +//=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines PointerArithChecker, a builtin checker that checks for +// pointer arithmetic on locations other than array elements. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/PointerSubChecker.cpp b/lib/EntoSA/Checkers/PointerSubChecker.cpp new file mode 100644 index 0000000000..923d0a86d8 --- /dev/null +++ b/lib/EntoSA/Checkers/PointerSubChecker.cpp @@ -0,0 +1,79 @@ +//=== PointerSubChecker.cpp - Pointer subtraction checker ------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This files defines PointerSubChecker, a builtin checker that checks for +// pointer subtractions on two pointers pointing to different memory chunks. +// This check corresponds to CWE-469. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/PthreadLockChecker.cpp b/lib/EntoSA/Checkers/PthreadLockChecker.cpp new file mode 100644 index 0000000000..56c9adb509 --- /dev/null +++ b/lib/EntoSA/Checkers/PthreadLockChecker.cpp @@ -0,0 +1,147 @@ +//===--- PthreadLockChecker.h - Undefined arguments checker ----*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines PthreadLockChecker, a simple lock -> unlock checker. Eventually +// this shouldn't be registered with ExprEngineInternalChecks. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "clang/EntoSA/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/EntoSA/Checkers/ReturnPointerRangeChecker.cpp b/lib/EntoSA/Checkers/ReturnPointerRangeChecker.cpp new file mode 100644 index 0000000000..8afe9847aa --- /dev/null +++ b/lib/EntoSA/Checkers/ReturnPointerRangeChecker.cpp @@ -0,0 +1,95 @@ +//== ReturnPointerRangeChecker.cpp ------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ReturnPointerRangeChecker, which is a path-sensitive check +// which looks for an out-of-bound pointer being returned to callers. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/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/EntoSA/Checkers/ReturnUndefChecker.cpp b/lib/EntoSA/Checkers/ReturnUndefChecker.cpp new file mode 100644 index 0000000000..8e46519b21 --- /dev/null +++ b/lib/EntoSA/Checkers/ReturnUndefChecker.cpp @@ -0,0 +1,69 @@ +//== ReturnUndefChecker.cpp -------------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines ReturnUndefChecker, which is a path-sensitive +// check which looks for undefined or garbage values being returned to the +// caller. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/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/EntoSA/Checkers/StackAddrLeakChecker.cpp b/lib/EntoSA/Checkers/StackAddrLeakChecker.cpp new file mode 100644 index 0000000000..c9aebadca1 --- /dev/null +++ b/lib/EntoSA/Checkers/StackAddrLeakChecker.cpp @@ -0,0 +1,205 @@ +//=== StackAddrLeakChecker.cpp ------------------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines stack address leak checker, which checks if an invalid +// stack address is stored into a global or heap location. See CERT DCL30-C. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/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/EntoSA/Checkers/StreamChecker.cpp b/lib/EntoSA/Checkers/StreamChecker.cpp new file mode 100644 index 0000000000..cb7899fbb2 --- /dev/null +++ b/lib/EntoSA/Checkers/StreamChecker.cpp @@ -0,0 +1,466 @@ +//===-- StreamChecker.cpp -----------------------------------------*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines checkers that model and check stream handling functions. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineExperimentalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/EntoSA/PathSensitive/GRStateTrait.h" +#include "clang/EntoSA/PathSensitive/SymbolManager.h" +#include "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/EntoSA/Checkers/UndefBranchChecker.cpp b/lib/EntoSA/Checkers/UndefBranchChecker.cpp new file mode 100644 index 0000000000..30d0728530 --- /dev/null +++ b/lib/EntoSA/Checkers/UndefBranchChecker.cpp @@ -0,0 +1,120 @@ +//=== UndefBranchChecker.cpp -----------------------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines UndefBranchChecker, which checks for undefined branch +// condition. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/EntoSA/Checkers/UndefCapturedBlockVarChecker.cpp new file mode 100644 index 0000000000..dcdcf1f741 --- /dev/null +++ b/lib/EntoSA/Checkers/UndefCapturedBlockVarChecker.cpp @@ -0,0 +1,102 @@ +// UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This checker detects blocks that capture uninitialized values. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/EntoSA/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/EntoSA/Checkers/UndefResultChecker.cpp b/lib/EntoSA/Checkers/UndefResultChecker.cpp new file mode 100644 index 0000000000..c0427402b1 --- /dev/null +++ b/lib/EntoSA/Checkers/UndefResultChecker.cpp @@ -0,0 +1,87 @@ +//=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefResultChecker, a builtin check in ExprEngine that +// performs checks for undefined results of non-assignment binary operators. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/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/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp new file mode 100644 index 0000000000..aea78c048b --- /dev/null +++ b/lib/EntoSA/Checkers/UndefinedArraySubscriptChecker.cpp @@ -0,0 +1,57 @@ +//===--- UndefinedArraySubscriptChecker.h ----------------------*- C++ -*--===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedArraySubscriptChecker, a builtin check in ExprEngine +// that performs checks for undefined array subscripts. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/UndefinedAssignmentChecker.cpp b/lib/EntoSA/Checkers/UndefinedAssignmentChecker.cpp new file mode 100644 index 0000000000..5060cdff96 --- /dev/null +++ b/lib/EntoSA/Checkers/UndefinedAssignmentChecker.cpp @@ -0,0 +1,94 @@ +//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UndefinedAssginmentChecker, a builtin check in ExprEngine that +// checks for assigning undefined values. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/UnixAPIChecker.cpp b/lib/EntoSA/Checkers/UnixAPIChecker.cpp new file mode 100644 index 0000000000..c45659c011 --- /dev/null +++ b/lib/EntoSA/Checkers/UnixAPIChecker.cpp @@ -0,0 +1,277 @@ +//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines UnixAPIChecker, which is an assortment of checks on calls +// to various, widely used UNIX/Posix functions. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/Basic/TargetInfo.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/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/EntoSA/Checkers/UnreachableCodeChecker.cpp b/lib/EntoSA/Checkers/UnreachableCodeChecker.cpp new file mode 100644 index 0000000000..e3ea72e133 --- /dev/null +++ b/lib/EntoSA/Checkers/UnreachableCodeChecker.cpp @@ -0,0 +1,223 @@ +//==- UnreachableCodeChecker.cpp - Generalized dead code checker -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// This file implements a generalized unreachable code checker using a +// path-sensitive analysis. We mark any path visited, and then walk the CFG as a +// post-analysis to determine what was never visited. +// +// A similar flow-sensitive only check exists in Analysis/ReachableCode.cpp +//===----------------------------------------------------------------------===// + +#include "clang/AST/ParentMap.h" +#include "clang/Basic/Builtins.h" +#include "clang/Basic/SourceManager.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/PathSensitive/ExplodedGraph.h" +#include "clang/EntoSA/PathSensitive/SVals.h" +#include "clang/EntoSA/PathSensitive/CheckerHelpers.h" +#include "clang/EntoSA/BugReporter/BugReporter.h" +#include "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/EntoSA/Checkers/VLASizeChecker.cpp b/lib/EntoSA/Checkers/VLASizeChecker.cpp new file mode 100644 index 0000000000..0d17238ee7 --- /dev/null +++ b/lib/EntoSA/Checkers/VLASizeChecker.cpp @@ -0,0 +1,138 @@ +//=== VLASizeChecker.cpp - Undefined dereference checker --------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This defines VLASizeChecker, a builtin check in ExprEngine that +// performs checks for declaration of VLA of undefined or zero size. +// In addition, VLASizeChecker is responsible for defining the extent +// of the MemRegion that represents a VLA. +// +//===----------------------------------------------------------------------===// + +#include "ExprEngineInternalChecks.h" +#include "clang/AST/CharUnits.h" +#include "clang/EntoSA/BugReporter/BugType.h" +#include "clang/EntoSA/PathSensitive/CheckerVisitor.h" +#include "clang/EntoSA/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/EntoSA/CoreEngine.cpp b/lib/EntoSA/CoreEngine.cpp new file mode 100644 index 0000000000..478e77b4af --- /dev/null +++ b/lib/EntoSA/CoreEngine.cpp @@ -0,0 +1,809 @@ +//==- CoreEngine.cpp - Path-Sensitive Dataflow Engine ------------*- C++ -*-// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines a generic engine for intraprocedural, path-sensitive, +// dataflow analysis via graph reachability engine. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathSensitive/AnalysisManager.h" +#include "clang/EntoSA/PathSensitive/CoreEngine.h" +#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/Index/TranslationUnit.h" +#include "clang/AST/Expr.h" +#include "llvm/Support/Casting.h" +#include "llvm/ADT/DenseMap.h" +#include +#include + +using llvm::cast; +using llvm::isa; +using namespace clang; +using namespace ento; + +// This should be removed in the future. +namespace clang { +namespace ento { +TransferFuncs* MakeCFRefCountTF(ASTContext& Ctx, bool GCEnabled, + const LangOptions& lopts); +} +} + +//===----------------------------------------------------------------------===// +// Worklist classes for exploration of reachable states. +//===----------------------------------------------------------------------===// + +WorkList::Visitor::~Visitor() {} + +namespace { +class DFS : public WorkList { + llvm::SmallVector Stack; +public: + virtual bool hasWork() const { + return !Stack.empty(); + } + + virtual void Enqueue(const WorkListUnit& U) { + Stack.push_back(U); + } + + virtual WorkListUnit Dequeue() { + assert (!Stack.empty()); + const WorkListUnit& U = Stack.back(); + Stack.pop_back(); // This technically "invalidates" U, but we are fine. + return U; + } + + virtual bool VisitItemsInWorkList(Visitor &V) { + for (llvm::SmallVectorImpl::iterator + I = Stack.begin(), E = Stack.end(); I != E; ++I) { + if (V.Visit(*I)) + return true; + } + return false; + } +}; + +class BFS : public WorkList { + std::deque Queue; +public: + virtual bool hasWork() const { + return !Queue.empty(); + } + + virtual void Enqueue(const WorkListUnit& U) { + Queue.push_front(U); + } + + virtual WorkListUnit Dequeue() { + WorkListUnit U = Queue.front(); + Queue.pop_front(); + return U; + } + + virtual bool VisitItemsInWorkList(Visitor &V) { + for (std::deque::iterator + I = Queue.begin(), E = Queue.end(); I != E; ++I) { + if (V.Visit(*I)) + return true; + } + return false; + } +}; + +} // end anonymous namespace + +// Place the dstor for WorkList here because it contains virtual member +// functions, and we the code for the dstor generated in one compilation unit. +WorkList::~WorkList() {} + +WorkList *WorkList::MakeDFS() { return new DFS(); } +WorkList *WorkList::MakeBFS() { return new BFS(); } + +namespace { + class BFSBlockDFSContents : public WorkList { + std::deque Queue; + llvm::SmallVector Stack; + public: + virtual bool hasWork() const { + return !Queue.empty() || !Stack.empty(); + } + + virtual void Enqueue(const WorkListUnit& U) { + if (isa(U.getNode()->getLocation())) + Queue.push_front(U); + else + Stack.push_back(U); + } + + virtual WorkListUnit Dequeue() { + // Process all basic blocks to completion. + if (!Stack.empty()) { + const WorkListUnit& U = Stack.back(); + Stack.pop_back(); // This technically "invalidates" U, but we are fine. + return U; + } + + assert(!Queue.empty()); + // Don't use const reference. The subsequent pop_back() might make it + // unsafe. + WorkListUnit U = Queue.front(); + Queue.pop_front(); + return U; + } + virtual bool VisitItemsInWorkList(Visitor &V) { + for (llvm::SmallVectorImpl::iterator + I = Stack.begin(), E = Stack.end(); I != E; ++I) { + if (V.Visit(*I)) + return true; + } + for (std::deque::iterator + I = Queue.begin(), E = Queue.end(); I != E; ++I) { + if (V.Visit(*I)) + return true; + } + return false; + } + + }; +} // end anonymous namespace + +WorkList* WorkList::MakeBFSBlockDFSContents() { + return new BFSBlockDFSContents(); +} + +//===----------------------------------------------------------------------===// +// Core analysis engine. +//===----------------------------------------------------------------------===// + +/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps. +bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps, + const GRState *InitState) { + + if (G->num_roots() == 0) { // Initialize the analysis by constructing + // the root if none exists. + + const CFGBlock* Entry = &(L->getCFG()->getEntry()); + + assert (Entry->empty() && + "Entry block must be empty."); + + assert (Entry->succ_size() == 1 && + "Entry block must have 1 successor."); + + // Get the solitary successor. + const CFGBlock* Succ = *(Entry->succ_begin()); + + // Construct an edge representing the + // starting location in the function. + BlockEdge StartLoc(Entry, Succ, L); + + // Set the current block counter to being empty. + WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); + + if (!InitState) + // Generate the root. + generateNode(StartLoc, getInitialState(L), 0); + else + generateNode(StartLoc, InitState, 0); + } + + // Check if we have a steps limit + bool UnlimitedSteps = Steps == 0; + + while (WList->hasWork()) { + if (!UnlimitedSteps) { + if (Steps == 0) + break; + --Steps; + } + + const WorkListUnit& WU = WList->Dequeue(); + + // Set the current block counter. + WList->setBlockCounter(WU.getBlockCounter()); + + // Retrieve the node. + ExplodedNode* Node = WU.getNode(); + + // Dispatch on the location type. + switch (Node->getLocation().getKind()) { + case ProgramPoint::BlockEdgeKind: + HandleBlockEdge(cast(Node->getLocation()), Node); + break; + + case ProgramPoint::BlockEntranceKind: + HandleBlockEntrance(cast(Node->getLocation()), Node); + break; + + case ProgramPoint::BlockExitKind: + assert (false && "BlockExit location never occur in forward analysis."); + break; + + case ProgramPoint::CallEnterKind: + HandleCallEnter(cast(Node->getLocation()), WU.getBlock(), + WU.getIndex(), Node); + break; + + case ProgramPoint::CallExitKind: + HandleCallExit(cast(Node->getLocation()), Node); + break; + + default: + assert(isa(Node->getLocation()) || + isa(Node->getLocation())); + HandlePostStmt(WU.getBlock(), WU.getIndex(), Node); + break; + } + } + + SubEng.ProcessEndWorklist(hasWorkRemaining()); + return WList->hasWork(); +} + +void CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L, + unsigned Steps, + const GRState *InitState, + ExplodedNodeSet &Dst) { + ExecuteWorkList(L, Steps, InitState); + for (llvm::SmallVectorImpl::iterator I = G->EndNodes.begin(), + E = G->EndNodes.end(); I != E; ++I) { + Dst.Add(*I); + } +} + +void CoreEngine::HandleCallEnter(const CallEnter &L, const CFGBlock *Block, + unsigned Index, ExplodedNode *Pred) { + CallEnterNodeBuilder Builder(*this, Pred, L.getCallExpr(), + L.getCalleeContext(), Block, Index); + ProcessCallEnter(Builder); +} + +void CoreEngine::HandleCallExit(const CallExit &L, ExplodedNode *Pred) { + CallExitNodeBuilder Builder(*this, Pred); + ProcessCallExit(Builder); +} + +void CoreEngine::HandleBlockEdge(const BlockEdge& L, ExplodedNode* Pred) { + + const CFGBlock* Blk = L.getDst(); + + // Check if we are entering the EXIT block. + if (Blk == &(L.getLocationContext()->getCFG()->getExit())) { + + assert (L.getLocationContext()->getCFG()->getExit().size() == 0 + && "EXIT block cannot contain Stmts."); + + // Process the final state transition. + EndPathNodeBuilder Builder(Blk, Pred, this); + ProcessEndPath(Builder); + + // This path is done. Don't enqueue any more nodes. + return; + } + + // FIXME: Should we allow ProcessBlockEntrance to also manipulate state? + + if (ProcessBlockEntrance(Blk, Pred, WList->getBlockCounter())) + generateNode(BlockEntrance(Blk, Pred->getLocationContext()), + Pred->State, Pred); + else { + blocksAborted.push_back(std::make_pair(L, Pred)); + } +} + +void CoreEngine::HandleBlockEntrance(const BlockEntrance& L, + ExplodedNode* Pred) { + + // Increment the block counter. + BlockCounter Counter = WList->getBlockCounter(); + Counter = BCounterFactory.IncrementCount(Counter, + Pred->getLocationContext()->getCurrentStackFrame(), + L.getBlock()->getBlockID()); + WList->setBlockCounter(Counter); + + // Process the entrance of the block. + if (CFGElement E = L.getFirstElement()) { + StmtNodeBuilder Builder(L.getBlock(), 0, Pred, this, + SubEng.getStateManager()); + ProcessElement(E, Builder); + } + else + HandleBlockExit(L.getBlock(), Pred); +} + +void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode* Pred) { + + if (const Stmt* Term = B->getTerminator()) { + switch (Term->getStmtClass()) { + default: + assert(false && "Analysis for this terminator not implemented."); + break; + + case Stmt::BinaryOperatorClass: // '&&' and '||' + HandleBranch(cast(Term)->getLHS(), Term, B, Pred); + return; + + case Stmt::ConditionalOperatorClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + // FIXME: Use constant-folding in CFG construction to simplify this + // case. + + case Stmt::ChooseExprClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::DoStmtClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::ForStmtClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::ContinueStmtClass: + case Stmt::BreakStmtClass: + case Stmt::GotoStmtClass: + break; + + case Stmt::IfStmtClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + + case Stmt::IndirectGotoStmtClass: { + // Only 1 successor: the indirect goto dispatch block. + assert (B->succ_size() == 1); + + IndirectGotoNodeBuilder + builder(Pred, B, cast(Term)->getTarget(), + *(B->succ_begin()), this); + + ProcessIndirectGoto(builder); + return; + } + + case Stmt::ObjCForCollectionStmtClass: { + // In the case of ObjCForCollectionStmt, it appears twice in a CFG: + // + // (1) inside a basic block, which represents the binding of the + // 'element' variable to a value. + // (2) in a terminator, which represents the branch. + // + // For (1), subengines will bind a value (i.e., 0 or 1) indicating + // whether or not collection contains any more elements. We cannot + // just test to see if the element is nil because a container can + // contain nil elements. + HandleBranch(Term, Term, B, Pred); + return; + } + + case Stmt::SwitchStmtClass: { + SwitchNodeBuilder builder(Pred, B, cast(Term)->getCond(), + this); + + ProcessSwitch(builder); + return; + } + + case Stmt::WhileStmtClass: + HandleBranch(cast(Term)->getCond(), Term, B, Pred); + return; + } + } + + assert (B->succ_size() == 1 && + "Blocks with no terminator should have at most 1 successor."); + + generateNode(BlockEdge(B, *(B->succ_begin()), Pred->getLocationContext()), + Pred->State, Pred); +} + +void CoreEngine::HandleBranch(const Stmt* Cond, const Stmt* Term, + const CFGBlock * B, ExplodedNode* Pred) { + assert (B->succ_size() == 2); + + BranchNodeBuilder Builder(B, *(B->succ_begin()), *(B->succ_begin()+1), + Pred, this); + + ProcessBranch(Cond, Term, Builder); +} + +void CoreEngine::HandlePostStmt(const CFGBlock* B, unsigned StmtIdx, + ExplodedNode* Pred) { + assert (!B->empty()); + + if (StmtIdx == B->size()) + HandleBlockExit(B, Pred); + else { + StmtNodeBuilder Builder(B, StmtIdx, Pred, this, + SubEng.getStateManager()); + ProcessElement((*B)[StmtIdx], Builder); + } +} + +/// generateNode - Utility method to generate nodes, hook up successors, +/// and add nodes to the worklist. +void CoreEngine::generateNode(const ProgramPoint& Loc, + const GRState* State, ExplodedNode* Pred) { + + bool IsNew; + ExplodedNode* Node = G->getNode(Loc, State, &IsNew); + + if (Pred) + Node->addPredecessor(Pred, *G); // Link 'Node' with its predecessor. + else { + assert (IsNew); + G->addRoot(Node); // 'Node' has no predecessor. Make it a root. + } + + // Only add 'Node' to the worklist if it was freshly generated. + if (IsNew) WList->Enqueue(Node); +} + +StmtNodeBuilder::StmtNodeBuilder(const CFGBlock* b, unsigned idx, + ExplodedNode* N, CoreEngine* e, + GRStateManager &mgr) + : Eng(*e), B(*b), Idx(idx), Pred(N), Mgr(mgr), + PurgingDeadSymbols(false), BuildSinks(false), HasGeneratedNode(false), + PointKind(ProgramPoint::PostStmtKind), Tag(0) { + Deferred.insert(N); + CleanedState = Pred->getState(); +} + +StmtNodeBuilder::~StmtNodeBuilder() { + for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) + if (!(*I)->isSink()) + GenerateAutoTransition(*I); +} + +void StmtNodeBuilder::GenerateAutoTransition(ExplodedNode* N) { + assert (!N->isSink()); + + // Check if this node entered a callee. + if (isa(N->getLocation())) { + // Still use the index of the CallExpr. It's needed to create the callee + // StackFrameContext. + Eng.WList->Enqueue(N, &B, Idx); + return; + } + + // Do not create extra nodes. Move to the next CFG element. + if (isa(N->getLocation())) { + Eng.WList->Enqueue(N, &B, Idx+1); + return; + } + + PostStmt Loc(getStmt(), N->getLocationContext()); + + if (Loc == N->getLocation()) { + // Note: 'N' should be a fresh node because otherwise it shouldn't be + // a member of Deferred. + Eng.WList->Enqueue(N, &B, Idx+1); + return; + } + + bool IsNew; + ExplodedNode* Succ = Eng.G->getNode(Loc, N->State, &IsNew); + Succ->addPredecessor(N, *Eng.G); + + if (IsNew) + Eng.WList->Enqueue(Succ, &B, Idx+1); +} + +ExplodedNode* StmtNodeBuilder::MakeNode(ExplodedNodeSet& Dst, const Stmt* S, + ExplodedNode* Pred, const GRState* St, + ProgramPoint::Kind K) { + + ExplodedNode* N = generateNode(S, St, Pred, K); + + if (N) { + if (BuildSinks) + N->markAsSink(); + else + Dst.Add(N); + } + + return N; +} + +static ProgramPoint GetProgramPoint(const Stmt *S, ProgramPoint::Kind K, + const LocationContext *LC, const void *tag){ + switch (K) { + default: + assert(false && "Unhandled ProgramPoint kind"); + case ProgramPoint::PreStmtKind: + return PreStmt(S, LC, tag); + case ProgramPoint::PostStmtKind: + return PostStmt(S, LC, tag); + case ProgramPoint::PreLoadKind: + return PreLoad(S, LC, tag); + case ProgramPoint::PostLoadKind: + return PostLoad(S, LC, tag); + case ProgramPoint::PreStoreKind: + return PreStore(S, LC, tag); + case ProgramPoint::PostStoreKind: + return PostStore(S, LC, tag); + case ProgramPoint::PostLValueKind: + return PostLValue(S, LC, tag); + case ProgramPoint::PostPurgeDeadSymbolsKind: + return PostPurgeDeadSymbols(S, LC, tag); + } +} + +ExplodedNode* +StmtNodeBuilder::generateNodeInternal(const Stmt* S, const GRState* state, + ExplodedNode* Pred, + ProgramPoint::Kind K, + const void *tag) { + + const ProgramPoint &L = GetProgramPoint(S, K, Pred->getLocationContext(),tag); + return generateNodeInternal(L, state, Pred); +} + +ExplodedNode* +StmtNodeBuilder::generateNodeInternal(const ProgramPoint &Loc, + const GRState* State, + ExplodedNode* Pred) { + bool IsNew; + ExplodedNode* N = Eng.G->getNode(Loc, State, &IsNew); + N->addPredecessor(Pred, *Eng.G); + Deferred.erase(Pred); + + if (IsNew) { + Deferred.insert(N); + return N; + } + + return NULL; +} + +ExplodedNode* BranchNodeBuilder::generateNode(const GRState* State, + bool branch) { + + // If the branch has been marked infeasible we should not generate a node. + if (!isFeasible(branch)) + return NULL; + + bool IsNew; + + ExplodedNode* Succ = + Eng.G->getNode(BlockEdge(Src,branch ? DstT:DstF,Pred->getLocationContext()), + State, &IsNew); + + Succ->addPredecessor(Pred, *Eng.G); + + if (branch) + GeneratedTrue = true; + else + GeneratedFalse = true; + + if (IsNew) { + Deferred.push_back(Succ); + return Succ; + } + + return NULL; +} + +BranchNodeBuilder::~BranchNodeBuilder() { + if (!GeneratedTrue) generateNode(Pred->State, true); + if (!GeneratedFalse) generateNode(Pred->State, false); + + for (DeferredTy::iterator I=Deferred.begin(), E=Deferred.end(); I!=E; ++I) + if (!(*I)->isSink()) Eng.WList->Enqueue(*I); +} + + +ExplodedNode* +IndirectGotoNodeBuilder::generateNode(const iterator& I, const GRState* St, + bool isSink) { + bool IsNew; + + ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), + Pred->getLocationContext()), St, &IsNew); + + Succ->addPredecessor(Pred, *Eng.G); + + if (IsNew) { + + if (isSink) + Succ->markAsSink(); + else + Eng.WList->Enqueue(Succ); + + return Succ; + } + + return NULL; +} + + +ExplodedNode* +SwitchNodeBuilder::generateCaseStmtNode(const iterator& I, const GRState* St){ + + bool IsNew; + + ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, I.getBlock(), + Pred->getLocationContext()), St, &IsNew); + Succ->addPredecessor(Pred, *Eng.G); + + if (IsNew) { + Eng.WList->Enqueue(Succ); + return Succ; + } + + return NULL; +} + + +ExplodedNode* +SwitchNodeBuilder::generateDefaultCaseNode(const GRState* St, bool isSink) { + + // Get the block for the default case. + assert (Src->succ_rbegin() != Src->succ_rend()); + CFGBlock* DefaultBlock = *Src->succ_rbegin(); + + bool IsNew; + + ExplodedNode* Succ = Eng.G->getNode(BlockEdge(Src, DefaultBlock, + Pred->getLocationContext()), St, &IsNew); + Succ->addPredecessor(Pred, *Eng.G); + + if (IsNew) { + if (isSink) + Succ->markAsSink(); + else + Eng.WList->Enqueue(Succ); + + return Succ; + } + + return NULL; +} + +EndPathNodeBuilder::~EndPathNodeBuilder() { + // Auto-generate an EOP node if one has not been generated. + if (!HasGeneratedNode) { + // If we are in an inlined call, generate CallExit node. + if (Pred->getLocationContext()->getParent()) + GenerateCallExitNode(Pred->State); + else + generateNode(Pred->State); + } +} + +ExplodedNode* +EndPathNodeBuilder::generateNode(const GRState* State, const void *tag, + ExplodedNode* P) { + HasGeneratedNode = true; + bool IsNew; + + ExplodedNode* Node = Eng.G->getNode(BlockEntrance(&B, + Pred->getLocationContext(), tag), State, &IsNew); + + Node->addPredecessor(P ? P : Pred, *Eng.G); + + if (IsNew) { + Eng.G->addEndOfPath(Node); + return Node; + } + + return NULL; +} + +void EndPathNodeBuilder::GenerateCallExitNode(const GRState *state) { + HasGeneratedNode = true; + // Create a CallExit node and enqueue it. + const StackFrameContext *LocCtx + = cast(Pred->getLocationContext()); + const Stmt *CE = LocCtx->getCallSite(); + + // Use the the callee location context. + CallExit Loc(CE, LocCtx); + + bool isNew; + ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); + Node->addPredecessor(Pred, *Eng.G); + + if (isNew) + Eng.WList->Enqueue(Node); +} + + +void CallEnterNodeBuilder::generateNode(const GRState *state) { + // Check if the callee is in the same translation unit. + if (CalleeCtx->getTranslationUnit() != + Pred->getLocationContext()->getTranslationUnit()) { + // Create a new engine. We must be careful that the new engine should not + // reference data structures owned by the old engine. + + AnalysisManager &OldMgr = Eng.SubEng.getAnalysisManager(); + + // Get the callee's translation unit. + idx::TranslationUnit *TU = CalleeCtx->getTranslationUnit(); + + // Create a new AnalysisManager with components of the callee's + // TranslationUnit. + // The Diagnostic is actually shared when we create ASTUnits from AST files. + AnalysisManager AMgr(TU->getASTContext(), TU->getDiagnostic(), + OldMgr.getLangOptions(), + OldMgr.getPathDiagnosticClient(), + OldMgr.getStoreManagerCreator(), + OldMgr.getConstraintManagerCreator(), + OldMgr.getIndexer(), + OldMgr.getMaxNodes(), OldMgr.getMaxVisit(), + OldMgr.shouldVisualizeGraphviz(), + OldMgr.shouldVisualizeUbigraph(), + OldMgr.shouldPurgeDead(), + OldMgr.shouldEagerlyAssume(), + OldMgr.shouldTrimGraph(), + OldMgr.shouldInlineCall(), + OldMgr.getAnalysisContextManager().getUseUnoptimizedCFG(), + OldMgr.getAnalysisContextManager().getAddImplicitDtors(), + OldMgr.getAnalysisContextManager().getAddInitializers()); + llvm::OwningPtr TF(MakeCFRefCountTF(AMgr.getASTContext(), + /* GCEnabled */ false, + AMgr.getLangOptions())); + // Create the new engine. + ExprEngine NewEng(AMgr, TF.take()); + + // Create the new LocationContext. + AnalysisContext *NewAnaCtx = AMgr.getAnalysisContext(CalleeCtx->getDecl(), + CalleeCtx->getTranslationUnit()); + const StackFrameContext *OldLocCtx = CalleeCtx; + const StackFrameContext *NewLocCtx = AMgr.getStackFrame(NewAnaCtx, + OldLocCtx->getParent(), + OldLocCtx->getCallSite(), + OldLocCtx->getCallSiteBlock(), + OldLocCtx->getIndex()); + + // Now create an initial state for the new engine. + const GRState *NewState = NewEng.getStateManager().MarshalState(state, + NewLocCtx); + ExplodedNodeSet ReturnNodes; + NewEng.ExecuteWorkListWithInitialState(NewLocCtx, AMgr.getMaxNodes(), + NewState, ReturnNodes); + return; + } + + // Get the callee entry block. + const CFGBlock *Entry = &(CalleeCtx->getCFG()->getEntry()); + assert(Entry->empty()); + assert(Entry->succ_size() == 1); + + // Get the solitary successor. + const CFGBlock *SuccB = *(Entry->succ_begin()); + + // Construct an edge representing the starting location in the callee. + BlockEdge Loc(Entry, SuccB, CalleeCtx); + + bool isNew; + ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); + Node->addPredecessor(const_cast(Pred), *Eng.G); + + if (isNew) + Eng.WList->Enqueue(Node); +} + +void CallExitNodeBuilder::generateNode(const GRState *state) { + // Get the callee's location context. + const StackFrameContext *LocCtx + = cast(Pred->getLocationContext()); + // When exiting an implicit automatic obj dtor call, the callsite is the Stmt + // that triggers the dtor. + PostStmt Loc(LocCtx->getCallSite(), LocCtx->getParent()); + bool isNew; + ExplodedNode *Node = Eng.G->getNode(Loc, state, &isNew); + Node->addPredecessor(const_cast(Pred), *Eng.G); + if (isNew) + Eng.WList->Enqueue(Node, LocCtx->getCallSiteBlock(), + LocCtx->getIndex() + 1); +} diff --git a/lib/EntoSA/Environment.cpp b/lib/EntoSA/Environment.cpp new file mode 100644 index 0000000000..eecbabbb2e --- /dev/null +++ b/lib/EntoSA/Environment.cpp @@ -0,0 +1,236 @@ +//== Environment.cpp - Map from Stmt* to Locations/Values -------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defined the Environment and EnvironmentManager classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/AnalysisContext.h" +#include "clang/Analysis/CFG.h" +#include "clang/EntoSA/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/EntoSA/ExplodedGraph.cpp b/lib/EntoSA/ExplodedGraph.cpp new file mode 100644 index 0000000000..ec66ba6028 --- /dev/null +++ b/lib/EntoSA/ExplodedGraph.cpp @@ -0,0 +1,282 @@ +//=-- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -*- C++ -*------=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the template classes ExplodedNode and ExplodedGraph, +// which represent a path-sensitive, intra-procedural "exploded graph." +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathSensitive/ExplodedGraph.h" +#include "clang/EntoSA/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/EntoSA/FlatStore.cpp b/lib/EntoSA/FlatStore.cpp new file mode 100644 index 0000000000..8ded4d16b6 --- /dev/null +++ b/lib/EntoSA/FlatStore.cpp @@ -0,0 +1,203 @@ +//=== FlatStore.cpp - Flat region-based store model -------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/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/EntoSA/GRState.cpp b/lib/EntoSA/GRState.cpp new file mode 100644 index 0000000000..62396230ff --- /dev/null +++ b/lib/EntoSA/GRState.cpp @@ -0,0 +1,551 @@ +//= GRState.cpp - Path-Sensitive "State" for tracking values -----*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements GRState and GRStateManager. +// +//===----------------------------------------------------------------------===// + +#include "clang/Analysis/CFG.h" +#include "clang/EntoSA/PathSensitive/GRStateTrait.h" +#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/EntoSA/PathSensitive/SubEngine.h" +#include "clang/EntoSA/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/EntoSA/HTMLDiagnostics.cpp b/lib/EntoSA/HTMLDiagnostics.cpp new file mode 100644 index 0000000000..a0d338ae1e --- /dev/null +++ b/lib/EntoSA/HTMLDiagnostics.cpp @@ -0,0 +1,578 @@ +//===--- HTMLDiagnostics.cpp - HTML Diagnostics for Paths ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the HTMLDiagnostics object. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathDiagnosticClients.h" +#include "clang/EntoSA/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/EntoSA/Makefile b/lib/EntoSA/Makefile new file mode 100644 index 0000000000..9678138695 --- /dev/null +++ b/lib/EntoSA/Makefile @@ -0,0 +1,19 @@ +##===- clang/lib/Checker/Makefile --------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## +# +# This implements analyses built on top of source-level CFGs. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL := ../.. +LIBRARYNAME := clangEntoCore +PARALLEL_DIRS := Checkers + +include $(CLANG_LEVEL)/Makefile + diff --git a/lib/EntoSA/ManagerRegistry.cpp b/lib/EntoSA/ManagerRegistry.cpp new file mode 100644 index 0000000000..99b7a70748 --- /dev/null +++ b/lib/EntoSA/ManagerRegistry.cpp @@ -0,0 +1,21 @@ +//===- ManagerRegistry.cpp - Pluggble Analyzer module creators --*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the pluggable analyzer module creators. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/ManagerRegistry.h" + +using namespace clang; +using namespace ento; + +StoreManagerCreator ManagerRegistry::StoreMgrCreator = 0; + +ConstraintManagerCreator ManagerRegistry::ConstraintMgrCreator = 0; diff --git a/lib/EntoSA/MemRegion.cpp b/lib/EntoSA/MemRegion.cpp new file mode 100644 index 0000000000..ff0f34044e --- /dev/null +++ b/lib/EntoSA/MemRegion.cpp @@ -0,0 +1,987 @@ +//== MemRegion.cpp - Abstract memory regions for static analysis --*- C++ -*--// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines MemRegion and its subclasses. MemRegion defines a +// partially-typed abstraction of memory useful for path-sensitive dataflow +// analyses. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathSensitive/MemRegion.h" +#include "clang/EntoSA/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/EntoSA/PathDiagnostic.cpp b/lib/EntoSA/PathDiagnostic.cpp new file mode 100644 index 0000000000..bf8c8b0d3d --- /dev/null +++ b/lib/EntoSA/PathDiagnostic.cpp @@ -0,0 +1,279 @@ +//===--- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the PathDiagnostic-related interfaces. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/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/EntoSA/PlistDiagnostics.cpp b/lib/EntoSA/PlistDiagnostics.cpp new file mode 100644 index 0000000000..6e54d494c5 --- /dev/null +++ b/lib/EntoSA/PlistDiagnostics.cpp @@ -0,0 +1,472 @@ +//===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the PlistDiagnostics object. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathDiagnosticClients.h" +#include "clang/EntoSA/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/EntoSA/README.txt b/lib/EntoSA/README.txt new file mode 100644 index 0000000000..dd16ccc980 --- /dev/null +++ b/lib/EntoSA/README.txt @@ -0,0 +1,117 @@ +//===----------------------------------------------------------------------===// +// Clang Static Analyzer +//===----------------------------------------------------------------------===// + += Library Structure = + +The analyzer library has two layers: a (low-level) static analysis +engine (GRExprEngine.cpp and friends), and some static checkers +(*Checker.cpp). The latter are built on top of the former via the +Checker and CheckerVisitor interfaces (Checker.h and +CheckerVisitor.h). The Checker interface is designed to be minimal +and simple for checker writers, and attempts to isolate them from much +of the gore of the internal analysis engine. + += How It Works = + +The analyzer is inspired by several foundational research papers ([1], +[2]). (FIXME: kremenek to add more links) + +In a nutshell, the analyzer is basically a source code simulator that +traces out possible paths of execution. The state of the program +(values of variables and expressions) is encapsulated by the state +(GRState). A location in the program is called a program point +(ProgramPoint), and the combination of state and program point is a +node in an exploded graph (ExplodedGraph). The term "exploded" comes +from exploding the control-flow edges in the control-flow graph (CFG). + +Conceptually the analyzer does a reachability analysis through the +ExplodedGraph. We start at a root node, which has the entry program +point and initial state, and then simulate transitions by analyzing +individual expressions. The analysis of an expression can cause the +state to change, resulting in a new node in the ExplodedGraph with an +updated program point and an updated state. A bug is found by hitting +a node that satisfies some "bug condition" (basically a violation of a +checking invariant). + +The analyzer traces out multiple paths by reasoning about branches and +then bifurcating the state: on the true branch the conditions of the +branch are assumed to be true and on the false branch the conditions +of the branch are assumed to be false. Such "assumptions" create +constraints on the values of the program, and those constraints are +recorded in the GRState object (and are manipulated by the +ConstraintManager). If assuming the conditions of a branch would +cause the constraints to be unsatisfiable, the branch is considered +infeasible and that path is not taken. This is how we get +path-sensitivity. We reduce exponential blow-up by caching nodes. If +a new node with the same state and program point as an existing node +would get generated, the path "caches out" and we simply reuse the +existing node. Thus the ExplodedGraph is not a DAG; it can contain +cycles as paths loop back onto each other and cache out. + +GRState and ExplodedNodes are basically immutable once created. Once +one creates a GRState, you need to create a new one to get a new +GRState. This immutability is key since the ExplodedGraph represents +the behavior of the analyzed program from the entry point. To +represent these efficiently, we use functional data structures (e.g., +ImmutableMaps) which share data between instances. + +Finally, individual Checkers work by also manipulating the analysis +state. The analyzer engine talks to them via a visitor interface. +For example, the PreVisitCallExpr() method is called by GRExprEngine +to tell the Checker that we are about to analyze a CallExpr, and the +checker is asked to check for any preconditions that might not be +satisfied. The checker can do nothing, or it can generate a new +GRState and ExplodedNode which contains updated checker state. If it +finds a bug, it can tell the BugReporter object about the bug, +providing it an ExplodedNode which is the last node in the path that +triggered the problem. + += Working on the Analyzer = + +If you are interested in bringing up support for C++ expressions, the +best place to look is the visitation logic in GRExprEngine, which +handles the simulation of individual expressions. There are plenty of +examples there of how other expressions are handled. + +If you are interested in writing checkers, look at the Checker and +CheckerVisitor interfaces (Checker.h and CheckerVisitor.h). Also look +at the files named *Checker.cpp for examples on how you can implement +these interfaces. + += Debugging the Analyzer = + +There are some useful command-line options for debugging. For example: + +$ clang -cc1 -help | grep analyze + -analyze-function + -analyzer-display-progress + -analyzer-viz-egraph-graphviz + ... + +The first allows you to specify only analyzing a specific function. +The second prints to the console what function is being analyzed. The +third generates a graphviz dot file of the ExplodedGraph. This is +extremely useful when debugging the analyzer and viewing the +simulation results. + +Of course, viewing the CFG (Control-Flow Graph) is also useful: + +$ clang -cc1 -help | grep cfg + -cfg-add-implicit-dtors Add C++ implicit destructors to CFGs for all analyses + -cfg-add-initializers Add C++ initializers to CFGs for all analyses + -cfg-dump Display Control-Flow Graphs + -cfg-view View Control-Flow Graphs using GraphViz + -unoptimized-cfg Generate unoptimized CFGs for all analyses + +-cfg-dump dumps a textual representation of the CFG to the console, +and -cfg-view creates a GraphViz representation. + += References = + +[1] Precise interprocedural dataflow analysis via graph reachability, + T Reps, S Horwitz, and M Sagiv, POPL '95, + http://portal.acm.org/citation.cfm?id=199462 + +[2] A memory model for static analysis of C programs, Z Xu, T + Kremenek, and J Zhang, http://lcs.ios.ac.cn/~xzx/memmodel.pdf diff --git a/lib/EntoSA/RangeConstraintManager.cpp b/lib/EntoSA/RangeConstraintManager.cpp new file mode 100644 index 0000000000..5c2d674515 --- /dev/null +++ b/lib/EntoSA/RangeConstraintManager.cpp @@ -0,0 +1,442 @@ +//== RangeConstraintManager.cpp - Manage range constraints.------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines RangeConstraintManager, a class that tracks simple +// equality and inequality constraints on symbolic values of GRState. +// +//===----------------------------------------------------------------------===// + +#include "SimpleConstraintManager.h" +#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/EntoSA/PathSensitive/GRStateTrait.h" +#include "clang/EntoSA/PathSensitive/TransferFuncs.h" +#include "clang/EntoSA/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/EntoSA/RegionStore.cpp b/lib/EntoSA/RegionStore.cpp new file mode 100644 index 0000000000..4913d1a389 --- /dev/null +++ b/lib/EntoSA/RegionStore.cpp @@ -0,0 +1,1872 @@ +//== 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/EntoSA/PathSensitive/GRState.h" +#include "clang/EntoSA/PathSensitive/GRStateTrait.h" +#include "clang/EntoSA/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 = cast(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/EntoSA/SValBuilder.cpp b/lib/EntoSA/SValBuilder.cpp new file mode 100644 index 0000000000..c4efaae906 --- /dev/null +++ b/lib/EntoSA/SValBuilder.cpp @@ -0,0 +1,310 @@ +// SValBuilder.cpp - Basic class for all SValBuilder implementations -*- C++ -*- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SValBuilder, the base class for all (complete) SValBuilder +// implementations. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathSensitive/MemRegion.h" +#include "clang/EntoSA/PathSensitive/SVals.h" +#include "clang/EntoSA/PathSensitive/SValBuilder.h" +#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/EntoSA/PathSensitive/BasicValueFactory.h" + +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/EntoSA/SVals.cpp b/lib/EntoSA/SVals.cpp new file mode 100644 index 0000000000..ea23293924 --- /dev/null +++ b/lib/EntoSA/SVals.cpp @@ -0,0 +1,361 @@ +//= RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -*- C++ -*-==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SVal, Loc, and NonLoc, classes that represent +// abstract r-values for use with path-sensitive value tracking. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/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/EntoSA/SimpleConstraintManager.cpp b/lib/EntoSA/SimpleConstraintManager.cpp new file mode 100644 index 0000000000..ef26fc5d93 --- /dev/null +++ b/lib/EntoSA/SimpleConstraintManager.cpp @@ -0,0 +1,303 @@ +//== SimpleConstraintManager.cpp --------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SimpleConstraintManager, a class that holds code shared +// between BasicConstraintManager and RangeConstraintManager. +// +//===----------------------------------------------------------------------===// + +#include "SimpleConstraintManager.h" +#include "clang/EntoSA/PathSensitive/ExprEngine.h" +#include "clang/EntoSA/PathSensitive/GRState.h" +#include "clang/EntoSA/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/EntoSA/SimpleConstraintManager.h b/lib/EntoSA/SimpleConstraintManager.h new file mode 100644 index 0000000000..83db8cc9ce --- /dev/null +++ b/lib/EntoSA/SimpleConstraintManager.h @@ -0,0 +1,93 @@ +//== SimpleConstraintManager.h ----------------------------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Code shared between BasicConstraintManager and RangeConstraintManager. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_GR_SIMPLE_CONSTRAINT_MANAGER_H +#define LLVM_CLANG_GR_SIMPLE_CONSTRAINT_MANAGER_H + +#include "clang/EntoSA/PathSensitive/ConstraintManager.h" +#include "clang/EntoSA/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/EntoSA/SimpleSValBuilder.cpp b/lib/EntoSA/SimpleSValBuilder.cpp new file mode 100644 index 0000000000..ac0bfa62ef --- /dev/null +++ b/lib/EntoSA/SimpleSValBuilder.cpp @@ -0,0 +1,884 @@ +// SimpleSValBuilder.cpp - A basic SValBuilder -----------------------*- C++ -*- +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SimpleSValBuilder, a basic implementation of SValBuilder. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathSensitive/SValBuilder.h" +#include "clang/EntoSA/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/EntoSA/Store.cpp b/lib/EntoSA/Store.cpp new file mode 100644 index 0000000000..29b0ee1b5f --- /dev/null +++ b/lib/EntoSA/Store.cpp @@ -0,0 +1,334 @@ +//== Store.cpp - Interface for maps from Locations to Values ----*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defined the types Store and StoreManager. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathSensitive/Store.h" +#include "clang/EntoSA/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/EntoSA/SymbolManager.cpp b/lib/EntoSA/SymbolManager.cpp new file mode 100644 index 0000000000..75e0774837 --- /dev/null +++ b/lib/EntoSA/SymbolManager.cpp @@ -0,0 +1,343 @@ +//== SymbolManager.h - Management of Symbolic Values ------------*- C++ -*--==// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines SymbolManager, a class that manages symbolic values +// created for use by ExprEngine and related classes. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathSensitive/SymbolManager.h" +#include "clang/Analysis/Analyses/LiveVariables.h" +#include "clang/EntoSA/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/EntoSA/TextPathDiagnostics.cpp b/lib/EntoSA/TextPathDiagnostics.cpp new file mode 100644 index 0000000000..bf98301dc5 --- /dev/null +++ b/lib/EntoSA/TextPathDiagnostics.cpp @@ -0,0 +1,70 @@ +//===--- TextPathDiagnostics.cpp - Text Diagnostics for Paths ---*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the TextPathDiagnostics object. +// +//===----------------------------------------------------------------------===// + +#include "clang/EntoSA/PathDiagnosticClients.h" +#include "clang/EntoSA/BugReporter/PathDiagnostic.h" +#include "clang/Lex/Preprocessor.h" +#include "llvm/Support/raw_ostream.h" +using namespace clang; +using namespace ento; +using namespace llvm; + +namespace { + +/// \brief Simple path diagnostic client used for outputting as diagnostic notes +/// the sequence of events. +class TextPathDiagnostics : public PathDiagnosticClient { + const std::string OutputFile; + Diagnostic &Diag; + +public: + TextPathDiagnostics(const std::string& output, Diagnostic &diag) + : OutputFile(output), Diag(diag) {} + + void HandlePathDiagnostic(const PathDiagnostic* D); + + void FlushDiagnostics(llvm::SmallVectorImpl *FilesMade) { } + + virtual llvm::StringRef getName() const { + return "TextPathDiagnostics"; + } + + PathGenerationScheme getGenerationScheme() const { return Minimal; } + bool supportsLogicalOpControlFlow() const { return true; } + bool supportsAllBlockEdges() const { return true; } + virtual bool useVerboseDescription() const { return true; } +}; + +} // end anonymous namespace + +PathDiagnosticClient* +ento::createTextPathDiagnosticClient(const std::string& out, + const Preprocessor &PP) { + return new TextPathDiagnostics(out, PP.getDiagnostics()); +} + +void TextPathDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) { + if (!D) + return; + + if (D->empty()) { + delete D; + return; + } + + for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I) { + unsigned diagID = Diag.getDiagnosticIDs()->getCustomDiagID( + DiagnosticIDs::Note, I->getString()); + Diag.Report(I->getLocation().asLocation(), diagID); + } +} diff --git a/lib/FrontendTool/CMakeLists.txt b/lib/FrontendTool/CMakeLists.txt index f4ace98146..89937e1762 100644 --- a/lib/FrontendTool/CMakeLists.txt +++ b/lib/FrontendTool/CMakeLists.txt @@ -1,7 +1,7 @@ set(LLVM_NO_RTTI 1) set(LLVM_USED_LIBS clangDriver clangFrontend clangRewrite clangCodeGen - clangGRCheckers clangGRCore) + clangEntoCheckers clangEntoCore) add_clang_library(clangFrontendTool ExecuteCompilerInvocation.cpp diff --git a/lib/FrontendTool/ExecuteCompilerInvocation.cpp b/lib/FrontendTool/ExecuteCompilerInvocation.cpp index ae6194beb8..e17e8b7156 100644 --- a/lib/FrontendTool/ExecuteCompilerInvocation.cpp +++ b/lib/FrontendTool/ExecuteCompilerInvocation.cpp @@ -13,7 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang/FrontendTool/Utils.h" -#include "clang/GR/FrontendActions.h" +#include "clang/EntoSA/FrontendActions.h" #include "clang/CodeGen/CodeGenAction.h" #include "clang/Driver/CC1Options.h" #include "clang/Driver/OptTable.h" diff --git a/lib/GR/AggExprVisitor.cpp b/lib/GR/AggExprVisitor.cpp deleted file mode 100644 index af30c56974..0000000000 --- a/lib/GR/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/GR/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/GR/AnalysisManager.cpp b/lib/GR/AnalysisManager.cpp deleted file mode 100644 index 767ae4ab75..0000000000 --- a/lib/GR/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/GR/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/GR/AnalyzerStatsChecker.cpp b/lib/GR/AnalyzerStatsChecker.cpp deleted file mode 100644 index fa8ae59894..0000000000 --- a/lib/GR/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/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/PathSensitive/ExplodedGraph.h" -#include "clang/GR/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/GR/BasicConstraintManager.cpp b/lib/GR/BasicConstraintManager.cpp deleted file mode 100644 index da91f5c985..0000000000 --- a/lib/GR/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/GR/PathSensitive/GRState.h" -#include "clang/GR/PathSensitive/GRStateTrait.h" -#include "clang/GR/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/GR/BasicStore.cpp b/lib/GR/BasicStore.cpp deleted file mode 100644 index 9eb8d79ce4..0000000000 --- a/lib/GR/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/GR/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/GR/BasicValueFactory.cpp b/lib/GR/BasicValueFactory.cpp deleted file mode 100644 index 8e51361e00..0000000000 --- a/lib/GR/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/GR/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/GR/BlockCounter.cpp b/lib/GR/BlockCounter.cpp deleted file mode 100644 index dadf707cf2..0000000000 --- a/lib/GR/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/GR/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/GR/BugReporter.cpp b/lib/GR/BugReporter.cpp deleted file mode 100644 index 769e2b9c4d..0000000000 --- a/lib/GR/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/GR/BugReporter/BugReporter.h" -#include "clang/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/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/GR/BugReporterVisitors.cpp b/lib/GR/BugReporterVisitors.cpp deleted file mode 100644 index 78b5ad66ab..0000000000 --- a/lib/GR/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/GR/BugReporter/BugReporter.h" -#include "clang/GR/BugReporter/PathDiagnostic.h" -#include "clang/GR/PathSensitive/ExplodedGraph.h" -#include "clang/GR/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/GR/CFRefCount.cpp b/lib/GR/CFRefCount.cpp deleted file mode 100644 index 2fe6b18c6d..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/BugReporter/PathDiagnostic.h" -#include "clang/GR/Checkers/LocalCheckers.h" -#include "clang/Analysis/DomainSpecific/CocoaConventions.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/PathSensitive/ExprEngineBuilders.h" -#include "clang/GR/PathSensitive/GRStateTrait.h" -#include "clang/GR/PathSensitive/TransferFuncs.h" -#include "clang/GR/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/GR/CMakeLists.txt b/lib/GR/CMakeLists.txt deleted file mode 100644 index 748f7123f7..0000000000 --- a/lib/GR/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(clangGRCore - 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(clangGRCore ClangAttrClasses ClangAttrList ClangDeclNodes - ClangStmtNodes) diff --git a/lib/GR/CXXExprEngine.cpp b/lib/GR/CXXExprEngine.cpp deleted file mode 100644 index eee90f2b21..0000000000 --- a/lib/GR/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/GR/PathSensitive/AnalysisManager.h" -#include "clang/GR/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/GR/Checker.cpp b/lib/GR/Checker.cpp deleted file mode 100644 index 975d8475bc..0000000000 --- a/lib/GR/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/GR/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/GR/CheckerHelpers.cpp b/lib/GR/CheckerHelpers.cpp deleted file mode 100644 index 81e83399ed..0000000000 --- a/lib/GR/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/GR/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/GR/Checkers/AdjustedReturnValueChecker.cpp b/lib/GR/Checkers/AdjustedReturnValueChecker.cpp deleted file mode 100644 index d4e5389f85..0000000000 --- a/lib/GR/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/GR/BugReporter/BugReporter.h" -#include "clang/GR/PathSensitive/ExprEngine.h" -#include "clang/GR/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/GR/Checkers/AnalysisConsumer.cpp b/lib/GR/Checkers/AnalysisConsumer.cpp deleted file mode 100644 index f70de0596b..0000000000 --- a/lib/GR/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/GR/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/GR/Checkers/LocalCheckers.h" -#include "clang/GR/ManagerRegistry.h" -#include "clang/GR/BugReporter/PathDiagnostic.h" -#include "clang/GR/PathSensitive/AnalysisManager.h" -#include "clang/GR/BugReporter/BugReporter.h" -#include "clang/GR/PathSensitive/ExprEngine.h" -#include "clang/GR/PathSensitive/TransferFuncs.h" -#include "clang/GR/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/GR/Checkers/ArrayBoundChecker.cpp b/lib/GR/Checkers/ArrayBoundChecker.cpp deleted file mode 100644 index 9d4e3c4fd9..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/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/GR/Checkers/ArrayBoundCheckerV2.cpp b/lib/GR/Checkers/ArrayBoundCheckerV2.cpp deleted file mode 100644 index 1753ac52a8..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/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/GR/Checkers/AttrNonNullChecker.cpp b/lib/GR/Checkers/AttrNonNullChecker.cpp deleted file mode 100644 index 5c2aede815..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/BasicObjCFoundationChecks.cpp b/lib/GR/Checkers/BasicObjCFoundationChecks.cpp deleted file mode 100644 index a172477bd6..0000000000 --- a/lib/GR/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/GR/PathSensitive/ExplodedGraph.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/PathSensitive/ExprEngine.h" -#include "clang/GR/PathSensitive/GRState.h" -#include "clang/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/MemRegion.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/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/GR/Checkers/BasicObjCFoundationChecks.h b/lib/GR/Checkers/BasicObjCFoundationChecks.h deleted file mode 100644 index f4966e8ae8..0000000000 --- a/lib/GR/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/GR/Checkers/BuiltinFunctionChecker.cpp b/lib/GR/Checkers/BuiltinFunctionChecker.cpp deleted file mode 100644 index 697f0a4c00..0000000000 --- a/lib/GR/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/GR/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/GR/Checkers/CMakeLists.txt b/lib/GR/Checkers/CMakeLists.txt deleted file mode 100644 index 2f2c7a7b5c..0000000000 --- a/lib/GR/Checkers/CMakeLists.txt +++ /dev/null @@ -1,53 +0,0 @@ -add_clang_library(clangGRCheckers - 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(clangGRCore ClangAttrClasses ClangAttrList ClangDeclNodes - ClangStmtNodes) diff --git a/lib/GR/Checkers/CStringChecker.cpp b/lib/GR/Checkers/CStringChecker.cpp deleted file mode 100644 index d658c2f3dd..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/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/GR/Checkers/CallAndMessageChecker.cpp b/lib/GR/Checkers/CallAndMessageChecker.cpp deleted file mode 100644 index 24fc20ef13..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/CastSizeChecker.cpp b/lib/GR/Checkers/CastSizeChecker.cpp deleted file mode 100644 index 7844f51f06..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/CastToStructChecker.cpp b/lib/GR/Checkers/CastToStructChecker.cpp deleted file mode 100644 index 236623fc4a..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/CheckDeadStores.cpp b/lib/GR/Checkers/CheckDeadStores.cpp deleted file mode 100644 index d2ffd6acb4..0000000000 --- a/lib/GR/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/GR/Checkers/LocalCheckers.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/Analysis/Visitors/CFGRecStmtVisitor.h" -#include "clang/GR/BugReporter/BugReporter.h" -#include "clang/GR/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/GR/Checkers/CheckObjCDealloc.cpp b/lib/GR/Checkers/CheckObjCDealloc.cpp deleted file mode 100644 index 1aa0249282..0000000000 --- a/lib/GR/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/GR/Checkers/LocalCheckers.h" -#include "clang/GR/BugReporter/PathDiagnostic.h" -#include "clang/GR/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/GR/Checkers/CheckObjCInstMethSignature.cpp b/lib/GR/Checkers/CheckObjCInstMethSignature.cpp deleted file mode 100644 index 62c5e60005..0000000000 --- a/lib/GR/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/GR/Checkers/LocalCheckers.h" -#include "clang/GR/BugReporter/PathDiagnostic.h" -#include "clang/GR/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/GR/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/GR/Checkers/CheckSecuritySyntaxOnly.cpp deleted file mode 100644 index bdb03f8038..0000000000 --- a/lib/GR/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/GR/BugReporter/BugReporter.h" -#include "clang/GR/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/GR/Checkers/CheckSizeofPointer.cpp b/lib/GR/Checkers/CheckSizeofPointer.cpp deleted file mode 100644 index 3a2e8c7b50..0000000000 --- a/lib/GR/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/GR/BugReporter/BugReporter.h" -#include "clang/AST/StmtVisitor.h" -#include "clang/GR/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/GR/Checkers/ChrootChecker.cpp b/lib/GR/Checkers/ChrootChecker.cpp deleted file mode 100644 index 6a36593894..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/PathSensitive/GRState.h" -#include "clang/GR/PathSensitive/GRStateTrait.h" -#include "clang/GR/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/GR/Checkers/DereferenceChecker.cpp b/lib/GR/Checkers/DereferenceChecker.cpp deleted file mode 100644 index cecea57192..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/Checkers/DereferenceChecker.h" -#include "clang/GR/PathSensitive/Checker.h" -#include "clang/GR/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/GR/Checkers/DivZeroChecker.cpp b/lib/GR/Checkers/DivZeroChecker.cpp deleted file mode 100644 index 2119a0ca2f..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/ExprEngine.cpp b/lib/GR/Checkers/ExprEngine.cpp deleted file mode 100644 index 2ff665c941..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/AnalysisManager.h" -#include "clang/GR/PathSensitive/ExprEngine.h" -#include "clang/GR/PathSensitive/ExprEngineBuilders.h" -#include "clang/GR/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/GR/Checkers/ExprEngineExperimentalChecks.cpp b/lib/GR/Checkers/ExprEngineExperimentalChecks.cpp deleted file mode 100644 index 9f3c04ae91..0000000000 --- a/lib/GR/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/GR/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/GR/Checkers/ExprEngineExperimentalChecks.h b/lib/GR/Checkers/ExprEngineExperimentalChecks.h deleted file mode 100644 index f8359d1c5c..0000000000 --- a/lib/GR/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/GR/Checkers/ExprEngineInternalChecks.h b/lib/GR/Checkers/ExprEngineInternalChecks.h deleted file mode 100644 index f67371da0f..0000000000 --- a/lib/GR/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/GR/Checkers/FixedAddressChecker.cpp b/lib/GR/Checkers/FixedAddressChecker.cpp deleted file mode 100644 index 1d629ee55e..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/FrontendActions.cpp b/lib/GR/Checkers/FrontendActions.cpp deleted file mode 100644 index c88378279a..0000000000 --- a/lib/GR/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/GR/FrontendActions.h" -#include "clang/Frontend/CompilerInstance.h" -#include "clang/GR/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/GR/Checkers/IdempotentOperationChecker.cpp b/lib/GR/Checkers/IdempotentOperationChecker.cpp deleted file mode 100644 index 56bb542e92..0000000000 --- a/lib/GR/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/GR/BugReporter/BugReporter.h" -#include "clang/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerHelpers.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/PathSensitive/CoreEngine.h" -#include "clang/GR/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/GR/Checkers/LLVMConventionsChecker.cpp b/lib/GR/Checkers/LLVMConventionsChecker.cpp deleted file mode 100644 index 864dd85093..0000000000 --- a/lib/GR/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/GR/Checkers/LocalCheckers.h" -#include "clang/GR/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/GR/Checkers/MacOSXAPIChecker.cpp b/lib/GR/Checkers/MacOSXAPIChecker.cpp deleted file mode 100644 index 5934c5ffbf..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/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/GR/Checkers/Makefile b/lib/GR/Checkers/Makefile deleted file mode 100644 index b3d213ab07..0000000000 --- a/lib/GR/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 := clangGRCheckers - -include $(CLANG_LEVEL)/Makefile diff --git a/lib/GR/Checkers/MallocChecker.cpp b/lib/GR/Checkers/MallocChecker.cpp deleted file mode 100644 index a4d317f8b0..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/PathSensitive/GRState.h" -#include "clang/GR/PathSensitive/GRStateTrait.h" -#include "clang/GR/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/GR/Checkers/NSAutoreleasePoolChecker.cpp b/lib/GR/Checkers/NSAutoreleasePoolChecker.cpp deleted file mode 100644 index c93121fcd9..0000000000 --- a/lib/GR/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/GR/BugReporter/BugReporter.h" -#include "clang/GR/PathSensitive/ExprEngine.h" -#include "clang/GR/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/GR/Checkers/NSErrorChecker.cpp b/lib/GR/Checkers/NSErrorChecker.cpp deleted file mode 100644 index ee3f901ad5..0000000000 --- a/lib/GR/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/GR/Checkers/LocalCheckers.h" -#include "clang/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/ExprEngine.h" -#include "clang/GR/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/GR/Checkers/NoReturnFunctionChecker.cpp b/lib/GR/Checkers/NoReturnFunctionChecker.cpp deleted file mode 100644 index ad86ac500d..0000000000 --- a/lib/GR/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/GR/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/GR/Checkers/OSAtomicChecker.cpp b/lib/GR/Checkers/OSAtomicChecker.cpp deleted file mode 100644 index 3d230ce76e..0000000000 --- a/lib/GR/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/GR/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/GR/Checkers/ObjCAtSyncChecker.cpp b/lib/GR/Checkers/ObjCAtSyncChecker.cpp deleted file mode 100644 index b44fae8b6c..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/Checkers/DereferenceChecker.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/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/GR/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/GR/Checkers/ObjCUnusedIVarsChecker.cpp deleted file mode 100644 index 177f6a2267..0000000000 --- a/lib/GR/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/GR/Checkers/LocalCheckers.h" -#include "clang/GR/BugReporter/PathDiagnostic.h" -#include "clang/GR/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/GR/Checkers/PointerArithChecker.cpp b/lib/GR/Checkers/PointerArithChecker.cpp deleted file mode 100644 index f40eeec52f..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/PointerSubChecker.cpp b/lib/GR/Checkers/PointerSubChecker.cpp deleted file mode 100644 index 63aa90429e..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/PthreadLockChecker.cpp b/lib/GR/Checkers/PthreadLockChecker.cpp deleted file mode 100644 index 517f6b15ab..0000000000 --- a/lib/GR/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/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/BugReporter/BugReporter.h" -#include "clang/GR/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/GR/Checkers/ReturnPointerRangeChecker.cpp b/lib/GR/Checkers/ReturnPointerRangeChecker.cpp deleted file mode 100644 index 80316f58c1..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/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/GR/Checkers/ReturnUndefChecker.cpp b/lib/GR/Checkers/ReturnUndefChecker.cpp deleted file mode 100644 index 17a2a55ebd..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/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/GR/Checkers/StackAddrLeakChecker.cpp b/lib/GR/Checkers/StackAddrLeakChecker.cpp deleted file mode 100644 index 229356ad67..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/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/GR/Checkers/StreamChecker.cpp b/lib/GR/Checkers/StreamChecker.cpp deleted file mode 100644 index 32e22b0419..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/PathSensitive/GRState.h" -#include "clang/GR/PathSensitive/GRStateTrait.h" -#include "clang/GR/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/GR/Checkers/UndefBranchChecker.cpp b/lib/GR/Checkers/UndefBranchChecker.cpp deleted file mode 100644 index fc668be35c..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/GR/Checkers/UndefCapturedBlockVarChecker.cpp deleted file mode 100644 index 8ceda3ddc5..0000000000 --- a/lib/GR/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/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/PathSensitive/ExprEngine.h" -#include "clang/GR/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/GR/Checkers/UndefResultChecker.cpp b/lib/GR/Checkers/UndefResultChecker.cpp deleted file mode 100644 index 3d4faa2988..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/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/GR/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/GR/Checkers/UndefinedArraySubscriptChecker.cpp deleted file mode 100644 index 90f7d5db41..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/UndefinedAssignmentChecker.cpp b/lib/GR/Checkers/UndefinedAssignmentChecker.cpp deleted file mode 100644 index 8addd4458e..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/UnixAPIChecker.cpp b/lib/GR/Checkers/UnixAPIChecker.cpp deleted file mode 100644 index b9d8b57794..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/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/GR/Checkers/UnreachableCodeChecker.cpp b/lib/GR/Checkers/UnreachableCodeChecker.cpp deleted file mode 100644 index b52dc87c04..0000000000 --- a/lib/GR/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/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/PathSensitive/ExplodedGraph.h" -#include "clang/GR/PathSensitive/SVals.h" -#include "clang/GR/PathSensitive/CheckerHelpers.h" -#include "clang/GR/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/GR/Checkers/VLASizeChecker.cpp b/lib/GR/Checkers/VLASizeChecker.cpp deleted file mode 100644 index d21ad21780..0000000000 --- a/lib/GR/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/GR/BugReporter/BugType.h" -#include "clang/GR/PathSensitive/CheckerVisitor.h" -#include "clang/GR/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/GR/CoreEngine.cpp b/lib/GR/CoreEngine.cpp deleted file mode 100644 index 3236b92378..0000000000 --- a/lib/GR/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/GR/PathSensitive/AnalysisManager.h" -#include "clang/GR/PathSensitive/CoreEngine.h" -#include "clang/GR/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/GR/Environment.cpp b/lib/GR/Environment.cpp deleted file mode 100644 index 4961704509..0000000000 --- a/lib/GR/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/GR/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/GR/ExplodedGraph.cpp b/lib/GR/ExplodedGraph.cpp deleted file mode 100644 index dd206aba00..0000000000 --- a/lib/GR/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/GR/PathSensitive/ExplodedGraph.h" -#include "clang/GR/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/GR/FlatStore.cpp b/lib/GR/FlatStore.cpp deleted file mode 100644 index 6809c3b53c..0000000000 --- a/lib/GR/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/GR/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/GR/GRState.cpp b/lib/GR/GRState.cpp deleted file mode 100644 index e531cc9d99..0000000000 --- a/lib/GR/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/GR/PathSensitive/GRStateTrait.h" -#include "clang/GR/PathSensitive/GRState.h" -#include "clang/GR/PathSensitive/SubEngine.h" -#include "clang/GR/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/GR/HTMLDiagnostics.cpp b/lib/GR/HTMLDiagnostics.cpp deleted file mode 100644 index 72a854a85d..0000000000 --- a/lib/GR/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/GR/PathDiagnosticClients.h" -#include "clang/GR/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/GR/Makefile b/lib/GR/Makefile deleted file mode 100644 index 3261983107..0000000000 --- a/lib/GR/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 := clangGRCore -PARALLEL_DIRS := Checkers - -include $(CLANG_LEVEL)/Makefile - diff --git a/lib/GR/ManagerRegistry.cpp b/lib/GR/ManagerRegistry.cpp deleted file mode 100644 index 4058c11f7c..0000000000 --- a/lib/GR/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/GR/ManagerRegistry.h" - -using namespace clang; -using namespace ento; - -StoreManagerCreator ManagerRegistry::StoreMgrCreator = 0; - -ConstraintManagerCreator ManagerRegistry::ConstraintMgrCreator = 0; diff --git a/lib/GR/MemRegion.cpp b/lib/GR/MemRegion.cpp deleted file mode 100644 index f16223636f..0000000000 --- a/lib/GR/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/GR/PathSensitive/MemRegion.h" -#include "clang/GR/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/GR/PathDiagnostic.cpp b/lib/GR/PathDiagnostic.cpp deleted file mode 100644 index 69c660b4a4..0000000000 --- a/lib/GR/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/GR/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/GR/PlistDiagnostics.cpp b/lib/GR/PlistDiagnostics.cpp deleted file mode 100644 index ebab2b2002..0000000000 --- a/lib/GR/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/GR/PathDiagnosticClients.h" -#include "clang/GR/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/GR/README.txt b/lib/GR/README.txt deleted file mode 100644 index dd16ccc980..0000000000 --- a/lib/GR/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/GR/RangeConstraintManager.cpp b/lib/GR/RangeConstraintManager.cpp deleted file mode 100644 index 5ce69bafd1..0000000000 --- a/lib/GR/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/GR/PathSensitive/GRState.h" -#include "clang/GR/PathSensitive/GRStateTrait.h" -#include "clang/GR/PathSensitive/TransferFuncs.h" -#include "clang/GR/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/GR/RegionStore.cpp b/lib/GR/RegionStore.cpp deleted file mode 100644 index 0ae68cf79a..0000000000 --- a/lib/GR/RegionStore.cpp +++ /dev/null @@ -1,1872 +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/GR/PathSensitive/GRState.h" -#include "clang/GR/PathSensitive/GRStateTrait.h" -#include "clang/GR/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 = cast(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/GR/SValBuilder.cpp b/lib/GR/SValBuilder.cpp deleted file mode 100644 index eec5193ef3..0000000000 --- a/lib/GR/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/GR/PathSensitive/MemRegion.h" -#include "clang/GR/PathSensitive/SVals.h" -#include "clang/GR/PathSensitive/SValBuilder.h" -#include "clang/GR/PathSensitive/GRState.h" -#include "clang/GR/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/GR/SVals.cpp b/lib/GR/SVals.cpp deleted file mode 100644 index ed2bc13e49..0000000000 --- a/lib/GR/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/GR/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/GR/SimpleConstraintManager.cpp b/lib/GR/SimpleConstraintManager.cpp deleted file mode 100644 index f0d50da299..0000000000 --- a/lib/GR/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/GR/PathSensitive/ExprEngine.h" -#include "clang/GR/PathSensitive/GRState.h" -#include "clang/GR/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/GR/SimpleConstraintManager.h b/lib/GR/SimpleConstraintManager.h deleted file mode 100644 index 85d133f2f8..0000000000 --- a/lib/GR/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/GR/PathSensitive/ConstraintManager.h" -#include "clang/GR/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/GR/SimpleSValBuilder.cpp b/lib/GR/SimpleSValBuilder.cpp deleted file mode 100644 index bcd303e8fb..0000000000 --- a/lib/GR/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/GR/PathSensitive/SValBuilder.h" -#include "clang/GR/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/GR/Store.cpp b/lib/GR/Store.cpp deleted file mode 100644 index 2e9857a9a0..0000000000 --- a/lib/GR/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/GR/PathSensitive/Store.h" -#include "clang/GR/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/GR/SymbolManager.cpp b/lib/GR/SymbolManager.cpp deleted file mode 100644 index 7727138b02..0000000000 --- a/lib/GR/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/GR/PathSensitive/SymbolManager.h" -#include "clang/Analysis/Analyses/LiveVariables.h" -#include "clang/GR/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/GR/TextPathDiagnostics.cpp b/lib/GR/TextPathDiagnostics.cpp deleted file mode 100644 index 02e6c2e8d6..0000000000 --- a/lib/GR/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/GR/PathDiagnosticClients.h" -#include "clang/GR/BugReporter/PathDiagnostic.h" -#include "clang/Lex/Preprocessor.h" -#include "llvm/Support/raw_ostream.h" -using namespace clang; -using namespace ento; -using namespace llvm; - -namespace { - -/// \brief Simple path diagnostic client used for outputting as diagnostic notes -/// the sequence of events. -class TextPathDiagnostics : public PathDiagnosticClient { - const std::string OutputFile; - Diagnostic &Diag; - -public: - TextPathDiagnostics(const std::string& output, Diagnostic &diag) - : OutputFile(output), Diag(diag) {} - - void HandlePathDiagnostic(const PathDiagnostic* D); - - void FlushDiagnostics(llvm::SmallVectorImpl *FilesMade) { } - - virtual llvm::StringRef getName() const { - return "TextPathDiagnostics"; - } - - PathGenerationScheme getGenerationScheme() const { return Minimal; } - bool supportsLogicalOpControlFlow() const { return true; } - bool supportsAllBlockEdges() const { return true; } - virtual bool useVerboseDescription() const { return true; } -}; - -} // end anonymous namespace - -PathDiagnosticClient* -ento::createTextPathDiagnosticClient(const std::string& out, - const Preprocessor &PP) { - return new TextPathDiagnostics(out, PP.getDiagnostics()); -} - -void TextPathDiagnostics::HandlePathDiagnostic(const PathDiagnostic* D) { - if (!D) - return; - - if (D->empty()) { - delete D; - return; - } - - for (PathDiagnostic::const_iterator I=D->begin(), E=D->end(); I != E; ++I) { - unsigned diagID = Diag.getDiagnosticIDs()->getCustomDiagID( - DiagnosticIDs::Note, I->getString()); - Diag.Report(I->getLocation().asLocation(), diagID); - } -} diff --git a/lib/Makefile b/lib/Makefile index 6509fd59c6..300b9ea097 100755 --- a/lib/Makefile +++ b/lib/Makefile @@ -9,7 +9,7 @@ CLANG_LEVEL := .. PARALLEL_DIRS = Headers Basic Lex Parse AST Sema CodeGen Analysis \ - GR Rewrite Serialization Frontend FrontendTool Index Driver + EntoSA Rewrite Serialization Frontend FrontendTool Index Driver include $(CLANG_LEVEL)/Makefile -- cgit v1.2.3-18-g5258