aboutsummaryrefslogtreecommitdiff
path: root/contrib/llvm/tools/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/llvm/tools/clang/lib/StaticAnalyzer')
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AllocationState.h39
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp178
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp142
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp93
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp356
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp1269
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp185
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp157
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp122
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp2493
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp292
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp62
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp620
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp149
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp122
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp1096
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp140
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp930
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp93
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp335
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp155
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp204
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp198
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp481
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp235
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp150
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp306
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp227
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp101
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp208
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp998
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp128
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp411
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp67
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp229
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp299
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp748
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp515
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp312
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h27
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp2408
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp748
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp316
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp1411
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp116
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h108
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp190
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h105
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp284
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h67
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp664
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp176
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp3133
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp340
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp252
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp90
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp740
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp81
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp323
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp145
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp219
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp140
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp1216
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp351
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp94
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp209
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp174
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp189
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp263
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp81
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp439
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp289
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp188
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp353
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp345
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp75
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp483
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp1547
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h393
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp794
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h85
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp92
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp123
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp205
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp271
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp371
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp1058
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp411
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp62
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp263
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp114
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp255
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp111
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp102
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp188
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp64
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp122
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h349
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp538
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp282
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp479
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp259
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp185
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp412
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp218
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp285
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/APSIntType.cpp49
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp62
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp156
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp350
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp85
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp3142
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp2479
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CallEvent.cpp1424
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp40
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp132
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp115
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp835
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp21
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp44
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp655
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp86
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp242
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp441
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp3115
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp1127
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp908
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp1088
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp288
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp31
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp1017
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/IssueHash.cpp205
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp294
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp100
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp1618
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp1416
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp1117
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h45
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp823
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp751
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp210
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp2591
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp1229
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp655
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp384
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp349
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp133
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp1343
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp561
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp14
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp567
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TaintManager.cpp23
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/WorkList.cpp314
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp841
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp771
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp137
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp247
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp28
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp42
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp115
-rw-r--r--contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h70
161 files changed, 76240 insertions, 0 deletions
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AllocationState.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AllocationState.h
new file mode 100644
index 000000000000..c8193f77f928
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AllocationState.h
@@ -0,0 +1,39 @@
+//===--- AllocationState.h ------------------------------------- *- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONSTATE_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_ALLOCATIONSTATE_H
+
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+
+namespace clang {
+namespace ento {
+
+namespace allocation_state {
+
+ProgramStateRef markReleased(ProgramStateRef State, SymbolRef Sym,
+ const Expr *Origin);
+
+/// This function provides an additional visitor that augments the bug report
+/// with information relevant to memory errors caused by the misuse of
+/// AF_InnerBuffer symbols.
+std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym);
+
+/// 'Sym' represents a pointer to the inner buffer of a container object.
+/// This function looks up the memory region of that object in
+/// DanglingInternalBufferChecker's program state map.
+const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym);
+
+} // end namespace allocation_state
+
+} // end namespace ento
+} // end namespace clang
+
+#endif
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
new file mode 100644
index 000000000000..b5d0f6620a1d
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
@@ -0,0 +1,178 @@
+//===- AnalysisOrderChecker - Print callbacks called ------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker prints callbacks that are called during analysis.
+// This is required to ensure that callbacks are fired in order
+// and do not duplicate or get lost.
+// Feel free to extend this checker with any callback you need to check.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class AnalysisOrderChecker
+ : public Checker<check::PreStmt<CastExpr>,
+ check::PostStmt<CastExpr>,
+ check::PreStmt<ArraySubscriptExpr>,
+ check::PostStmt<ArraySubscriptExpr>,
+ check::PreStmt<CXXNewExpr>,
+ check::PostStmt<CXXNewExpr>,
+ check::PreStmt<OffsetOfExpr>,
+ check::PostStmt<OffsetOfExpr>,
+ check::PreCall,
+ check::PostCall,
+ check::EndFunction,
+ check::NewAllocator,
+ check::Bind,
+ check::RegionChanges,
+ check::LiveSymbols> {
+
+ bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const {
+ return Opts.getCheckerBooleanOption("*", false, this) ||
+ Opts.getCheckerBooleanOption(CallbackName, false, this);
+ }
+
+ bool isCallbackEnabled(CheckerContext &C, StringRef CallbackName) const {
+ AnalyzerOptions &Opts = C.getAnalysisManager().getAnalyzerOptions();
+ return isCallbackEnabled(Opts, CallbackName);
+ }
+
+ bool isCallbackEnabled(ProgramStateRef State, StringRef CallbackName) const {
+ AnalyzerOptions &Opts = State->getStateManager().getOwningEngine()
+ .getAnalysisManager().getAnalyzerOptions();
+ return isCallbackEnabled(Opts, CallbackName);
+ }
+
+public:
+ void checkPreStmt(const CastExpr *CE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PreStmtCastExpr"))
+ llvm::errs() << "PreStmt<CastExpr> (Kind : " << CE->getCastKindName()
+ << ")\n";
+ }
+
+ void checkPostStmt(const CastExpr *CE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PostStmtCastExpr"))
+ llvm::errs() << "PostStmt<CastExpr> (Kind : " << CE->getCastKindName()
+ << ")\n";
+ }
+
+ void checkPreStmt(const ArraySubscriptExpr *SubExpr,
+ CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PreStmtArraySubscriptExpr"))
+ llvm::errs() << "PreStmt<ArraySubscriptExpr>\n";
+ }
+
+ void checkPostStmt(const ArraySubscriptExpr *SubExpr,
+ CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PostStmtArraySubscriptExpr"))
+ llvm::errs() << "PostStmt<ArraySubscriptExpr>\n";
+ }
+
+ void checkPreStmt(const CXXNewExpr *NE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PreStmtCXXNewExpr"))
+ llvm::errs() << "PreStmt<CXXNewExpr>\n";
+ }
+
+ void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PostStmtCXXNewExpr"))
+ llvm::errs() << "PostStmt<CXXNewExpr>\n";
+ }
+
+ void checkPreStmt(const OffsetOfExpr *OOE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PreStmtOffsetOfExpr"))
+ llvm::errs() << "PreStmt<OffsetOfExpr>\n";
+ }
+
+ void checkPostStmt(const OffsetOfExpr *OOE, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PostStmtOffsetOfExpr"))
+ llvm::errs() << "PostStmt<OffsetOfExpr>\n";
+ }
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PreCall")) {
+ llvm::errs() << "PreCall";
+ if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
+ llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
+ llvm::errs() << '\n';
+ }
+ }
+
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "PostCall")) {
+ llvm::errs() << "PostCall";
+ if (const NamedDecl *ND = dyn_cast_or_null<NamedDecl>(Call.getDecl()))
+ llvm::errs() << " (" << ND->getQualifiedNameAsString() << ')';
+ llvm::errs() << '\n';
+ }
+ }
+
+ void checkEndFunction(const ReturnStmt *S, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "EndFunction")) {
+ llvm::errs() << "EndFunction\nReturnStmt: " << (S ? "yes" : "no") << "\n";
+ if (!S)
+ return;
+
+ llvm::errs() << "CFGElement: ";
+ CFGStmtMap *Map = C.getCurrentAnalysisDeclContext()->getCFGStmtMap();
+ CFGElement LastElement = Map->getBlock(S)->back();
+
+ if (LastElement.getAs<CFGStmt>())
+ llvm::errs() << "CFGStmt\n";
+ else if (LastElement.getAs<CFGAutomaticObjDtor>())
+ llvm::errs() << "CFGAutomaticObjDtor\n";
+ }
+ }
+
+ void checkNewAllocator(const CXXNewExpr *CNE, SVal Target,
+ CheckerContext &C) const {
+ if (isCallbackEnabled(C, "NewAllocator"))
+ llvm::errs() << "NewAllocator\n";
+ }
+
+ void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {
+ if (isCallbackEnabled(C, "Bind"))
+ llvm::errs() << "Bind\n";
+ }
+
+ void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SymReaper) const {
+ if (isCallbackEnabled(State, "LiveSymbols"))
+ llvm::errs() << "LiveSymbols\n";
+ }
+
+ ProgramStateRef
+ checkRegionChanges(ProgramStateRef State,
+ const InvalidatedSymbols *Invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx, const CallEvent *Call) const {
+ if (isCallbackEnabled(State, "RegionChanges"))
+ llvm::errs() << "RegionChanges\n";
+ return State;
+ }
+};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Registration.
+//===----------------------------------------------------------------------===//
+
+void ento::registerAnalysisOrderChecker(CheckerManager &mgr) {
+ mgr.registerChecker<AnalysisOrderChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
new file mode 100644
index 000000000000..5e01012401b2
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
@@ -0,0 +1,142 @@
+//==--AnalyzerStatsChecker.cpp - Analyzer visitation statistics --*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This file reports various statistics about analyzer visitation.
+//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+#define DEBUG_TYPE "StatsChecker"
+
+STATISTIC(NumBlocks,
+ "The # of blocks in top level functions");
+STATISTIC(NumBlocksUnreachable,
+ "The # of unreachable blocks in analyzing top level functions");
+
+namespace {
+class AnalyzerStatsChecker : public Checker<check::EndAnalysis> {
+public:
+ void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const;
+};
+}
+
+void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
+ BugReporter &B,
+ ExprEngine &Eng) const {
+ const CFG *C = nullptr;
+ const SourceManager &SM = B.getSourceManager();
+ llvm::SmallPtrSet<const CFGBlock*, 32> reachable;
+
+ // Root node should have the location context of the top most function.
+ const ExplodedNode *GraphRoot = *G.roots_begin();
+ const LocationContext *LC = GraphRoot->getLocation().getLocationContext();
+
+ const Decl *D = LC->getDecl();
+
+ // Iterate over the exploded graph.
+ for (ExplodedGraph::node_iterator I = G.nodes_begin();
+ I != G.nodes_end(); ++I) {
+ const ProgramPoint &P = I->getLocation();
+
+ // Only check the coverage in the top level function (optimization).
+ if (D != P.getLocationContext()->getDecl())
+ continue;
+
+ if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
+ const CFGBlock *CB = BE->getBlock();
+ reachable.insert(CB);
+ }
+ }
+
+ // Get the CFG and the Decl of this block.
+ C = LC->getCFG();
+
+ unsigned total = 0, unreachable = 0;
+
+ // Find CFGBlocks that were not covered by any node
+ for (CFG::const_iterator I = C->begin(); I != C->end(); ++I) {
+ const CFGBlock *CB = *I;
+ ++total;
+ // Check if the block is unreachable
+ if (!reachable.count(CB)) {
+ ++unreachable;
+ }
+ }
+
+ // We never 'reach' the entry block, so correct the unreachable count
+ unreachable--;
+ // There is no BlockEntrance corresponding to the exit block as well, so
+ // assume it is reached as well.
+ unreachable--;
+
+ // Generate the warning string
+ SmallString<128> buf;
+ llvm::raw_svector_ostream output(buf);
+ PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
+ if (!Loc.isValid())
+ return;
+
+ if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
+ const NamedDecl *ND = cast<NamedDecl>(D);
+ output << *ND;
+ }
+ else if (isa<BlockDecl>(D)) {
+ output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn();
+ }
+
+ NumBlocksUnreachable += unreachable;
+ NumBlocks += total;
+ std::string NameOfRootFunction = output.str();
+
+ output << " -> Total CFGBlocks: " << total << " | Unreachable CFGBlocks: "
+ << unreachable << " | Exhausted Block: "
+ << (Eng.wasBlocksExhausted() ? "yes" : "no")
+ << " | Empty WorkList: "
+ << (Eng.hasEmptyWorkList() ? "yes" : "no");
+
+ B.EmitBasicReport(D, this, "Analyzer Statistics", "Internal Statistics",
+ output.str(), PathDiagnosticLocation(D, SM));
+
+ // Emit warning for each block we bailed out on.
+ typedef CoreEngine::BlocksExhausted::const_iterator ExhaustedIterator;
+ const CoreEngine &CE = Eng.getCoreEngine();
+ for (ExhaustedIterator I = CE.blocks_exhausted_begin(),
+ E = CE.blocks_exhausted_end(); I != E; ++I) {
+ const BlockEdge &BE = I->first;
+ const CFGBlock *Exit = BE.getDst();
+ if (Exit->empty())
+ continue;
+ const CFGElement &CE = Exit->front();
+ if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
+ SmallString<128> bufI;
+ llvm::raw_svector_ostream outputI(bufI);
+ outputI << "(" << NameOfRootFunction << ")" <<
+ ": The analyzer generated a sink at this point";
+ B.EmitBasicReport(
+ D, this, "Sink Point", "Internal Statistics", outputI.str(),
+ PathDiagnosticLocation::createBegin(CS->getStmt(), SM, LC));
+ }
+ }
+}
+
+void ento::registerAnalyzerStatsChecker(CheckerManager &mgr) {
+ mgr.registerChecker<AnalyzerStatsChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
new file mode 100644
index 000000000000..20f3092fdba4
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
@@ -0,0 +1,93 @@
+//== ArrayBoundChecker.cpp ------------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ArrayBoundChecker, which is a path-sensitive check
+// which looks for an out-of-bound array element access.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ArrayBoundChecker :
+ public Checker<check::Location> {
+ mutable std::unique_ptr<BuiltinBug> BT;
+
+public:
+ void checkLocation(SVal l, bool isLoad, const Stmt* S,
+ CheckerContext &C) const;
+};
+}
+
+void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
+ CheckerContext &C) const {
+ // Check for out of bound array element access.
+ const MemRegion *R = l.getAsRegion();
+ if (!R)
+ return;
+
+ const ElementRegion *ER = dyn_cast<ElementRegion>(R);
+ if (!ER)
+ return;
+
+ // Get the index of the accessed element.
+ DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
+
+ // Zero index is always in bound, this also passes ElementRegions created for
+ // pointer casts.
+ if (Idx.isZeroConstant())
+ return;
+
+ ProgramStateRef state = C.getState();
+
+ // Get the size of the array.
+ DefinedOrUnknownSVal NumElements
+ = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
+ ER->getValueType());
+
+ ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
+ ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
+ if (StOutBound && !StInBound) {
+ ExplodedNode *N = C.generateErrorNode(StOutBound);
+ if (!N)
+ return;
+
+ if (!BT)
+ BT.reset(new BuiltinBug(
+ this, "Out-of-bound array access",
+ "Access out-of-bound array element (buffer overflow)"));
+
+ // FIXME: It would be nice to eventually make this diagnostic more clear,
+ // e.g., by referencing the original declaration or by saying *why* this
+ // reference is outside the range.
+
+ // Generate a report for this bug.
+ auto report = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N);
+
+ report->addRange(LoadS->getSourceRange());
+ C.emitReport(std::move(report));
+ return;
+ }
+
+ // Array bound check succeeded. From this point forward the array bound
+ // should always succeed.
+ C.addTransition(StInBound);
+}
+
+void ento::registerArrayBoundChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ArrayBoundChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
new file mode 100644
index 000000000000..26887be9f258
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
@@ -0,0 +1,356 @@
+//== ArrayBoundCheckerV2.cpp ------------------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ArrayBoundCheckerV2, which is a path-sensitive check
+// which looks for an out-of-bound array element access.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ArrayBoundCheckerV2 :
+ public Checker<check::Location> {
+ mutable std::unique_ptr<BuiltinBug> BT;
+
+ enum OOB_Kind { OOB_Precedes, OOB_Excedes, OOB_Tainted };
+
+ void reportOOB(CheckerContext &C, ProgramStateRef errorState, OOB_Kind kind,
+ std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
+
+public:
+ void checkLocation(SVal l, bool isLoad, const Stmt*S,
+ CheckerContext &C) const;
+};
+
+// FIXME: Eventually replace RegionRawOffset with this class.
+class RegionRawOffsetV2 {
+private:
+ const SubRegion *baseRegion;
+ SVal byteOffset;
+
+ RegionRawOffsetV2()
+ : baseRegion(nullptr), byteOffset(UnknownVal()) {}
+
+public:
+ RegionRawOffsetV2(const SubRegion* base, SVal offset)
+ : baseRegion(base), byteOffset(offset) {}
+
+ NonLoc getByteOffset() const { return byteOffset.castAs<NonLoc>(); }
+ const SubRegion *getRegion() const { return baseRegion; }
+
+ static RegionRawOffsetV2 computeOffset(ProgramStateRef state,
+ SValBuilder &svalBuilder,
+ SVal location);
+
+ void dump() const;
+ void dumpToStream(raw_ostream &os) const;
+};
+}
+
+static SVal computeExtentBegin(SValBuilder &svalBuilder,
+ const MemRegion *region) {
+ const MemSpaceRegion *SR = region->getMemorySpace();
+ if (SR->getKind() == MemRegion::UnknownSpaceRegionKind)
+ return UnknownVal();
+ else
+ return svalBuilder.makeZeroArrayIndex();
+}
+
+// TODO: once the constraint manager is smart enough to handle non simplified
+// symbolic expressions remove this function. Note that this can not be used in
+// the constraint manager as is, since this does not handle overflows. It is
+// safe to assume, however, that memory offsets will not overflow.
+static std::pair<NonLoc, nonloc::ConcreteInt>
+getSimplifiedOffsets(NonLoc offset, nonloc::ConcreteInt extent,
+ SValBuilder &svalBuilder) {
+ Optional<nonloc::SymbolVal> SymVal = offset.getAs<nonloc::SymbolVal>();
+ if (SymVal && SymVal->isExpression()) {
+ if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SymVal->getSymbol())) {
+ llvm::APSInt constant =
+ APSIntType(extent.getValue()).convert(SIE->getRHS());
+ switch (SIE->getOpcode()) {
+ case BO_Mul:
+ // The constant should never be 0 here, since it the result of scaling
+ // based on the size of a type which is never 0.
+ if ((extent.getValue() % constant) != 0)
+ return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent);
+ else
+ return getSimplifiedOffsets(
+ nonloc::SymbolVal(SIE->getLHS()),
+ svalBuilder.makeIntVal(extent.getValue() / constant),
+ svalBuilder);
+ case BO_Add:
+ return getSimplifiedOffsets(
+ nonloc::SymbolVal(SIE->getLHS()),
+ svalBuilder.makeIntVal(extent.getValue() - constant), svalBuilder);
+ default:
+ break;
+ }
+ }
+ }
+
+ return std::pair<NonLoc, nonloc::ConcreteInt>(offset, extent);
+}
+
+void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
+ const Stmt* LoadS,
+ CheckerContext &checkerContext) const {
+
+ // NOTE: Instead of using ProgramState::assumeInBound(), we are prototyping
+ // some new logic here that reasons directly about memory region extents.
+ // Once that logic is more mature, we can bring it back to assumeInBound()
+ // for all clients to use.
+ //
+ // The algorithm we are using here for bounds checking is to see if the
+ // memory access is within the extent of the base region. Since we
+ // have some flexibility in defining the base region, we can achieve
+ // various levels of conservatism in our buffer overflow checking.
+ ProgramStateRef state = checkerContext.getState();
+
+ SValBuilder &svalBuilder = checkerContext.getSValBuilder();
+ const RegionRawOffsetV2 &rawOffset =
+ RegionRawOffsetV2::computeOffset(state, svalBuilder, location);
+
+ if (!rawOffset.getRegion())
+ return;
+
+ NonLoc rawOffsetVal = rawOffset.getByteOffset();
+
+ // CHECK LOWER BOUND: Is byteOffset < extent begin?
+ // If so, we are doing a load/store
+ // before the first valid offset in the memory region.
+
+ SVal extentBegin = computeExtentBegin(svalBuilder, rawOffset.getRegion());
+
+ if (Optional<NonLoc> NV = extentBegin.getAs<NonLoc>()) {
+ if (NV->getAs<nonloc::ConcreteInt>()) {
+ std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
+ getSimplifiedOffsets(rawOffset.getByteOffset(),
+ NV->castAs<nonloc::ConcreteInt>(),
+ svalBuilder);
+ rawOffsetVal = simplifiedOffsets.first;
+ *NV = simplifiedOffsets.second;
+ }
+
+ SVal lowerBound = svalBuilder.evalBinOpNN(state, BO_LT, rawOffsetVal, *NV,
+ svalBuilder.getConditionType());
+
+ Optional<NonLoc> lowerBoundToCheck = lowerBound.getAs<NonLoc>();
+ if (!lowerBoundToCheck)
+ return;
+
+ ProgramStateRef state_precedesLowerBound, state_withinLowerBound;
+ std::tie(state_precedesLowerBound, state_withinLowerBound) =
+ state->assume(*lowerBoundToCheck);
+
+ // Are we constrained enough to definitely precede the lower bound?
+ if (state_precedesLowerBound && !state_withinLowerBound) {
+ reportOOB(checkerContext, state_precedesLowerBound, OOB_Precedes);
+ return;
+ }
+
+ // Otherwise, assume the constraint of the lower bound.
+ assert(state_withinLowerBound);
+ state = state_withinLowerBound;
+ }
+
+ do {
+ // CHECK UPPER BOUND: Is byteOffset >= extent(baseRegion)? If so,
+ // we are doing a load/store after the last valid offset.
+ DefinedOrUnknownSVal extentVal =
+ rawOffset.getRegion()->getExtent(svalBuilder);
+ if (!extentVal.getAs<NonLoc>())
+ break;
+
+ if (extentVal.getAs<nonloc::ConcreteInt>()) {
+ std::pair<NonLoc, nonloc::ConcreteInt> simplifiedOffsets =
+ getSimplifiedOffsets(rawOffset.getByteOffset(),
+ extentVal.castAs<nonloc::ConcreteInt>(),
+ svalBuilder);
+ rawOffsetVal = simplifiedOffsets.first;
+ extentVal = simplifiedOffsets.second;
+ }
+
+ SVal upperbound = svalBuilder.evalBinOpNN(state, BO_GE, rawOffsetVal,
+ extentVal.castAs<NonLoc>(),
+ svalBuilder.getConditionType());
+
+ Optional<NonLoc> upperboundToCheck = upperbound.getAs<NonLoc>();
+ if (!upperboundToCheck)
+ break;
+
+ ProgramStateRef state_exceedsUpperBound, state_withinUpperBound;
+ std::tie(state_exceedsUpperBound, state_withinUpperBound) =
+ state->assume(*upperboundToCheck);
+
+ // If we are under constrained and the index variables are tainted, report.
+ if (state_exceedsUpperBound && state_withinUpperBound) {
+ SVal ByteOffset = rawOffset.getByteOffset();
+ if (state->isTainted(ByteOffset)) {
+ reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted,
+ llvm::make_unique<TaintBugVisitor>(ByteOffset));
+ return;
+ }
+ } else if (state_exceedsUpperBound) {
+ // If we are constrained enough to definitely exceed the upper bound,
+ // report.
+ assert(!state_withinUpperBound);
+ reportOOB(checkerContext, state_exceedsUpperBound, OOB_Excedes);
+ return;
+ }
+
+ assert(state_withinUpperBound);
+ state = state_withinUpperBound;
+ }
+ while (false);
+
+ checkerContext.addTransition(state);
+}
+
+void ArrayBoundCheckerV2::reportOOB(
+ CheckerContext &checkerContext, ProgramStateRef errorState, OOB_Kind kind,
+ std::unique_ptr<BugReporterVisitor> Visitor) const {
+
+ ExplodedNode *errorNode = checkerContext.generateErrorNode(errorState);
+ if (!errorNode)
+ return;
+
+ if (!BT)
+ BT.reset(new BuiltinBug(this, "Out-of-bound access"));
+
+ // FIXME: This diagnostics are preliminary. We should get far better
+ // diagnostics for explaining buffer overruns.
+
+ SmallString<256> buf;
+ llvm::raw_svector_ostream os(buf);
+ os << "Out of bound memory access ";
+ switch (kind) {
+ case OOB_Precedes:
+ os << "(accessed memory precedes memory block)";
+ break;
+ case OOB_Excedes:
+ os << "(access exceeds upper limit of memory block)";
+ break;
+ case OOB_Tainted:
+ os << "(index is tainted)";
+ break;
+ }
+
+ auto BR = llvm::make_unique<BugReport>(*BT, os.str(), errorNode);
+ BR->addVisitor(std::move(Visitor));
+ checkerContext.emitReport(std::move(BR));
+}
+
+#ifndef NDEBUG
+LLVM_DUMP_METHOD void RegionRawOffsetV2::dump() const {
+ dumpToStream(llvm::errs());
+}
+
+void RegionRawOffsetV2::dumpToStream(raw_ostream &os) const {
+ os << "raw_offset_v2{" << getRegion() << ',' << getByteOffset() << '}';
+}
+#endif
+
+// Lazily computes a value to be used by 'computeOffset'. If 'val'
+// is unknown or undefined, we lazily substitute '0'. Otherwise,
+// return 'val'.
+static inline SVal getValue(SVal val, SValBuilder &svalBuilder) {
+ return val.getAs<UndefinedVal>() ? svalBuilder.makeArrayIndex(0) : val;
+}
+
+// Scale a base value by a scaling factor, and return the scaled
+// value as an SVal. Used by 'computeOffset'.
+static inline SVal scaleValue(ProgramStateRef state,
+ NonLoc baseVal, CharUnits scaling,
+ SValBuilder &sb) {
+ return sb.evalBinOpNN(state, BO_Mul, baseVal,
+ sb.makeArrayIndex(scaling.getQuantity()),
+ sb.getArrayIndexType());
+}
+
+// Add an SVal to another, treating unknown and undefined values as
+// summing to UnknownVal. Used by 'computeOffset'.
+static SVal addValue(ProgramStateRef state, SVal x, SVal y,
+ SValBuilder &svalBuilder) {
+ // We treat UnknownVals and UndefinedVals the same here because we
+ // only care about computing offsets.
+ if (x.isUnknownOrUndef() || y.isUnknownOrUndef())
+ return UnknownVal();
+
+ return svalBuilder.evalBinOpNN(state, BO_Add, x.castAs<NonLoc>(),
+ y.castAs<NonLoc>(),
+ svalBuilder.getArrayIndexType());
+}
+
+/// Compute a raw byte offset from a base region. Used for array bounds
+/// checking.
+RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state,
+ SValBuilder &svalBuilder,
+ SVal location)
+{
+ const MemRegion *region = location.getAsRegion();
+ SVal offset = UndefinedVal();
+
+ while (region) {
+ switch (region->getKind()) {
+ default: {
+ if (const SubRegion *subReg = dyn_cast<SubRegion>(region)) {
+ offset = getValue(offset, svalBuilder);
+ if (!offset.isUnknownOrUndef())
+ return RegionRawOffsetV2(subReg, offset);
+ }
+ return RegionRawOffsetV2();
+ }
+ case MemRegion::ElementRegionKind: {
+ const ElementRegion *elemReg = cast<ElementRegion>(region);
+ SVal index = elemReg->getIndex();
+ if (!index.getAs<NonLoc>())
+ return RegionRawOffsetV2();
+ QualType elemType = elemReg->getElementType();
+ // If the element is an incomplete type, go no further.
+ ASTContext &astContext = svalBuilder.getContext();
+ if (elemType->isIncompleteType())
+ return RegionRawOffsetV2();
+
+ // Update the offset.
+ offset = addValue(state,
+ getValue(offset, svalBuilder),
+ scaleValue(state,
+ index.castAs<NonLoc>(),
+ astContext.getTypeSizeInChars(elemType),
+ svalBuilder),
+ svalBuilder);
+
+ if (offset.isUnknownOrUndef())
+ return RegionRawOffsetV2();
+
+ region = elemReg->getSuperRegion();
+ continue;
+ }
+ }
+ }
+ return RegionRawOffsetV2();
+}
+
+void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) {
+ mgr.registerChecker<ArrayBoundCheckerV2>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
new file mode 100644
index 000000000000..577b5349f62e
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -0,0 +1,1269 @@
+//== BasicObjCFoundationChecks.cpp - Simple Apple-Foundation checks -*- C++ -*--
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines BasicObjCFoundationChecks, a class that encapsulates
+// a set of simple checks to run on Objective-C code using Apple's Foundation
+// classes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/StmtObjC.h"
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
+#include "clang/Analysis/SelectorExtras.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+using namespace llvm;
+
+namespace {
+class APIMisuse : public BugType {
+public:
+ APIMisuse(const CheckerBase *checker, const char *name)
+ : BugType(checker, name, "API Misuse (Apple)") {}
+};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Utility functions.
+//===----------------------------------------------------------------------===//
+
+static StringRef GetReceiverInterfaceName(const ObjCMethodCall &msg) {
+ if (const ObjCInterfaceDecl *ID = msg.getReceiverInterface())
+ return ID->getIdentifier()->getName();
+ return StringRef();
+}
+
+enum FoundationClass {
+ FC_None,
+ FC_NSArray,
+ FC_NSDictionary,
+ FC_NSEnumerator,
+ FC_NSNull,
+ FC_NSOrderedSet,
+ FC_NSSet,
+ FC_NSString
+};
+
+static FoundationClass findKnownClass(const ObjCInterfaceDecl *ID,
+ bool IncludeSuperclasses = true) {
+ static llvm::StringMap<FoundationClass> Classes;
+ if (Classes.empty()) {
+ Classes["NSArray"] = FC_NSArray;
+ Classes["NSDictionary"] = FC_NSDictionary;
+ Classes["NSEnumerator"] = FC_NSEnumerator;
+ Classes["NSNull"] = FC_NSNull;
+ Classes["NSOrderedSet"] = FC_NSOrderedSet;
+ Classes["NSSet"] = FC_NSSet;
+ Classes["NSString"] = FC_NSString;
+ }
+
+ // FIXME: Should we cache this at all?
+ FoundationClass result = Classes.lookup(ID->getIdentifier()->getName());
+ if (result == FC_None && IncludeSuperclasses)
+ if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
+ return findKnownClass(Super);
+
+ return result;
+}
+
+//===----------------------------------------------------------------------===//
+// NilArgChecker - Check for prohibited nil arguments to ObjC method calls.
+//===----------------------------------------------------------------------===//
+
+namespace {
+ class NilArgChecker : public Checker<check::PreObjCMessage,
+ check::PostStmt<ObjCDictionaryLiteral>,
+ check::PostStmt<ObjCArrayLiteral> > {
+ mutable std::unique_ptr<APIMisuse> BT;
+
+ mutable llvm::SmallDenseMap<Selector, unsigned, 16> StringSelectors;
+ mutable Selector ArrayWithObjectSel;
+ mutable Selector AddObjectSel;
+ mutable Selector InsertObjectAtIndexSel;
+ mutable Selector ReplaceObjectAtIndexWithObjectSel;
+ mutable Selector SetObjectAtIndexedSubscriptSel;
+ mutable Selector ArrayByAddingObjectSel;
+ mutable Selector DictionaryWithObjectForKeySel;
+ mutable Selector SetObjectForKeySel;
+ mutable Selector SetObjectForKeyedSubscriptSel;
+ mutable Selector RemoveObjectForKeySel;
+
+ void warnIfNilExpr(const Expr *E,
+ const char *Msg,
+ CheckerContext &C) const;
+
+ void warnIfNilArg(CheckerContext &C,
+ const ObjCMethodCall &msg, unsigned Arg,
+ FoundationClass Class,
+ bool CanBeSubscript = false) const;
+
+ void generateBugReport(ExplodedNode *N,
+ StringRef Msg,
+ SourceRange Range,
+ const Expr *Expr,
+ CheckerContext &C) const;
+
+ public:
+ void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+ void checkPostStmt(const ObjCDictionaryLiteral *DL,
+ CheckerContext &C) const;
+ void checkPostStmt(const ObjCArrayLiteral *AL,
+ CheckerContext &C) const;
+ };
+} // end anonymous namespace
+
+void NilArgChecker::warnIfNilExpr(const Expr *E,
+ const char *Msg,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ if (State->isNull(C.getSVal(E)).isConstrainedTrue()) {
+
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ generateBugReport(N, Msg, E->getSourceRange(), E, C);
+ }
+ }
+}
+
+void NilArgChecker::warnIfNilArg(CheckerContext &C,
+ const ObjCMethodCall &msg,
+ unsigned int Arg,
+ FoundationClass Class,
+ bool CanBeSubscript) const {
+ // Check if the argument is nil.
+ ProgramStateRef State = C.getState();
+ if (!State->isNull(msg.getArgSVal(Arg)).isConstrainedTrue())
+ return;
+
+ // NOTE: We cannot throw non-fatal errors from warnIfNilExpr,
+ // because it's called multiple times from some callers, so it'd cause
+ // an unwanted state split if two or more non-fatal errors are thrown
+ // within the same checker callback. For now we don't want to, but
+ // it'll need to be fixed if we ever want to.
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+
+ if (CanBeSubscript && msg.getMessageKind() == OCM_Subscript) {
+
+ if (Class == FC_NSArray) {
+ os << "Array element cannot be nil";
+ } else if (Class == FC_NSDictionary) {
+ if (Arg == 0) {
+ os << "Value stored into '";
+ os << GetReceiverInterfaceName(msg) << "' cannot be nil";
+ } else {
+ assert(Arg == 1);
+ os << "'"<< GetReceiverInterfaceName(msg) << "' key cannot be nil";
+ }
+ } else
+ llvm_unreachable("Missing foundation class for the subscript expr");
+
+ } else {
+ if (Class == FC_NSDictionary) {
+ if (Arg == 0)
+ os << "Value argument ";
+ else {
+ assert(Arg == 1);
+ os << "Key argument ";
+ }
+ os << "to '";
+ msg.getSelector().print(os);
+ os << "' cannot be nil";
+ } else {
+ os << "Argument to '" << GetReceiverInterfaceName(msg) << "' method '";
+ msg.getSelector().print(os);
+ os << "' cannot be nil";
+ }
+ }
+
+ generateBugReport(N, os.str(), msg.getArgSourceRange(Arg),
+ msg.getArgExpr(Arg), C);
+ }
+}
+
+void NilArgChecker::generateBugReport(ExplodedNode *N,
+ StringRef Msg,
+ SourceRange Range,
+ const Expr *E,
+ CheckerContext &C) const {
+ if (!BT)
+ BT.reset(new APIMisuse(this, "nil argument"));
+
+ auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
+ R->addRange(Range);
+ bugreporter::trackExpressionValue(N, E, *R);
+ C.emitReport(std::move(R));
+}
+
+void NilArgChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
+ CheckerContext &C) const {
+ const ObjCInterfaceDecl *ID = msg.getReceiverInterface();
+ if (!ID)
+ return;
+
+ FoundationClass Class = findKnownClass(ID);
+
+ static const unsigned InvalidArgIndex = UINT_MAX;
+ unsigned Arg = InvalidArgIndex;
+ bool CanBeSubscript = false;
+
+ if (Class == FC_NSString) {
+ Selector S = msg.getSelector();
+
+ if (S.isUnarySelector())
+ return;
+
+ if (StringSelectors.empty()) {
+ ASTContext &Ctx = C.getASTContext();
+ Selector Sels[] = {
+ getKeywordSelector(Ctx, "caseInsensitiveCompare"),
+ getKeywordSelector(Ctx, "compare"),
+ getKeywordSelector(Ctx, "compare", "options"),
+ getKeywordSelector(Ctx, "compare", "options", "range"),
+ getKeywordSelector(Ctx, "compare", "options", "range", "locale"),
+ getKeywordSelector(Ctx, "componentsSeparatedByCharactersInSet"),
+ getKeywordSelector(Ctx, "initWithFormat"),
+ getKeywordSelector(Ctx, "localizedCaseInsensitiveCompare"),
+ getKeywordSelector(Ctx, "localizedCompare"),
+ getKeywordSelector(Ctx, "localizedStandardCompare"),
+ };
+ for (Selector KnownSel : Sels)
+ StringSelectors[KnownSel] = 0;
+ }
+ auto I = StringSelectors.find(S);
+ if (I == StringSelectors.end())
+ return;
+ Arg = I->second;
+ } else if (Class == FC_NSArray) {
+ Selector S = msg.getSelector();
+
+ if (S.isUnarySelector())
+ return;
+
+ if (ArrayWithObjectSel.isNull()) {
+ ASTContext &Ctx = C.getASTContext();
+ ArrayWithObjectSel = getKeywordSelector(Ctx, "arrayWithObject");
+ AddObjectSel = getKeywordSelector(Ctx, "addObject");
+ InsertObjectAtIndexSel =
+ getKeywordSelector(Ctx, "insertObject", "atIndex");
+ ReplaceObjectAtIndexWithObjectSel =
+ getKeywordSelector(Ctx, "replaceObjectAtIndex", "withObject");
+ SetObjectAtIndexedSubscriptSel =
+ getKeywordSelector(Ctx, "setObject", "atIndexedSubscript");
+ ArrayByAddingObjectSel = getKeywordSelector(Ctx, "arrayByAddingObject");
+ }
+
+ if (S == ArrayWithObjectSel || S == AddObjectSel ||
+ S == InsertObjectAtIndexSel || S == ArrayByAddingObjectSel) {
+ Arg = 0;
+ } else if (S == SetObjectAtIndexedSubscriptSel) {
+ Arg = 0;
+ CanBeSubscript = true;
+ } else if (S == ReplaceObjectAtIndexWithObjectSel) {
+ Arg = 1;
+ }
+ } else if (Class == FC_NSDictionary) {
+ Selector S = msg.getSelector();
+
+ if (S.isUnarySelector())
+ return;
+
+ if (DictionaryWithObjectForKeySel.isNull()) {
+ ASTContext &Ctx = C.getASTContext();
+ DictionaryWithObjectForKeySel =
+ getKeywordSelector(Ctx, "dictionaryWithObject", "forKey");
+ SetObjectForKeySel = getKeywordSelector(Ctx, "setObject", "forKey");
+ SetObjectForKeyedSubscriptSel =
+ getKeywordSelector(Ctx, "setObject", "forKeyedSubscript");
+ RemoveObjectForKeySel = getKeywordSelector(Ctx, "removeObjectForKey");
+ }
+
+ if (S == DictionaryWithObjectForKeySel || S == SetObjectForKeySel) {
+ Arg = 0;
+ warnIfNilArg(C, msg, /* Arg */1, Class);
+ } else if (S == SetObjectForKeyedSubscriptSel) {
+ CanBeSubscript = true;
+ Arg = 1;
+ } else if (S == RemoveObjectForKeySel) {
+ Arg = 0;
+ }
+ }
+
+ // If argument is '0', report a warning.
+ if ((Arg != InvalidArgIndex))
+ warnIfNilArg(C, msg, Arg, Class, CanBeSubscript);
+}
+
+void NilArgChecker::checkPostStmt(const ObjCArrayLiteral *AL,
+ CheckerContext &C) const {
+ unsigned NumOfElements = AL->getNumElements();
+ for (unsigned i = 0; i < NumOfElements; ++i) {
+ warnIfNilExpr(AL->getElement(i), "Array element cannot be nil", C);
+ }
+}
+
+void NilArgChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
+ CheckerContext &C) const {
+ unsigned NumOfElements = DL->getNumElements();
+ for (unsigned i = 0; i < NumOfElements; ++i) {
+ ObjCDictionaryElement Element = DL->getKeyValueElement(i);
+ warnIfNilExpr(Element.Key, "Dictionary key cannot be nil", C);
+ warnIfNilExpr(Element.Value, "Dictionary value cannot be nil", C);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Checking for mismatched types passed to CFNumberCreate/CFNumberGetValue.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class CFNumberChecker : public Checker< check::PreStmt<CallExpr> > {
+ mutable std::unique_ptr<APIMisuse> BT;
+ mutable IdentifierInfo *ICreate, *IGetValue;
+public:
+ CFNumberChecker() : ICreate(nullptr), IGetValue(nullptr) {}
+
+ void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+
+private:
+ void EmitError(const TypedRegion* R, const Expr *Ex,
+ uint64_t SourceSize, uint64_t TargetSize, uint64_t NumberKind);
+};
+} // end anonymous namespace
+
+enum CFNumberType {
+ kCFNumberSInt8Type = 1,
+ kCFNumberSInt16Type = 2,
+ kCFNumberSInt32Type = 3,
+ kCFNumberSInt64Type = 4,
+ kCFNumberFloat32Type = 5,
+ kCFNumberFloat64Type = 6,
+ kCFNumberCharType = 7,
+ kCFNumberShortType = 8,
+ kCFNumberIntType = 9,
+ kCFNumberLongType = 10,
+ kCFNumberLongLongType = 11,
+ kCFNumberFloatType = 12,
+ kCFNumberDoubleType = 13,
+ kCFNumberCFIndexType = 14,
+ kCFNumberNSIntegerType = 15,
+ kCFNumberCGFloatType = 16
+};
+
+static Optional<uint64_t> GetCFNumberSize(ASTContext &Ctx, uint64_t i) {
+ static const unsigned char FixedSize[] = { 8, 16, 32, 64, 32, 64 };
+
+ if (i < kCFNumberCharType)
+ return FixedSize[i-1];
+
+ QualType T;
+
+ switch (i) {
+ case kCFNumberCharType: T = Ctx.CharTy; break;
+ case kCFNumberShortType: T = Ctx.ShortTy; break;
+ case kCFNumberIntType: T = Ctx.IntTy; break;
+ case kCFNumberLongType: T = Ctx.LongTy; break;
+ case kCFNumberLongLongType: T = Ctx.LongLongTy; break;
+ case kCFNumberFloatType: T = Ctx.FloatTy; break;
+ case kCFNumberDoubleType: T = Ctx.DoubleTy; break;
+ case kCFNumberCFIndexType:
+ case kCFNumberNSIntegerType:
+ case kCFNumberCGFloatType:
+ // FIXME: We need a way to map from names to Type*.
+ default:
+ return None;
+ }
+
+ return Ctx.getTypeSize(T);
+}
+
+#if 0
+static const char* GetCFNumberTypeStr(uint64_t i) {
+ static const char* Names[] = {
+ "kCFNumberSInt8Type",
+ "kCFNumberSInt16Type",
+ "kCFNumberSInt32Type",
+ "kCFNumberSInt64Type",
+ "kCFNumberFloat32Type",
+ "kCFNumberFloat64Type",
+ "kCFNumberCharType",
+ "kCFNumberShortType",
+ "kCFNumberIntType",
+ "kCFNumberLongType",
+ "kCFNumberLongLongType",
+ "kCFNumberFloatType",
+ "kCFNumberDoubleType",
+ "kCFNumberCFIndexType",
+ "kCFNumberNSIntegerType",
+ "kCFNumberCGFloatType"
+ };
+
+ return i <= kCFNumberCGFloatType ? Names[i-1] : "Invalid CFNumberType";
+}
+#endif
+
+void CFNumberChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ ProgramStateRef state = C.getState();
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD)
+ return;
+
+ ASTContext &Ctx = C.getASTContext();
+ if (!ICreate) {
+ ICreate = &Ctx.Idents.get("CFNumberCreate");
+ IGetValue = &Ctx.Idents.get("CFNumberGetValue");
+ }
+ if (!(FD->getIdentifier() == ICreate || FD->getIdentifier() == IGetValue) ||
+ CE->getNumArgs() != 3)
+ return;
+
+ // Get the value of the "theType" argument.
+ SVal TheTypeVal = C.getSVal(CE->getArg(1));
+
+ // FIXME: We really should allow ranges of valid theType values, and
+ // bifurcate the state appropriately.
+ Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>();
+ if (!V)
+ return;
+
+ uint64_t NumberKind = V->getValue().getLimitedValue();
+ Optional<uint64_t> OptCFNumberSize = GetCFNumberSize(Ctx, NumberKind);
+
+ // FIXME: In some cases we can emit an error.
+ if (!OptCFNumberSize)
+ return;
+
+ uint64_t CFNumberSize = *OptCFNumberSize;
+
+ // Look at the value of the integer being passed by reference. Essentially
+ // we want to catch cases where the value passed in is not equal to the
+ // size of the type being created.
+ SVal TheValueExpr = C.getSVal(CE->getArg(2));
+
+ // FIXME: Eventually we should handle arbitrary locations. We can do this
+ // by having an enhanced memory model that does low-level typing.
+ Optional<loc::MemRegionVal> LV = TheValueExpr.getAs<loc::MemRegionVal>();
+ if (!LV)
+ return;
+
+ const TypedValueRegion* R = dyn_cast<TypedValueRegion>(LV->stripCasts());
+ if (!R)
+ return;
+
+ QualType T = Ctx.getCanonicalType(R->getValueType());
+
+ // FIXME: If the pointee isn't an integer type, should we flag a warning?
+ // People can do weird stuff with pointers.
+
+ if (!T->isIntegralOrEnumerationType())
+ return;
+
+ uint64_t PrimitiveTypeSize = Ctx.getTypeSize(T);
+
+ if (PrimitiveTypeSize == CFNumberSize)
+ return;
+
+ // FIXME: We can actually create an abstract "CFNumber" object that has
+ // the bits initialized to the provided values.
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (N) {
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ bool isCreate = (FD->getIdentifier() == ICreate);
+
+ if (isCreate) {
+ os << (PrimitiveTypeSize == 8 ? "An " : "A ")
+ << PrimitiveTypeSize << "-bit integer is used to initialize a "
+ << "CFNumber object that represents "
+ << (CFNumberSize == 8 ? "an " : "a ")
+ << CFNumberSize << "-bit integer; ";
+ } else {
+ os << "A CFNumber object that represents "
+ << (CFNumberSize == 8 ? "an " : "a ")
+ << CFNumberSize << "-bit integer is used to initialize "
+ << (PrimitiveTypeSize == 8 ? "an " : "a ")
+ << PrimitiveTypeSize << "-bit integer; ";
+ }
+
+ if (PrimitiveTypeSize < CFNumberSize)
+ os << (CFNumberSize - PrimitiveTypeSize)
+ << " bits of the CFNumber value will "
+ << (isCreate ? "be garbage." : "overwrite adjacent storage.");
+ else
+ os << (PrimitiveTypeSize - CFNumberSize)
+ << " bits of the integer value will be "
+ << (isCreate ? "lost." : "garbage.");
+
+ if (!BT)
+ BT.reset(new APIMisuse(this, "Bad use of CFNumber APIs"));
+
+ auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ report->addRange(CE->getArg(2)->getSourceRange());
+ C.emitReport(std::move(report));
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// CFRetain/CFRelease/CFMakeCollectable/CFAutorelease checking for null arguments.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class CFRetainReleaseChecker : public Checker<check::PreCall> {
+ mutable APIMisuse BT{this, "null passed to CF memory management function"};
+ CallDescription CFRetain{"CFRetain", 1},
+ CFRelease{"CFRelease", 1},
+ CFMakeCollectable{"CFMakeCollectable", 1},
+ CFAutorelease{"CFAutorelease", 1};
+
+public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // TODO: Make this check part of CallDescription.
+ if (!Call.isGlobalCFunction())
+ return;
+
+ // Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease.
+ if (!(Call.isCalled(CFRetain) || Call.isCalled(CFRelease) ||
+ Call.isCalled(CFMakeCollectable) || Call.isCalled(CFAutorelease)))
+ return;
+
+ // Get the argument's value.
+ SVal ArgVal = Call.getArgSVal(0);
+ Optional<DefinedSVal> DefArgVal = ArgVal.getAs<DefinedSVal>();
+ if (!DefArgVal)
+ return;
+
+ // Is it null?
+ ProgramStateRef state = C.getState();
+ ProgramStateRef stateNonNull, stateNull;
+ std::tie(stateNonNull, stateNull) = state->assume(*DefArgVal);
+
+ if (!stateNonNull) {
+ ExplodedNode *N = C.generateErrorNode(stateNull);
+ if (!N)
+ return;
+
+ SmallString<64> Str;
+ raw_svector_ostream OS(Str);
+ OS << "Null pointer argument in call to "
+ << cast<FunctionDecl>(Call.getDecl())->getName();
+
+ auto report = llvm::make_unique<BugReport>(BT, OS.str(), N);
+ report->addRange(Call.getArgSourceRange(0));
+ bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *report);
+ C.emitReport(std::move(report));
+ return;
+ }
+
+ // From here on, we know the argument is non-null.
+ C.addTransition(stateNonNull);
+}
+
+//===----------------------------------------------------------------------===//
+// Check for sending 'retain', 'release', or 'autorelease' directly to a Class.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class ClassReleaseChecker : public Checker<check::PreObjCMessage> {
+ mutable Selector releaseS;
+ mutable Selector retainS;
+ mutable Selector autoreleaseS;
+ mutable Selector drainS;
+ mutable std::unique_ptr<BugType> BT;
+
+public:
+ void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+void ClassReleaseChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
+ CheckerContext &C) const {
+ if (!BT) {
+ BT.reset(new APIMisuse(
+ this, "message incorrectly sent to class instead of class instance"));
+
+ ASTContext &Ctx = C.getASTContext();
+ releaseS = GetNullarySelector("release", Ctx);
+ retainS = GetNullarySelector("retain", Ctx);
+ autoreleaseS = GetNullarySelector("autorelease", Ctx);
+ drainS = GetNullarySelector("drain", Ctx);
+ }
+
+ if (msg.isInstanceMessage())
+ return;
+ const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
+ assert(Class);
+
+ Selector S = msg.getSelector();
+ if (!(S == releaseS || S == retainS || S == autoreleaseS || S == drainS))
+ return;
+
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << "The '";
+ S.print(os);
+ os << "' message should be sent to instances "
+ "of class '" << Class->getName()
+ << "' and not the class directly";
+
+ auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ report->addRange(msg.getSourceRange());
+ C.emitReport(std::move(report));
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Check for passing non-Objective-C types to variadic methods that expect
+// only Objective-C types.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class VariadicMethodTypeChecker : public Checker<check::PreObjCMessage> {
+ mutable Selector arrayWithObjectsS;
+ mutable Selector dictionaryWithObjectsAndKeysS;
+ mutable Selector setWithObjectsS;
+ mutable Selector orderedSetWithObjectsS;
+ mutable Selector initWithObjectsS;
+ mutable Selector initWithObjectsAndKeysS;
+ mutable std::unique_ptr<BugType> BT;
+
+ bool isVariadicMessage(const ObjCMethodCall &msg) const;
+
+public:
+ void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+/// isVariadicMessage - Returns whether the given message is a variadic message,
+/// where all arguments must be Objective-C types.
+bool
+VariadicMethodTypeChecker::isVariadicMessage(const ObjCMethodCall &msg) const {
+ const ObjCMethodDecl *MD = msg.getDecl();
+
+ if (!MD || !MD->isVariadic() || isa<ObjCProtocolDecl>(MD->getDeclContext()))
+ return false;
+
+ Selector S = msg.getSelector();
+
+ if (msg.isInstanceMessage()) {
+ // FIXME: Ideally we'd look at the receiver interface here, but that's not
+ // useful for init, because alloc returns 'id'. In theory, this could lead
+ // to false positives, for example if there existed a class that had an
+ // initWithObjects: implementation that does accept non-Objective-C pointer
+ // types, but the chance of that happening is pretty small compared to the
+ // gains that this analysis gives.
+ const ObjCInterfaceDecl *Class = MD->getClassInterface();
+
+ switch (findKnownClass(Class)) {
+ case FC_NSArray:
+ case FC_NSOrderedSet:
+ case FC_NSSet:
+ return S == initWithObjectsS;
+ case FC_NSDictionary:
+ return S == initWithObjectsAndKeysS;
+ default:
+ return false;
+ }
+ } else {
+ const ObjCInterfaceDecl *Class = msg.getReceiverInterface();
+
+ switch (findKnownClass(Class)) {
+ case FC_NSArray:
+ return S == arrayWithObjectsS;
+ case FC_NSOrderedSet:
+ return S == orderedSetWithObjectsS;
+ case FC_NSSet:
+ return S == setWithObjectsS;
+ case FC_NSDictionary:
+ return S == dictionaryWithObjectsAndKeysS;
+ default:
+ return false;
+ }
+ }
+}
+
+void VariadicMethodTypeChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
+ CheckerContext &C) const {
+ if (!BT) {
+ BT.reset(new APIMisuse(this,
+ "Arguments passed to variadic method aren't all "
+ "Objective-C pointer types"));
+
+ ASTContext &Ctx = C.getASTContext();
+ arrayWithObjectsS = GetUnarySelector("arrayWithObjects", Ctx);
+ dictionaryWithObjectsAndKeysS =
+ GetUnarySelector("dictionaryWithObjectsAndKeys", Ctx);
+ setWithObjectsS = GetUnarySelector("setWithObjects", Ctx);
+ orderedSetWithObjectsS = GetUnarySelector("orderedSetWithObjects", Ctx);
+
+ initWithObjectsS = GetUnarySelector("initWithObjects", Ctx);
+ initWithObjectsAndKeysS = GetUnarySelector("initWithObjectsAndKeys", Ctx);
+ }
+
+ if (!isVariadicMessage(msg))
+ return;
+
+ // We are not interested in the selector arguments since they have
+ // well-defined types, so the compiler will issue a warning for them.
+ unsigned variadicArgsBegin = msg.getSelector().getNumArgs();
+
+ // We're not interested in the last argument since it has to be nil or the
+ // compiler would have issued a warning for it elsewhere.
+ unsigned variadicArgsEnd = msg.getNumArgs() - 1;
+
+ if (variadicArgsEnd <= variadicArgsBegin)
+ return;
+
+ // Verify that all arguments have Objective-C types.
+ Optional<ExplodedNode*> errorNode;
+
+ for (unsigned I = variadicArgsBegin; I != variadicArgsEnd; ++I) {
+ QualType ArgTy = msg.getArgExpr(I)->getType();
+ if (ArgTy->isObjCObjectPointerType())
+ continue;
+
+ // Block pointers are treaded as Objective-C pointers.
+ if (ArgTy->isBlockPointerType())
+ continue;
+
+ // Ignore pointer constants.
+ if (msg.getArgSVal(I).getAs<loc::ConcreteInt>())
+ continue;
+
+ // Ignore pointer types annotated with 'NSObject' attribute.
+ if (C.getASTContext().isObjCNSObjectType(ArgTy))
+ continue;
+
+ // Ignore CF references, which can be toll-free bridged.
+ if (coreFoundation::isCFObjectRef(ArgTy))
+ continue;
+
+ // Generate only one error node to use for all bug reports.
+ if (!errorNode.hasValue())
+ errorNode = C.generateNonFatalErrorNode();
+
+ if (!errorNode.getValue())
+ continue;
+
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+
+ StringRef TypeName = GetReceiverInterfaceName(msg);
+ if (!TypeName.empty())
+ os << "Argument to '" << TypeName << "' method '";
+ else
+ os << "Argument to method '";
+
+ msg.getSelector().print(os);
+ os << "' should be an Objective-C pointer type, not '";
+ ArgTy.print(os, C.getLangOpts());
+ os << "'";
+
+ auto R = llvm::make_unique<BugReport>(*BT, os.str(), errorNode.getValue());
+ R->addRange(msg.getArgSourceRange(I));
+ C.emitReport(std::move(R));
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Improves the modeling of loops over Cocoa collections.
+//===----------------------------------------------------------------------===//
+
+// The map from container symbol to the container count symbol.
+// We currently will remember the last container count symbol encountered.
+REGISTER_MAP_WITH_PROGRAMSTATE(ContainerCountMap, SymbolRef, SymbolRef)
+REGISTER_MAP_WITH_PROGRAMSTATE(ContainerNonEmptyMap, SymbolRef, bool)
+
+namespace {
+class ObjCLoopChecker
+ : public Checker<check::PostStmt<ObjCForCollectionStmt>,
+ check::PostObjCMessage,
+ check::DeadSymbols,
+ check::PointerEscape > {
+ mutable IdentifierInfo *CountSelectorII;
+
+ bool isCollectionCountMethod(const ObjCMethodCall &M,
+ CheckerContext &C) const;
+
+public:
+ ObjCLoopChecker() : CountSelectorII(nullptr) {}
+ void checkPostStmt(const ObjCForCollectionStmt *FCS, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ ProgramStateRef checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
+};
+} // end anonymous namespace
+
+static bool isKnownNonNilCollectionType(QualType T) {
+ const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
+ if (!PT)
+ return false;
+
+ const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
+ if (!ID)
+ return false;
+
+ switch (findKnownClass(ID)) {
+ case FC_NSArray:
+ case FC_NSDictionary:
+ case FC_NSEnumerator:
+ case FC_NSOrderedSet:
+ case FC_NSSet:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/// Assumes that the collection is non-nil.
+///
+/// If the collection is known to be nil, returns NULL to indicate an infeasible
+/// path.
+static ProgramStateRef checkCollectionNonNil(CheckerContext &C,
+ ProgramStateRef State,
+ const ObjCForCollectionStmt *FCS) {
+ if (!State)
+ return nullptr;
+
+ SVal CollectionVal = C.getSVal(FCS->getCollection());
+ Optional<DefinedSVal> KnownCollection = CollectionVal.getAs<DefinedSVal>();
+ if (!KnownCollection)
+ return State;
+
+ ProgramStateRef StNonNil, StNil;
+ std::tie(StNonNil, StNil) = State->assume(*KnownCollection);
+ if (StNil && !StNonNil) {
+ // The collection is nil. This path is infeasible.
+ return nullptr;
+ }
+
+ return StNonNil;
+}
+
+/// Assumes that the collection elements are non-nil.
+///
+/// This only applies if the collection is one of those known not to contain
+/// nil values.
+static ProgramStateRef checkElementNonNil(CheckerContext &C,
+ ProgramStateRef State,
+ const ObjCForCollectionStmt *FCS) {
+ if (!State)
+ return nullptr;
+
+ // See if the collection is one where we /know/ the elements are non-nil.
+ if (!isKnownNonNilCollectionType(FCS->getCollection()->getType()))
+ return State;
+
+ const LocationContext *LCtx = C.getLocationContext();
+ const Stmt *Element = FCS->getElement();
+
+ // FIXME: Copied from ExprEngineObjC.
+ Optional<Loc> ElementLoc;
+ if (const DeclStmt *DS = dyn_cast<DeclStmt>(Element)) {
+ const VarDecl *ElemDecl = cast<VarDecl>(DS->getSingleDecl());
+ assert(ElemDecl->getInit() == nullptr);
+ ElementLoc = State->getLValue(ElemDecl, LCtx);
+ } else {
+ ElementLoc = State->getSVal(Element, LCtx).getAs<Loc>();
+ }
+
+ if (!ElementLoc)
+ return State;
+
+ // Go ahead and assume the value is non-nil.
+ SVal Val = State->getSVal(*ElementLoc);
+ return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true);
+}
+
+/// Returns NULL state if the collection is known to contain elements
+/// (or is known not to contain elements if the Assumption parameter is false.)
+static ProgramStateRef
+assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
+ SymbolRef CollectionS, bool Assumption) {
+ if (!State || !CollectionS)
+ return State;
+
+ const SymbolRef *CountS = State->get<ContainerCountMap>(CollectionS);
+ if (!CountS) {
+ const bool *KnownNonEmpty = State->get<ContainerNonEmptyMap>(CollectionS);
+ if (!KnownNonEmpty)
+ return State->set<ContainerNonEmptyMap>(CollectionS, Assumption);
+ return (Assumption == *KnownNonEmpty) ? State : nullptr;
+ }
+
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ SVal CountGreaterThanZeroVal =
+ SvalBuilder.evalBinOp(State, BO_GT,
+ nonloc::SymbolVal(*CountS),
+ SvalBuilder.makeIntVal(0, (*CountS)->getType()),
+ SvalBuilder.getConditionType());
+ Optional<DefinedSVal> CountGreaterThanZero =
+ CountGreaterThanZeroVal.getAs<DefinedSVal>();
+ if (!CountGreaterThanZero) {
+ // The SValBuilder cannot construct a valid SVal for this condition.
+ // This means we cannot properly reason about it.
+ return State;
+ }
+
+ return State->assume(*CountGreaterThanZero, Assumption);
+}
+
+static ProgramStateRef
+assumeCollectionNonEmpty(CheckerContext &C, ProgramStateRef State,
+ const ObjCForCollectionStmt *FCS,
+ bool Assumption) {
+ if (!State)
+ return nullptr;
+
+ SymbolRef CollectionS = C.getSVal(FCS->getCollection()).getAsSymbol();
+ return assumeCollectionNonEmpty(C, State, CollectionS, Assumption);
+}
+
+/// If the fist block edge is a back edge, we are reentering the loop.
+static bool alreadyExecutedAtLeastOneLoopIteration(const ExplodedNode *N,
+ const ObjCForCollectionStmt *FCS) {
+ if (!N)
+ return false;
+
+ ProgramPoint P = N->getLocation();
+ if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
+ return BE->getSrc()->getLoopTarget() == FCS;
+ }
+
+ // Keep looking for a block edge.
+ for (ExplodedNode::const_pred_iterator I = N->pred_begin(),
+ E = N->pred_end(); I != E; ++I) {
+ if (alreadyExecutedAtLeastOneLoopIteration(*I, FCS))
+ return true;
+ }
+
+ return false;
+}
+
+void ObjCLoopChecker::checkPostStmt(const ObjCForCollectionStmt *FCS,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // Check if this is the branch for the end of the loop.
+ SVal CollectionSentinel = C.getSVal(FCS);
+ if (CollectionSentinel.isZeroConstant()) {
+ if (!alreadyExecutedAtLeastOneLoopIteration(C.getPredecessor(), FCS))
+ State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/false);
+
+ // Otherwise, this is a branch that goes through the loop body.
+ } else {
+ State = checkCollectionNonNil(C, State, FCS);
+ State = checkElementNonNil(C, State, FCS);
+ State = assumeCollectionNonEmpty(C, State, FCS, /*Assumption*/true);
+ }
+
+ if (!State)
+ C.generateSink(C.getState(), C.getPredecessor());
+ else if (State != C.getState())
+ C.addTransition(State);
+}
+
+bool ObjCLoopChecker::isCollectionCountMethod(const ObjCMethodCall &M,
+ CheckerContext &C) const {
+ Selector S = M.getSelector();
+ // Initialize the identifiers on first use.
+ if (!CountSelectorII)
+ CountSelectorII = &C.getASTContext().Idents.get("count");
+
+ // If the method returns collection count, record the value.
+ return S.isUnarySelector() &&
+ (S.getIdentifierInfoForSlot(0) == CountSelectorII);
+}
+
+void ObjCLoopChecker::checkPostObjCMessage(const ObjCMethodCall &M,
+ CheckerContext &C) const {
+ if (!M.isInstanceMessage())
+ return;
+
+ const ObjCInterfaceDecl *ClassID = M.getReceiverInterface();
+ if (!ClassID)
+ return;
+
+ FoundationClass Class = findKnownClass(ClassID);
+ if (Class != FC_NSDictionary &&
+ Class != FC_NSArray &&
+ Class != FC_NSSet &&
+ Class != FC_NSOrderedSet)
+ return;
+
+ SymbolRef ContainerS = M.getReceiverSVal().getAsSymbol();
+ if (!ContainerS)
+ return;
+
+ // If we are processing a call to "count", get the symbolic value returned by
+ // a call to "count" and add it to the map.
+ if (!isCollectionCountMethod(M, C))
+ return;
+
+ const Expr *MsgExpr = M.getOriginExpr();
+ SymbolRef CountS = C.getSVal(MsgExpr).getAsSymbol();
+ if (CountS) {
+ ProgramStateRef State = C.getState();
+
+ C.getSymbolManager().addSymbolDependency(ContainerS, CountS);
+ State = State->set<ContainerCountMap>(ContainerS, CountS);
+
+ if (const bool *NonEmpty = State->get<ContainerNonEmptyMap>(ContainerS)) {
+ State = State->remove<ContainerNonEmptyMap>(ContainerS);
+ State = assumeCollectionNonEmpty(C, State, ContainerS, *NonEmpty);
+ }
+
+ C.addTransition(State);
+ }
+}
+
+static SymbolRef getMethodReceiverIfKnownImmutable(const CallEvent *Call) {
+ const ObjCMethodCall *Message = dyn_cast_or_null<ObjCMethodCall>(Call);
+ if (!Message)
+ return nullptr;
+
+ const ObjCMethodDecl *MD = Message->getDecl();
+ if (!MD)
+ return nullptr;
+
+ const ObjCInterfaceDecl *StaticClass;
+ if (isa<ObjCProtocolDecl>(MD->getDeclContext())) {
+ // We can't find out where the method was declared without doing more work.
+ // Instead, see if the receiver is statically typed as a known immutable
+ // collection.
+ StaticClass = Message->getOriginExpr()->getReceiverInterface();
+ } else {
+ StaticClass = MD->getClassInterface();
+ }
+
+ if (!StaticClass)
+ return nullptr;
+
+ switch (findKnownClass(StaticClass, /*IncludeSuper=*/false)) {
+ case FC_None:
+ return nullptr;
+ case FC_NSArray:
+ case FC_NSDictionary:
+ case FC_NSEnumerator:
+ case FC_NSNull:
+ case FC_NSOrderedSet:
+ case FC_NSSet:
+ case FC_NSString:
+ break;
+ }
+
+ return Message->getReceiverSVal().getAsSymbol();
+}
+
+ProgramStateRef
+ObjCLoopChecker::checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const {
+ SymbolRef ImmutableReceiver = getMethodReceiverIfKnownImmutable(Call);
+
+ // Remove the invalidated symbols form the collection count map.
+ for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
+ E = Escaped.end();
+ I != E; ++I) {
+ SymbolRef Sym = *I;
+
+ // Don't invalidate this symbol's count if we know the method being called
+ // is declared on an immutable class. This isn't completely correct if the
+ // receiver is also passed as an argument, but in most uses of NSArray,
+ // NSDictionary, etc. this isn't likely to happen in a dangerous way.
+ if (Sym == ImmutableReceiver)
+ continue;
+
+ // The symbol escaped. Pessimistically, assume that the count could have
+ // changed.
+ State = State->remove<ContainerCountMap>(Sym);
+ State = State->remove<ContainerNonEmptyMap>(Sym);
+ }
+ return State;
+}
+
+void ObjCLoopChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // Remove the dead symbols from the collection count map.
+ ContainerCountMapTy Tracked = State->get<ContainerCountMap>();
+ for (ContainerCountMapTy::iterator I = Tracked.begin(),
+ E = Tracked.end(); I != E; ++I) {
+ SymbolRef Sym = I->first;
+ if (SymReaper.isDead(Sym)) {
+ State = State->remove<ContainerCountMap>(Sym);
+ State = State->remove<ContainerNonEmptyMap>(Sym);
+ }
+ }
+
+ C.addTransition(State);
+}
+
+namespace {
+/// \class ObjCNonNilReturnValueChecker
+/// The checker restricts the return values of APIs known to
+/// never (or almost never) return 'nil'.
+class ObjCNonNilReturnValueChecker
+ : public Checker<check::PostObjCMessage,
+ check::PostStmt<ObjCArrayLiteral>,
+ check::PostStmt<ObjCDictionaryLiteral>,
+ check::PostStmt<ObjCBoxedExpr> > {
+ mutable bool Initialized;
+ mutable Selector ObjectAtIndex;
+ mutable Selector ObjectAtIndexedSubscript;
+ mutable Selector NullSelector;
+
+public:
+ ObjCNonNilReturnValueChecker() : Initialized(false) {}
+
+ ProgramStateRef assumeExprIsNonNull(const Expr *NonNullExpr,
+ ProgramStateRef State,
+ CheckerContext &C) const;
+ void assumeExprIsNonNull(const Expr *E, CheckerContext &C) const {
+ C.addTransition(assumeExprIsNonNull(E, C.getState(), C));
+ }
+
+ void checkPostStmt(const ObjCArrayLiteral *E, CheckerContext &C) const {
+ assumeExprIsNonNull(E, C);
+ }
+ void checkPostStmt(const ObjCDictionaryLiteral *E, CheckerContext &C) const {
+ assumeExprIsNonNull(E, C);
+ }
+ void checkPostStmt(const ObjCBoxedExpr *E, CheckerContext &C) const {
+ assumeExprIsNonNull(E, C);
+ }
+
+ void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+ProgramStateRef
+ObjCNonNilReturnValueChecker::assumeExprIsNonNull(const Expr *NonNullExpr,
+ ProgramStateRef State,
+ CheckerContext &C) const {
+ SVal Val = C.getSVal(NonNullExpr);
+ if (Optional<DefinedOrUnknownSVal> DV = Val.getAs<DefinedOrUnknownSVal>())
+ return State->assume(*DV, true);
+ return State;
+}
+
+void ObjCNonNilReturnValueChecker::checkPostObjCMessage(const ObjCMethodCall &M,
+ CheckerContext &C)
+ const {
+ ProgramStateRef State = C.getState();
+
+ if (!Initialized) {
+ ASTContext &Ctx = C.getASTContext();
+ ObjectAtIndex = GetUnarySelector("objectAtIndex", Ctx);
+ ObjectAtIndexedSubscript = GetUnarySelector("objectAtIndexedSubscript", Ctx);
+ NullSelector = GetNullarySelector("null", Ctx);
+ }
+
+ // Check the receiver type.
+ if (const ObjCInterfaceDecl *Interface = M.getReceiverInterface()) {
+
+ // Assume that object returned from '[self init]' or '[super init]' is not
+ // 'nil' if we are processing an inlined function/method.
+ //
+ // A defensive callee will (and should) check if the object returned by
+ // '[super init]' is 'nil' before doing it's own initialization. However,
+ // since 'nil' is rarely returned in practice, we should not warn when the
+ // caller to the defensive constructor uses the object in contexts where
+ // 'nil' is not accepted.
+ if (!C.inTopFrame() && M.getDecl() &&
+ M.getDecl()->getMethodFamily() == OMF_init &&
+ M.isReceiverSelfOrSuper()) {
+ State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
+ }
+
+ FoundationClass Cl = findKnownClass(Interface);
+
+ // Objects returned from
+ // [NSArray|NSOrderedSet]::[ObjectAtIndex|ObjectAtIndexedSubscript]
+ // are never 'nil'.
+ if (Cl == FC_NSArray || Cl == FC_NSOrderedSet) {
+ Selector Sel = M.getSelector();
+ if (Sel == ObjectAtIndex || Sel == ObjectAtIndexedSubscript) {
+ // Go ahead and assume the value is non-nil.
+ State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
+ }
+ }
+
+ // Objects returned from [NSNull null] are not nil.
+ if (Cl == FC_NSNull) {
+ if (M.getSelector() == NullSelector) {
+ // Go ahead and assume the value is non-nil.
+ State = assumeExprIsNonNull(M.getOriginExpr(), State, C);
+ }
+ }
+ }
+ C.addTransition(State);
+}
+
+//===----------------------------------------------------------------------===//
+// Check registration.
+//===----------------------------------------------------------------------===//
+
+void ento::registerNilArgChecker(CheckerManager &mgr) {
+ mgr.registerChecker<NilArgChecker>();
+}
+
+void ento::registerCFNumberChecker(CheckerManager &mgr) {
+ mgr.registerChecker<CFNumberChecker>();
+}
+
+void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
+ mgr.registerChecker<CFRetainReleaseChecker>();
+}
+
+void ento::registerClassReleaseChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ClassReleaseChecker>();
+}
+
+void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
+ mgr.registerChecker<VariadicMethodTypeChecker>();
+}
+
+void ento::registerObjCLoopChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ObjCLoopChecker>();
+}
+
+void
+ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ObjCNonNilReturnValueChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
new file mode 100644
index 000000000000..00d08b371f37
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -0,0 +1,185 @@
+//===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for blocks in critical sections. This checker should find
+// the calls to blocking functions (for example: sleep, getc, fgets, read,
+// recv etc.) inside a critical section. When sleep(x) is called while a mutex
+// is held, other threades cannot lock the same mutex. This might take some
+// time, leading to bad performance or even deadlock.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class BlockInCriticalSectionChecker : public Checker<check::PostCall> {
+
+ mutable IdentifierInfo *IILockGuard, *IIUniqueLock;
+
+ CallDescription LockFn, UnlockFn, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn,
+ PthreadLockFn, PthreadTryLockFn, PthreadUnlockFn,
+ MtxLock, MtxTimedLock, MtxTryLock, MtxUnlock;
+
+ StringRef ClassLockGuard, ClassUniqueLock;
+
+ mutable bool IdentifierInfoInitialized;
+
+ std::unique_ptr<BugType> BlockInCritSectionBugType;
+
+ void initIdentifierInfo(ASTContext &Ctx) const;
+
+ void reportBlockInCritSection(SymbolRef FileDescSym,
+ const CallEvent &call,
+ CheckerContext &C) const;
+
+public:
+ BlockInCriticalSectionChecker();
+
+ bool isBlockingFunction(const CallEvent &Call) const;
+ bool isLockFunction(const CallEvent &Call) const;
+ bool isUnlockFunction(const CallEvent &Call) const;
+
+ /// Process unlock.
+ /// Process lock.
+ /// Process blocking functions (sleep, getc, fgets, read, recv)
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+};
+
+} // end anonymous namespace
+
+REGISTER_TRAIT_WITH_PROGRAMSTATE(MutexCounter, unsigned)
+
+BlockInCriticalSectionChecker::BlockInCriticalSectionChecker()
+ : IILockGuard(nullptr), IIUniqueLock(nullptr),
+ LockFn("lock"), UnlockFn("unlock"), SleepFn("sleep"), GetcFn("getc"),
+ FgetsFn("fgets"), ReadFn("read"), RecvFn("recv"),
+ PthreadLockFn("pthread_mutex_lock"),
+ PthreadTryLockFn("pthread_mutex_trylock"),
+ PthreadUnlockFn("pthread_mutex_unlock"),
+ MtxLock("mtx_lock"),
+ MtxTimedLock("mtx_timedlock"),
+ MtxTryLock("mtx_trylock"),
+ MtxUnlock("mtx_unlock"),
+ ClassLockGuard("lock_guard"),
+ ClassUniqueLock("unique_lock"),
+ IdentifierInfoInitialized(false) {
+ // Initialize the bug type.
+ BlockInCritSectionBugType.reset(
+ new BugType(this, "Call to blocking function in critical section",
+ "Blocking Error"));
+}
+
+void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
+ if (!IdentifierInfoInitialized) {
+ /* In case of checking C code, or when the corresponding headers are not
+ * included, we might end up query the identifier table every time when this
+ * function is called instead of early returning it. To avoid this, a bool
+ * variable (IdentifierInfoInitialized) is used and the function will be run
+ * only once. */
+ IILockGuard = &Ctx.Idents.get(ClassLockGuard);
+ IIUniqueLock = &Ctx.Idents.get(ClassUniqueLock);
+ IdentifierInfoInitialized = true;
+ }
+}
+
+bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
+ if (Call.isCalled(SleepFn)
+ || Call.isCalled(GetcFn)
+ || Call.isCalled(FgetsFn)
+ || Call.isCalled(ReadFn)
+ || Call.isCalled(RecvFn)) {
+ return true;
+ }
+ return false;
+}
+
+bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
+ if (const auto *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
+ auto IdentifierInfo = Ctor->getDecl()->getParent()->getIdentifier();
+ if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
+ return true;
+ }
+
+ if (Call.isCalled(LockFn)
+ || Call.isCalled(PthreadLockFn)
+ || Call.isCalled(PthreadTryLockFn)
+ || Call.isCalled(MtxLock)
+ || Call.isCalled(MtxTimedLock)
+ || Call.isCalled(MtxTryLock)) {
+ return true;
+ }
+ return false;
+}
+
+bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
+ if (const auto *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
+ const auto *DRecordDecl = dyn_cast<CXXRecordDecl>(Dtor->getDecl()->getParent());
+ auto IdentifierInfo = DRecordDecl->getIdentifier();
+ if (IdentifierInfo == IILockGuard || IdentifierInfo == IIUniqueLock)
+ return true;
+ }
+
+ if (Call.isCalled(UnlockFn)
+ || Call.isCalled(PthreadUnlockFn)
+ || Call.isCalled(MtxUnlock)) {
+ return true;
+ }
+ return false;
+}
+
+void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ initIdentifierInfo(C.getASTContext());
+
+ if (!isBlockingFunction(Call)
+ && !isLockFunction(Call)
+ && !isUnlockFunction(Call))
+ return;
+
+ ProgramStateRef State = C.getState();
+ unsigned mutexCount = State->get<MutexCounter>();
+ if (isUnlockFunction(Call) && mutexCount > 0) {
+ State = State->set<MutexCounter>(--mutexCount);
+ C.addTransition(State);
+ } else if (isLockFunction(Call)) {
+ State = State->set<MutexCounter>(++mutexCount);
+ C.addTransition(State);
+ } else if (mutexCount > 0) {
+ SymbolRef BlockDesc = Call.getReturnValue().getAsSymbol();
+ reportBlockInCritSection(BlockDesc, Call, C);
+ }
+}
+
+void BlockInCriticalSectionChecker::reportBlockInCritSection(
+ SymbolRef BlockDescSym, const CallEvent &Call, CheckerContext &C) const {
+ ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
+ if (!ErrNode)
+ return;
+
+ std::string msg;
+ llvm::raw_string_ostream os(msg);
+ os << "Call to blocking function '" << Call.getCalleeIdentifier()->getName()
+ << "' inside of critical section";
+ auto R = llvm::make_unique<BugReport>(*BlockInCritSectionBugType, os.str(), ErrNode);
+ R->addRange(Call.getSourceRange());
+ R->markInteresting(BlockDescSym);
+ C.emitReport(std::move(R));
+}
+
+void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
+ mgr.registerChecker<BlockInCriticalSectionChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
new file mode 100644
index 000000000000..3008eddd397e
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
@@ -0,0 +1,157 @@
+//== BoolAssignmentChecker.cpp - Boolean assignment checker -----*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines BoolAssignmentChecker, a builtin check in ExprEngine that
+// performs checks for assignment of non-Boolean values to Boolean variables.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+ class BoolAssignmentChecker : public Checker< check::Bind > {
+ mutable std::unique_ptr<BuiltinBug> BT;
+ void emitReport(ProgramStateRef state, CheckerContext &C) const;
+ public:
+ void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
+ };
+} // end anonymous namespace
+
+void BoolAssignmentChecker::emitReport(ProgramStateRef state,
+ CheckerContext &C) const {
+ if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
+ if (!BT)
+ BT.reset(new BuiltinBug(this, "Assignment of a non-Boolean value"));
+ C.emitReport(llvm::make_unique<BugReport>(*BT, BT->getDescription(), N));
+ }
+}
+
+static bool isBooleanType(QualType Ty) {
+ if (Ty->isBooleanType()) // C++ or C99
+ return true;
+
+ if (const TypedefType *TT = Ty->getAs<TypedefType>())
+ return TT->getDecl()->getName() == "BOOL" || // Objective-C
+ TT->getDecl()->getName() == "_Bool" || // stdbool.h < C99
+ TT->getDecl()->getName() == "Boolean"; // MacTypes.h
+
+ return false;
+}
+
+void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
+ CheckerContext &C) const {
+
+ // We are only interested in stores into Booleans.
+ const TypedValueRegion *TR =
+ dyn_cast_or_null<TypedValueRegion>(loc.getAsRegion());
+
+ if (!TR)
+ return;
+
+ QualType valTy = TR->getValueType();
+
+ if (!isBooleanType(valTy))
+ return;
+
+ // Get the value of the right-hand side. We only care about values
+ // that are defined (UnknownVals and UndefinedVals are handled by other
+ // checkers).
+ Optional<DefinedSVal> DV = val.getAs<DefinedSVal>();
+ if (!DV)
+ return;
+
+ // Check if the assigned value meets our criteria for correctness. It must
+ // be a value that is either 0 or 1. One way to check this is to see if
+ // the value is possibly < 0 (for a negative value) or greater than 1.
+ ProgramStateRef state = C.getState();
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ ConstraintManager &CM = C.getConstraintManager();
+
+ // First, ensure that the value is >= 0.
+ DefinedSVal zeroVal = svalBuilder.makeIntVal(0, valTy);
+ SVal greaterThanOrEqualToZeroVal =
+ svalBuilder.evalBinOp(state, BO_GE, *DV, zeroVal,
+ svalBuilder.getConditionType());
+
+ Optional<DefinedSVal> greaterThanEqualToZero =
+ greaterThanOrEqualToZeroVal.getAs<DefinedSVal>();
+
+ if (!greaterThanEqualToZero) {
+ // The SValBuilder cannot construct a valid SVal for this condition.
+ // This means we cannot properly reason about it.
+ return;
+ }
+
+ ProgramStateRef stateLT, stateGE;
+ std::tie(stateGE, stateLT) = CM.assumeDual(state, *greaterThanEqualToZero);
+
+ // Is it possible for the value to be less than zero?
+ if (stateLT) {
+ // It is possible for the value to be less than zero. We only
+ // want to emit a warning, however, if that value is fully constrained.
+ // If it it possible for the value to be >= 0, then essentially the
+ // value is underconstrained and there is nothing left to be done.
+ if (!stateGE)
+ emitReport(stateLT, C);
+
+ // In either case, we are done.
+ return;
+ }
+
+ // If we reach here, it must be the case that the value is constrained
+ // to only be >= 0.
+ assert(stateGE == state);
+
+ // At this point we know that the value is >= 0.
+ // Now check to ensure that the value is <= 1.
+ DefinedSVal OneVal = svalBuilder.makeIntVal(1, valTy);
+ SVal lessThanEqToOneVal =
+ svalBuilder.evalBinOp(state, BO_LE, *DV, OneVal,
+ svalBuilder.getConditionType());
+
+ Optional<DefinedSVal> lessThanEqToOne =
+ lessThanEqToOneVal.getAs<DefinedSVal>();
+
+ if (!lessThanEqToOne) {
+ // The SValBuilder cannot construct a valid SVal for this condition.
+ // This means we cannot properly reason about it.
+ return;
+ }
+
+ ProgramStateRef stateGT, stateLE;
+ std::tie(stateLE, stateGT) = CM.assumeDual(state, *lessThanEqToOne);
+
+ // Is it possible for the value to be greater than one?
+ if (stateGT) {
+ // It is possible for the value to be greater than one. We only
+ // want to emit a warning, however, if that value is fully constrained.
+ // If it is possible for the value to be <= 1, then essentially the
+ // value is underconstrained and there is nothing left to be done.
+ if (!stateLE)
+ emitReport(stateGT, C);
+
+ // In either case, we are done.
+ return;
+ }
+
+ // If we reach here, it must be the case that the value is constrained
+ // to only be <= 1.
+ assert(stateLE == state);
+}
+
+void ento::registerBoolAssignmentChecker(CheckerManager &mgr) {
+ mgr.registerChecker<BoolAssignmentChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
new file mode 100644
index 000000000000..f98027942e18
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
@@ -0,0 +1,122 @@
+//=== BuiltinFunctionChecker.cpp --------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker evaluates clang builtin functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class BuiltinFunctionChecker : public Checker<eval::Call> {
+public:
+ bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+};
+
+}
+
+bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
+ CheckerContext &C) const {
+ ProgramStateRef state = C.getState();
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ const LocationContext *LCtx = C.getLocationContext();
+ if (!FD)
+ return false;
+
+ switch (FD->getBuiltinID()) {
+ default:
+ return false;
+
+ case Builtin::BI__builtin_assume: {
+ assert (CE->arg_begin() != CE->arg_end());
+ SVal ArgSVal = C.getSVal(CE->getArg(0));
+ if (ArgSVal.isUndef())
+ return true; // Return true to model purity.
+
+ state = state->assume(ArgSVal.castAs<DefinedOrUnknownSVal>(), true);
+ // FIXME: do we want to warn here? Not right now. The most reports might
+ // come from infeasible paths, thus being false positives.
+ if (!state) {
+ C.generateSink(C.getState(), C.getPredecessor());
+ return true;
+ }
+
+ C.addTransition(state);
+ return true;
+ }
+
+ case Builtin::BI__builtin_unpredictable:
+ case Builtin::BI__builtin_expect:
+ case Builtin::BI__builtin_assume_aligned:
+ case Builtin::BI__builtin_addressof: {
+ // For __builtin_unpredictable, __builtin_expect, and
+ // __builtin_assume_aligned, just return the value of the subexpression.
+ // __builtin_addressof is going from a reference to a pointer, but those
+ // are represented the same way in the analyzer.
+ assert (CE->arg_begin() != CE->arg_end());
+ SVal X = C.getSVal(*(CE->arg_begin()));
+ C.addTransition(state->BindExpr(CE, LCtx, X));
+ return true;
+ }
+
+ case Builtin::BI__builtin_alloca_with_align:
+ case Builtin::BI__builtin_alloca: {
+ // FIXME: Refactor into StoreManager itself?
+ MemRegionManager& RM = C.getStoreManager().getRegionManager();
+ const AllocaRegion* R =
+ RM.getAllocaRegion(CE, C.blockCount(), C.getLocationContext());
+
+ // Set the extent of the region in bytes. This enables us to use the
+ // SVal of the argument directly. If we save the extent in bits, we
+ // cannot represent values like symbol*8.
+ auto Size = C.getSVal(*(CE->arg_begin())).castAs<DefinedOrUnknownSVal>();
+
+ SValBuilder& svalBuilder = C.getSValBuilder();
+ DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder);
+ DefinedOrUnknownSVal extentMatchesSizeArg =
+ svalBuilder.evalEQ(state, Extent, Size);
+ state = state->assume(extentMatchesSizeArg, true);
+ assert(state && "The region should not have any previous constraints");
+
+ C.addTransition(state->BindExpr(CE, LCtx, loc::MemRegionVal(R)));
+ return true;
+ }
+
+ case Builtin::BI__builtin_object_size:
+ case Builtin::BI__builtin_constant_p: {
+ // This must be resolvable at compile time, so we defer to the constant
+ // evaluator for a value.
+ SVal V = UnknownVal();
+ Expr::EvalResult EVResult;
+ if (CE->EvaluateAsInt(EVResult, C.getASTContext(), Expr::SE_NoSideEffects)) {
+ // Make sure the result has the correct type.
+ llvm::APSInt Result = EVResult.Val.getInt();
+ SValBuilder &SVB = C.getSValBuilder();
+ BasicValueFactory &BVF = SVB.getBasicValueFactory();
+ BVF.getAPSIntType(CE->getType()).apply(Result);
+ V = SVB.makeIntVal(Result);
+ }
+
+ C.addTransition(state->BindExpr(CE, LCtx, V));
+ return true;
+ }
+ }
+}
+
+void ento::registerBuiltinFunctionChecker(CheckerManager &mgr) {
+ mgr.registerChecker<BuiltinFunctionChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
new file mode 100644
index 000000000000..8bffada69b9b
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -0,0 +1,2493 @@
+//= CStringChecker.cpp - Checks calls to C string functions --------*- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines CStringChecker, which is an assortment of checks on calls
+// to functions in <string.h>.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "InterCheckerAPI.h"
+#include "clang/Basic/CharInfo.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class CStringChecker : public Checker< eval::Call,
+ check::PreStmt<DeclStmt>,
+ check::LiveSymbols,
+ check::DeadSymbols,
+ check::RegionChanges
+ > {
+ mutable std::unique_ptr<BugType> BT_Null, BT_Bounds, BT_Overlap,
+ BT_NotCString, BT_AdditionOverflow;
+
+ mutable const char *CurrentFunctionDescription;
+
+public:
+ /// The filter is used to filter out the diagnostics which are not enabled by
+ /// the user.
+ struct CStringChecksFilter {
+ DefaultBool CheckCStringNullArg;
+ DefaultBool CheckCStringOutOfBounds;
+ DefaultBool CheckCStringBufferOverlap;
+ DefaultBool CheckCStringNotNullTerm;
+
+ CheckName CheckNameCStringNullArg;
+ CheckName CheckNameCStringOutOfBounds;
+ CheckName CheckNameCStringBufferOverlap;
+ CheckName CheckNameCStringNotNullTerm;
+ };
+
+ CStringChecksFilter Filter;
+
+ static void *getTag() { static int tag; return &tag; }
+
+ bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;
+ void checkLiveSymbols(ProgramStateRef state, SymbolReaper &SR) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+
+ ProgramStateRef
+ checkRegionChanges(ProgramStateRef state,
+ const InvalidatedSymbols *,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call) const;
+
+ typedef void (CStringChecker::*FnCheck)(CheckerContext &,
+ const CallExpr *) const;
+
+ void evalMemcpy(CheckerContext &C, const CallExpr *CE) const;
+ void evalMempcpy(CheckerContext &C, const CallExpr *CE) const;
+ void evalMemmove(CheckerContext &C, const CallExpr *CE) const;
+ void evalBcopy(CheckerContext &C, const CallExpr *CE) const;
+ void evalCopyCommon(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef state,
+ const Expr *Size,
+ const Expr *Source,
+ const Expr *Dest,
+ bool Restricted = false,
+ bool IsMempcpy = false) const;
+
+ void evalMemcmp(CheckerContext &C, const CallExpr *CE) const;
+
+ void evalstrLength(CheckerContext &C, const CallExpr *CE) const;
+ void evalstrnLength(CheckerContext &C, const CallExpr *CE) const;
+ void evalstrLengthCommon(CheckerContext &C,
+ const CallExpr *CE,
+ bool IsStrnlen = false) const;
+
+ void evalStrcpy(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrncpy(CheckerContext &C, const CallExpr *CE) const;
+ void evalStpcpy(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrlcpy(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrcpyCommon(CheckerContext &C,
+ const CallExpr *CE,
+ bool returnEnd,
+ bool isBounded,
+ bool isAppending,
+ bool returnPtr = true) const;
+
+ void evalStrcat(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrncat(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrlcat(CheckerContext &C, const CallExpr *CE) const;
+
+ void evalStrcmp(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrncmp(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrcasecmp(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrncasecmp(CheckerContext &C, const CallExpr *CE) const;
+ void evalStrcmpCommon(CheckerContext &C,
+ const CallExpr *CE,
+ bool isBounded = false,
+ bool ignoreCase = false) const;
+
+ void evalStrsep(CheckerContext &C, const CallExpr *CE) const;
+
+ void evalStdCopy(CheckerContext &C, const CallExpr *CE) const;
+ void evalStdCopyBackward(CheckerContext &C, const CallExpr *CE) const;
+ void evalStdCopyCommon(CheckerContext &C, const CallExpr *CE) const;
+ void evalMemset(CheckerContext &C, const CallExpr *CE) const;
+ void evalBzero(CheckerContext &C, const CallExpr *CE) const;
+
+ // Utility methods
+ std::pair<ProgramStateRef , ProgramStateRef >
+ static assumeZero(CheckerContext &C,
+ ProgramStateRef state, SVal V, QualType Ty);
+
+ static ProgramStateRef setCStringLength(ProgramStateRef state,
+ const MemRegion *MR,
+ SVal strLength);
+ static SVal getCStringLengthForRegion(CheckerContext &C,
+ ProgramStateRef &state,
+ const Expr *Ex,
+ const MemRegion *MR,
+ bool hypothetical);
+ SVal getCStringLength(CheckerContext &C,
+ ProgramStateRef &state,
+ const Expr *Ex,
+ SVal Buf,
+ bool hypothetical = false) const;
+
+ const StringLiteral *getCStringLiteral(CheckerContext &C,
+ ProgramStateRef &state,
+ const Expr *expr,
+ SVal val) const;
+
+ static ProgramStateRef InvalidateBuffer(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *Ex, SVal V,
+ bool IsSourceBuffer,
+ const Expr *Size);
+
+ static bool SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
+ const MemRegion *MR);
+
+ static bool memsetAux(const Expr *DstBuffer, SVal CharE,
+ const Expr *Size, CheckerContext &C,
+ ProgramStateRef &State);
+
+ // Re-usable checks
+ ProgramStateRef checkNonNull(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *S,
+ SVal l) const;
+ ProgramStateRef CheckLocation(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *S,
+ SVal l,
+ const char *message = nullptr) const;
+ ProgramStateRef CheckBufferAccess(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *Size,
+ const Expr *FirstBuf,
+ const Expr *SecondBuf,
+ const char *firstMessage = nullptr,
+ const char *secondMessage = nullptr,
+ bool WarnAboutSize = false) const;
+
+ ProgramStateRef CheckBufferAccess(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *Size,
+ const Expr *Buf,
+ const char *message = nullptr,
+ bool WarnAboutSize = false) const {
+ // This is a convenience overload.
+ return CheckBufferAccess(C, state, Size, Buf, nullptr, message, nullptr,
+ WarnAboutSize);
+ }
+ ProgramStateRef CheckOverlap(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *Size,
+ const Expr *First,
+ const Expr *Second) const;
+ void emitOverlapBug(CheckerContext &C,
+ ProgramStateRef state,
+ const Stmt *First,
+ const Stmt *Second) const;
+
+ void emitNullArgBug(CheckerContext &C, ProgramStateRef State, const Stmt *S,
+ StringRef WarningMsg) const;
+ void emitOutOfBoundsBug(CheckerContext &C, ProgramStateRef State,
+ const Stmt *S, StringRef WarningMsg) const;
+ void emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
+ const Stmt *S, StringRef WarningMsg) const;
+ void emitAdditionOverflowBug(CheckerContext &C, ProgramStateRef State) const;
+
+ ProgramStateRef checkAdditionOverflow(CheckerContext &C,
+ ProgramStateRef state,
+ NonLoc left,
+ NonLoc right) const;
+
+ // Return true if the destination buffer of the copy function may be in bound.
+ // Expects SVal of Size to be positive and unsigned.
+ // Expects SVal of FirstBuf to be a FieldRegion.
+ static bool IsFirstBufInBound(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *FirstBuf,
+ const Expr *Size);
+};
+
+} //end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(CStringLength, const MemRegion *, SVal)
+
+//===----------------------------------------------------------------------===//
+// Individual checks and utility methods.
+//===----------------------------------------------------------------------===//
+
+std::pair<ProgramStateRef , ProgramStateRef >
+CStringChecker::assumeZero(CheckerContext &C, ProgramStateRef state, SVal V,
+ QualType Ty) {
+ Optional<DefinedSVal> val = V.getAs<DefinedSVal>();
+ if (!val)
+ return std::pair<ProgramStateRef , ProgramStateRef >(state, state);
+
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ DefinedOrUnknownSVal zero = svalBuilder.makeZeroVal(Ty);
+ return state->assume(svalBuilder.evalEQ(state, *val, zero));
+}
+
+ProgramStateRef CStringChecker::checkNonNull(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *S, SVal l) const {
+ // If a previous check has failed, propagate the failure.
+ if (!state)
+ return nullptr;
+
+ ProgramStateRef stateNull, stateNonNull;
+ std::tie(stateNull, stateNonNull) = assumeZero(C, state, l, S->getType());
+
+ if (stateNull && !stateNonNull) {
+ if (Filter.CheckCStringNullArg) {
+ SmallString<80> buf;
+ llvm::raw_svector_ostream os(buf);
+ assert(CurrentFunctionDescription);
+ os << "Null pointer argument in call to " << CurrentFunctionDescription;
+
+ emitNullArgBug(C, stateNull, S, os.str());
+ }
+ return nullptr;
+ }
+
+ // From here on, assume that the value is non-null.
+ assert(stateNonNull);
+ return stateNonNull;
+}
+
+// FIXME: This was originally copied from ArrayBoundChecker.cpp. Refactor?
+ProgramStateRef CStringChecker::CheckLocation(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *S, SVal l,
+ const char *warningMsg) const {
+ // If a previous check has failed, propagate the failure.
+ if (!state)
+ return nullptr;
+
+ // Check for out of bound array element access.
+ const MemRegion *R = l.getAsRegion();
+ if (!R)
+ return state;
+
+ const ElementRegion *ER = dyn_cast<ElementRegion>(R);
+ if (!ER)
+ return state;
+
+ if (ER->getValueType() != C.getASTContext().CharTy)
+ return state;
+
+ // Get the size of the array.
+ const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion());
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ SVal Extent =
+ svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder));
+ DefinedOrUnknownSVal Size = Extent.castAs<DefinedOrUnknownSVal>();
+
+ // Get the index of the accessed element.
+ DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
+
+ ProgramStateRef StInBound = state->assumeInBound(Idx, Size, true);
+ ProgramStateRef StOutBound = state->assumeInBound(Idx, Size, false);
+ if (StOutBound && !StInBound) {
+ // These checks are either enabled by the CString out-of-bounds checker
+ // explicitly or implicitly by the Malloc checker.
+ // In the latter case we only do modeling but do not emit warning.
+ if (!Filter.CheckCStringOutOfBounds)
+ return nullptr;
+ // Emit a bug report.
+ if (warningMsg) {
+ emitOutOfBoundsBug(C, StOutBound, S, warningMsg);
+ } else {
+ assert(CurrentFunctionDescription);
+ assert(CurrentFunctionDescription[0] != '\0');
+
+ SmallString<80> buf;
+ llvm::raw_svector_ostream os(buf);
+ os << toUppercase(CurrentFunctionDescription[0])
+ << &CurrentFunctionDescription[1]
+ << " accesses out-of-bound array element";
+ emitOutOfBoundsBug(C, StOutBound, S, os.str());
+ }
+ return nullptr;
+ }
+
+ // Array bound check succeeded. From this point forward the array bound
+ // should always succeed.
+ return StInBound;
+}
+
+ProgramStateRef CStringChecker::CheckBufferAccess(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *Size,
+ const Expr *FirstBuf,
+ const Expr *SecondBuf,
+ const char *firstMessage,
+ const char *secondMessage,
+ bool WarnAboutSize) const {
+ // If a previous check has failed, propagate the failure.
+ if (!state)
+ return nullptr;
+
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ ASTContext &Ctx = svalBuilder.getContext();
+ const LocationContext *LCtx = C.getLocationContext();
+
+ QualType sizeTy = Size->getType();
+ QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
+
+ // Check that the first buffer is non-null.
+ SVal BufVal = C.getSVal(FirstBuf);
+ state = checkNonNull(C, state, FirstBuf, BufVal);
+ if (!state)
+ return nullptr;
+
+ // If out-of-bounds checking is turned off, skip the rest.
+ if (!Filter.CheckCStringOutOfBounds)
+ return state;
+
+ // Get the access length and make sure it is known.
+ // FIXME: This assumes the caller has already checked that the access length
+ // is positive. And that it's unsigned.
+ SVal LengthVal = C.getSVal(Size);
+ Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
+ if (!Length)
+ return state;
+
+ // Compute the offset of the last element to be accessed: size-1.
+ NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
+ SVal Offset = svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy);
+ if (Offset.isUnknown())
+ return nullptr;
+ NonLoc LastOffset = Offset.castAs<NonLoc>();
+
+ // Check that the first buffer is sufficiently long.
+ SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType());
+ if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) {
+ const Expr *warningExpr = (WarnAboutSize ? Size : FirstBuf);
+
+ SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc,
+ LastOffset, PtrTy);
+ state = CheckLocation(C, state, warningExpr, BufEnd, firstMessage);
+
+ // If the buffer isn't large enough, abort.
+ if (!state)
+ return nullptr;
+ }
+
+ // If there's a second buffer, check it as well.
+ if (SecondBuf) {
+ BufVal = state->getSVal(SecondBuf, LCtx);
+ state = checkNonNull(C, state, SecondBuf, BufVal);
+ if (!state)
+ return nullptr;
+
+ BufStart = svalBuilder.evalCast(BufVal, PtrTy, SecondBuf->getType());
+ if (Optional<Loc> BufLoc = BufStart.getAs<Loc>()) {
+ const Expr *warningExpr = (WarnAboutSize ? Size : SecondBuf);
+
+ SVal BufEnd = svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc,
+ LastOffset, PtrTy);
+ state = CheckLocation(C, state, warningExpr, BufEnd, secondMessage);
+ }
+ }
+
+ // Large enough or not, return this state!
+ return state;
+}
+
+ProgramStateRef CStringChecker::CheckOverlap(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *Size,
+ const Expr *First,
+ const Expr *Second) const {
+ if (!Filter.CheckCStringBufferOverlap)
+ return state;
+
+ // Do a simple check for overlap: if the two arguments are from the same
+ // buffer, see if the end of the first is greater than the start of the second
+ // or vice versa.
+
+ // If a previous check has failed, propagate the failure.
+ if (!state)
+ return nullptr;
+
+ ProgramStateRef stateTrue, stateFalse;
+
+ // Get the buffer values and make sure they're known locations.
+ const LocationContext *LCtx = C.getLocationContext();
+ SVal firstVal = state->getSVal(First, LCtx);
+ SVal secondVal = state->getSVal(Second, LCtx);
+
+ Optional<Loc> firstLoc = firstVal.getAs<Loc>();
+ if (!firstLoc)
+ return state;
+
+ Optional<Loc> secondLoc = secondVal.getAs<Loc>();
+ if (!secondLoc)
+ return state;
+
+ // Are the two values the same?
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ std::tie(stateTrue, stateFalse) =
+ state->assume(svalBuilder.evalEQ(state, *firstLoc, *secondLoc));
+
+ if (stateTrue && !stateFalse) {
+ // If the values are known to be equal, that's automatically an overlap.
+ emitOverlapBug(C, stateTrue, First, Second);
+ return nullptr;
+ }
+
+ // assume the two expressions are not equal.
+ assert(stateFalse);
+ state = stateFalse;
+
+ // Which value comes first?
+ QualType cmpTy = svalBuilder.getConditionType();
+ SVal reverse = svalBuilder.evalBinOpLL(state, BO_GT,
+ *firstLoc, *secondLoc, cmpTy);
+ Optional<DefinedOrUnknownSVal> reverseTest =
+ reverse.getAs<DefinedOrUnknownSVal>();
+ if (!reverseTest)
+ return state;
+
+ std::tie(stateTrue, stateFalse) = state->assume(*reverseTest);
+ if (stateTrue) {
+ if (stateFalse) {
+ // If we don't know which one comes first, we can't perform this test.
+ return state;
+ } else {
+ // Switch the values so that firstVal is before secondVal.
+ std::swap(firstLoc, secondLoc);
+
+ // Switch the Exprs as well, so that they still correspond.
+ std::swap(First, Second);
+ }
+ }
+
+ // Get the length, and make sure it too is known.
+ SVal LengthVal = state->getSVal(Size, LCtx);
+ Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
+ if (!Length)
+ return state;
+
+ // Convert the first buffer's start address to char*.
+ // Bail out if the cast fails.
+ ASTContext &Ctx = svalBuilder.getContext();
+ QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
+ SVal FirstStart = svalBuilder.evalCast(*firstLoc, CharPtrTy,
+ First->getType());
+ Optional<Loc> FirstStartLoc = FirstStart.getAs<Loc>();
+ if (!FirstStartLoc)
+ return state;
+
+ // Compute the end of the first buffer. Bail out if THAT fails.
+ SVal FirstEnd = svalBuilder.evalBinOpLN(state, BO_Add,
+ *FirstStartLoc, *Length, CharPtrTy);
+ Optional<Loc> FirstEndLoc = FirstEnd.getAs<Loc>();
+ if (!FirstEndLoc)
+ return state;
+
+ // Is the end of the first buffer past the start of the second buffer?
+ SVal Overlap = svalBuilder.evalBinOpLL(state, BO_GT,
+ *FirstEndLoc, *secondLoc, cmpTy);
+ Optional<DefinedOrUnknownSVal> OverlapTest =
+ Overlap.getAs<DefinedOrUnknownSVal>();
+ if (!OverlapTest)
+ return state;
+
+ std::tie(stateTrue, stateFalse) = state->assume(*OverlapTest);
+
+ if (stateTrue && !stateFalse) {
+ // Overlap!
+ emitOverlapBug(C, stateTrue, First, Second);
+ return nullptr;
+ }
+
+ // assume the two expressions don't overlap.
+ assert(stateFalse);
+ return stateFalse;
+}
+
+void CStringChecker::emitOverlapBug(CheckerContext &C, ProgramStateRef state,
+ const Stmt *First, const Stmt *Second) const {
+ ExplodedNode *N = C.generateErrorNode(state);
+ if (!N)
+ return;
+
+ if (!BT_Overlap)
+ BT_Overlap.reset(new BugType(Filter.CheckNameCStringBufferOverlap,
+ categories::UnixAPI, "Improper arguments"));
+
+ // Generate a report for this bug.
+ auto report = llvm::make_unique<BugReport>(
+ *BT_Overlap, "Arguments must not be overlapping buffers", N);
+ report->addRange(First->getSourceRange());
+ report->addRange(Second->getSourceRange());
+
+ C.emitReport(std::move(report));
+}
+
+void CStringChecker::emitNullArgBug(CheckerContext &C, ProgramStateRef State,
+ const Stmt *S, StringRef WarningMsg) const {
+ if (ExplodedNode *N = C.generateErrorNode(State)) {
+ if (!BT_Null)
+ BT_Null.reset(new BuiltinBug(
+ Filter.CheckNameCStringNullArg, categories::UnixAPI,
+ "Null pointer argument in call to byte string function"));
+
+ BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Null.get());
+ auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N);
+ Report->addRange(S->getSourceRange());
+ if (const auto *Ex = dyn_cast<Expr>(S))
+ bugreporter::trackExpressionValue(N, Ex, *Report);
+ C.emitReport(std::move(Report));
+ }
+}
+
+void CStringChecker::emitOutOfBoundsBug(CheckerContext &C,
+ ProgramStateRef State, const Stmt *S,
+ StringRef WarningMsg) const {
+ if (ExplodedNode *N = C.generateErrorNode(State)) {
+ if (!BT_Bounds)
+ BT_Bounds.reset(new BuiltinBug(
+ Filter.CheckCStringOutOfBounds ? Filter.CheckNameCStringOutOfBounds
+ : Filter.CheckNameCStringNullArg,
+ "Out-of-bound array access",
+ "Byte string function accesses out-of-bound array element"));
+
+ BuiltinBug *BT = static_cast<BuiltinBug *>(BT_Bounds.get());
+
+ // FIXME: It would be nice to eventually make this diagnostic more clear,
+ // e.g., by referencing the original declaration or by saying *why* this
+ // reference is outside the range.
+ auto Report = llvm::make_unique<BugReport>(*BT, WarningMsg, N);
+ Report->addRange(S->getSourceRange());
+ C.emitReport(std::move(Report));
+ }
+}
+
+void CStringChecker::emitNotCStringBug(CheckerContext &C, ProgramStateRef State,
+ const Stmt *S,
+ StringRef WarningMsg) const {
+ if (ExplodedNode *N = C.generateNonFatalErrorNode(State)) {
+ if (!BT_NotCString)
+ BT_NotCString.reset(new BuiltinBug(
+ Filter.CheckNameCStringNotNullTerm, categories::UnixAPI,
+ "Argument is not a null-terminated string."));
+
+ auto Report = llvm::make_unique<BugReport>(*BT_NotCString, WarningMsg, N);
+
+ Report->addRange(S->getSourceRange());
+ C.emitReport(std::move(Report));
+ }
+}
+
+void CStringChecker::emitAdditionOverflowBug(CheckerContext &C,
+ ProgramStateRef State) const {
+ if (ExplodedNode *N = C.generateErrorNode(State)) {
+ if (!BT_NotCString)
+ BT_NotCString.reset(
+ new BuiltinBug(Filter.CheckNameCStringOutOfBounds, "API",
+ "Sum of expressions causes overflow."));
+
+ // This isn't a great error message, but this should never occur in real
+ // code anyway -- you'd have to create a buffer longer than a size_t can
+ // represent, which is sort of a contradiction.
+ const char *WarningMsg =
+ "This expression will create a string whose length is too big to "
+ "be represented as a size_t";
+
+ auto Report = llvm::make_unique<BugReport>(*BT_NotCString, WarningMsg, N);
+ C.emitReport(std::move(Report));
+ }
+}
+
+ProgramStateRef CStringChecker::checkAdditionOverflow(CheckerContext &C,
+ ProgramStateRef state,
+ NonLoc left,
+ NonLoc right) const {
+ // If out-of-bounds checking is turned off, skip the rest.
+ if (!Filter.CheckCStringOutOfBounds)
+ return state;
+
+ // If a previous check has failed, propagate the failure.
+ if (!state)
+ return nullptr;
+
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
+
+ QualType sizeTy = svalBuilder.getContext().getSizeType();
+ const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy);
+ NonLoc maxVal = svalBuilder.makeIntVal(maxValInt);
+
+ SVal maxMinusRight;
+ if (right.getAs<nonloc::ConcreteInt>()) {
+ maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, right,
+ sizeTy);
+ } else {
+ // Try switching the operands. (The order of these two assignments is
+ // important!)
+ maxMinusRight = svalBuilder.evalBinOpNN(state, BO_Sub, maxVal, left,
+ sizeTy);
+ left = right;
+ }
+
+ if (Optional<NonLoc> maxMinusRightNL = maxMinusRight.getAs<NonLoc>()) {
+ QualType cmpTy = svalBuilder.getConditionType();
+ // If left > max - right, we have an overflow.
+ SVal willOverflow = svalBuilder.evalBinOpNN(state, BO_GT, left,
+ *maxMinusRightNL, cmpTy);
+
+ ProgramStateRef stateOverflow, stateOkay;
+ std::tie(stateOverflow, stateOkay) =
+ state->assume(willOverflow.castAs<DefinedOrUnknownSVal>());
+
+ if (stateOverflow && !stateOkay) {
+ // We have an overflow. Emit a bug report.
+ emitAdditionOverflowBug(C, stateOverflow);
+ return nullptr;
+ }
+
+ // From now on, assume an overflow didn't occur.
+ assert(stateOkay);
+ state = stateOkay;
+ }
+
+ return state;
+}
+
+ProgramStateRef CStringChecker::setCStringLength(ProgramStateRef state,
+ const MemRegion *MR,
+ SVal strLength) {
+ assert(!strLength.isUndef() && "Attempt to set an undefined string length");
+
+ MR = MR->StripCasts();
+
+ switch (MR->getKind()) {
+ case MemRegion::StringRegionKind:
+ // FIXME: This can happen if we strcpy() into a string region. This is
+ // undefined [C99 6.4.5p6], but we should still warn about it.
+ return state;
+
+ case MemRegion::SymbolicRegionKind:
+ case MemRegion::AllocaRegionKind:
+ case MemRegion::VarRegionKind:
+ case MemRegion::FieldRegionKind:
+ case MemRegion::ObjCIvarRegionKind:
+ // These are the types we can currently track string lengths for.
+ break;
+
+ case MemRegion::ElementRegionKind:
+ // FIXME: Handle element regions by upper-bounding the parent region's
+ // string length.
+ return state;
+
+ default:
+ // Other regions (mostly non-data) can't have a reliable C string length.
+ // For now, just ignore the change.
+ // FIXME: These are rare but not impossible. We should output some kind of
+ // warning for things like strcpy((char[]){'a', 0}, "b");
+ return state;
+ }
+
+ if (strLength.isUnknown())
+ return state->remove<CStringLength>(MR);
+
+ return state->set<CStringLength>(MR, strLength);
+}
+
+SVal CStringChecker::getCStringLengthForRegion(CheckerContext &C,
+ ProgramStateRef &state,
+ const Expr *Ex,
+ const MemRegion *MR,
+ bool hypothetical) {
+ if (!hypothetical) {
+ // If there's a recorded length, go ahead and return it.
+ const SVal *Recorded = state->get<CStringLength>(MR);
+ if (Recorded)
+ return *Recorded;
+ }
+
+ // Otherwise, get a new symbol and update the state.
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ QualType sizeTy = svalBuilder.getContext().getSizeType();
+ SVal strLength = svalBuilder.getMetadataSymbolVal(CStringChecker::getTag(),
+ MR, Ex, sizeTy,
+ C.getLocationContext(),
+ C.blockCount());
+
+ if (!hypothetical) {
+ if (Optional<NonLoc> strLn = strLength.getAs<NonLoc>()) {
+ // In case of unbounded calls strlen etc bound the range to SIZE_MAX/4
+ BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
+ const llvm::APSInt &maxValInt = BVF.getMaxValue(sizeTy);
+ llvm::APSInt fourInt = APSIntType(maxValInt).getValue(4);
+ const llvm::APSInt *maxLengthInt = BVF.evalAPSInt(BO_Div, maxValInt,
+ fourInt);
+ NonLoc maxLength = svalBuilder.makeIntVal(*maxLengthInt);
+ SVal evalLength = svalBuilder.evalBinOpNN(state, BO_LE, *strLn,
+ maxLength, sizeTy);
+ state = state->assume(evalLength.castAs<DefinedOrUnknownSVal>(), true);
+ }
+ state = state->set<CStringLength>(MR, strLength);
+ }
+
+ return strLength;
+}
+
+SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
+ const Expr *Ex, SVal Buf,
+ bool hypothetical) const {
+ const MemRegion *MR = Buf.getAsRegion();
+ if (!MR) {
+ // If we can't get a region, see if it's something we /know/ isn't a
+ // C string. In the context of locations, the only time we can issue such
+ // a warning is for labels.
+ if (Optional<loc::GotoLabel> Label = Buf.getAs<loc::GotoLabel>()) {
+ if (Filter.CheckCStringNotNullTerm) {
+ SmallString<120> buf;
+ llvm::raw_svector_ostream os(buf);
+ assert(CurrentFunctionDescription);
+ os << "Argument to " << CurrentFunctionDescription
+ << " is the address of the label '" << Label->getLabel()->getName()
+ << "', which is not a null-terminated string";
+
+ emitNotCStringBug(C, state, Ex, os.str());
+ }
+ return UndefinedVal();
+ }
+
+ // If it's not a region and not a label, give up.
+ return UnknownVal();
+ }
+
+ // If we have a region, strip casts from it and see if we can figure out
+ // its length. For anything we can't figure out, just return UnknownVal.
+ MR = MR->StripCasts();
+
+ switch (MR->getKind()) {
+ case MemRegion::StringRegionKind: {
+ // Modifying the contents of string regions is undefined [C99 6.4.5p6],
+ // so we can assume that the byte length is the correct C string length.
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ QualType sizeTy = svalBuilder.getContext().getSizeType();
+ const StringLiteral *strLit = cast<StringRegion>(MR)->getStringLiteral();
+ return svalBuilder.makeIntVal(strLit->getByteLength(), sizeTy);
+ }
+ case MemRegion::SymbolicRegionKind:
+ case MemRegion::AllocaRegionKind:
+ case MemRegion::VarRegionKind:
+ case MemRegion::FieldRegionKind:
+ case MemRegion::ObjCIvarRegionKind:
+ return getCStringLengthForRegion(C, state, Ex, MR, hypothetical);
+ case MemRegion::CompoundLiteralRegionKind:
+ // FIXME: Can we track this? Is it necessary?
+ return UnknownVal();
+ case MemRegion::ElementRegionKind:
+ // FIXME: How can we handle this? It's not good enough to subtract the
+ // offset from the base string length; consider "123\x00567" and &a[5].
+ return UnknownVal();
+ default:
+ // Other regions (mostly non-data) can't have a reliable C string length.
+ // In this case, an error is emitted and UndefinedVal is returned.
+ // The caller should always be prepared to handle this case.
+ if (Filter.CheckCStringNotNullTerm) {
+ SmallString<120> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ assert(CurrentFunctionDescription);
+ os << "Argument to " << CurrentFunctionDescription << " is ";
+
+ if (SummarizeRegion(os, C.getASTContext(), MR))
+ os << ", which is not a null-terminated string";
+ else
+ os << "not a null-terminated string";
+
+ emitNotCStringBug(C, state, Ex, os.str());
+ }
+ return UndefinedVal();
+ }
+}
+
+const StringLiteral *CStringChecker::getCStringLiteral(CheckerContext &C,
+ ProgramStateRef &state, const Expr *expr, SVal val) const {
+
+ // Get the memory region pointed to by the val.
+ const MemRegion *bufRegion = val.getAsRegion();
+ if (!bufRegion)
+ return nullptr;
+
+ // Strip casts off the memory region.
+ bufRegion = bufRegion->StripCasts();
+
+ // Cast the memory region to a string region.
+ const StringRegion *strRegion= dyn_cast<StringRegion>(bufRegion);
+ if (!strRegion)
+ return nullptr;
+
+ // Return the actual string in the string region.
+ return strRegion->getStringLiteral();
+}
+
+bool CStringChecker::IsFirstBufInBound(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *FirstBuf,
+ const Expr *Size) {
+ // If we do not know that the buffer is long enough we return 'true'.
+ // Otherwise the parent region of this field region would also get
+ // invalidated, which would lead to warnings based on an unknown state.
+
+ // Originally copied from CheckBufferAccess and CheckLocation.
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ ASTContext &Ctx = svalBuilder.getContext();
+ const LocationContext *LCtx = C.getLocationContext();
+
+ QualType sizeTy = Size->getType();
+ QualType PtrTy = Ctx.getPointerType(Ctx.CharTy);
+ SVal BufVal = state->getSVal(FirstBuf, LCtx);
+
+ SVal LengthVal = state->getSVal(Size, LCtx);
+ Optional<NonLoc> Length = LengthVal.getAs<NonLoc>();
+ if (!Length)
+ return true; // cf top comment.
+
+ // Compute the offset of the last element to be accessed: size-1.
+ NonLoc One = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
+ SVal Offset = svalBuilder.evalBinOpNN(state, BO_Sub, *Length, One, sizeTy);
+ if (Offset.isUnknown())
+ return true; // cf top comment
+ NonLoc LastOffset = Offset.castAs<NonLoc>();
+
+ // Check that the first buffer is sufficiently long.
+ SVal BufStart = svalBuilder.evalCast(BufVal, PtrTy, FirstBuf->getType());
+ Optional<Loc> BufLoc = BufStart.getAs<Loc>();
+ if (!BufLoc)
+ return true; // cf top comment.
+
+ SVal BufEnd =
+ svalBuilder.evalBinOpLN(state, BO_Add, *BufLoc, LastOffset, PtrTy);
+
+ // Check for out of bound array element access.
+ const MemRegion *R = BufEnd.getAsRegion();
+ if (!R)
+ return true; // cf top comment.
+
+ const ElementRegion *ER = dyn_cast<ElementRegion>(R);
+ if (!ER)
+ return true; // cf top comment.
+
+ // FIXME: Does this crash when a non-standard definition
+ // of a library function is encountered?
+ assert(ER->getValueType() == C.getASTContext().CharTy &&
+ "IsFirstBufInBound should only be called with char* ElementRegions");
+
+ // Get the size of the array.
+ const SubRegion *superReg = cast<SubRegion>(ER->getSuperRegion());
+ SVal Extent =
+ svalBuilder.convertToArrayIndex(superReg->getExtent(svalBuilder));
+ DefinedOrUnknownSVal ExtentSize = Extent.castAs<DefinedOrUnknownSVal>();
+
+ // Get the index of the accessed element.
+ DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
+
+ ProgramStateRef StInBound = state->assumeInBound(Idx, ExtentSize, true);
+
+ return static_cast<bool>(StInBound);
+}
+
+ProgramStateRef CStringChecker::InvalidateBuffer(CheckerContext &C,
+ ProgramStateRef state,
+ const Expr *E, SVal V,
+ bool IsSourceBuffer,
+ const Expr *Size) {
+ Optional<Loc> L = V.getAs<Loc>();
+ if (!L)
+ return state;
+
+ // FIXME: This is a simplified version of what's in CFRefCount.cpp -- it makes
+ // some assumptions about the value that CFRefCount can't. Even so, it should
+ // probably be refactored.
+ if (Optional<loc::MemRegionVal> MR = L->getAs<loc::MemRegionVal>()) {
+ const MemRegion *R = MR->getRegion()->StripCasts();
+
+ // Are we dealing with an ElementRegion? If so, we should be invalidating
+ // the super-region.
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
+ R = ER->getSuperRegion();
+ // FIXME: What about layers of ElementRegions?
+ }
+
+ // Invalidate this region.
+ const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
+
+ bool CausesPointerEscape = false;
+ RegionAndSymbolInvalidationTraits ITraits;
+ // Invalidate and escape only indirect regions accessible through the source
+ // buffer.
+ if (IsSourceBuffer) {
+ ITraits.setTrait(R->getBaseRegion(),
+ RegionAndSymbolInvalidationTraits::TK_PreserveContents);
+ ITraits.setTrait(R, RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
+ CausesPointerEscape = true;
+ } else {
+ const MemRegion::Kind& K = R->getKind();
+ if (K == MemRegion::FieldRegionKind)
+ if (Size && IsFirstBufInBound(C, state, E, Size)) {
+ // If destination buffer is a field region and access is in bound,
+ // do not invalidate its super region.
+ ITraits.setTrait(
+ R,
+ RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
+ }
+ }
+
+ return state->invalidateRegions(R, E, C.blockCount(), LCtx,
+ CausesPointerEscape, nullptr, nullptr,
+ &ITraits);
+ }
+
+ // If we have a non-region value by chance, just remove the binding.
+ // FIXME: is this necessary or correct? This handles the non-Region
+ // cases. Is it ever valid to store to these?
+ return state->killBinding(*L);
+}
+
+bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
+ const MemRegion *MR) {
+ const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(MR);
+
+ switch (MR->getKind()) {
+ case MemRegion::FunctionCodeRegionKind: {
+ const NamedDecl *FD = cast<FunctionCodeRegion>(MR)->getDecl();
+ if (FD)
+ os << "the address of the function '" << *FD << '\'';
+ else
+ os << "the address of a function";
+ return true;
+ }
+ case MemRegion::BlockCodeRegionKind:
+ os << "block text";
+ return true;
+ case MemRegion::BlockDataRegionKind:
+ os << "a block";
+ return true;
+ case MemRegion::CXXThisRegionKind:
+ case MemRegion::CXXTempObjectRegionKind:
+ os << "a C++ temp object of type " << TVR->getValueType().getAsString();
+ return true;
+ case MemRegion::VarRegionKind:
+ os << "a variable of type" << TVR->getValueType().getAsString();
+ return true;
+ case MemRegion::FieldRegionKind:
+ os << "a field of type " << TVR->getValueType().getAsString();
+ return true;
+ case MemRegion::ObjCIvarRegionKind:
+ os << "an instance variable of type " << TVR->getValueType().getAsString();
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool CStringChecker::memsetAux(const Expr *DstBuffer, SVal CharVal,
+ const Expr *Size, CheckerContext &C,
+ ProgramStateRef &State) {
+ SVal MemVal = C.getSVal(DstBuffer);
+ SVal SizeVal = C.getSVal(Size);
+ const MemRegion *MR = MemVal.getAsRegion();
+ if (!MR)
+ return false;
+
+ // We're about to model memset by producing a "default binding" in the Store.
+ // Our current implementation - RegionStore - doesn't support default bindings
+ // that don't cover the whole base region. So we should first get the offset
+ // and the base region to figure out whether the offset of buffer is 0.
+ RegionOffset Offset = MR->getAsOffset();
+ const MemRegion *BR = Offset.getRegion();
+
+ Optional<NonLoc> SizeNL = SizeVal.getAs<NonLoc>();
+ if (!SizeNL)
+ return false;
+
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ ASTContext &Ctx = C.getASTContext();
+
+ // void *memset(void *dest, int ch, size_t count);
+ // For now we can only handle the case of offset is 0 and concrete char value.
+ if (Offset.isValid() && !Offset.hasSymbolicOffset() &&
+ Offset.getOffset() == 0) {
+ // Get the base region's extent.
+ auto *SubReg = cast<SubRegion>(BR);
+ DefinedOrUnknownSVal Extent = SubReg->getExtent(svalBuilder);
+
+ ProgramStateRef StateWholeReg, StateNotWholeReg;
+ std::tie(StateWholeReg, StateNotWholeReg) =
+ State->assume(svalBuilder.evalEQ(State, Extent, *SizeNL));
+
+ // With the semantic of 'memset()', we should convert the CharVal to
+ // unsigned char.
+ CharVal = svalBuilder.evalCast(CharVal, Ctx.UnsignedCharTy, Ctx.IntTy);
+
+ ProgramStateRef StateNullChar, StateNonNullChar;
+ std::tie(StateNullChar, StateNonNullChar) =
+ assumeZero(C, State, CharVal, Ctx.UnsignedCharTy);
+
+ if (StateWholeReg && !StateNotWholeReg && StateNullChar &&
+ !StateNonNullChar) {
+ // If the 'memset()' acts on the whole region of destination buffer and
+ // the value of the second argument of 'memset()' is zero, bind the second
+ // argument's value to the destination buffer with 'default binding'.
+ // FIXME: Since there is no perfect way to bind the non-zero character, we
+ // can only deal with zero value here. In the future, we need to deal with
+ // the binding of non-zero value in the case of whole region.
+ State = State->bindDefaultZero(svalBuilder.makeLoc(BR),
+ C.getLocationContext());
+ } else {
+ // If the destination buffer's extent is not equal to the value of
+ // third argument, just invalidate buffer.
+ State = InvalidateBuffer(C, State, DstBuffer, MemVal,
+ /*IsSourceBuffer*/ false, Size);
+ }
+
+ if (StateNullChar && !StateNonNullChar) {
+ // If the value of the second argument of 'memset()' is zero, set the
+ // string length of destination buffer to 0 directly.
+ State = setCStringLength(State, MR,
+ svalBuilder.makeZeroVal(Ctx.getSizeType()));
+ } else if (!StateNullChar && StateNonNullChar) {
+ SVal NewStrLen = svalBuilder.getMetadataSymbolVal(
+ CStringChecker::getTag(), MR, DstBuffer, Ctx.getSizeType(),
+ C.getLocationContext(), C.blockCount());
+
+ // If the value of second argument is not zero, then the string length
+ // is at least the size argument.
+ SVal NewStrLenGESize = svalBuilder.evalBinOp(
+ State, BO_GE, NewStrLen, SizeVal, svalBuilder.getConditionType());
+
+ State = setCStringLength(
+ State->assume(NewStrLenGESize.castAs<DefinedOrUnknownSVal>(), true),
+ MR, NewStrLen);
+ }
+ } else {
+ // If the offset is not zero and char value is not concrete, we can do
+ // nothing but invalidate the buffer.
+ State = InvalidateBuffer(C, State, DstBuffer, MemVal,
+ /*IsSourceBuffer*/ false, Size);
+ }
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// evaluation of individual function calls.
+//===----------------------------------------------------------------------===//
+
+void CStringChecker::evalCopyCommon(CheckerContext &C,
+ const CallExpr *CE,
+ ProgramStateRef state,
+ const Expr *Size, const Expr *Dest,
+ const Expr *Source, bool Restricted,
+ bool IsMempcpy) const {
+ CurrentFunctionDescription = "memory copy function";
+
+ // See if the size argument is zero.
+ const LocationContext *LCtx = C.getLocationContext();
+ SVal sizeVal = state->getSVal(Size, LCtx);
+ QualType sizeTy = Size->getType();
+
+ ProgramStateRef stateZeroSize, stateNonZeroSize;
+ std::tie(stateZeroSize, stateNonZeroSize) =
+ assumeZero(C, state, sizeVal, sizeTy);
+
+ // Get the value of the Dest.
+ SVal destVal = state->getSVal(Dest, LCtx);
+
+ // If the size is zero, there won't be any actual memory access, so
+ // just bind the return value to the destination buffer and return.
+ if (stateZeroSize && !stateNonZeroSize) {
+ stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, destVal);
+ C.addTransition(stateZeroSize);
+ return;
+ }
+
+ // If the size can be nonzero, we have to check the other arguments.
+ if (stateNonZeroSize) {
+ state = stateNonZeroSize;
+
+ // Ensure the destination is not null. If it is NULL there will be a
+ // NULL pointer dereference.
+ state = checkNonNull(C, state, Dest, destVal);
+ if (!state)
+ return;
+
+ // Get the value of the Src.
+ SVal srcVal = state->getSVal(Source, LCtx);
+
+ // Ensure the source is not null. If it is NULL there will be a
+ // NULL pointer dereference.
+ state = checkNonNull(C, state, Source, srcVal);
+ if (!state)
+ return;
+
+ // Ensure the accesses are valid and that the buffers do not overlap.
+ const char * const writeWarning =
+ "Memory copy function overflows destination buffer";
+ state = CheckBufferAccess(C, state, Size, Dest, Source,
+ writeWarning, /* sourceWarning = */ nullptr);
+ if (Restricted)
+ state = CheckOverlap(C, state, Size, Dest, Source);
+
+ if (!state)
+ return;
+
+ // If this is mempcpy, get the byte after the last byte copied and
+ // bind the expr.
+ if (IsMempcpy) {
+ // Get the byte after the last byte copied.
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ ASTContext &Ctx = SvalBuilder.getContext();
+ QualType CharPtrTy = Ctx.getPointerType(Ctx.CharTy);
+ SVal DestRegCharVal =
+ SvalBuilder.evalCast(destVal, CharPtrTy, Dest->getType());
+ SVal lastElement = C.getSValBuilder().evalBinOp(
+ state, BO_Add, DestRegCharVal, sizeVal, Dest->getType());
+ // If we don't know how much we copied, we can at least
+ // conjure a return value for later.
+ if (lastElement.isUnknown())
+ lastElement = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx,
+ C.blockCount());
+
+ // The byte after the last byte copied is the return value.
+ state = state->BindExpr(CE, LCtx, lastElement);
+ } else {
+ // All other copies return the destination buffer.
+ // (Well, bcopy() has a void return type, but this won't hurt.)
+ state = state->BindExpr(CE, LCtx, destVal);
+ }
+
+ // Invalidate the destination (regular invalidation without pointer-escaping
+ // the address of the top-level region).
+ // FIXME: Even if we can't perfectly model the copy, we should see if we
+ // can use LazyCompoundVals to copy the source values into the destination.
+ // This would probably remove any existing bindings past the end of the
+ // copied region, but that's still an improvement over blank invalidation.
+ state = InvalidateBuffer(C, state, Dest, C.getSVal(Dest),
+ /*IsSourceBuffer*/false, Size);
+
+ // Invalidate the source (const-invalidation without const-pointer-escaping
+ // the address of the top-level region).
+ state = InvalidateBuffer(C, state, Source, C.getSVal(Source),
+ /*IsSourceBuffer*/true, nullptr);
+
+ C.addTransition(state);
+ }
+}
+
+
+void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ // void *memcpy(void *restrict dst, const void *restrict src, size_t n);
+ // The return value is the address of the destination buffer.
+ const Expr *Dest = CE->getArg(0);
+ ProgramStateRef state = C.getState();
+
+ evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true);
+}
+
+void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ // void *mempcpy(void *restrict dst, const void *restrict src, size_t n);
+ // The return value is a pointer to the byte following the last written byte.
+ const Expr *Dest = CE->getArg(0);
+ ProgramStateRef state = C.getState();
+
+ evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1), true, true);
+}
+
+void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ // void *memmove(void *dst, const void *src, size_t n);
+ // The return value is the address of the destination buffer.
+ const Expr *Dest = CE->getArg(0);
+ ProgramStateRef state = C.getState();
+
+ evalCopyCommon(C, CE, state, CE->getArg(2), Dest, CE->getArg(1));
+}
+
+void CStringChecker::evalBcopy(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ // void bcopy(const void *src, void *dst, size_t n);
+ evalCopyCommon(C, CE, C.getState(),
+ CE->getArg(2), CE->getArg(1), CE->getArg(0));
+}
+
+void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ // int memcmp(const void *s1, const void *s2, size_t n);
+ CurrentFunctionDescription = "memory comparison function";
+
+ const Expr *Left = CE->getArg(0);
+ const Expr *Right = CE->getArg(1);
+ const Expr *Size = CE->getArg(2);
+
+ ProgramStateRef state = C.getState();
+ SValBuilder &svalBuilder = C.getSValBuilder();
+
+ // See if the size argument is zero.
+ const LocationContext *LCtx = C.getLocationContext();
+ SVal sizeVal = state->getSVal(Size, LCtx);
+ QualType sizeTy = Size->getType();
+
+ ProgramStateRef stateZeroSize, stateNonZeroSize;
+ std::tie(stateZeroSize, stateNonZeroSize) =
+ assumeZero(C, state, sizeVal, sizeTy);
+
+ // If the size can be zero, the result will be 0 in that case, and we don't
+ // have to check either of the buffers.
+ if (stateZeroSize) {
+ state = stateZeroSize;
+ state = state->BindExpr(CE, LCtx,
+ svalBuilder.makeZeroVal(CE->getType()));
+ C.addTransition(state);
+ }
+
+ // If the size can be nonzero, we have to check the other arguments.
+ if (stateNonZeroSize) {
+ state = stateNonZeroSize;
+ // If we know the two buffers are the same, we know the result is 0.
+ // First, get the two buffers' addresses. Another checker will have already
+ // made sure they're not undefined.
+ DefinedOrUnknownSVal LV =
+ state->getSVal(Left, LCtx).castAs<DefinedOrUnknownSVal>();
+ DefinedOrUnknownSVal RV =
+ state->getSVal(Right, LCtx).castAs<DefinedOrUnknownSVal>();
+
+ // See if they are the same.
+ DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV);
+ ProgramStateRef StSameBuf, StNotSameBuf;
+ std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
+
+ // If the two arguments might be the same buffer, we know the result is 0,
+ // and we only need to check one size.
+ if (StSameBuf) {
+ state = StSameBuf;
+ state = CheckBufferAccess(C, state, Size, Left);
+ if (state) {
+ state = StSameBuf->BindExpr(CE, LCtx,
+ svalBuilder.makeZeroVal(CE->getType()));
+ C.addTransition(state);
+ }
+ }
+
+ // If the two arguments might be different buffers, we have to check the
+ // size of both of them.
+ if (StNotSameBuf) {
+ state = StNotSameBuf;
+ state = CheckBufferAccess(C, state, Size, Left, Right);
+ if (state) {
+ // The return value is the comparison result, which we don't know.
+ SVal CmpV = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
+ C.blockCount());
+ state = state->BindExpr(CE, LCtx, CmpV);
+ C.addTransition(state);
+ }
+ }
+ }
+}
+
+void CStringChecker::evalstrLength(CheckerContext &C,
+ const CallExpr *CE) const {
+ if (CE->getNumArgs() < 1)
+ return;
+
+ // size_t strlen(const char *s);
+ evalstrLengthCommon(C, CE, /* IsStrnlen = */ false);
+}
+
+void CStringChecker::evalstrnLength(CheckerContext &C,
+ const CallExpr *CE) const {
+ if (CE->getNumArgs() < 2)
+ return;
+
+ // size_t strnlen(const char *s, size_t maxlen);
+ evalstrLengthCommon(C, CE, /* IsStrnlen = */ true);
+}
+
+void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
+ bool IsStrnlen) const {
+ CurrentFunctionDescription = "string length function";
+ ProgramStateRef state = C.getState();
+ const LocationContext *LCtx = C.getLocationContext();
+
+ if (IsStrnlen) {
+ const Expr *maxlenExpr = CE->getArg(1);
+ SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);
+
+ ProgramStateRef stateZeroSize, stateNonZeroSize;
+ std::tie(stateZeroSize, stateNonZeroSize) =
+ assumeZero(C, state, maxlenVal, maxlenExpr->getType());
+
+ // If the size can be zero, the result will be 0 in that case, and we don't
+ // have to check the string itself.
+ if (stateZeroSize) {
+ SVal zero = C.getSValBuilder().makeZeroVal(CE->getType());
+ stateZeroSize = stateZeroSize->BindExpr(CE, LCtx, zero);
+ C.addTransition(stateZeroSize);
+ }
+
+ // If the size is GUARANTEED to be zero, we're done!
+ if (!stateNonZeroSize)
+ return;
+
+ // Otherwise, record the assumption that the size is nonzero.
+ state = stateNonZeroSize;
+ }
+
+ // Check that the string argument is non-null.
+ const Expr *Arg = CE->getArg(0);
+ SVal ArgVal = state->getSVal(Arg, LCtx);
+
+ state = checkNonNull(C, state, Arg, ArgVal);
+
+ if (!state)
+ return;
+
+ SVal strLength = getCStringLength(C, state, Arg, ArgVal);
+
+ // If the argument isn't a valid C string, there's no valid state to
+ // transition to.
+ if (strLength.isUndef())
+ return;
+
+ DefinedOrUnknownSVal result = UnknownVal();
+
+ // If the check is for strnlen() then bind the return value to no more than
+ // the maxlen value.
+ if (IsStrnlen) {
+ QualType cmpTy = C.getSValBuilder().getConditionType();
+
+ // It's a little unfortunate to be getting this again,
+ // but it's not that expensive...
+ const Expr *maxlenExpr = CE->getArg(1);
+ SVal maxlenVal = state->getSVal(maxlenExpr, LCtx);
+
+ Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
+ Optional<NonLoc> maxlenValNL = maxlenVal.getAs<NonLoc>();
+
+ if (strLengthNL && maxlenValNL) {
+ ProgramStateRef stateStringTooLong, stateStringNotTooLong;
+
+ // Check if the strLength is greater than the maxlen.
+ std::tie(stateStringTooLong, stateStringNotTooLong) = state->assume(
+ C.getSValBuilder()
+ .evalBinOpNN(state, BO_GT, *strLengthNL, *maxlenValNL, cmpTy)
+ .castAs<DefinedOrUnknownSVal>());
+
+ if (stateStringTooLong && !stateStringNotTooLong) {
+ // If the string is longer than maxlen, return maxlen.
+ result = *maxlenValNL;
+ } else if (stateStringNotTooLong && !stateStringTooLong) {
+ // If the string is shorter than maxlen, return its length.
+ result = *strLengthNL;
+ }
+ }
+
+ if (result.isUnknown()) {
+ // If we don't have enough information for a comparison, there's
+ // no guarantee the full string length will actually be returned.
+ // All we know is the return value is the min of the string length
+ // and the limit. This is better than nothing.
+ result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx,
+ C.blockCount());
+ NonLoc resultNL = result.castAs<NonLoc>();
+
+ if (strLengthNL) {
+ state = state->assume(C.getSValBuilder().evalBinOpNN(
+ state, BO_LE, resultNL, *strLengthNL, cmpTy)
+ .castAs<DefinedOrUnknownSVal>(), true);
+ }
+
+ if (maxlenValNL) {
+ state = state->assume(C.getSValBuilder().evalBinOpNN(
+ state, BO_LE, resultNL, *maxlenValNL, cmpTy)
+ .castAs<DefinedOrUnknownSVal>(), true);
+ }
+ }
+
+ } else {
+ // This is a plain strlen(), not strnlen().
+ result = strLength.castAs<DefinedOrUnknownSVal>();
+
+ // If we don't know the length of the string, conjure a return
+ // value, so it can be used in constraints, at least.
+ if (result.isUnknown()) {
+ result = C.getSValBuilder().conjureSymbolVal(nullptr, CE, LCtx,
+ C.blockCount());
+ }
+ }
+
+ // Bind the return value.
+ assert(!result.isUnknown() && "Should have conjured a value by now");
+ state = state->BindExpr(CE, LCtx, result);
+ C.addTransition(state);
+}
+
+void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 2)
+ return;
+
+ // char *strcpy(char *restrict dst, const char *restrict src);
+ evalStrcpyCommon(C, CE,
+ /* returnEnd = */ false,
+ /* isBounded = */ false,
+ /* isAppending = */ false);
+}
+
+void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ // char *strncpy(char *restrict dst, const char *restrict src, size_t n);
+ evalStrcpyCommon(C, CE,
+ /* returnEnd = */ false,
+ /* isBounded = */ true,
+ /* isAppending = */ false);
+}
+
+void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 2)
+ return;
+
+ // char *stpcpy(char *restrict dst, const char *restrict src);
+ evalStrcpyCommon(C, CE,
+ /* returnEnd = */ true,
+ /* isBounded = */ false,
+ /* isAppending = */ false);
+}
+
+void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ // char *strlcpy(char *dst, const char *src, size_t n);
+ evalStrcpyCommon(C, CE,
+ /* returnEnd = */ true,
+ /* isBounded = */ true,
+ /* isAppending = */ false,
+ /* returnPtr = */ false);
+}
+
+void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 2)
+ return;
+
+ //char *strcat(char *restrict s1, const char *restrict s2);
+ evalStrcpyCommon(C, CE,
+ /* returnEnd = */ false,
+ /* isBounded = */ false,
+ /* isAppending = */ true);
+}
+
+void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ //char *strncat(char *restrict s1, const char *restrict s2, size_t n);
+ evalStrcpyCommon(C, CE,
+ /* returnEnd = */ false,
+ /* isBounded = */ true,
+ /* isAppending = */ true);
+}
+
+void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ //char *strlcat(char *s1, const char *s2, size_t n);
+ evalStrcpyCommon(C, CE,
+ /* returnEnd = */ false,
+ /* isBounded = */ true,
+ /* isAppending = */ true,
+ /* returnPtr = */ false);
+}
+
+void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
+ bool returnEnd, bool isBounded,
+ bool isAppending, bool returnPtr) const {
+ CurrentFunctionDescription = "string copy function";
+ ProgramStateRef state = C.getState();
+ const LocationContext *LCtx = C.getLocationContext();
+
+ // Check that the destination is non-null.
+ const Expr *Dst = CE->getArg(0);
+ SVal DstVal = state->getSVal(Dst, LCtx);
+
+ state = checkNonNull(C, state, Dst, DstVal);
+ if (!state)
+ return;
+
+ // Check that the source is non-null.
+ const Expr *srcExpr = CE->getArg(1);
+ SVal srcVal = state->getSVal(srcExpr, LCtx);
+ state = checkNonNull(C, state, srcExpr, srcVal);
+ if (!state)
+ return;
+
+ // Get the string length of the source.
+ SVal strLength = getCStringLength(C, state, srcExpr, srcVal);
+
+ // If the source isn't a valid C string, give up.
+ if (strLength.isUndef())
+ return;
+
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ QualType cmpTy = svalBuilder.getConditionType();
+ QualType sizeTy = svalBuilder.getContext().getSizeType();
+
+ // These two values allow checking two kinds of errors:
+ // - actual overflows caused by a source that doesn't fit in the destination
+ // - potential overflows caused by a bound that could exceed the destination
+ SVal amountCopied = UnknownVal();
+ SVal maxLastElementIndex = UnknownVal();
+ const char *boundWarning = nullptr;
+
+ state = CheckOverlap(C, state, isBounded ? CE->getArg(2) : CE->getArg(1), Dst, srcExpr);
+
+ if (!state)
+ return;
+
+ // If the function is strncpy, strncat, etc... it is bounded.
+ if (isBounded) {
+ // Get the max number of characters to copy.
+ const Expr *lenExpr = CE->getArg(2);
+ SVal lenVal = state->getSVal(lenExpr, LCtx);
+
+ // Protect against misdeclared strncpy().
+ lenVal = svalBuilder.evalCast(lenVal, sizeTy, lenExpr->getType());
+
+ Optional<NonLoc> strLengthNL = strLength.getAs<NonLoc>();
+ Optional<NonLoc> lenValNL = lenVal.getAs<NonLoc>();
+
+ // If we know both values, we might be able to figure out how much
+ // we're copying.
+ if (strLengthNL && lenValNL) {
+ ProgramStateRef stateSourceTooLong, stateSourceNotTooLong;
+
+ // Check if the max number to copy is less than the length of the src.
+ // If the bound is equal to the source length, strncpy won't null-
+ // terminate the result!
+ std::tie(stateSourceTooLong, stateSourceNotTooLong) = state->assume(
+ svalBuilder.evalBinOpNN(state, BO_GE, *strLengthNL, *lenValNL, cmpTy)
+ .castAs<DefinedOrUnknownSVal>());
+
+ if (stateSourceTooLong && !stateSourceNotTooLong) {
+ // Max number to copy is less than the length of the src, so the actual
+ // strLength copied is the max number arg.
+ state = stateSourceTooLong;
+ amountCopied = lenVal;
+
+ } else if (!stateSourceTooLong && stateSourceNotTooLong) {
+ // The source buffer entirely fits in the bound.
+ state = stateSourceNotTooLong;
+ amountCopied = strLength;
+ }
+ }
+
+ // We still want to know if the bound is known to be too large.
+ if (lenValNL) {
+ if (isAppending) {
+ // For strncat, the check is strlen(dst) + lenVal < sizeof(dst)
+
+ // Get the string length of the destination. If the destination is
+ // memory that can't have a string length, we shouldn't be copying
+ // into it anyway.
+ SVal dstStrLength = getCStringLength(C, state, Dst, DstVal);
+ if (dstStrLength.isUndef())
+ return;
+
+ if (Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>()) {
+ maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Add,
+ *lenValNL,
+ *dstStrLengthNL,
+ sizeTy);
+ boundWarning = "Size argument is greater than the free space in the "
+ "destination buffer";
+ }
+
+ } else {
+ // For strncpy, this is just checking that lenVal <= sizeof(dst)
+ // (Yes, strncpy and strncat differ in how they treat termination.
+ // strncat ALWAYS terminates, but strncpy doesn't.)
+
+ // We need a special case for when the copy size is zero, in which
+ // case strncpy will do no work at all. Our bounds check uses n-1
+ // as the last element accessed, so n == 0 is problematic.
+ ProgramStateRef StateZeroSize, StateNonZeroSize;
+ std::tie(StateZeroSize, StateNonZeroSize) =
+ assumeZero(C, state, *lenValNL, sizeTy);
+
+ // If the size is known to be zero, we're done.
+ if (StateZeroSize && !StateNonZeroSize) {
+ if (returnPtr) {
+ StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, DstVal);
+ } else {
+ StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, *lenValNL);
+ }
+ C.addTransition(StateZeroSize);
+ return;
+ }
+
+ // Otherwise, go ahead and figure out the last element we'll touch.
+ // We don't record the non-zero assumption here because we can't
+ // be sure. We won't warn on a possible zero.
+ NonLoc one = svalBuilder.makeIntVal(1, sizeTy).castAs<NonLoc>();
+ maxLastElementIndex = svalBuilder.evalBinOpNN(state, BO_Sub, *lenValNL,
+ one, sizeTy);
+ boundWarning = "Size argument is greater than the length of the "
+ "destination buffer";
+ }
+ }
+
+ // If we couldn't pin down the copy length, at least bound it.
+ // FIXME: We should actually run this code path for append as well, but
+ // right now it creates problems with constraints (since we can end up
+ // trying to pass constraints from symbol to symbol).
+ if (amountCopied.isUnknown() && !isAppending) {
+ // Try to get a "hypothetical" string length symbol, which we can later
+ // set as a real value if that turns out to be the case.
+ amountCopied = getCStringLength(C, state, lenExpr, srcVal, true);
+ assert(!amountCopied.isUndef());
+
+ if (Optional<NonLoc> amountCopiedNL = amountCopied.getAs<NonLoc>()) {
+ if (lenValNL) {
+ // amountCopied <= lenVal
+ SVal copiedLessThanBound = svalBuilder.evalBinOpNN(state, BO_LE,
+ *amountCopiedNL,
+ *lenValNL,
+ cmpTy);
+ state = state->assume(
+ copiedLessThanBound.castAs<DefinedOrUnknownSVal>(), true);
+ if (!state)
+ return;
+ }
+
+ if (strLengthNL) {
+ // amountCopied <= strlen(source)
+ SVal copiedLessThanSrc = svalBuilder.evalBinOpNN(state, BO_LE,
+ *amountCopiedNL,
+ *strLengthNL,
+ cmpTy);
+ state = state->assume(
+ copiedLessThanSrc.castAs<DefinedOrUnknownSVal>(), true);
+ if (!state)
+ return;
+ }
+ }
+ }
+
+ } else {
+ // The function isn't bounded. The amount copied should match the length
+ // of the source buffer.
+ amountCopied = strLength;
+ }
+
+ assert(state);
+
+ // This represents the number of characters copied into the destination
+ // buffer. (It may not actually be the strlen if the destination buffer
+ // is not terminated.)
+ SVal finalStrLength = UnknownVal();
+
+ // If this is an appending function (strcat, strncat...) then set the
+ // string length to strlen(src) + strlen(dst) since the buffer will
+ // ultimately contain both.
+ if (isAppending) {
+ // Get the string length of the destination. If the destination is memory
+ // that can't have a string length, we shouldn't be copying into it anyway.
+ SVal dstStrLength = getCStringLength(C, state, Dst, DstVal);
+ if (dstStrLength.isUndef())
+ return;
+
+ Optional<NonLoc> srcStrLengthNL = amountCopied.getAs<NonLoc>();
+ Optional<NonLoc> dstStrLengthNL = dstStrLength.getAs<NonLoc>();
+
+ // If we know both string lengths, we might know the final string length.
+ if (srcStrLengthNL && dstStrLengthNL) {
+ // Make sure the two lengths together don't overflow a size_t.
+ state = checkAdditionOverflow(C, state, *srcStrLengthNL, *dstStrLengthNL);
+ if (!state)
+ return;
+
+ finalStrLength = svalBuilder.evalBinOpNN(state, BO_Add, *srcStrLengthNL,
+ *dstStrLengthNL, sizeTy);
+ }
+
+ // If we couldn't get a single value for the final string length,
+ // we can at least bound it by the individual lengths.
+ if (finalStrLength.isUnknown()) {
+ // Try to get a "hypothetical" string length symbol, which we can later
+ // set as a real value if that turns out to be the case.
+ finalStrLength = getCStringLength(C, state, CE, DstVal, true);
+ assert(!finalStrLength.isUndef());
+
+ if (Optional<NonLoc> finalStrLengthNL = finalStrLength.getAs<NonLoc>()) {
+ if (srcStrLengthNL) {
+ // finalStrLength >= srcStrLength
+ SVal sourceInResult = svalBuilder.evalBinOpNN(state, BO_GE,
+ *finalStrLengthNL,
+ *srcStrLengthNL,
+ cmpTy);
+ state = state->assume(sourceInResult.castAs<DefinedOrUnknownSVal>(),
+ true);
+ if (!state)
+ return;
+ }
+
+ if (dstStrLengthNL) {
+ // finalStrLength >= dstStrLength
+ SVal destInResult = svalBuilder.evalBinOpNN(state, BO_GE,
+ *finalStrLengthNL,
+ *dstStrLengthNL,
+ cmpTy);
+ state =
+ state->assume(destInResult.castAs<DefinedOrUnknownSVal>(), true);
+ if (!state)
+ return;
+ }
+ }
+ }
+
+ } else {
+ // Otherwise, this is a copy-over function (strcpy, strncpy, ...), and
+ // the final string length will match the input string length.
+ finalStrLength = amountCopied;
+ }
+
+ SVal Result;
+
+ if (returnPtr) {
+ // The final result of the function will either be a pointer past the last
+ // copied element, or a pointer to the start of the destination buffer.
+ Result = (returnEnd ? UnknownVal() : DstVal);
+ } else {
+ Result = finalStrLength;
+ }
+
+ assert(state);
+
+ // If the destination is a MemRegion, try to check for a buffer overflow and
+ // record the new string length.
+ if (Optional<loc::MemRegionVal> dstRegVal =
+ DstVal.getAs<loc::MemRegionVal>()) {
+ QualType ptrTy = Dst->getType();
+
+ // If we have an exact value on a bounded copy, use that to check for
+ // overflows, rather than our estimate about how much is actually copied.
+ if (boundWarning) {
+ if (Optional<NonLoc> maxLastNL = maxLastElementIndex.getAs<NonLoc>()) {
+ SVal maxLastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,
+ *maxLastNL, ptrTy);
+ state = CheckLocation(C, state, CE->getArg(2), maxLastElement,
+ boundWarning);
+ if (!state)
+ return;
+ }
+ }
+
+ // Then, if the final length is known...
+ if (Optional<NonLoc> knownStrLength = finalStrLength.getAs<NonLoc>()) {
+ SVal lastElement = svalBuilder.evalBinOpLN(state, BO_Add, *dstRegVal,
+ *knownStrLength, ptrTy);
+
+ // ...and we haven't checked the bound, we'll check the actual copy.
+ if (!boundWarning) {
+ const char * const warningMsg =
+ "String copy function overflows destination buffer";
+ state = CheckLocation(C, state, Dst, lastElement, warningMsg);
+ if (!state)
+ return;
+ }
+
+ // If this is a stpcpy-style copy, the last element is the return value.
+ if (returnPtr && returnEnd)
+ Result = lastElement;
+ }
+
+ // Invalidate the destination (regular invalidation without pointer-escaping
+ // the address of the top-level region). This must happen before we set the
+ // C string length because invalidation will clear the length.
+ // FIXME: Even if we can't perfectly model the copy, we should see if we
+ // can use LazyCompoundVals to copy the source values into the destination.
+ // This would probably remove any existing bindings past the end of the
+ // string, but that's still an improvement over blank invalidation.
+ state = InvalidateBuffer(C, state, Dst, *dstRegVal,
+ /*IsSourceBuffer*/false, nullptr);
+
+ // Invalidate the source (const-invalidation without const-pointer-escaping
+ // the address of the top-level region).
+ state = InvalidateBuffer(C, state, srcExpr, srcVal, /*IsSourceBuffer*/true,
+ nullptr);
+
+ // Set the C string length of the destination, if we know it.
+ if (isBounded && !isAppending) {
+ // strncpy is annoying in that it doesn't guarantee to null-terminate
+ // the result string. If the original string didn't fit entirely inside
+ // the bound (including the null-terminator), we don't know how long the
+ // result is.
+ if (amountCopied != strLength)
+ finalStrLength = UnknownVal();
+ }
+ state = setCStringLength(state, dstRegVal->getRegion(), finalStrLength);
+ }
+
+ assert(state);
+
+ if (returnPtr) {
+ // If this is a stpcpy-style copy, but we were unable to check for a buffer
+ // overflow, we still need a result. Conjure a return value.
+ if (returnEnd && Result.isUnknown()) {
+ Result = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+ }
+ }
+ // Set the return value.
+ state = state->BindExpr(CE, LCtx, Result);
+ C.addTransition(state);
+}
+
+void CStringChecker::evalStrcmp(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 2)
+ return;
+
+ //int strcmp(const char *s1, const char *s2);
+ evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ false);
+}
+
+void CStringChecker::evalStrncmp(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ //int strncmp(const char *s1, const char *s2, size_t n);
+ evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ false);
+}
+
+void CStringChecker::evalStrcasecmp(CheckerContext &C,
+ const CallExpr *CE) const {
+ if (CE->getNumArgs() < 2)
+ return;
+
+ //int strcasecmp(const char *s1, const char *s2);
+ evalStrcmpCommon(C, CE, /* isBounded = */ false, /* ignoreCase = */ true);
+}
+
+void CStringChecker::evalStrncasecmp(CheckerContext &C,
+ const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ //int strncasecmp(const char *s1, const char *s2, size_t n);
+ evalStrcmpCommon(C, CE, /* isBounded = */ true, /* ignoreCase = */ true);
+}
+
+void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
+ bool isBounded, bool ignoreCase) const {
+ CurrentFunctionDescription = "string comparison function";
+ ProgramStateRef state = C.getState();
+ const LocationContext *LCtx = C.getLocationContext();
+
+ // Check that the first string is non-null
+ const Expr *s1 = CE->getArg(0);
+ SVal s1Val = state->getSVal(s1, LCtx);
+ state = checkNonNull(C, state, s1, s1Val);
+ if (!state)
+ return;
+
+ // Check that the second string is non-null.
+ const Expr *s2 = CE->getArg(1);
+ SVal s2Val = state->getSVal(s2, LCtx);
+ state = checkNonNull(C, state, s2, s2Val);
+ if (!state)
+ return;
+
+ // Get the string length of the first string or give up.
+ SVal s1Length = getCStringLength(C, state, s1, s1Val);
+ if (s1Length.isUndef())
+ return;
+
+ // Get the string length of the second string or give up.
+ SVal s2Length = getCStringLength(C, state, s2, s2Val);
+ if (s2Length.isUndef())
+ return;
+
+ // If we know the two buffers are the same, we know the result is 0.
+ // First, get the two buffers' addresses. Another checker will have already
+ // made sure they're not undefined.
+ DefinedOrUnknownSVal LV = s1Val.castAs<DefinedOrUnknownSVal>();
+ DefinedOrUnknownSVal RV = s2Val.castAs<DefinedOrUnknownSVal>();
+
+ // See if they are the same.
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ DefinedOrUnknownSVal SameBuf = svalBuilder.evalEQ(state, LV, RV);
+ ProgramStateRef StSameBuf, StNotSameBuf;
+ std::tie(StSameBuf, StNotSameBuf) = state->assume(SameBuf);
+
+ // If the two arguments might be the same buffer, we know the result is 0,
+ // and we only need to check one size.
+ if (StSameBuf) {
+ StSameBuf = StSameBuf->BindExpr(CE, LCtx,
+ svalBuilder.makeZeroVal(CE->getType()));
+ C.addTransition(StSameBuf);
+
+ // If the two arguments are GUARANTEED to be the same, we're done!
+ if (!StNotSameBuf)
+ return;
+ }
+
+ assert(StNotSameBuf);
+ state = StNotSameBuf;
+
+ // At this point we can go about comparing the two buffers.
+ // For now, we only do this if they're both known string literals.
+
+ // Attempt to extract string literals from both expressions.
+ const StringLiteral *s1StrLiteral = getCStringLiteral(C, state, s1, s1Val);
+ const StringLiteral *s2StrLiteral = getCStringLiteral(C, state, s2, s2Val);
+ bool canComputeResult = false;
+ SVal resultVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
+ C.blockCount());
+
+ if (s1StrLiteral && s2StrLiteral) {
+ StringRef s1StrRef = s1StrLiteral->getString();
+ StringRef s2StrRef = s2StrLiteral->getString();
+
+ if (isBounded) {
+ // Get the max number of characters to compare.
+ const Expr *lenExpr = CE->getArg(2);
+ SVal lenVal = state->getSVal(lenExpr, LCtx);
+
+ // If the length is known, we can get the right substrings.
+ if (const llvm::APSInt *len = svalBuilder.getKnownValue(state, lenVal)) {
+ // Create substrings of each to compare the prefix.
+ s1StrRef = s1StrRef.substr(0, (size_t)len->getZExtValue());
+ s2StrRef = s2StrRef.substr(0, (size_t)len->getZExtValue());
+ canComputeResult = true;
+ }
+ } else {
+ // This is a normal, unbounded strcmp.
+ canComputeResult = true;
+ }
+
+ if (canComputeResult) {
+ // Real strcmp stops at null characters.
+ size_t s1Term = s1StrRef.find('\0');
+ if (s1Term != StringRef::npos)
+ s1StrRef = s1StrRef.substr(0, s1Term);
+
+ size_t s2Term = s2StrRef.find('\0');
+ if (s2Term != StringRef::npos)
+ s2StrRef = s2StrRef.substr(0, s2Term);
+
+ // Use StringRef's comparison methods to compute the actual result.
+ int compareRes = ignoreCase ? s1StrRef.compare_lower(s2StrRef)
+ : s1StrRef.compare(s2StrRef);
+
+ // The strcmp function returns an integer greater than, equal to, or less
+ // than zero, [c11, p7.24.4.2].
+ if (compareRes == 0) {
+ resultVal = svalBuilder.makeIntVal(compareRes, CE->getType());
+ }
+ else {
+ DefinedSVal zeroVal = svalBuilder.makeIntVal(0, CE->getType());
+ // Constrain strcmp's result range based on the result of StringRef's
+ // comparison methods.
+ BinaryOperatorKind op = (compareRes == 1) ? BO_GT : BO_LT;
+ SVal compareWithZero =
+ svalBuilder.evalBinOp(state, op, resultVal, zeroVal,
+ svalBuilder.getConditionType());
+ DefinedSVal compareWithZeroVal = compareWithZero.castAs<DefinedSVal>();
+ state = state->assume(compareWithZeroVal, true);
+ }
+ }
+ }
+
+ state = state->BindExpr(CE, LCtx, resultVal);
+
+ // Record this as a possible path.
+ C.addTransition(state);
+}
+
+void CStringChecker::evalStrsep(CheckerContext &C, const CallExpr *CE) const {
+ //char *strsep(char **stringp, const char *delim);
+ if (CE->getNumArgs() < 2)
+ return;
+
+ // Sanity: does the search string parameter match the return type?
+ const Expr *SearchStrPtr = CE->getArg(0);
+ QualType CharPtrTy = SearchStrPtr->getType()->getPointeeType();
+ if (CharPtrTy.isNull() ||
+ CE->getType().getUnqualifiedType() != CharPtrTy.getUnqualifiedType())
+ return;
+
+ CurrentFunctionDescription = "strsep()";
+ ProgramStateRef State = C.getState();
+ const LocationContext *LCtx = C.getLocationContext();
+
+ // Check that the search string pointer is non-null (though it may point to
+ // a null string).
+ SVal SearchStrVal = State->getSVal(SearchStrPtr, LCtx);
+ State = checkNonNull(C, State, SearchStrPtr, SearchStrVal);
+ if (!State)
+ return;
+
+ // Check that the delimiter string is non-null.
+ const Expr *DelimStr = CE->getArg(1);
+ SVal DelimStrVal = State->getSVal(DelimStr, LCtx);
+ State = checkNonNull(C, State, DelimStr, DelimStrVal);
+ if (!State)
+ return;
+
+ SValBuilder &SVB = C.getSValBuilder();
+ SVal Result;
+ if (Optional<Loc> SearchStrLoc = SearchStrVal.getAs<Loc>()) {
+ // Get the current value of the search string pointer, as a char*.
+ Result = State->getSVal(*SearchStrLoc, CharPtrTy);
+
+ // Invalidate the search string, representing the change of one delimiter
+ // character to NUL.
+ State = InvalidateBuffer(C, State, SearchStrPtr, Result,
+ /*IsSourceBuffer*/false, nullptr);
+
+ // Overwrite the search string pointer. The new value is either an address
+ // further along in the same string, or NULL if there are no more tokens.
+ State = State->bindLoc(*SearchStrLoc,
+ SVB.conjureSymbolVal(getTag(),
+ CE,
+ LCtx,
+ CharPtrTy,
+ C.blockCount()),
+ LCtx);
+ } else {
+ assert(SearchStrVal.isUnknown());
+ // Conjure a symbolic value. It's the best we can do.
+ Result = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+ }
+
+ // Set the return value, and finish.
+ State = State->BindExpr(CE, LCtx, Result);
+ C.addTransition(State);
+}
+
+// These should probably be moved into a C++ standard library checker.
+void CStringChecker::evalStdCopy(CheckerContext &C, const CallExpr *CE) const {
+ evalStdCopyCommon(C, CE);
+}
+
+void CStringChecker::evalStdCopyBackward(CheckerContext &C,
+ const CallExpr *CE) const {
+ evalStdCopyCommon(C, CE);
+}
+
+void CStringChecker::evalStdCopyCommon(CheckerContext &C,
+ const CallExpr *CE) const {
+ if (CE->getNumArgs() < 3)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ const LocationContext *LCtx = C.getLocationContext();
+
+ // template <class _InputIterator, class _OutputIterator>
+ // _OutputIterator
+ // copy(_InputIterator __first, _InputIterator __last,
+ // _OutputIterator __result)
+
+ // Invalidate the destination buffer
+ const Expr *Dst = CE->getArg(2);
+ SVal DstVal = State->getSVal(Dst, LCtx);
+ State = InvalidateBuffer(C, State, Dst, DstVal, /*IsSource=*/false,
+ /*Size=*/nullptr);
+
+ SValBuilder &SVB = C.getSValBuilder();
+
+ SVal ResultVal = SVB.conjureSymbolVal(nullptr, CE, LCtx, C.blockCount());
+ State = State->BindExpr(CE, LCtx, ResultVal);
+
+ C.addTransition(State);
+}
+
+void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() != 3)
+ return;
+
+ CurrentFunctionDescription = "memory set function";
+
+ const Expr *Mem = CE->getArg(0);
+ const Expr *CharE = CE->getArg(1);
+ const Expr *Size = CE->getArg(2);
+ ProgramStateRef State = C.getState();
+
+ // See if the size argument is zero.
+ const LocationContext *LCtx = C.getLocationContext();
+ SVal SizeVal = State->getSVal(Size, LCtx);
+ QualType SizeTy = Size->getType();
+
+ ProgramStateRef StateZeroSize, StateNonZeroSize;
+ std::tie(StateZeroSize, StateNonZeroSize) =
+ assumeZero(C, State, SizeVal, SizeTy);
+
+ // Get the value of the memory area.
+ SVal MemVal = State->getSVal(Mem, LCtx);
+
+ // If the size is zero, there won't be any actual memory access, so
+ // just bind the return value to the Mem buffer and return.
+ if (StateZeroSize && !StateNonZeroSize) {
+ StateZeroSize = StateZeroSize->BindExpr(CE, LCtx, MemVal);
+ C.addTransition(StateZeroSize);
+ return;
+ }
+
+ // Ensure the memory area is not null.
+ // If it is NULL there will be a NULL pointer dereference.
+ State = checkNonNull(C, StateNonZeroSize, Mem, MemVal);
+ if (!State)
+ return;
+
+ State = CheckBufferAccess(C, State, Size, Mem);
+ if (!State)
+ return;
+
+ // According to the values of the arguments, bind the value of the second
+ // argument to the destination buffer and set string length, or just
+ // invalidate the destination buffer.
+ if (!memsetAux(Mem, C.getSVal(CharE), Size, C, State))
+ return;
+
+ State = State->BindExpr(CE, LCtx, MemVal);
+ C.addTransition(State);
+}
+
+void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
+ if (CE->getNumArgs() != 2)
+ return;
+
+ CurrentFunctionDescription = "memory clearance function";
+
+ const Expr *Mem = CE->getArg(0);
+ const Expr *Size = CE->getArg(1);
+ SVal Zero = C.getSValBuilder().makeZeroVal(C.getASTContext().IntTy);
+
+ ProgramStateRef State = C.getState();
+
+ // See if the size argument is zero.
+ SVal SizeVal = C.getSVal(Size);
+ QualType SizeTy = Size->getType();
+
+ ProgramStateRef StateZeroSize, StateNonZeroSize;
+ std::tie(StateZeroSize, StateNonZeroSize) =
+ assumeZero(C, State, SizeVal, SizeTy);
+
+ // If the size is zero, there won't be any actual memory access,
+ // In this case we just return.
+ if (StateZeroSize && !StateNonZeroSize) {
+ C.addTransition(StateZeroSize);
+ return;
+ }
+
+ // Get the value of the memory area.
+ SVal MemVal = C.getSVal(Mem);
+
+ // Ensure the memory area is not null.
+ // If it is NULL there will be a NULL pointer dereference.
+ State = checkNonNull(C, StateNonZeroSize, Mem, MemVal);
+ if (!State)
+ return;
+
+ State = CheckBufferAccess(C, State, Size, Mem);
+ if (!State)
+ return;
+
+ if (!memsetAux(Mem, Zero, Size, C, State))
+ return;
+
+ C.addTransition(State);
+}
+
+static bool isCPPStdLibraryFunction(const FunctionDecl *FD, StringRef Name) {
+ IdentifierInfo *II = FD->getIdentifier();
+ if (!II)
+ return false;
+
+ if (!AnalysisDeclContext::isInStdNamespace(FD))
+ return false;
+
+ if (II->getName().equals(Name))
+ return true;
+
+ return false;
+}
+//===----------------------------------------------------------------------===//
+// The driver method, and other Checker callbacks.
+//===----------------------------------------------------------------------===//
+
+static CStringChecker::FnCheck identifyCall(const CallExpr *CE,
+ CheckerContext &C) {
+ const FunctionDecl *FDecl = C.getCalleeDecl(CE);
+ if (!FDecl)
+ return nullptr;
+
+ // Pro-actively check that argument types are safe to do arithmetic upon.
+ // We do not want to crash if someone accidentally passes a structure
+ // into, say, a C++ overload of any of these functions.
+ if (isCPPStdLibraryFunction(FDecl, "copy")) {
+ if (CE->getNumArgs() < 3 || !CE->getArg(2)->getType()->isPointerType())
+ return nullptr;
+ return &CStringChecker::evalStdCopy;
+ } else if (isCPPStdLibraryFunction(FDecl, "copy_backward")) {
+ if (CE->getNumArgs() < 3 || !CE->getArg(2)->getType()->isPointerType())
+ return nullptr;
+ return &CStringChecker::evalStdCopyBackward;
+ } else {
+ // An umbrella check for all C library functions.
+ for (auto I: CE->arguments()) {
+ QualType T = I->getType();
+ if (!T->isIntegralOrEnumerationType() && !T->isPointerType())
+ return nullptr;
+ }
+ }
+
+ // FIXME: Poorly-factored string switches are slow.
+ if (C.isCLibraryFunction(FDecl, "memcpy"))
+ return &CStringChecker::evalMemcpy;
+ else if (C.isCLibraryFunction(FDecl, "mempcpy"))
+ return &CStringChecker::evalMempcpy;
+ else if (C.isCLibraryFunction(FDecl, "memcmp"))
+ return &CStringChecker::evalMemcmp;
+ else if (C.isCLibraryFunction(FDecl, "memmove"))
+ return &CStringChecker::evalMemmove;
+ else if (C.isCLibraryFunction(FDecl, "memset") ||
+ C.isCLibraryFunction(FDecl, "explicit_memset"))
+ return &CStringChecker::evalMemset;
+ else if (C.isCLibraryFunction(FDecl, "strcpy"))
+ return &CStringChecker::evalStrcpy;
+ else if (C.isCLibraryFunction(FDecl, "strncpy"))
+ return &CStringChecker::evalStrncpy;
+ else if (C.isCLibraryFunction(FDecl, "stpcpy"))
+ return &CStringChecker::evalStpcpy;
+ else if (C.isCLibraryFunction(FDecl, "strlcpy"))
+ return &CStringChecker::evalStrlcpy;
+ else if (C.isCLibraryFunction(FDecl, "strcat"))
+ return &CStringChecker::evalStrcat;
+ else if (C.isCLibraryFunction(FDecl, "strncat"))
+ return &CStringChecker::evalStrncat;
+ else if (C.isCLibraryFunction(FDecl, "strlcat"))
+ return &CStringChecker::evalStrlcat;
+ else if (C.isCLibraryFunction(FDecl, "strlen"))
+ return &CStringChecker::evalstrLength;
+ else if (C.isCLibraryFunction(FDecl, "strnlen"))
+ return &CStringChecker::evalstrnLength;
+ else if (C.isCLibraryFunction(FDecl, "strcmp"))
+ return &CStringChecker::evalStrcmp;
+ else if (C.isCLibraryFunction(FDecl, "strncmp"))
+ return &CStringChecker::evalStrncmp;
+ else if (C.isCLibraryFunction(FDecl, "strcasecmp"))
+ return &CStringChecker::evalStrcasecmp;
+ else if (C.isCLibraryFunction(FDecl, "strncasecmp"))
+ return &CStringChecker::evalStrncasecmp;
+ else if (C.isCLibraryFunction(FDecl, "strsep"))
+ return &CStringChecker::evalStrsep;
+ else if (C.isCLibraryFunction(FDecl, "bcopy"))
+ return &CStringChecker::evalBcopy;
+ else if (C.isCLibraryFunction(FDecl, "bcmp"))
+ return &CStringChecker::evalMemcmp;
+ else if (C.isCLibraryFunction(FDecl, "bzero") ||
+ C.isCLibraryFunction(FDecl, "explicit_bzero"))
+ return &CStringChecker::evalBzero;
+
+ return nullptr;
+}
+
+bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
+
+ FnCheck evalFunction = identifyCall(CE, C);
+
+ // If the callee isn't a string function, let another checker handle it.
+ if (!evalFunction)
+ return false;
+
+ // Check and evaluate the call.
+ (this->*evalFunction)(C, CE);
+
+ // If the evaluate call resulted in no change, chain to the next eval call
+ // handler.
+ // Note, the custom CString evaluation calls assume that basic safety
+ // properties are held. However, if the user chooses to turn off some of these
+ // checks, we ignore the issues and leave the call evaluation to a generic
+ // handler.
+ return C.isDifferent();
+}
+
+void CStringChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
+ // Record string length for char a[] = "abc";
+ ProgramStateRef state = C.getState();
+
+ for (const auto *I : DS->decls()) {
+ const VarDecl *D = dyn_cast<VarDecl>(I);
+ if (!D)
+ continue;
+
+ // FIXME: Handle array fields of structs.
+ if (!D->getType()->isArrayType())
+ continue;
+
+ const Expr *Init = D->getInit();
+ if (!Init)
+ continue;
+ if (!isa<StringLiteral>(Init))
+ continue;
+
+ Loc VarLoc = state->getLValue(D, C.getLocationContext());
+ const MemRegion *MR = VarLoc.getAsRegion();
+ if (!MR)
+ continue;
+
+ SVal StrVal = C.getSVal(Init);
+ assert(StrVal.isValid() && "Initializer string is unknown or undefined");
+ DefinedOrUnknownSVal strLength =
+ getCStringLength(C, state, Init, StrVal).castAs<DefinedOrUnknownSVal>();
+
+ state = state->set<CStringLength>(MR, strLength);
+ }
+
+ C.addTransition(state);
+}
+
+ProgramStateRef
+CStringChecker::checkRegionChanges(ProgramStateRef state,
+ const InvalidatedSymbols *,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call) const {
+ CStringLengthTy Entries = state->get<CStringLength>();
+ if (Entries.isEmpty())
+ return state;
+
+ llvm::SmallPtrSet<const MemRegion *, 8> Invalidated;
+ llvm::SmallPtrSet<const MemRegion *, 32> SuperRegions;
+
+ // First build sets for the changed regions and their super-regions.
+ for (ArrayRef<const MemRegion *>::iterator
+ I = Regions.begin(), E = Regions.end(); I != E; ++I) {
+ const MemRegion *MR = *I;
+ Invalidated.insert(MR);
+
+ SuperRegions.insert(MR);
+ while (const SubRegion *SR = dyn_cast<SubRegion>(MR)) {
+ MR = SR->getSuperRegion();
+ SuperRegions.insert(MR);
+ }
+ }
+
+ CStringLengthTy::Factory &F = state->get_context<CStringLength>();
+
+ // Then loop over the entries in the current state.
+ for (CStringLengthTy::iterator I = Entries.begin(),
+ E = Entries.end(); I != E; ++I) {
+ const MemRegion *MR = I.getKey();
+
+ // Is this entry for a super-region of a changed region?
+ if (SuperRegions.count(MR)) {
+ Entries = F.remove(Entries, MR);
+ continue;
+ }
+
+ // Is this entry for a sub-region of a changed region?
+ const MemRegion *Super = MR;
+ while (const SubRegion *SR = dyn_cast<SubRegion>(Super)) {
+ Super = SR->getSuperRegion();
+ if (Invalidated.count(Super)) {
+ Entries = F.remove(Entries, MR);
+ break;
+ }
+ }
+ }
+
+ return state->set<CStringLength>(Entries);
+}
+
+void CStringChecker::checkLiveSymbols(ProgramStateRef state,
+ SymbolReaper &SR) const {
+ // Mark all symbols in our string length map as valid.
+ CStringLengthTy Entries = state->get<CStringLength>();
+
+ for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end();
+ I != E; ++I) {
+ SVal Len = I.getData();
+
+ for (SymExpr::symbol_iterator si = Len.symbol_begin(),
+ se = Len.symbol_end(); si != se; ++si)
+ SR.markInUse(*si);
+ }
+}
+
+void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
+ ProgramStateRef state = C.getState();
+ CStringLengthTy Entries = state->get<CStringLength>();
+ if (Entries.isEmpty())
+ return;
+
+ CStringLengthTy::Factory &F = state->get_context<CStringLength>();
+ for (CStringLengthTy::iterator I = Entries.begin(), E = Entries.end();
+ I != E; ++I) {
+ SVal Len = I.getData();
+ if (SymbolRef Sym = Len.getAsSymbol()) {
+ if (SR.isDead(Sym))
+ Entries = F.remove(Entries, I.getKey());
+ }
+ }
+
+ state = state->set<CStringLength>(Entries);
+ C.addTransition(state);
+}
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ CStringChecker *checker = mgr.registerChecker<CStringChecker>(); \
+ checker->Filter.Check##name = true; \
+ checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \
+ }
+
+ REGISTER_CHECKER(CStringNullArg)
+ REGISTER_CHECKER(CStringOutOfBounds)
+ REGISTER_CHECKER(CStringBufferOverlap)
+REGISTER_CHECKER(CStringNotNullTerm)
+
+ void ento::registerCStringCheckerBasic(CheckerManager &Mgr) {
+ Mgr.registerChecker<CStringChecker>();
+ }
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
new file mode 100644
index 000000000000..bbeb41c5f3cf
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
@@ -0,0 +1,292 @@
+//== CStringSyntaxChecker.cpp - CoreFoundation containers API *- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// An AST checker that looks for common pitfalls when using C string APIs.
+// - Identifies erroneous patterns in the last argument to strncat - the number
+// of bytes to copy.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/Basic/TypeTraits.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class WalkAST: public StmtVisitor<WalkAST> {
+ const CheckerBase *Checker;
+ BugReporter &BR;
+ AnalysisDeclContext* AC;
+
+ /// Check if two expressions refer to the same declaration.
+ bool sameDecl(const Expr *A1, const Expr *A2) {
+ if (const auto *D1 = dyn_cast<DeclRefExpr>(A1->IgnoreParenCasts()))
+ if (const auto *D2 = dyn_cast<DeclRefExpr>(A2->IgnoreParenCasts()))
+ return D1->getDecl() == D2->getDecl();
+ return false;
+ }
+
+ /// Check if the expression E is a sizeof(WithArg).
+ bool isSizeof(const Expr *E, const Expr *WithArg) {
+ if (const auto *UE = dyn_cast<UnaryExprOrTypeTraitExpr>(E))
+ if (UE->getKind() == UETT_SizeOf && !UE->isArgumentType())
+ return sameDecl(UE->getArgumentExpr(), WithArg);
+ return false;
+ }
+
+ /// Check if the expression E is a strlen(WithArg).
+ bool isStrlen(const Expr *E, const Expr *WithArg) {
+ if (const auto *CE = dyn_cast<CallExpr>(E)) {
+ const FunctionDecl *FD = CE->getDirectCallee();
+ if (!FD)
+ return false;
+ return (CheckerContext::isCLibraryFunction(FD, "strlen") &&
+ sameDecl(CE->getArg(0), WithArg));
+ }
+ return false;
+ }
+
+ /// Check if the expression is an integer literal with value 1.
+ bool isOne(const Expr *E) {
+ if (const auto *IL = dyn_cast<IntegerLiteral>(E))
+ return (IL->getValue().isIntN(1));
+ return false;
+ }
+
+ StringRef getPrintableName(const Expr *E) {
+ if (const auto *D = dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
+ return D->getDecl()->getName();
+ return StringRef();
+ }
+
+ /// Identify erroneous patterns in the last argument to strncat - the number
+ /// of bytes to copy.
+ bool containsBadStrncatPattern(const CallExpr *CE);
+
+ /// Identify erroneous patterns in the last argument to strlcpy - the number
+ /// of bytes to copy.
+ /// The bad pattern checked is when the size is known
+ /// to be larger than the destination can handle.
+ /// char dst[2];
+ /// size_t cpy = 4;
+ /// strlcpy(dst, "abcd", sizeof("abcd") - 1);
+ /// strlcpy(dst, "abcd", 4);
+ /// strlcpy(dst + 3, "abcd", 2);
+ /// strlcpy(dst, "abcd", cpy);
+ /// Identify erroneous patterns in the last argument to strlcat - the number
+ /// of bytes to copy.
+ /// The bad pattern checked is when the last argument is basically
+ /// pointing to the destination buffer size or argument larger or
+ /// equal to.
+ /// char dst[2];
+ /// strlcat(dst, src2, sizeof(dst));
+ /// strlcat(dst, src2, 2);
+ /// strlcat(dst, src2, 10);
+ bool containsBadStrlcpyStrlcatPattern(const CallExpr *CE);
+
+public:
+ WalkAST(const CheckerBase *Checker, BugReporter &BR, AnalysisDeclContext *AC)
+ : Checker(Checker), BR(BR), AC(AC) {}
+
+ // Statement visitor methods.
+ void VisitChildren(Stmt *S);
+ void VisitStmt(Stmt *S) {
+ VisitChildren(S);
+ }
+ void VisitCallExpr(CallExpr *CE);
+};
+} // end anonymous namespace
+
+// The correct size argument should look like following:
+// strncat(dst, src, sizeof(dst) - strlen(dest) - 1);
+// We look for the following anti-patterns:
+// - strncat(dst, src, sizeof(dst) - strlen(dst));
+// - strncat(dst, src, sizeof(dst) - 1);
+// - strncat(dst, src, sizeof(dst));
+bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) {
+ if (CE->getNumArgs() != 3)
+ return false;
+ const Expr *DstArg = CE->getArg(0);
+ const Expr *SrcArg = CE->getArg(1);
+ const Expr *LenArg = CE->getArg(2);
+
+ // Identify wrong size expressions, which are commonly used instead.
+ if (const auto *BE = dyn_cast<BinaryOperator>(LenArg->IgnoreParenCasts())) {
+ // - sizeof(dst) - strlen(dst)
+ if (BE->getOpcode() == BO_Sub) {
+ const Expr *L = BE->getLHS();
+ const Expr *R = BE->getRHS();
+ if (isSizeof(L, DstArg) && isStrlen(R, DstArg))
+ return true;
+
+ // - sizeof(dst) - 1
+ if (isSizeof(L, DstArg) && isOne(R->IgnoreParenCasts()))
+ return true;
+ }
+ }
+ // - sizeof(dst)
+ if (isSizeof(LenArg, DstArg))
+ return true;
+
+ // - sizeof(src)
+ if (isSizeof(LenArg, SrcArg))
+ return true;
+ return false;
+}
+
+bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) {
+ if (CE->getNumArgs() != 3)
+ return false;
+ const FunctionDecl *FD = CE->getDirectCallee();
+ bool Append = CheckerContext::isCLibraryFunction(FD, "strlcat");
+ const Expr *DstArg = CE->getArg(0);
+ const Expr *LenArg = CE->getArg(2);
+
+ const auto *DstArgDecl = dyn_cast<DeclRefExpr>(DstArg->IgnoreParenImpCasts());
+ const auto *LenArgDecl = dyn_cast<DeclRefExpr>(LenArg->IgnoreParenLValueCasts());
+ uint64_t DstOff = 0;
+ if (isSizeof(LenArg, DstArg))
+ return false;
+ // - size_t dstlen = sizeof(dst)
+ if (LenArgDecl) {
+ const auto *LenArgVal = dyn_cast<VarDecl>(LenArgDecl->getDecl());
+ if (LenArgVal->getInit())
+ LenArg = LenArgVal->getInit();
+ }
+
+ // - integral value
+ // We try to figure out if the last argument is possibly longer
+ // than the destination can possibly handle if its size can be defined.
+ if (const auto *IL = dyn_cast<IntegerLiteral>(LenArg->IgnoreParenImpCasts())) {
+ uint64_t ILRawVal = IL->getValue().getZExtValue();
+
+ // Case when there is pointer arithmetic on the destination buffer
+ // especially when we offset from the base decreasing the
+ // buffer length accordingly.
+ if (!DstArgDecl) {
+ if (const auto *BE = dyn_cast<BinaryOperator>(DstArg->IgnoreParenImpCasts())) {
+ DstArgDecl = dyn_cast<DeclRefExpr>(BE->getLHS()->IgnoreParenImpCasts());
+ if (BE->getOpcode() == BO_Add) {
+ if ((IL = dyn_cast<IntegerLiteral>(BE->getRHS()->IgnoreParenImpCasts()))) {
+ DstOff = IL->getValue().getZExtValue();
+ }
+ }
+ }
+ }
+ if (DstArgDecl) {
+ if (const auto *Buffer = dyn_cast<ConstantArrayType>(DstArgDecl->getType())) {
+ ASTContext &C = BR.getContext();
+ uint64_t BufferLen = C.getTypeSize(Buffer) / 8;
+ auto RemainingBufferLen = BufferLen - DstOff;
+ if (Append) {
+ if (RemainingBufferLen <= ILRawVal)
+ return true;
+ } else {
+ if (RemainingBufferLen < ILRawVal)
+ return true;
+ }
+ }
+ }
+ }
+
+ return false;
+}
+
+void WalkAST::VisitCallExpr(CallExpr *CE) {
+ const FunctionDecl *FD = CE->getDirectCallee();
+ if (!FD)
+ return;
+
+ if (CheckerContext::isCLibraryFunction(FD, "strncat")) {
+ if (containsBadStrncatPattern(CE)) {
+ const Expr *DstArg = CE->getArg(0);
+ const Expr *LenArg = CE->getArg(2);
+ PathDiagnosticLocation Loc =
+ PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC);
+
+ StringRef DstName = getPrintableName(DstArg);
+
+ SmallString<256> S;
+ llvm::raw_svector_ostream os(S);
+ os << "Potential buffer overflow. ";
+ if (!DstName.empty()) {
+ os << "Replace with 'sizeof(" << DstName << ") "
+ "- strlen(" << DstName <<") - 1'";
+ os << " or u";
+ } else
+ os << "U";
+ os << "se a safer 'strlcat' API";
+
+ BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
+ "C String API", os.str(), Loc,
+ LenArg->getSourceRange());
+ }
+ } else if (CheckerContext::isCLibraryFunction(FD, "strlcpy") ||
+ CheckerContext::isCLibraryFunction(FD, "strlcat")) {
+ if (containsBadStrlcpyStrlcatPattern(CE)) {
+ const Expr *DstArg = CE->getArg(0);
+ const Expr *LenArg = CE->getArg(2);
+ PathDiagnosticLocation Loc =
+ PathDiagnosticLocation::createBegin(LenArg, BR.getSourceManager(), AC);
+
+ StringRef DstName = getPrintableName(DstArg);
+
+ SmallString<256> S;
+ llvm::raw_svector_ostream os(S);
+ os << "The third argument allows to potentially copy more bytes than it should. ";
+ os << "Replace with the value ";
+ if (!DstName.empty())
+ os << "sizeof(" << DstName << ")";
+ else
+ os << "sizeof(<destination buffer>)";
+ os << " or lower";
+
+ BR.EmitBasicReport(FD, Checker, "Anti-pattern in the argument",
+ "C String API", os.str(), Loc,
+ LenArg->getSourceRange());
+ }
+ }
+
+ // Recurse and check children.
+ VisitChildren(CE);
+}
+
+void WalkAST::VisitChildren(Stmt *S) {
+ for (Stmt *Child : S->children())
+ if (Child)
+ Visit(Child);
+}
+
+namespace {
+class CStringSyntaxChecker: public Checker<check::ASTCodeBody> {
+public:
+
+ void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
+ BugReporter &BR) const {
+ WalkAST walker(this, BR, Mgr.getAnalysisDeclContext(D));
+ walker.Visit(D->getBody());
+ }
+};
+}
+
+void ento::registerCStringSyntaxChecker(CheckerManager &mgr) {
+ mgr.registerChecker<CStringSyntaxChecker>();
+}
+
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
new file mode 100644
index 000000000000..0b539e1188eb
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
@@ -0,0 +1,62 @@
+//=== CXXSelfAssignmentChecker.cpp -----------------------------*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines CXXSelfAssignmentChecker, which tests all custom defined
+// copy and move assignment operators for the case of self assignment, thus
+// where the parameter refers to the same location where the this pointer
+// points to. The checker itself does not do any checks at all, but it
+// causes the analyzer to check every copy and move assignment operator twice:
+// once for when 'this' aliases with the parameter and once for when it may not.
+// It is the task of the other enabled checkers to find the bugs in these two
+// different cases.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class CXXSelfAssignmentChecker : public Checker<check::BeginFunction> {
+public:
+ CXXSelfAssignmentChecker();
+ void checkBeginFunction(CheckerContext &C) const;
+};
+}
+
+CXXSelfAssignmentChecker::CXXSelfAssignmentChecker() {}
+
+void CXXSelfAssignmentChecker::checkBeginFunction(CheckerContext &C) const {
+ if (!C.inTopFrame())
+ return;
+ const auto *LCtx = C.getLocationContext();
+ const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
+ if (!MD)
+ return;
+ if (!MD->isCopyAssignmentOperator() && !MD->isMoveAssignmentOperator())
+ return;
+ auto &State = C.getState();
+ auto &SVB = C.getSValBuilder();
+ auto ThisVal =
+ State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
+ auto Param = SVB.makeLoc(State->getRegion(MD->getParamDecl(0), LCtx));
+ auto ParamVal = State->getSVal(Param);
+ ProgramStateRef SelfAssignState = State->bindLoc(Param, ThisVal, LCtx);
+ C.addTransition(SelfAssignState);
+ ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal, LCtx);
+ C.addTransition(NonSelfAssignState);
+}
+
+void ento::registerCXXSelfAssignmentChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<CXXSelfAssignmentChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
new file mode 100644
index 000000000000..ef30dc74c39d
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -0,0 +1,620 @@
+//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines CallAndMessageChecker, a builtin checker that checks for various
+// errors of call and objc message expressions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+struct ChecksFilter {
+ DefaultBool Check_CallAndMessageUnInitRefArg;
+ DefaultBool Check_CallAndMessageChecker;
+
+ CheckName CheckName_CallAndMessageUnInitRefArg;
+ CheckName CheckName_CallAndMessageChecker;
+};
+
+class CallAndMessageChecker
+ : public Checker< check::PreStmt<CallExpr>,
+ check::PreStmt<CXXDeleteExpr>,
+ check::PreObjCMessage,
+ check::ObjCMessageNil,
+ check::PreCall > {
+ mutable std::unique_ptr<BugType> BT_call_null;
+ mutable std::unique_ptr<BugType> BT_call_undef;
+ mutable std::unique_ptr<BugType> BT_cxx_call_null;
+ mutable std::unique_ptr<BugType> BT_cxx_call_undef;
+ mutable std::unique_ptr<BugType> BT_call_arg;
+ mutable std::unique_ptr<BugType> BT_cxx_delete_undef;
+ mutable std::unique_ptr<BugType> BT_msg_undef;
+ mutable std::unique_ptr<BugType> BT_objc_prop_undef;
+ mutable std::unique_ptr<BugType> BT_objc_subscript_undef;
+ mutable std::unique_ptr<BugType> BT_msg_arg;
+ mutable std::unique_ptr<BugType> BT_msg_ret;
+ mutable std::unique_ptr<BugType> BT_call_few_args;
+
+public:
+ ChecksFilter Filter;
+
+ void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+ void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
+ void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
+
+ /// Fill in the return value that results from messaging nil based on the
+ /// return type and architecture and diagnose if the return value will be
+ /// garbage.
+ void checkObjCMessageNil(const ObjCMethodCall &msg, CheckerContext &C) const;
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+private:
+ bool PreVisitProcessArg(CheckerContext &C, SVal V, SourceRange ArgRange,
+ const Expr *ArgEx, int ArgumentNumber,
+ bool CheckUninitFields, const CallEvent &Call,
+ std::unique_ptr<BugType> &BT,
+ const ParmVarDecl *ParamDecl) const;
+
+ static void emitBadCall(BugType *BT, CheckerContext &C, const Expr *BadE);
+ void emitNilReceiverBug(CheckerContext &C, const ObjCMethodCall &msg,
+ ExplodedNode *N) const;
+
+ void HandleNilReceiver(CheckerContext &C,
+ ProgramStateRef state,
+ const ObjCMethodCall &msg) const;
+
+ void LazyInit_BT(const char *desc, std::unique_ptr<BugType> &BT) const {
+ if (!BT)
+ BT.reset(new BuiltinBug(this, desc));
+ }
+ bool uninitRefOrPointer(CheckerContext &C, const SVal &V,
+ SourceRange ArgRange, const Expr *ArgEx,
+ std::unique_ptr<BugType> &BT,
+ const ParmVarDecl *ParamDecl, const char *BD,
+ int ArgumentNumber) const;
+};
+} // end anonymous namespace
+
+void CallAndMessageChecker::emitBadCall(BugType *BT, CheckerContext &C,
+ const Expr *BadE) {
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+
+ auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
+ if (BadE) {
+ R->addRange(BadE->getSourceRange());
+ if (BadE->isGLValue())
+ BadE = bugreporter::getDerefExpr(BadE);
+ bugreporter::trackExpressionValue(N, BadE, *R);
+ }
+ C.emitReport(std::move(R));
+}
+
+static void describeUninitializedArgumentInCall(const CallEvent &Call,
+ int ArgumentNumber,
+ llvm::raw_svector_ostream &Os) {
+ switch (Call.getKind()) {
+ case CE_ObjCMessage: {
+ const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);
+ switch (Msg.getMessageKind()) {
+ case OCM_Message:
+ Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
+ << " argument in message expression is an uninitialized value";
+ return;
+ case OCM_PropertyAccess:
+ assert(Msg.isSetter() && "Getters have no args");
+ Os << "Argument for property setter is an uninitialized value";
+ return;
+ case OCM_Subscript:
+ if (Msg.isSetter() && (ArgumentNumber == 0))
+ Os << "Argument for subscript setter is an uninitialized value";
+ else
+ Os << "Subscript index is an uninitialized value";
+ return;
+ }
+ llvm_unreachable("Unknown message kind.");
+ }
+ case CE_Block:
+ Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
+ << " block call argument is an uninitialized value";
+ return;
+ default:
+ Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
+ << " function call argument is an uninitialized value";
+ return;
+ }
+}
+
+bool CallAndMessageChecker::uninitRefOrPointer(
+ CheckerContext &C, const SVal &V, SourceRange ArgRange, const Expr *ArgEx,
+ std::unique_ptr<BugType> &BT, const ParmVarDecl *ParamDecl, const char *BD,
+ int ArgumentNumber) const {
+ if (!Filter.Check_CallAndMessageUnInitRefArg)
+ return false;
+
+ // No parameter declaration available, i.e. variadic function argument.
+ if(!ParamDecl)
+ return false;
+
+ // If parameter is declared as pointer to const in function declaration,
+ // then check if corresponding argument in function call is
+ // pointing to undefined symbol value (uninitialized memory).
+ SmallString<200> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ if (ParamDecl->getType()->isPointerType()) {
+ Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
+ << " function call argument is a pointer to uninitialized value";
+ } else if (ParamDecl->getType()->isReferenceType()) {
+ Os << (ArgumentNumber + 1) << llvm::getOrdinalSuffix(ArgumentNumber + 1)
+ << " function call argument is an uninitialized value";
+ } else
+ return false;
+
+ if(!ParamDecl->getType()->getPointeeType().isConstQualified())
+ return false;
+
+ if (const MemRegion *SValMemRegion = V.getAsRegion()) {
+ const ProgramStateRef State = C.getState();
+ const SVal PSV = State->getSVal(SValMemRegion, C.getASTContext().CharTy);
+ if (PSV.isUndef()) {
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ LazyInit_BT(BD, BT);
+ auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N);
+ R->addRange(ArgRange);
+ if (ArgEx)
+ bugreporter::trackExpressionValue(N, ArgEx, *R);
+
+ C.emitReport(std::move(R));
+ }
+ return true;
+ }
+ }
+ return false;
+}
+
+namespace {
+class FindUninitializedField {
+public:
+ SmallVector<const FieldDecl *, 10> FieldChain;
+
+private:
+ StoreManager &StoreMgr;
+ MemRegionManager &MrMgr;
+ Store store;
+
+public:
+ FindUninitializedField(StoreManager &storeMgr, MemRegionManager &mrMgr,
+ Store s)
+ : StoreMgr(storeMgr), MrMgr(mrMgr), store(s) {}
+
+ bool Find(const TypedValueRegion *R) {
+ QualType T = R->getValueType();
+ if (const RecordType *RT = T->getAsStructureType()) {
+ const RecordDecl *RD = RT->getDecl()->getDefinition();
+ assert(RD && "Referred record has no definition");
+ for (const auto *I : RD->fields()) {
+ const FieldRegion *FR = MrMgr.getFieldRegion(I, R);
+ FieldChain.push_back(I);
+ T = I->getType();
+ if (T->getAsStructureType()) {
+ if (Find(FR))
+ return true;
+ } else {
+ const SVal &V = StoreMgr.getBinding(store, loc::MemRegionVal(FR));
+ if (V.isUndef())
+ return true;
+ }
+ FieldChain.pop_back();
+ }
+ }
+
+ return false;
+ }
+};
+} // namespace
+
+bool CallAndMessageChecker::PreVisitProcessArg(CheckerContext &C,
+ SVal V,
+ SourceRange ArgRange,
+ const Expr *ArgEx,
+ int ArgumentNumber,
+ bool CheckUninitFields,
+ const CallEvent &Call,
+ std::unique_ptr<BugType> &BT,
+ const ParmVarDecl *ParamDecl
+ ) const {
+ const char *BD = "Uninitialized argument value";
+
+ if (uninitRefOrPointer(C, V, ArgRange, ArgEx, BT, ParamDecl, BD,
+ ArgumentNumber))
+ return true;
+
+ if (V.isUndef()) {
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ LazyInit_BT(BD, BT);
+ // Generate a report for this bug.
+ SmallString<200> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+ describeUninitializedArgumentInCall(Call, ArgumentNumber, Os);
+ auto R = llvm::make_unique<BugReport>(*BT, Os.str(), N);
+
+ R->addRange(ArgRange);
+ if (ArgEx)
+ bugreporter::trackExpressionValue(N, ArgEx, *R);
+ C.emitReport(std::move(R));
+ }
+ return true;
+ }
+
+ if (!CheckUninitFields)
+ return false;
+
+ if (auto LV = V.getAs<nonloc::LazyCompoundVal>()) {
+ const LazyCompoundValData *D = LV->getCVData();
+ FindUninitializedField F(C.getState()->getStateManager().getStoreManager(),
+ C.getSValBuilder().getRegionManager(),
+ D->getStore());
+
+ if (F.Find(D->getRegion())) {
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ LazyInit_BT(BD, BT);
+ SmallString<512> Str;
+ llvm::raw_svector_ostream os(Str);
+ os << "Passed-by-value struct argument contains uninitialized data";
+
+ if (F.FieldChain.size() == 1)
+ os << " (e.g., field: '" << *F.FieldChain[0] << "')";
+ else {
+ os << " (e.g., via the field chain: '";
+ bool first = true;
+ for (SmallVectorImpl<const FieldDecl *>::iterator
+ DI = F.FieldChain.begin(), DE = F.FieldChain.end(); DI!=DE;++DI){
+ if (first)
+ first = false;
+ else
+ os << '.';
+ os << **DI;
+ }
+ os << "')";
+ }
+
+ // Generate a report for this bug.
+ auto R = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ R->addRange(ArgRange);
+
+ if (ArgEx)
+ bugreporter::trackExpressionValue(N, ArgEx, *R);
+ // FIXME: enhance track back for uninitialized value for arbitrary
+ // memregions
+ C.emitReport(std::move(R));
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void CallAndMessageChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const{
+
+ const Expr *Callee = CE->getCallee()->IgnoreParens();
+ ProgramStateRef State = C.getState();
+ const LocationContext *LCtx = C.getLocationContext();
+ SVal L = State->getSVal(Callee, LCtx);
+
+ if (L.isUndef()) {
+ if (!BT_call_undef)
+ BT_call_undef.reset(new BuiltinBug(
+ this, "Called function pointer is an uninitialized pointer value"));
+ emitBadCall(BT_call_undef.get(), C, Callee);
+ return;
+ }
+
+ ProgramStateRef StNonNull, StNull;
+ std::tie(StNonNull, StNull) = State->assume(L.castAs<DefinedOrUnknownSVal>());
+
+ if (StNull && !StNonNull) {
+ if (!BT_call_null)
+ BT_call_null.reset(new BuiltinBug(
+ this, "Called function pointer is null (null dereference)"));
+ emitBadCall(BT_call_null.get(), C, Callee);
+ return;
+ }
+
+ C.addTransition(StNonNull);
+}
+
+void CallAndMessageChecker::checkPreStmt(const CXXDeleteExpr *DE,
+ CheckerContext &C) const {
+
+ SVal Arg = C.getSVal(DE->getArgument());
+ if (Arg.isUndef()) {
+ StringRef Desc;
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+ if (!BT_cxx_delete_undef)
+ BT_cxx_delete_undef.reset(
+ new BuiltinBug(this, "Uninitialized argument value"));
+ if (DE->isArrayFormAsWritten())
+ Desc = "Argument to 'delete[]' is uninitialized";
+ else
+ Desc = "Argument to 'delete' is uninitialized";
+ BugType *BT = BT_cxx_delete_undef.get();
+ auto R = llvm::make_unique<BugReport>(*BT, Desc, N);
+ bugreporter::trackExpressionValue(N, DE, *R);
+ C.emitReport(std::move(R));
+ return;
+ }
+}
+
+void CallAndMessageChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // If this is a call to a C++ method, check if the callee is null or
+ // undefined.
+ if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) {
+ SVal V = CC->getCXXThisVal();
+ if (V.isUndef()) {
+ if (!BT_cxx_call_undef)
+ BT_cxx_call_undef.reset(
+ new BuiltinBug(this, "Called C++ object pointer is uninitialized"));
+ emitBadCall(BT_cxx_call_undef.get(), C, CC->getCXXThisExpr());
+ return;
+ }
+
+ ProgramStateRef StNonNull, StNull;
+ std::tie(StNonNull, StNull) =
+ State->assume(V.castAs<DefinedOrUnknownSVal>());
+
+ if (StNull && !StNonNull) {
+ if (!BT_cxx_call_null)
+ BT_cxx_call_null.reset(
+ new BuiltinBug(this, "Called C++ object pointer is null"));
+ emitBadCall(BT_cxx_call_null.get(), C, CC->getCXXThisExpr());
+ return;
+ }
+
+ State = StNonNull;
+ }
+
+ const Decl *D = Call.getDecl();
+ if (D && (isa<FunctionDecl>(D) || isa<BlockDecl>(D))) {
+ // If we have a function or block declaration, we can make sure we pass
+ // enough parameters.
+ unsigned Params = Call.parameters().size();
+ if (Call.getNumArgs() < Params) {
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+
+ LazyInit_BT("Function call with too few arguments", BT_call_few_args);
+
+ SmallString<512> Str;
+ llvm::raw_svector_ostream os(Str);
+ if (isa<FunctionDecl>(D)) {
+ os << "Function ";
+ } else {
+ assert(isa<BlockDecl>(D));
+ os << "Block ";
+ }
+ os << "taking " << Params << " argument"
+ << (Params == 1 ? "" : "s") << " is called with fewer ("
+ << Call.getNumArgs() << ")";
+
+ C.emitReport(
+ llvm::make_unique<BugReport>(*BT_call_few_args, os.str(), N));
+ }
+ }
+
+ // Don't check for uninitialized field values in arguments if the
+ // caller has a body that is available and we have the chance to inline it.
+ // This is a hack, but is a reasonable compromise betweens sometimes warning
+ // and sometimes not depending on if we decide to inline a function.
+ const bool checkUninitFields =
+ !(C.getAnalysisManager().shouldInlineCall() && (D && D->getBody()));
+
+ std::unique_ptr<BugType> *BT;
+ if (isa<ObjCMethodCall>(Call))
+ BT = &BT_msg_arg;
+ else
+ BT = &BT_call_arg;
+
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(D);
+ for (unsigned i = 0, e = Call.getNumArgs(); i != e; ++i) {
+ const ParmVarDecl *ParamDecl = nullptr;
+ if(FD && i < FD->getNumParams())
+ ParamDecl = FD->getParamDecl(i);
+ if (PreVisitProcessArg(C, Call.getArgSVal(i), Call.getArgSourceRange(i),
+ Call.getArgExpr(i), i,
+ checkUninitFields, Call, *BT, ParamDecl))
+ return;
+ }
+
+ // If we make it here, record our assumptions about the callee.
+ C.addTransition(State);
+}
+
+void CallAndMessageChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
+ CheckerContext &C) const {
+ SVal recVal = msg.getReceiverSVal();
+ if (recVal.isUndef()) {
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ BugType *BT = nullptr;
+ switch (msg.getMessageKind()) {
+ case OCM_Message:
+ if (!BT_msg_undef)
+ BT_msg_undef.reset(new BuiltinBug(this,
+ "Receiver in message expression "
+ "is an uninitialized value"));
+ BT = BT_msg_undef.get();
+ break;
+ case OCM_PropertyAccess:
+ if (!BT_objc_prop_undef)
+ BT_objc_prop_undef.reset(new BuiltinBug(
+ this, "Property access on an uninitialized object pointer"));
+ BT = BT_objc_prop_undef.get();
+ break;
+ case OCM_Subscript:
+ if (!BT_objc_subscript_undef)
+ BT_objc_subscript_undef.reset(new BuiltinBug(
+ this, "Subscript access on an uninitialized object pointer"));
+ BT = BT_objc_subscript_undef.get();
+ break;
+ }
+ assert(BT && "Unknown message kind.");
+
+ auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
+ const ObjCMessageExpr *ME = msg.getOriginExpr();
+ R->addRange(ME->getReceiverRange());
+
+ // FIXME: getTrackNullOrUndefValueVisitor can't handle "super" yet.
+ if (const Expr *ReceiverE = ME->getInstanceReceiver())
+ bugreporter::trackExpressionValue(N, ReceiverE, *R);
+ C.emitReport(std::move(R));
+ }
+ return;
+ }
+}
+
+void CallAndMessageChecker::checkObjCMessageNil(const ObjCMethodCall &msg,
+ CheckerContext &C) const {
+ HandleNilReceiver(C, C.getState(), msg);
+}
+
+void CallAndMessageChecker::emitNilReceiverBug(CheckerContext &C,
+ const ObjCMethodCall &msg,
+ ExplodedNode *N) const {
+
+ if (!BT_msg_ret)
+ BT_msg_ret.reset(
+ new BuiltinBug(this, "Receiver in message expression is 'nil'"));
+
+ const ObjCMessageExpr *ME = msg.getOriginExpr();
+
+ QualType ResTy = msg.getResultType();
+
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
+ os << "The receiver of message '";
+ ME->getSelector().print(os);
+ os << "' is nil";
+ if (ResTy->isReferenceType()) {
+ os << ", which results in forming a null reference";
+ } else {
+ os << " and returns a value of type '";
+ msg.getResultType().print(os, C.getLangOpts());
+ os << "' that will be garbage";
+ }
+
+ auto report = llvm::make_unique<BugReport>(*BT_msg_ret, os.str(), N);
+ report->addRange(ME->getReceiverRange());
+ // FIXME: This won't track "self" in messages to super.
+ if (const Expr *receiver = ME->getInstanceReceiver()) {
+ bugreporter::trackExpressionValue(N, receiver, *report);
+ }
+ C.emitReport(std::move(report));
+}
+
+static bool supportsNilWithFloatRet(const llvm::Triple &triple) {
+ return (triple.getVendor() == llvm::Triple::Apple &&
+ (triple.isiOS() || triple.isWatchOS() ||
+ !triple.isMacOSXVersionLT(10,5)));
+}
+
+void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
+ ProgramStateRef state,
+ const ObjCMethodCall &Msg) const {
+ ASTContext &Ctx = C.getASTContext();
+ static CheckerProgramPointTag Tag(this, "NilReceiver");
+
+ // Check the return type of the message expression. A message to nil will
+ // return different values depending on the return type and the architecture.
+ QualType RetTy = Msg.getResultType();
+ CanQualType CanRetTy = Ctx.getCanonicalType(RetTy);
+ const LocationContext *LCtx = C.getLocationContext();
+
+ if (CanRetTy->isStructureOrClassType()) {
+ // Structure returns are safe since the compiler zeroes them out.
+ SVal V = C.getSValBuilder().makeZeroVal(RetTy);
+ C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag);
+ return;
+ }
+
+ // Other cases: check if sizeof(return type) > sizeof(void*)
+ if (CanRetTy != Ctx.VoidTy && C.getLocationContext()->getParentMap()
+ .isConsumedExpr(Msg.getOriginExpr())) {
+ // Compute: sizeof(void *) and sizeof(return type)
+ const uint64_t voidPtrSize = Ctx.getTypeSize(Ctx.VoidPtrTy);
+ const uint64_t returnTypeSize = Ctx.getTypeSize(CanRetTy);
+
+ if (CanRetTy.getTypePtr()->isReferenceType()||
+ (voidPtrSize < returnTypeSize &&
+ !(supportsNilWithFloatRet(Ctx.getTargetInfo().getTriple()) &&
+ (Ctx.FloatTy == CanRetTy ||
+ Ctx.DoubleTy == CanRetTy ||
+ Ctx.LongDoubleTy == CanRetTy ||
+ Ctx.LongLongTy == CanRetTy ||
+ Ctx.UnsignedLongLongTy == CanRetTy)))) {
+ if (ExplodedNode *N = C.generateErrorNode(state, &Tag))
+ emitNilReceiverBug(C, Msg, N);
+ return;
+ }
+
+ // Handle the safe cases where the return value is 0 if the
+ // receiver is nil.
+ //
+ // FIXME: For now take the conservative approach that we only
+ // return null values if we *know* that the receiver is nil.
+ // This is because we can have surprises like:
+ //
+ // ... = [[NSScreens screens] objectAtIndex:0];
+ //
+ // What can happen is that [... screens] could return nil, but
+ // it most likely isn't nil. We should assume the semantics
+ // of this case unless we have *a lot* more knowledge.
+ //
+ SVal V = C.getSValBuilder().makeZeroVal(RetTy);
+ C.addTransition(state->BindExpr(Msg.getOriginExpr(), LCtx, V), &Tag);
+ return;
+ }
+
+ C.addTransition(state);
+}
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ CallAndMessageChecker *Checker = \
+ mgr.registerChecker<CallAndMessageChecker>(); \
+ Checker->Filter.Check_##name = true; \
+ Checker->Filter.CheckName_##name = mgr.getCurrentCheckName(); \
+ }
+
+REGISTER_CHECKER(CallAndMessageUnInitRefArg)
+REGISTER_CHECKER(CallAndMessageChecker)
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
new file mode 100644
index 000000000000..5deb62d32311
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
@@ -0,0 +1,149 @@
+//=== CastSizeChecker.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// CastSizeChecker checks when casting a malloc'ed symbolic region to type T,
+// whether the size of the symbolic region is a multiple of the size of T.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class CastSizeChecker : public Checker< check::PreStmt<CastExpr> > {
+ mutable std::unique_ptr<BuiltinBug> BT;
+
+public:
+ void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
+};
+}
+
+/// Check if we are casting to a struct with a flexible array at the end.
+/// \code
+/// struct foo {
+/// size_t len;
+/// struct bar data[];
+/// };
+/// \endcode
+/// or
+/// \code
+/// struct foo {
+/// size_t len;
+/// struct bar data[0];
+/// }
+/// \endcode
+/// In these cases it is also valid to allocate size of struct foo + a multiple
+/// of struct bar.
+static bool evenFlexibleArraySize(ASTContext &Ctx, CharUnits RegionSize,
+ CharUnits TypeSize, QualType ToPointeeTy) {
+ const RecordType *RT = ToPointeeTy->getAs<RecordType>();
+ if (!RT)
+ return false;
+
+ const RecordDecl *RD = RT->getDecl();
+ RecordDecl::field_iterator Iter(RD->field_begin());
+ RecordDecl::field_iterator End(RD->field_end());
+ const FieldDecl *Last = nullptr;
+ for (; Iter != End; ++Iter)
+ Last = *Iter;
+ assert(Last && "empty structs should already be handled");
+
+ const Type *ElemType = Last->getType()->getArrayElementTypeNoTypeQual();
+ CharUnits FlexSize;
+ if (const ConstantArrayType *ArrayTy =
+ Ctx.getAsConstantArrayType(Last->getType())) {
+ FlexSize = Ctx.getTypeSizeInChars(ElemType);
+ if (ArrayTy->getSize() == 1 && TypeSize > FlexSize)
+ TypeSize -= FlexSize;
+ else if (ArrayTy->getSize() != 0)
+ return false;
+ } else if (RD->hasFlexibleArrayMember()) {
+ FlexSize = Ctx.getTypeSizeInChars(ElemType);
+ } else {
+ return false;
+ }
+
+ if (FlexSize.isZero())
+ return false;
+
+ CharUnits Left = RegionSize - TypeSize;
+ if (Left.isNegative())
+ return false;
+
+ return Left % FlexSize == 0;
+}
+
+void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
+ const Expr *E = CE->getSubExpr();
+ ASTContext &Ctx = C.getASTContext();
+ QualType ToTy = Ctx.getCanonicalType(CE->getType());
+ const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr());
+
+ if (!ToPTy)
+ return;
+
+ QualType ToPointeeTy = ToPTy->getPointeeType();
+
+ // Only perform the check if 'ToPointeeTy' is a complete type.
+ if (ToPointeeTy->isIncompleteType())
+ return;
+
+ ProgramStateRef state = C.getState();
+ const MemRegion *R = C.getSVal(E).getAsRegion();
+ if (!R)
+ return;
+
+ const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R);
+ if (!SR)
+ return;
+
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ SVal extent = SR->getExtent(svalBuilder);
+ const llvm::APSInt *extentInt = svalBuilder.getKnownValue(state, extent);
+ if (!extentInt)
+ return;
+
+ CharUnits regionSize = CharUnits::fromQuantity(extentInt->getSExtValue());
+ CharUnits typeSize = C.getASTContext().getTypeSizeInChars(ToPointeeTy);
+
+ // Ignore void, and a few other un-sizeable types.
+ if (typeSize.isZero())
+ return;
+
+ if (regionSize % typeSize == 0)
+ return;
+
+ if (evenFlexibleArraySize(Ctx, regionSize, typeSize, ToPointeeTy))
+ return;
+
+ if (ExplodedNode *errorNode = C.generateErrorNode()) {
+ if (!BT)
+ BT.reset(new BuiltinBug(this, "Cast region with wrong size.",
+ "Cast a region whose size is not a multiple"
+ " of the destination type size."));
+ auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), errorNode);
+ R->addRange(CE->getSourceRange());
+ C.emitReport(std::move(R));
+ }
+}
+
+void ento::registerCastSizeChecker(CheckerManager &mgr) {
+ // PR31226: C++ is more complicated than what this checker currently supports.
+ // There are derived-to-base casts, there are different rules for 0-size
+ // structures, no flexible arrays, etc.
+ // FIXME: Disabled on C++ for now.
+ if (!mgr.getLangOpts().CPlusPlus)
+ mgr.registerChecker<CastSizeChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
new file mode 100644
index 000000000000..2bd3879627cb
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
@@ -0,0 +1,122 @@
+//=== CastToStructChecker.cpp ----------------------------------*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This files defines CastToStructChecker, a builtin checker that checks for
+// cast from non-struct pointer to struct pointer and widening struct data cast.
+// This check corresponds to CWE-588.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class CastToStructVisitor : public RecursiveASTVisitor<CastToStructVisitor> {
+ BugReporter &BR;
+ const CheckerBase *Checker;
+ AnalysisDeclContext *AC;
+
+public:
+ explicit CastToStructVisitor(BugReporter &B, const CheckerBase *Checker,
+ AnalysisDeclContext *A)
+ : BR(B), Checker(Checker), AC(A) {}
+ bool VisitCastExpr(const CastExpr *CE);
+};
+}
+
+bool CastToStructVisitor::VisitCastExpr(const CastExpr *CE) {
+ const Expr *E = CE->getSubExpr();
+ ASTContext &Ctx = AC->getASTContext();
+ QualType OrigTy = Ctx.getCanonicalType(E->getType());
+ QualType ToTy = Ctx.getCanonicalType(CE->getType());
+
+ const PointerType *OrigPTy = dyn_cast<PointerType>(OrigTy.getTypePtr());
+ const PointerType *ToPTy = dyn_cast<PointerType>(ToTy.getTypePtr());
+
+ if (!ToPTy || !OrigPTy)
+ return true;
+
+ QualType OrigPointeeTy = OrigPTy->getPointeeType();
+ QualType ToPointeeTy = ToPTy->getPointeeType();
+
+ if (!ToPointeeTy->isStructureOrClassType())
+ return true;
+
+ // We allow cast from void*.
+ if (OrigPointeeTy->isVoidType())
+ return true;
+
+ // Now the cast-to-type is struct pointer, the original type is not void*.
+ if (!OrigPointeeTy->isRecordType()) {
+ SourceRange Sr[1] = {CE->getSourceRange()};
+ PathDiagnosticLocation Loc(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(
+ AC->getDecl(), Checker, "Cast from non-struct type to struct type",
+ categories::LogicError, "Casting a non-structure type to a structure "
+ "type and accessing a field can lead to memory "
+ "access errors or data corruption.",
+ Loc, Sr);
+ } else {
+ // Don't warn when size of data is unknown.
+ const auto *U = dyn_cast<UnaryOperator>(E);
+ if (!U || U->getOpcode() != UO_AddrOf)
+ return true;
+
+ // Don't warn for references
+ const ValueDecl *VD = nullptr;
+ if (const auto *SE = dyn_cast<DeclRefExpr>(U->getSubExpr()))
+ VD = SE->getDecl();
+ else if (const auto *SE = dyn_cast<MemberExpr>(U->getSubExpr()))
+ VD = SE->getMemberDecl();
+ if (!VD || VD->getType()->isReferenceType())
+ return true;
+
+ if (ToPointeeTy->isIncompleteType() ||
+ OrigPointeeTy->isIncompleteType())
+ return true;
+
+ // Warn when there is widening cast.
+ unsigned ToWidth = Ctx.getTypeInfo(ToPointeeTy).Width;
+ unsigned OrigWidth = Ctx.getTypeInfo(OrigPointeeTy).Width;
+ if (ToWidth <= OrigWidth)
+ return true;
+
+ PathDiagnosticLocation Loc(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), Checker, "Widening cast to struct type",
+ categories::LogicError,
+ "Casting data to a larger structure type and accessing "
+ "a field can lead to memory access errors or data "
+ "corruption.",
+ Loc, CE->getSourceRange());
+ }
+
+ return true;
+}
+
+namespace {
+class CastToStructChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const {
+ CastToStructVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
+ Visitor.TraverseDecl(const_cast<Decl *>(D));
+ }
+};
+} // end anonymous namespace
+
+void ento::registerCastToStructChecker(CheckerManager &mgr) {
+ mgr.registerChecker<CastToStructChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
new file mode 100644
index 000000000000..00a912f27a8d
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -0,0 +1,1096 @@
+//==- CheckObjCDealloc.cpp - Check ObjC -dealloc implementation --*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker analyzes Objective-C -dealloc methods and their callees
+// to warn about improper releasing of instance variables that back synthesized
+// properties. It warns about missing releases in the following cases:
+// - When a class has a synthesized instance variable for a 'retain' or 'copy'
+// property and lacks a -dealloc method in its implementation.
+// - When a class has a synthesized instance variable for a 'retain'/'copy'
+// property but the ivar is not released in -dealloc by either -release
+// or by nilling out the property.
+//
+// It warns about extra releases in -dealloc (but not in callees) when a
+// synthesized instance variable is released in the following cases:
+// - When the property is 'assign' and is not 'readonly'.
+// - When the property is 'weak'.
+//
+// This checker only warns for instance variables synthesized to back
+// properties. Handling the more general case would require inferring whether
+// an instance variable is stored retained or not. For synthesized properties,
+// this is specified in the property declaration itself.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+/// Indicates whether an instance variable is required to be released in
+/// -dealloc.
+enum class ReleaseRequirement {
+ /// The instance variable must be released, either by calling
+ /// -release on it directly or by nilling it out with a property setter.
+ MustRelease,
+
+ /// The instance variable must not be directly released with -release.
+ MustNotReleaseDirectly,
+
+ /// The requirement for the instance variable could not be determined.
+ Unknown
+};
+
+/// Returns true if the property implementation is synthesized and the
+/// type of the property is retainable.
+static bool isSynthesizedRetainableProperty(const ObjCPropertyImplDecl *I,
+ const ObjCIvarDecl **ID,
+ const ObjCPropertyDecl **PD) {
+
+ if (I->getPropertyImplementation() != ObjCPropertyImplDecl::Synthesize)
+ return false;
+
+ (*ID) = I->getPropertyIvarDecl();
+ if (!(*ID))
+ return false;
+
+ QualType T = (*ID)->getType();
+ if (!T->isObjCRetainableType())
+ return false;
+
+ (*PD) = I->getPropertyDecl();
+ // Shouldn't be able to synthesize a property that doesn't exist.
+ assert(*PD);
+
+ return true;
+}
+
+namespace {
+
+class ObjCDeallocChecker
+ : public Checker<check::ASTDecl<ObjCImplementationDecl>,
+ check::PreObjCMessage, check::PostObjCMessage,
+ check::PreCall,
+ check::BeginFunction, check::EndFunction,
+ eval::Assume,
+ check::PointerEscape,
+ check::PreStmt<ReturnStmt>> {
+
+ mutable IdentifierInfo *NSObjectII, *SenTestCaseII, *XCTestCaseII,
+ *Block_releaseII, *CIFilterII;
+
+ mutable Selector DeallocSel, ReleaseSel;
+
+ std::unique_ptr<BugType> MissingReleaseBugType;
+ std::unique_ptr<BugType> ExtraReleaseBugType;
+ std::unique_ptr<BugType> MistakenDeallocBugType;
+
+public:
+ ObjCDeallocChecker();
+
+ void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
+ BugReporter &BR) const;
+ void checkBeginFunction(CheckerContext &Ctx) const;
+ void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+
+ ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
+ bool Assumption) const;
+
+ ProgramStateRef checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
+ void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
+
+private:
+ void diagnoseMissingReleases(CheckerContext &C) const;
+
+ bool diagnoseExtraRelease(SymbolRef ReleasedValue, const ObjCMethodCall &M,
+ CheckerContext &C) const;
+
+ bool diagnoseMistakenDealloc(SymbolRef DeallocedValue,
+ const ObjCMethodCall &M,
+ CheckerContext &C) const;
+
+ SymbolRef getValueReleasedByNillingOut(const ObjCMethodCall &M,
+ CheckerContext &C) const;
+
+ const ObjCIvarRegion *getIvarRegionForIvarSymbol(SymbolRef IvarSym) const;
+ SymbolRef getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const;
+
+ const ObjCPropertyImplDecl*
+ findPropertyOnDeallocatingInstance(SymbolRef IvarSym,
+ CheckerContext &C) const;
+
+ ReleaseRequirement
+ getDeallocReleaseRequirement(const ObjCPropertyImplDecl *PropImpl) const;
+
+ bool isInInstanceDealloc(const CheckerContext &C, SVal &SelfValOut) const;
+ bool isInInstanceDealloc(const CheckerContext &C, const LocationContext *LCtx,
+ SVal &SelfValOut) const;
+ bool instanceDeallocIsOnStack(const CheckerContext &C,
+ SVal &InstanceValOut) const;
+
+ bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
+
+ const ObjCImplDecl *getContainingObjCImpl(const LocationContext *LCtx) const;
+
+ const ObjCPropertyDecl *
+ findShadowedPropertyDecl(const ObjCPropertyImplDecl *PropImpl) const;
+
+ void transitionToReleaseValue(CheckerContext &C, SymbolRef Value) const;
+ ProgramStateRef removeValueRequiringRelease(ProgramStateRef State,
+ SymbolRef InstanceSym,
+ SymbolRef ValueSym) const;
+
+ void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
+
+ bool classHasSeparateTeardown(const ObjCInterfaceDecl *ID) const;
+
+ bool isReleasedByCIFilterDealloc(const ObjCPropertyImplDecl *PropImpl) const;
+ bool isNibLoadedIvarWithoutRetain(const ObjCPropertyImplDecl *PropImpl) const;
+};
+} // End anonymous namespace.
+
+
+/// Maps from the symbol for a class instance to the set of
+/// symbols remaining that must be released in -dealloc.
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef)
+REGISTER_MAP_WITH_PROGRAMSTATE(UnreleasedIvarMap, SymbolRef, SymbolSet)
+
+
+/// An AST check that diagnose when the class requires a -dealloc method and
+/// is missing one.
+void ObjCDeallocChecker::checkASTDecl(const ObjCImplementationDecl *D,
+ AnalysisManager &Mgr,
+ BugReporter &BR) const {
+ assert(Mgr.getLangOpts().getGC() != LangOptions::GCOnly);
+ assert(!Mgr.getLangOpts().ObjCAutoRefCount);
+ initIdentifierInfoAndSelectors(Mgr.getASTContext());
+
+ const ObjCInterfaceDecl *ID = D->getClassInterface();
+ // If the class is known to have a lifecycle with a separate teardown method
+ // then it may not require a -dealloc method.
+ if (classHasSeparateTeardown(ID))
+ return;
+
+ // Does the class contain any synthesized properties that are retainable?
+ // If not, skip the check entirely.
+ const ObjCPropertyImplDecl *PropImplRequiringRelease = nullptr;
+ bool HasOthers = false;
+ for (const auto *I : D->property_impls()) {
+ if (getDeallocReleaseRequirement(I) == ReleaseRequirement::MustRelease) {
+ if (!PropImplRequiringRelease)
+ PropImplRequiringRelease = I;
+ else {
+ HasOthers = true;
+ break;
+ }
+ }
+ }
+
+ if (!PropImplRequiringRelease)
+ return;
+
+ const ObjCMethodDecl *MD = nullptr;
+
+ // Scan the instance methods for "dealloc".
+ for (const auto *I : D->instance_methods()) {
+ if (I->getSelector() == DeallocSel) {
+ MD = I;
+ break;
+ }
+ }
+
+ if (!MD) { // No dealloc found.
+ const char* Name = "Missing -dealloc";
+
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ OS << "'" << *D << "' lacks a 'dealloc' instance method but "
+ << "must release '" << *PropImplRequiringRelease->getPropertyIvarDecl()
+ << "'";
+
+ if (HasOthers)
+ OS << " and others";
+ PathDiagnosticLocation DLoc =
+ PathDiagnosticLocation::createBegin(D, BR.getSourceManager());
+
+ BR.EmitBasicReport(D, this, Name, categories::CoreFoundationObjectiveC,
+ OS.str(), DLoc);
+ return;
+ }
+}
+
+/// If this is the beginning of -dealloc, mark the values initially stored in
+/// instance variables that must be released by the end of -dealloc
+/// as unreleased in the state.
+void ObjCDeallocChecker::checkBeginFunction(
+ CheckerContext &C) const {
+ initIdentifierInfoAndSelectors(C.getASTContext());
+
+ // Only do this if the current method is -dealloc.
+ SVal SelfVal;
+ if (!isInInstanceDealloc(C, SelfVal))
+ return;
+
+ SymbolRef SelfSymbol = SelfVal.getAsSymbol();
+
+ const LocationContext *LCtx = C.getLocationContext();
+ ProgramStateRef InitialState = C.getState();
+
+ ProgramStateRef State = InitialState;
+
+ SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
+
+ // Symbols that must be released by the end of the -dealloc;
+ SymbolSet RequiredReleases = F.getEmptySet();
+
+ // If we're an inlined -dealloc, we should add our symbols to the existing
+ // set from our subclass.
+ if (const SymbolSet *CurrSet = State->get<UnreleasedIvarMap>(SelfSymbol))
+ RequiredReleases = *CurrSet;
+
+ for (auto *PropImpl : getContainingObjCImpl(LCtx)->property_impls()) {
+ ReleaseRequirement Requirement = getDeallocReleaseRequirement(PropImpl);
+ if (Requirement != ReleaseRequirement::MustRelease)
+ continue;
+
+ SVal LVal = State->getLValue(PropImpl->getPropertyIvarDecl(), SelfVal);
+ Optional<Loc> LValLoc = LVal.getAs<Loc>();
+ if (!LValLoc)
+ continue;
+
+ SVal InitialVal = State->getSVal(LValLoc.getValue());
+ SymbolRef Symbol = InitialVal.getAsSymbol();
+ if (!Symbol || !isa<SymbolRegionValue>(Symbol))
+ continue;
+
+ // Mark the value as requiring a release.
+ RequiredReleases = F.add(RequiredReleases, Symbol);
+ }
+
+ if (!RequiredReleases.isEmpty()) {
+ State = State->set<UnreleasedIvarMap>(SelfSymbol, RequiredReleases);
+ }
+
+ if (State != InitialState) {
+ C.addTransition(State);
+ }
+}
+
+/// Given a symbol for an ivar, return the ivar region it was loaded from.
+/// Returns nullptr if the instance symbol cannot be found.
+const ObjCIvarRegion *
+ObjCDeallocChecker::getIvarRegionForIvarSymbol(SymbolRef IvarSym) const {
+ return dyn_cast_or_null<ObjCIvarRegion>(IvarSym->getOriginRegion());
+}
+
+/// Given a symbol for an ivar, return a symbol for the instance containing
+/// the ivar. Returns nullptr if the instance symbol cannot be found.
+SymbolRef
+ObjCDeallocChecker::getInstanceSymbolFromIvarSymbol(SymbolRef IvarSym) const {
+
+ const ObjCIvarRegion *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
+ if (!IvarRegion)
+ return nullptr;
+
+ return IvarRegion->getSymbolicBase()->getSymbol();
+}
+
+/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
+/// a release or a nilling-out property setter.
+void ObjCDeallocChecker::checkPreObjCMessage(
+ const ObjCMethodCall &M, CheckerContext &C) const {
+ // Only run if -dealloc is on the stack.
+ SVal DeallocedInstance;
+ if (!instanceDeallocIsOnStack(C, DeallocedInstance))
+ return;
+
+ SymbolRef ReleasedValue = nullptr;
+
+ if (M.getSelector() == ReleaseSel) {
+ ReleasedValue = M.getReceiverSVal().getAsSymbol();
+ } else if (M.getSelector() == DeallocSel && !M.isReceiverSelfOrSuper()) {
+ if (diagnoseMistakenDealloc(M.getReceiverSVal().getAsSymbol(), M, C))
+ return;
+ }
+
+ if (ReleasedValue) {
+ // An instance variable symbol was released with -release:
+ // [_property release];
+ if (diagnoseExtraRelease(ReleasedValue,M, C))
+ return;
+ } else {
+ // An instance variable symbol was released nilling out its property:
+ // self.property = nil;
+ ReleasedValue = getValueReleasedByNillingOut(M, C);
+ }
+
+ if (!ReleasedValue)
+ return;
+
+ transitionToReleaseValue(C, ReleasedValue);
+}
+
+/// If we are in -dealloc or -dealloc is on the stack, handle the call if it is
+/// call to Block_release().
+void ObjCDeallocChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const IdentifierInfo *II = Call.getCalleeIdentifier();
+ if (II != Block_releaseII)
+ return;
+
+ if (Call.getNumArgs() != 1)
+ return;
+
+ SymbolRef ReleasedValue = Call.getArgSVal(0).getAsSymbol();
+ if (!ReleasedValue)
+ return;
+
+ transitionToReleaseValue(C, ReleasedValue);
+}
+/// If the message was a call to '[super dealloc]', diagnose any missing
+/// releases.
+void ObjCDeallocChecker::checkPostObjCMessage(
+ const ObjCMethodCall &M, CheckerContext &C) const {
+ // We perform this check post-message so that if the super -dealloc
+ // calls a helper method and that this class overrides, any ivars released in
+ // the helper method will be recorded before checking.
+ if (isSuperDeallocMessage(M))
+ diagnoseMissingReleases(C);
+}
+
+/// Check for missing releases even when -dealloc does not call
+/// '[super dealloc]'.
+void ObjCDeallocChecker::checkEndFunction(
+ const ReturnStmt *RS, CheckerContext &C) const {
+ diagnoseMissingReleases(C);
+}
+
+/// Check for missing releases on early return.
+void ObjCDeallocChecker::checkPreStmt(
+ const ReturnStmt *RS, CheckerContext &C) const {
+ diagnoseMissingReleases(C);
+}
+
+/// When a symbol is assumed to be nil, remove it from the set of symbols
+/// require to be nil.
+ProgramStateRef ObjCDeallocChecker::evalAssume(ProgramStateRef State, SVal Cond,
+ bool Assumption) const {
+ if (State->get<UnreleasedIvarMap>().isEmpty())
+ return State;
+
+ auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
+ if (!CondBSE)
+ return State;
+
+ BinaryOperator::Opcode OpCode = CondBSE->getOpcode();
+ if (Assumption) {
+ if (OpCode != BO_EQ)
+ return State;
+ } else {
+ if (OpCode != BO_NE)
+ return State;
+ }
+
+ SymbolRef NullSymbol = nullptr;
+ if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
+ const llvm::APInt &RHS = SIE->getRHS();
+ if (RHS != 0)
+ return State;
+ NullSymbol = SIE->getLHS();
+ } else if (auto *SIE = dyn_cast<IntSymExpr>(CondBSE)) {
+ const llvm::APInt &LHS = SIE->getLHS();
+ if (LHS != 0)
+ return State;
+ NullSymbol = SIE->getRHS();
+ } else {
+ return State;
+ }
+
+ SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(NullSymbol);
+ if (!InstanceSymbol)
+ return State;
+
+ State = removeValueRequiringRelease(State, InstanceSymbol, NullSymbol);
+
+ return State;
+}
+
+/// If a symbol escapes conservatively assume unseen code released it.
+ProgramStateRef ObjCDeallocChecker::checkPointerEscape(
+ ProgramStateRef State, const InvalidatedSymbols &Escaped,
+ const CallEvent *Call, PointerEscapeKind Kind) const {
+
+ if (State->get<UnreleasedIvarMap>().isEmpty())
+ return State;
+
+ // Don't treat calls to '[super dealloc]' as escaping for the purposes
+ // of this checker. Because the checker diagnoses missing releases in the
+ // post-message handler for '[super dealloc], escaping here would cause
+ // the checker to never warn.
+ auto *OMC = dyn_cast_or_null<ObjCMethodCall>(Call);
+ if (OMC && isSuperDeallocMessage(*OMC))
+ return State;
+
+ for (const auto &Sym : Escaped) {
+ if (!Call || (Call && !Call->isInSystemHeader())) {
+ // If Sym is a symbol for an object with instance variables that
+ // must be released, remove these obligations when the object escapes
+ // unless via a call to a system function. System functions are
+ // very unlikely to release instance variables on objects passed to them,
+ // and are frequently called on 'self' in -dealloc (e.g., to remove
+ // observers) -- we want to avoid false negatives from escaping on
+ // them.
+ State = State->remove<UnreleasedIvarMap>(Sym);
+ }
+
+
+ SymbolRef InstanceSymbol = getInstanceSymbolFromIvarSymbol(Sym);
+ if (!InstanceSymbol)
+ continue;
+
+ State = removeValueRequiringRelease(State, InstanceSymbol, Sym);
+ }
+
+ return State;
+}
+
+/// Report any unreleased instance variables for the current instance being
+/// dealloced.
+void ObjCDeallocChecker::diagnoseMissingReleases(CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ SVal SelfVal;
+ if (!isInInstanceDealloc(C, SelfVal))
+ return;
+
+ const MemRegion *SelfRegion = SelfVal.castAs<loc::MemRegionVal>().getRegion();
+ const LocationContext *LCtx = C.getLocationContext();
+
+ ExplodedNode *ErrNode = nullptr;
+
+ SymbolRef SelfSym = SelfVal.getAsSymbol();
+ if (!SelfSym)
+ return;
+
+ const SymbolSet *OldUnreleased = State->get<UnreleasedIvarMap>(SelfSym);
+ if (!OldUnreleased)
+ return;
+
+ SymbolSet NewUnreleased = *OldUnreleased;
+ SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
+
+ ProgramStateRef InitialState = State;
+
+ for (auto *IvarSymbol : *OldUnreleased) {
+ const TypedValueRegion *TVR =
+ cast<SymbolRegionValue>(IvarSymbol)->getRegion();
+ const ObjCIvarRegion *IvarRegion = cast<ObjCIvarRegion>(TVR);
+
+ // Don't warn if the ivar is not for this instance.
+ if (SelfRegion != IvarRegion->getSuperRegion())
+ continue;
+
+ const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl();
+ // Prevent an inlined call to -dealloc in a super class from warning
+ // about the values the subclass's -dealloc should release.
+ if (IvarDecl->getContainingInterface() !=
+ cast<ObjCMethodDecl>(LCtx->getDecl())->getClassInterface())
+ continue;
+
+ // Prevents diagnosing multiple times for the same instance variable
+ // at, for example, both a return and at the end of the function.
+ NewUnreleased = F.remove(NewUnreleased, IvarSymbol);
+
+ if (State->getStateManager()
+ .getConstraintManager()
+ .isNull(State, IvarSymbol)
+ .isConstrainedTrue()) {
+ continue;
+ }
+
+ // A missing release manifests as a leak, so treat as a non-fatal error.
+ if (!ErrNode)
+ ErrNode = C.generateNonFatalErrorNode();
+ // If we've already reached this node on another path, return without
+ // diagnosing.
+ if (!ErrNode)
+ return;
+
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+
+ const ObjCInterfaceDecl *Interface = IvarDecl->getContainingInterface();
+ // If the class is known to have a lifecycle with teardown that is
+ // separate from -dealloc, do not warn about missing releases. We
+ // suppress here (rather than not tracking for instance variables in
+ // such classes) because these classes are rare.
+ if (classHasSeparateTeardown(Interface))
+ return;
+
+ ObjCImplDecl *ImplDecl = Interface->getImplementation();
+
+ const ObjCPropertyImplDecl *PropImpl =
+ ImplDecl->FindPropertyImplIvarDecl(IvarDecl->getIdentifier());
+
+ const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
+
+ assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Copy ||
+ PropDecl->getSetterKind() == ObjCPropertyDecl::Retain);
+
+ OS << "The '" << *IvarDecl << "' ivar in '" << *ImplDecl
+ << "' was ";
+
+ if (PropDecl->getSetterKind() == ObjCPropertyDecl::Retain)
+ OS << "retained";
+ else
+ OS << "copied";
+
+ OS << " by a synthesized property but not released"
+ " before '[super dealloc]'";
+
+ std::unique_ptr<BugReport> BR(
+ new BugReport(*MissingReleaseBugType, OS.str(), ErrNode));
+
+ C.emitReport(std::move(BR));
+ }
+
+ if (NewUnreleased.isEmpty()) {
+ State = State->remove<UnreleasedIvarMap>(SelfSym);
+ } else {
+ State = State->set<UnreleasedIvarMap>(SelfSym, NewUnreleased);
+ }
+
+ if (ErrNode) {
+ C.addTransition(State, ErrNode);
+ } else if (State != InitialState) {
+ C.addTransition(State);
+ }
+
+ // Make sure that after checking in the top-most frame the list of
+ // tracked ivars is empty. This is intended to detect accidental leaks in
+ // the UnreleasedIvarMap program state.
+ assert(!LCtx->inTopFrame() || State->get<UnreleasedIvarMap>().isEmpty());
+}
+
+/// Given a symbol, determine whether the symbol refers to an ivar on
+/// the top-most deallocating instance. If so, find the property for that
+/// ivar, if one exists. Otherwise return null.
+const ObjCPropertyImplDecl *
+ObjCDeallocChecker::findPropertyOnDeallocatingInstance(
+ SymbolRef IvarSym, CheckerContext &C) const {
+ SVal DeallocedInstance;
+ if (!isInInstanceDealloc(C, DeallocedInstance))
+ return nullptr;
+
+ // Try to get the region from which the ivar value was loaded.
+ auto *IvarRegion = getIvarRegionForIvarSymbol(IvarSym);
+ if (!IvarRegion)
+ return nullptr;
+
+ // Don't try to find the property if the ivar was not loaded from the
+ // given instance.
+ if (DeallocedInstance.castAs<loc::MemRegionVal>().getRegion() !=
+ IvarRegion->getSuperRegion())
+ return nullptr;
+
+ const LocationContext *LCtx = C.getLocationContext();
+ const ObjCIvarDecl *IvarDecl = IvarRegion->getDecl();
+
+ const ObjCImplDecl *Container = getContainingObjCImpl(LCtx);
+ const ObjCPropertyImplDecl *PropImpl =
+ Container->FindPropertyImplIvarDecl(IvarDecl->getIdentifier());
+ return PropImpl;
+}
+
+/// Emits a warning if the current context is -dealloc and ReleasedValue
+/// must not be directly released in a -dealloc. Returns true if a diagnostic
+/// was emitted.
+bool ObjCDeallocChecker::diagnoseExtraRelease(SymbolRef ReleasedValue,
+ const ObjCMethodCall &M,
+ CheckerContext &C) const {
+ // Try to get the region from which the released value was loaded.
+ // Note that, unlike diagnosing for missing releases, here we don't track
+ // values that must not be released in the state. This is because even if
+ // these values escape, it is still an error under the rules of MRR to
+ // release them in -dealloc.
+ const ObjCPropertyImplDecl *PropImpl =
+ findPropertyOnDeallocatingInstance(ReleasedValue, C);
+
+ if (!PropImpl)
+ return false;
+
+ // If the ivar belongs to a property that must not be released directly
+ // in dealloc, emit a warning.
+ if (getDeallocReleaseRequirement(PropImpl) !=
+ ReleaseRequirement::MustNotReleaseDirectly) {
+ return false;
+ }
+
+ // If the property is readwrite but it shadows a read-only property in its
+ // external interface, treat the property a read-only. If the outside
+ // world cannot write to a property then the internal implementation is free
+ // to make its own convention about whether the value is stored retained
+ // or not. We look up the shadow here rather than in
+ // getDeallocReleaseRequirement() because doing so can be expensive.
+ const ObjCPropertyDecl *PropDecl = findShadowedPropertyDecl(PropImpl);
+ if (PropDecl) {
+ if (PropDecl->isReadOnly())
+ return false;
+ } else {
+ PropDecl = PropImpl->getPropertyDecl();
+ }
+
+ ExplodedNode *ErrNode = C.generateNonFatalErrorNode();
+ if (!ErrNode)
+ return false;
+
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+
+ assert(PropDecl->getSetterKind() == ObjCPropertyDecl::Weak ||
+ (PropDecl->getSetterKind() == ObjCPropertyDecl::Assign &&
+ !PropDecl->isReadOnly()) ||
+ isReleasedByCIFilterDealloc(PropImpl)
+ );
+
+ const ObjCImplDecl *Container = getContainingObjCImpl(C.getLocationContext());
+ OS << "The '" << *PropImpl->getPropertyIvarDecl()
+ << "' ivar in '" << *Container;
+
+
+ if (isReleasedByCIFilterDealloc(PropImpl)) {
+ OS << "' will be released by '-[CIFilter dealloc]' but also released here";
+ } else {
+ OS << "' was synthesized for ";
+
+ if (PropDecl->getSetterKind() == ObjCPropertyDecl::Weak)
+ OS << "a weak";
+ else
+ OS << "an assign, readwrite";
+
+ OS << " property but was released in 'dealloc'";
+ }
+
+ std::unique_ptr<BugReport> BR(
+ new BugReport(*ExtraReleaseBugType, OS.str(), ErrNode));
+ BR->addRange(M.getOriginExpr()->getSourceRange());
+
+ C.emitReport(std::move(BR));
+
+ return true;
+}
+
+/// Emits a warning if the current context is -dealloc and DeallocedValue
+/// must not be directly dealloced in a -dealloc. Returns true if a diagnostic
+/// was emitted.
+bool ObjCDeallocChecker::diagnoseMistakenDealloc(SymbolRef DeallocedValue,
+ const ObjCMethodCall &M,
+ CheckerContext &C) const {
+ // TODO: Apart from unknown/undefined receivers, this may happen when
+ // dealloc is called as a class method. Should we warn?
+ if (!DeallocedValue)
+ return false;
+
+ // Find the property backing the instance variable that M
+ // is dealloc'ing.
+ const ObjCPropertyImplDecl *PropImpl =
+ findPropertyOnDeallocatingInstance(DeallocedValue, C);
+ if (!PropImpl)
+ return false;
+
+ if (getDeallocReleaseRequirement(PropImpl) !=
+ ReleaseRequirement::MustRelease) {
+ return false;
+ }
+
+ ExplodedNode *ErrNode = C.generateErrorNode();
+ if (!ErrNode)
+ return false;
+
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+
+ OS << "'" << *PropImpl->getPropertyIvarDecl()
+ << "' should be released rather than deallocated";
+
+ std::unique_ptr<BugReport> BR(
+ new BugReport(*MistakenDeallocBugType, OS.str(), ErrNode));
+ BR->addRange(M.getOriginExpr()->getSourceRange());
+
+ C.emitReport(std::move(BR));
+
+ return true;
+}
+
+ObjCDeallocChecker::ObjCDeallocChecker()
+ : NSObjectII(nullptr), SenTestCaseII(nullptr), XCTestCaseII(nullptr),
+ CIFilterII(nullptr) {
+
+ MissingReleaseBugType.reset(
+ new BugType(this, "Missing ivar release (leak)",
+ categories::MemoryRefCount));
+
+ ExtraReleaseBugType.reset(
+ new BugType(this, "Extra ivar release",
+ categories::MemoryRefCount));
+
+ MistakenDeallocBugType.reset(
+ new BugType(this, "Mistaken dealloc",
+ categories::MemoryRefCount));
+}
+
+void ObjCDeallocChecker::initIdentifierInfoAndSelectors(
+ ASTContext &Ctx) const {
+ if (NSObjectII)
+ return;
+
+ NSObjectII = &Ctx.Idents.get("NSObject");
+ SenTestCaseII = &Ctx.Idents.get("SenTestCase");
+ XCTestCaseII = &Ctx.Idents.get("XCTestCase");
+ Block_releaseII = &Ctx.Idents.get("_Block_release");
+ CIFilterII = &Ctx.Idents.get("CIFilter");
+
+ IdentifierInfo *DeallocII = &Ctx.Idents.get("dealloc");
+ IdentifierInfo *ReleaseII = &Ctx.Idents.get("release");
+ DeallocSel = Ctx.Selectors.getSelector(0, &DeallocII);
+ ReleaseSel = Ctx.Selectors.getSelector(0, &ReleaseII);
+}
+
+/// Returns true if M is a call to '[super dealloc]'.
+bool ObjCDeallocChecker::isSuperDeallocMessage(
+ const ObjCMethodCall &M) const {
+ if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
+ return false;
+
+ return M.getSelector() == DeallocSel;
+}
+
+/// Returns the ObjCImplDecl containing the method declaration in LCtx.
+const ObjCImplDecl *
+ObjCDeallocChecker::getContainingObjCImpl(const LocationContext *LCtx) const {
+ auto *MD = cast<ObjCMethodDecl>(LCtx->getDecl());
+ return cast<ObjCImplDecl>(MD->getDeclContext());
+}
+
+/// Returns the property that shadowed by PropImpl if one exists and
+/// nullptr otherwise.
+const ObjCPropertyDecl *ObjCDeallocChecker::findShadowedPropertyDecl(
+ const ObjCPropertyImplDecl *PropImpl) const {
+ const ObjCPropertyDecl *PropDecl = PropImpl->getPropertyDecl();
+
+ // Only readwrite properties can shadow.
+ if (PropDecl->isReadOnly())
+ return nullptr;
+
+ auto *CatDecl = dyn_cast<ObjCCategoryDecl>(PropDecl->getDeclContext());
+
+ // Only class extensions can contain shadowing properties.
+ if (!CatDecl || !CatDecl->IsClassExtension())
+ return nullptr;
+
+ IdentifierInfo *ID = PropDecl->getIdentifier();
+ DeclContext::lookup_result R = CatDecl->getClassInterface()->lookup(ID);
+ for (DeclContext::lookup_iterator I = R.begin(), E = R.end(); I != E; ++I) {
+ auto *ShadowedPropDecl = dyn_cast<ObjCPropertyDecl>(*I);
+ if (!ShadowedPropDecl)
+ continue;
+
+ if (ShadowedPropDecl->isInstanceProperty()) {
+ assert(ShadowedPropDecl->isReadOnly());
+ return ShadowedPropDecl;
+ }
+ }
+
+ return nullptr;
+}
+
+/// Add a transition noting the release of the given value.
+void ObjCDeallocChecker::transitionToReleaseValue(CheckerContext &C,
+ SymbolRef Value) const {
+ assert(Value);
+ SymbolRef InstanceSym = getInstanceSymbolFromIvarSymbol(Value);
+ if (!InstanceSym)
+ return;
+ ProgramStateRef InitialState = C.getState();
+
+ ProgramStateRef ReleasedState =
+ removeValueRequiringRelease(InitialState, InstanceSym, Value);
+
+ if (ReleasedState != InitialState) {
+ C.addTransition(ReleasedState);
+ }
+}
+
+/// Remove the Value requiring a release from the tracked set for
+/// Instance and return the resultant state.
+ProgramStateRef ObjCDeallocChecker::removeValueRequiringRelease(
+ ProgramStateRef State, SymbolRef Instance, SymbolRef Value) const {
+ assert(Instance);
+ assert(Value);
+ const ObjCIvarRegion *RemovedRegion = getIvarRegionForIvarSymbol(Value);
+ if (!RemovedRegion)
+ return State;
+
+ const SymbolSet *Unreleased = State->get<UnreleasedIvarMap>(Instance);
+ if (!Unreleased)
+ return State;
+
+ // Mark the value as no longer requiring a release.
+ SymbolSet::Factory &F = State->getStateManager().get_context<SymbolSet>();
+ SymbolSet NewUnreleased = *Unreleased;
+ for (auto &Sym : *Unreleased) {
+ const ObjCIvarRegion *UnreleasedRegion = getIvarRegionForIvarSymbol(Sym);
+ assert(UnreleasedRegion);
+ if (RemovedRegion->getDecl() == UnreleasedRegion->getDecl()) {
+ NewUnreleased = F.remove(NewUnreleased, Sym);
+ }
+ }
+
+ if (NewUnreleased.isEmpty()) {
+ return State->remove<UnreleasedIvarMap>(Instance);
+ }
+
+ return State->set<UnreleasedIvarMap>(Instance, NewUnreleased);
+}
+
+/// Determines whether the instance variable for \p PropImpl must or must not be
+/// released in -dealloc or whether it cannot be determined.
+ReleaseRequirement ObjCDeallocChecker::getDeallocReleaseRequirement(
+ const ObjCPropertyImplDecl *PropImpl) const {
+ const ObjCIvarDecl *IvarDecl;
+ const ObjCPropertyDecl *PropDecl;
+ if (!isSynthesizedRetainableProperty(PropImpl, &IvarDecl, &PropDecl))
+ return ReleaseRequirement::Unknown;
+
+ ObjCPropertyDecl::SetterKind SK = PropDecl->getSetterKind();
+
+ switch (SK) {
+ // Retain and copy setters retain/copy their values before storing and so
+ // the value in their instance variables must be released in -dealloc.
+ case ObjCPropertyDecl::Retain:
+ case ObjCPropertyDecl::Copy:
+ if (isReleasedByCIFilterDealloc(PropImpl))
+ return ReleaseRequirement::MustNotReleaseDirectly;
+
+ if (isNibLoadedIvarWithoutRetain(PropImpl))
+ return ReleaseRequirement::Unknown;
+
+ return ReleaseRequirement::MustRelease;
+
+ case ObjCPropertyDecl::Weak:
+ return ReleaseRequirement::MustNotReleaseDirectly;
+
+ case ObjCPropertyDecl::Assign:
+ // It is common for the ivars for read-only assign properties to
+ // always be stored retained, so their release requirement cannot be
+ // be determined.
+ if (PropDecl->isReadOnly())
+ return ReleaseRequirement::Unknown;
+
+ return ReleaseRequirement::MustNotReleaseDirectly;
+ }
+ llvm_unreachable("Unrecognized setter kind");
+}
+
+/// Returns the released value if M is a call a setter that releases
+/// and nils out its underlying instance variable.
+SymbolRef
+ObjCDeallocChecker::getValueReleasedByNillingOut(const ObjCMethodCall &M,
+ CheckerContext &C) const {
+ SVal ReceiverVal = M.getReceiverSVal();
+ if (!ReceiverVal.isValid())
+ return nullptr;
+
+ if (M.getNumArgs() == 0)
+ return nullptr;
+
+ if (!M.getArgExpr(0)->getType()->isObjCRetainableType())
+ return nullptr;
+
+ // Is the first argument nil?
+ SVal Arg = M.getArgSVal(0);
+ ProgramStateRef notNilState, nilState;
+ std::tie(notNilState, nilState) =
+ M.getState()->assume(Arg.castAs<DefinedOrUnknownSVal>());
+ if (!(nilState && !notNilState))
+ return nullptr;
+
+ const ObjCPropertyDecl *Prop = M.getAccessedProperty();
+ if (!Prop)
+ return nullptr;
+
+ ObjCIvarDecl *PropIvarDecl = Prop->getPropertyIvarDecl();
+ if (!PropIvarDecl)
+ return nullptr;
+
+ ProgramStateRef State = C.getState();
+
+ SVal LVal = State->getLValue(PropIvarDecl, ReceiverVal);
+ Optional<Loc> LValLoc = LVal.getAs<Loc>();
+ if (!LValLoc)
+ return nullptr;
+
+ SVal CurrentValInIvar = State->getSVal(LValLoc.getValue());
+ return CurrentValInIvar.getAsSymbol();
+}
+
+/// Returns true if the current context is a call to -dealloc and false
+/// otherwise. If true, it also sets SelfValOut to the value of
+/// 'self'.
+bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
+ SVal &SelfValOut) const {
+ return isInInstanceDealloc(C, C.getLocationContext(), SelfValOut);
+}
+
+/// Returns true if LCtx is a call to -dealloc and false
+/// otherwise. If true, it also sets SelfValOut to the value of
+/// 'self'.
+bool ObjCDeallocChecker::isInInstanceDealloc(const CheckerContext &C,
+ const LocationContext *LCtx,
+ SVal &SelfValOut) const {
+ auto *MD = dyn_cast<ObjCMethodDecl>(LCtx->getDecl());
+ if (!MD || !MD->isInstanceMethod() || MD->getSelector() != DeallocSel)
+ return false;
+
+ const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
+ assert(SelfDecl && "No self in -dealloc?");
+
+ ProgramStateRef State = C.getState();
+ SelfValOut = State->getSVal(State->getRegion(SelfDecl, LCtx));
+ return true;
+}
+
+/// Returns true if there is a call to -dealloc anywhere on the stack and false
+/// otherwise. If true, it also sets InstanceValOut to the value of
+/// 'self' in the frame for -dealloc.
+bool ObjCDeallocChecker::instanceDeallocIsOnStack(const CheckerContext &C,
+ SVal &InstanceValOut) const {
+ const LocationContext *LCtx = C.getLocationContext();
+
+ while (LCtx) {
+ if (isInInstanceDealloc(C, LCtx, InstanceValOut))
+ return true;
+
+ LCtx = LCtx->getParent();
+ }
+
+ return false;
+}
+
+/// Returns true if the ID is a class in which which is known to have
+/// a separate teardown lifecycle. In this case, -dealloc warnings
+/// about missing releases should be suppressed.
+bool ObjCDeallocChecker::classHasSeparateTeardown(
+ const ObjCInterfaceDecl *ID) const {
+ // Suppress if the class is not a subclass of NSObject.
+ for ( ; ID ; ID = ID->getSuperClass()) {
+ IdentifierInfo *II = ID->getIdentifier();
+
+ if (II == NSObjectII)
+ return false;
+
+ // FIXME: For now, ignore classes that subclass SenTestCase and XCTestCase,
+ // as these don't need to implement -dealloc. They implement tear down in
+ // another way, which we should try and catch later.
+ // http://llvm.org/bugs/show_bug.cgi?id=3187
+ if (II == XCTestCaseII || II == SenTestCaseII)
+ return true;
+ }
+
+ return true;
+}
+
+/// The -dealloc method in CIFilter highly unusual in that is will release
+/// instance variables belonging to its *subclasses* if the variable name
+/// starts with "input" or backs a property whose name starts with "input".
+/// Subclasses should not release these ivars in their own -dealloc method --
+/// doing so could result in an over release.
+///
+/// This method returns true if the property will be released by
+/// -[CIFilter dealloc].
+bool ObjCDeallocChecker::isReleasedByCIFilterDealloc(
+ const ObjCPropertyImplDecl *PropImpl) const {
+ assert(PropImpl->getPropertyIvarDecl());
+ StringRef PropName = PropImpl->getPropertyDecl()->getName();
+ StringRef IvarName = PropImpl->getPropertyIvarDecl()->getName();
+
+ const char *ReleasePrefix = "input";
+ if (!(PropName.startswith(ReleasePrefix) ||
+ IvarName.startswith(ReleasePrefix))) {
+ return false;
+ }
+
+ const ObjCInterfaceDecl *ID =
+ PropImpl->getPropertyIvarDecl()->getContainingInterface();
+ for ( ; ID ; ID = ID->getSuperClass()) {
+ IdentifierInfo *II = ID->getIdentifier();
+ if (II == CIFilterII)
+ return true;
+ }
+
+ return false;
+}
+
+/// Returns whether the ivar backing the property is an IBOutlet that
+/// has its value set by nib loading code without retaining the value.
+///
+/// On macOS, if there is no setter, the nib-loading code sets the ivar
+/// directly, without retaining the value,
+///
+/// On iOS and its derivatives, the nib-loading code will call
+/// -setValue:forKey:, which retains the value before directly setting the ivar.
+bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
+ const ObjCPropertyImplDecl *PropImpl) const {
+ const ObjCIvarDecl *IvarDecl = PropImpl->getPropertyIvarDecl();
+ if (!IvarDecl->hasAttr<IBOutletAttr>())
+ return false;
+
+ const llvm::Triple &Target =
+ IvarDecl->getASTContext().getTargetInfo().getTriple();
+
+ if (!Target.isMacOSX())
+ return false;
+
+ if (PropImpl->getPropertyDecl()->getSetterMethodDecl())
+ return false;
+
+ return true;
+}
+
+void ento::registerObjCDeallocChecker(CheckerManager &Mgr) {
+ const LangOptions &LangOpts = Mgr.getLangOpts();
+ // These checker only makes sense under MRR.
+ if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount)
+ return;
+
+ Mgr.registerChecker<ObjCDeallocChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
new file mode 100644
index 000000000000..fe6715595e6f
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
@@ -0,0 +1,140 @@
+//=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a CheckObjCInstMethSignature, a flow-insenstive check
+// that determines if an Objective-C class interface incorrectly redefines
+// the method signature in a subclass.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Type.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+static bool AreTypesCompatible(QualType Derived, QualType Ancestor,
+ ASTContext &C) {
+
+ // Right now don't compare the compatibility of pointers. That involves
+ // looking at subtyping relationships. FIXME: Future patch.
+ if (Derived->isAnyPointerType() && Ancestor->isAnyPointerType())
+ return true;
+
+ return C.typesAreCompatible(Derived, Ancestor);
+}
+
+static void CompareReturnTypes(const ObjCMethodDecl *MethDerived,
+ const ObjCMethodDecl *MethAncestor,
+ BugReporter &BR, ASTContext &Ctx,
+ const ObjCImplementationDecl *ID,
+ const CheckerBase *Checker) {
+
+ QualType ResDerived = MethDerived->getReturnType();
+ QualType ResAncestor = MethAncestor->getReturnType();
+
+ if (!AreTypesCompatible(ResDerived, ResAncestor, Ctx)) {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+
+ os << "The Objective-C class '"
+ << *MethDerived->getClassInterface()
+ << "', which is derived from class '"
+ << *MethAncestor->getClassInterface()
+ << "', defines the instance method '";
+ MethDerived->getSelector().print(os);
+ os << "' whose return type is '"
+ << ResDerived.getAsString()
+ << "'. A method with the same name (same selector) is also defined in "
+ "class '"
+ << *MethAncestor->getClassInterface()
+ << "' and has a return type of '"
+ << ResAncestor.getAsString()
+ << "'. These two types are incompatible, and may result in undefined "
+ "behavior for clients of these classes.";
+
+ PathDiagnosticLocation MethDLoc =
+ PathDiagnosticLocation::createBegin(MethDerived,
+ BR.getSourceManager());
+
+ BR.EmitBasicReport(
+ MethDerived, Checker, "Incompatible instance method return type",
+ categories::CoreFoundationObjectiveC, os.str(), MethDLoc);
+ }
+}
+
+static void CheckObjCInstMethSignature(const ObjCImplementationDecl *ID,
+ BugReporter &BR,
+ const CheckerBase *Checker) {
+
+ const ObjCInterfaceDecl *D = ID->getClassInterface();
+ const ObjCInterfaceDecl *C = D->getSuperClass();
+
+ if (!C)
+ return;
+
+ ASTContext &Ctx = BR.getContext();
+
+ // Build a DenseMap of the methods for quick querying.
+ typedef llvm::DenseMap<Selector,ObjCMethodDecl*> MapTy;
+ MapTy IMeths;
+ unsigned NumMethods = 0;
+
+ for (auto *M : ID->instance_methods()) {
+ IMeths[M->getSelector()] = M;
+ ++NumMethods;
+ }
+
+ // Now recurse the class hierarchy chain looking for methods with the
+ // same signatures.
+ while (C && NumMethods) {
+ for (const auto *M : C->instance_methods()) {
+ Selector S = M->getSelector();
+
+ MapTy::iterator MI = IMeths.find(S);
+
+ if (MI == IMeths.end() || MI->second == nullptr)
+ continue;
+
+ --NumMethods;
+ ObjCMethodDecl *MethDerived = MI->second;
+ MI->second = nullptr;
+
+ CompareReturnTypes(MethDerived, M, BR, Ctx, ID, Checker);
+ }
+
+ C = C->getSuperClass();
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// ObjCMethSigsChecker
+//===----------------------------------------------------------------------===//
+
+namespace {
+class ObjCMethSigsChecker : public Checker<
+ check::ASTDecl<ObjCImplementationDecl> > {
+public:
+ void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ CheckObjCInstMethSignature(D, BR, this);
+ }
+};
+}
+
+void ento::registerObjCMethSigsChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ObjCMethSigsChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
new file mode 100644
index 000000000000..163ca9d8556f
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -0,0 +1,930 @@
+//==- CheckSecuritySyntaxOnly.cpp - Basic security checks --------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a set of flow-insensitive security checks.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+static bool isArc4RandomAvailable(const ASTContext &Ctx) {
+ const llvm::Triple &T = Ctx.getTargetInfo().getTriple();
+ return T.getVendor() == llvm::Triple::Apple ||
+ T.getOS() == llvm::Triple::CloudABI ||
+ T.isOSFreeBSD() ||
+ T.isOSNetBSD() ||
+ T.isOSOpenBSD() ||
+ T.isOSDragonFly();
+}
+
+namespace {
+struct ChecksFilter {
+ DefaultBool check_bcmp;
+ DefaultBool check_bcopy;
+ DefaultBool check_bzero;
+ DefaultBool check_gets;
+ DefaultBool check_getpw;
+ DefaultBool check_mktemp;
+ DefaultBool check_mkstemp;
+ DefaultBool check_strcpy;
+ DefaultBool check_rand;
+ DefaultBool check_vfork;
+ DefaultBool check_FloatLoopCounter;
+ DefaultBool check_UncheckedReturn;
+
+ CheckName checkName_bcmp;
+ CheckName checkName_bcopy;
+ CheckName checkName_bzero;
+ CheckName checkName_gets;
+ CheckName checkName_getpw;
+ CheckName checkName_mktemp;
+ CheckName checkName_mkstemp;
+ CheckName checkName_strcpy;
+ CheckName checkName_rand;
+ CheckName checkName_vfork;
+ CheckName checkName_FloatLoopCounter;
+ CheckName checkName_UncheckedReturn;
+};
+
+class WalkAST : public StmtVisitor<WalkAST> {
+ BugReporter &BR;
+ AnalysisDeclContext* AC;
+ enum { num_setids = 6 };
+ IdentifierInfo *II_setid[num_setids];
+
+ const bool CheckRand;
+ const ChecksFilter &filter;
+
+public:
+ WalkAST(BugReporter &br, AnalysisDeclContext* ac,
+ const ChecksFilter &f)
+ : BR(br), AC(ac), II_setid(),
+ CheckRand(isArc4RandomAvailable(BR.getContext())),
+ filter(f) {}
+
+ // Statement visitor methods.
+ void VisitCallExpr(CallExpr *CE);
+ void VisitForStmt(ForStmt *S);
+ void VisitCompoundStmt (CompoundStmt *S);
+ void VisitStmt(Stmt *S) { VisitChildren(S); }
+
+ void VisitChildren(Stmt *S);
+
+ // Helpers.
+ bool checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD);
+
+ typedef void (WalkAST::*FnCheck)(const CallExpr *, const FunctionDecl *);
+
+ // Checker-specific methods.
+ void checkLoopConditionForFloat(const ForStmt *FS);
+ void checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_gets(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_rand(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_random(const CallExpr *CE, const FunctionDecl *FD);
+ void checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD);
+ void checkUncheckedReturnValue(CallExpr *CE);
+};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// AST walking.
+//===----------------------------------------------------------------------===//
+
+void WalkAST::VisitChildren(Stmt *S) {
+ for (Stmt *Child : S->children())
+ if (Child)
+ Visit(Child);
+}
+
+void WalkAST::VisitCallExpr(CallExpr *CE) {
+ // Get the callee.
+ const FunctionDecl *FD = CE->getDirectCallee();
+
+ if (!FD)
+ return;
+
+ // Get the name of the callee. If it's a builtin, strip off the prefix.
+ IdentifierInfo *II = FD->getIdentifier();
+ if (!II) // if no identifier, not a simple C function
+ return;
+ StringRef Name = II->getName();
+ if (Name.startswith("__builtin_"))
+ Name = Name.substr(10);
+
+ // Set the evaluation function by switching on the callee name.
+ FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
+ .Case("bcmp", &WalkAST::checkCall_bcmp)
+ .Case("bcopy", &WalkAST::checkCall_bcopy)
+ .Case("bzero", &WalkAST::checkCall_bzero)
+ .Case("gets", &WalkAST::checkCall_gets)
+ .Case("getpw", &WalkAST::checkCall_getpw)
+ .Case("mktemp", &WalkAST::checkCall_mktemp)
+ .Case("mkstemp", &WalkAST::checkCall_mkstemp)
+ .Case("mkdtemp", &WalkAST::checkCall_mkstemp)
+ .Case("mkstemps", &WalkAST::checkCall_mkstemp)
+ .Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
+ .Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
+ .Case("drand48", &WalkAST::checkCall_rand)
+ .Case("erand48", &WalkAST::checkCall_rand)
+ .Case("jrand48", &WalkAST::checkCall_rand)
+ .Case("lrand48", &WalkAST::checkCall_rand)
+ .Case("mrand48", &WalkAST::checkCall_rand)
+ .Case("nrand48", &WalkAST::checkCall_rand)
+ .Case("lcong48", &WalkAST::checkCall_rand)
+ .Case("rand", &WalkAST::checkCall_rand)
+ .Case("rand_r", &WalkAST::checkCall_rand)
+ .Case("random", &WalkAST::checkCall_random)
+ .Case("vfork", &WalkAST::checkCall_vfork)
+ .Default(nullptr);
+
+ // If the callee isn't defined, it is not of security concern.
+ // Check and evaluate the call.
+ if (evalFunction)
+ (this->*evalFunction)(CE, FD);
+
+ // Recurse and check children.
+ VisitChildren(CE);
+}
+
+void WalkAST::VisitCompoundStmt(CompoundStmt *S) {
+ for (Stmt *Child : S->children())
+ if (Child) {
+ if (CallExpr *CE = dyn_cast<CallExpr>(Child))
+ checkUncheckedReturnValue(CE);
+ Visit(Child);
+ }
+}
+
+void WalkAST::VisitForStmt(ForStmt *FS) {
+ checkLoopConditionForFloat(FS);
+
+ // Recurse and check children.
+ VisitChildren(FS);
+}
+
+//===----------------------------------------------------------------------===//
+// Check: floating point variable used as loop counter.
+// Originally: <rdar://problem/6336718>
+// Implements: CERT security coding advisory FLP-30.
+//===----------------------------------------------------------------------===//
+
+static const DeclRefExpr*
+getIncrementedVar(const Expr *expr, const VarDecl *x, const VarDecl *y) {
+ expr = expr->IgnoreParenCasts();
+
+ if (const BinaryOperator *B = dyn_cast<BinaryOperator>(expr)) {
+ if (!(B->isAssignmentOp() || B->isCompoundAssignmentOp() ||
+ B->getOpcode() == BO_Comma))
+ return nullptr;
+
+ if (const DeclRefExpr *lhs = getIncrementedVar(B->getLHS(), x, y))
+ return lhs;
+
+ if (const DeclRefExpr *rhs = getIncrementedVar(B->getRHS(), x, y))
+ return rhs;
+
+ return nullptr;
+ }
+
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(expr)) {
+ const NamedDecl *ND = DR->getDecl();
+ return ND == x || ND == y ? DR : nullptr;
+ }
+
+ if (const UnaryOperator *U = dyn_cast<UnaryOperator>(expr))
+ return U->isIncrementDecrementOp()
+ ? getIncrementedVar(U->getSubExpr(), x, y) : nullptr;
+
+ return nullptr;
+}
+
+/// CheckLoopConditionForFloat - This check looks for 'for' statements that
+/// use a floating point variable as a loop counter.
+/// CERT: FLP30-C, FLP30-CPP.
+///
+void WalkAST::checkLoopConditionForFloat(const ForStmt *FS) {
+ if (!filter.check_FloatLoopCounter)
+ return;
+
+ // Does the loop have a condition?
+ const Expr *condition = FS->getCond();
+
+ if (!condition)
+ return;
+
+ // Does the loop have an increment?
+ const Expr *increment = FS->getInc();
+
+ if (!increment)
+ return;
+
+ // Strip away '()' and casts.
+ condition = condition->IgnoreParenCasts();
+ increment = increment->IgnoreParenCasts();
+
+ // Is the loop condition a comparison?
+ const BinaryOperator *B = dyn_cast<BinaryOperator>(condition);
+
+ if (!B)
+ return;
+
+ // Is this a comparison?
+ if (!(B->isRelationalOp() || B->isEqualityOp()))
+ return;
+
+ // Are we comparing variables?
+ const DeclRefExpr *drLHS =
+ dyn_cast<DeclRefExpr>(B->getLHS()->IgnoreParenLValueCasts());
+ const DeclRefExpr *drRHS =
+ dyn_cast<DeclRefExpr>(B->getRHS()->IgnoreParenLValueCasts());
+
+ // Does at least one of the variables have a floating point type?
+ drLHS = drLHS && drLHS->getType()->isRealFloatingType() ? drLHS : nullptr;
+ drRHS = drRHS && drRHS->getType()->isRealFloatingType() ? drRHS : nullptr;
+
+ if (!drLHS && !drRHS)
+ return;
+
+ const VarDecl *vdLHS = drLHS ? dyn_cast<VarDecl>(drLHS->getDecl()) : nullptr;
+ const VarDecl *vdRHS = drRHS ? dyn_cast<VarDecl>(drRHS->getDecl()) : nullptr;
+
+ if (!vdLHS && !vdRHS)
+ return;
+
+ // Does either variable appear in increment?
+ const DeclRefExpr *drInc = getIncrementedVar(increment, vdLHS, vdRHS);
+
+ if (!drInc)
+ return;
+
+ // Emit the error. First figure out which DeclRefExpr in the condition
+ // referenced the compared variable.
+ assert(drInc->getDecl());
+ const DeclRefExpr *drCond = vdLHS == drInc->getDecl() ? drLHS : drRHS;
+
+ SmallVector<SourceRange, 2> ranges;
+ SmallString<256> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+
+ os << "Variable '" << drCond->getDecl()->getName()
+ << "' with floating point type '" << drCond->getType().getAsString()
+ << "' should not be used as a loop counter";
+
+ ranges.push_back(drCond->getSourceRange());
+ ranges.push_back(drInc->getSourceRange());
+
+ const char *bugType = "Floating point variable used as loop counter";
+
+ PathDiagnosticLocation FSLoc =
+ PathDiagnosticLocation::createBegin(FS, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_FloatLoopCounter,
+ bugType, "Security", os.str(),
+ FSLoc, ranges);
+}
+
+//===----------------------------------------------------------------------===//
+// Check: Any use of bcmp.
+// CWE-477: Use of Obsolete Functions
+// bcmp was deprecated in POSIX.1-2008
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_bcmp(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_bcmp)
+ return;
+
+ const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
+ if (!FPT)
+ return;
+
+ // Verify that the function takes three arguments.
+ if (FPT->getNumParams() != 3)
+ return;
+
+ for (int i = 0; i < 2; i++) {
+ // Verify the first and second argument type is void*.
+ const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
+ if (!PT)
+ return;
+
+ if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
+ return;
+ }
+
+ // Verify the third argument type is integer.
+ if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcmp,
+ "Use of deprecated function in call to 'bcmp()'",
+ "Security",
+ "The bcmp() function is obsoleted by memcmp().",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Check: Any use of bcopy.
+// CWE-477: Use of Obsolete Functions
+// bcopy was deprecated in POSIX.1-2008
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_bcopy(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_bcopy)
+ return;
+
+ const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
+ if (!FPT)
+ return;
+
+ // Verify that the function takes three arguments.
+ if (FPT->getNumParams() != 3)
+ return;
+
+ for (int i = 0; i < 2; i++) {
+ // Verify the first and second argument type is void*.
+ const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
+ if (!PT)
+ return;
+
+ if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
+ return;
+ }
+
+ // Verify the third argument type is integer.
+ if (!FPT->getParamType(2)->isIntegralOrUnscopedEnumerationType())
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_bcopy,
+ "Use of deprecated function in call to 'bcopy()'",
+ "Security",
+ "The bcopy() function is obsoleted by memcpy() "
+ "or memmove().",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Check: Any use of bzero.
+// CWE-477: Use of Obsolete Functions
+// bzero was deprecated in POSIX.1-2008
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_bzero(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_bzero)
+ return;
+
+ const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
+ if (!FPT)
+ return;
+
+ // Verify that the function takes two arguments.
+ if (FPT->getNumParams() != 2)
+ return;
+
+ // Verify the first argument type is void*.
+ const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
+ if (!PT)
+ return;
+
+ if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().VoidTy)
+ return;
+
+ // Verify the second argument type is integer.
+ if (!FPT->getParamType(1)->isIntegralOrUnscopedEnumerationType())
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_bzero,
+ "Use of deprecated function in call to 'bzero()'",
+ "Security",
+ "The bzero() function is obsoleted by memset().",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+
+//===----------------------------------------------------------------------===//
+// Check: Any use of 'gets' is insecure.
+// Originally: <rdar://problem/6335715>
+// Implements (part of): 300-BSI (buildsecurityin.us-cert.gov)
+// CWE-242: Use of Inherently Dangerous Function
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_gets(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_gets)
+ return;
+
+ const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
+ if (!FPT)
+ return;
+
+ // Verify that the function takes a single argument.
+ if (FPT->getNumParams() != 1)
+ return;
+
+ // Is the argument a 'char*'?
+ const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
+ if (!PT)
+ return;
+
+ if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_gets,
+ "Potential buffer overflow in call to 'gets'",
+ "Security",
+ "Call to function 'gets' is extremely insecure as it can "
+ "always result in a buffer overflow",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Check: Any use of 'getpwd' is insecure.
+// CWE-477: Use of Obsolete Functions
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_getpw(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_getpw)
+ return;
+
+ const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
+ if (!FPT)
+ return;
+
+ // Verify that the function takes two arguments.
+ if (FPT->getNumParams() != 2)
+ return;
+
+ // Verify the first argument type is integer.
+ if (!FPT->getParamType(0)->isIntegralOrUnscopedEnumerationType())
+ return;
+
+ // Verify the second argument type is char*.
+ const PointerType *PT = FPT->getParamType(1)->getAs<PointerType>();
+ if (!PT)
+ return;
+
+ if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_getpw,
+ "Potential buffer overflow in call to 'getpw'",
+ "Security",
+ "The getpw() function is dangerous as it may overflow the "
+ "provided buffer. It is obsoleted by getpwuid().",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Check: Any use of 'mktemp' is insecure. It is obsoleted by mkstemp().
+// CWE-377: Insecure Temporary File
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_mktemp) {
+ // Fall back to the security check of looking for enough 'X's in the
+ // format string, since that is a less severe warning.
+ checkCall_mkstemp(CE, FD);
+ return;
+ }
+
+ const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
+ if(!FPT)
+ return;
+
+ // Verify that the function takes a single argument.
+ if (FPT->getNumParams() != 1)
+ return;
+
+ // Verify that the argument is Pointer Type.
+ const PointerType *PT = FPT->getParamType(0)->getAs<PointerType>();
+ if (!PT)
+ return;
+
+ // Verify that the argument is a 'char*'.
+ if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_mktemp,
+ "Potential insecure temporary file in call 'mktemp'",
+ "Security",
+ "Call to function 'mktemp' is insecure as it always "
+ "creates or uses insecure temporary file. Use 'mkstemp' "
+ "instead",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+
+//===----------------------------------------------------------------------===//
+// Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_mkstemp)
+ return;
+
+ StringRef Name = FD->getIdentifier()->getName();
+ std::pair<signed, signed> ArgSuffix =
+ llvm::StringSwitch<std::pair<signed, signed> >(Name)
+ .Case("mktemp", std::make_pair(0,-1))
+ .Case("mkstemp", std::make_pair(0,-1))
+ .Case("mkdtemp", std::make_pair(0,-1))
+ .Case("mkstemps", std::make_pair(0,1))
+ .Default(std::make_pair(-1, -1));
+
+ assert(ArgSuffix.first >= 0 && "Unsupported function");
+
+ // Check if the number of arguments is consistent with out expectations.
+ unsigned numArgs = CE->getNumArgs();
+ if ((signed) numArgs <= ArgSuffix.first)
+ return;
+
+ const StringLiteral *strArg =
+ dyn_cast<StringLiteral>(CE->getArg((unsigned)ArgSuffix.first)
+ ->IgnoreParenImpCasts());
+
+ // Currently we only handle string literals. It is possible to do better,
+ // either by looking at references to const variables, or by doing real
+ // flow analysis.
+ if (!strArg || strArg->getCharByteWidth() != 1)
+ return;
+
+ // Count the number of X's, taking into account a possible cutoff suffix.
+ StringRef str = strArg->getString();
+ unsigned numX = 0;
+ unsigned n = str.size();
+
+ // Take into account the suffix.
+ unsigned suffix = 0;
+ if (ArgSuffix.second >= 0) {
+ const Expr *suffixEx = CE->getArg((unsigned)ArgSuffix.second);
+ Expr::EvalResult EVResult;
+ if (!suffixEx->EvaluateAsInt(EVResult, BR.getContext()))
+ return;
+ llvm::APSInt Result = EVResult.Val.getInt();
+ // FIXME: Issue a warning.
+ if (Result.isNegative())
+ return;
+ suffix = (unsigned) Result.getZExtValue();
+ n = (n > suffix) ? n - suffix : 0;
+ }
+
+ for (unsigned i = 0; i < n; ++i)
+ if (str[i] == 'X') ++numX;
+
+ if (numX >= 6)
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ SmallString<512> buf;
+ llvm::raw_svector_ostream out(buf);
+ out << "Call to '" << Name << "' should have at least 6 'X's in the"
+ " format string to be secure (" << numX << " 'X'";
+ if (numX != 1)
+ out << 's';
+ out << " seen";
+ if (suffix) {
+ out << ", " << suffix << " character";
+ if (suffix > 1)
+ out << 's';
+ out << " used as a suffix";
+ }
+ out << ')';
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_mkstemp,
+ "Insecure temporary file creation", "Security",
+ out.str(), CELoc, strArg->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Check: Any use of 'strcpy' is insecure.
+//
+// CWE-119: Improper Restriction of Operations within
+// the Bounds of a Memory Buffer
+//===----------------------------------------------------------------------===//
+void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_strcpy)
+ return;
+
+ if (!checkCall_strCommon(CE, FD))
+ return;
+
+ const auto *Target = CE->getArg(0)->IgnoreImpCasts(),
+ *Source = CE->getArg(1)->IgnoreImpCasts();
+
+ if (const auto *Array = dyn_cast<ConstantArrayType>(Target->getType())) {
+ uint64_t ArraySize = BR.getContext().getTypeSize(Array) / 8;
+ if (const auto *String = dyn_cast<StringLiteral>(Source)) {
+ if (ArraySize >= String->getLength() + 1)
+ return;
+ }
+ }
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
+ "Potential insecure memory buffer bounds restriction in "
+ "call 'strcpy'",
+ "Security",
+ "Call to function 'strcpy' is insecure as it does not "
+ "provide bounding of the memory buffer. Replace "
+ "unbounded copy functions with analogous functions that "
+ "support length arguments such as 'strlcpy'. CWE-119.",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Check: Any use of 'strcat' is insecure.
+//
+// CWE-119: Improper Restriction of Operations within
+// the Bounds of a Memory Buffer
+//===----------------------------------------------------------------------===//
+void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_strcpy)
+ return;
+
+ if (!checkCall_strCommon(CE, FD))
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_strcpy,
+ "Potential insecure memory buffer bounds restriction in "
+ "call 'strcat'",
+ "Security",
+ "Call to function 'strcat' is insecure as it does not "
+ "provide bounding of the memory buffer. Replace "
+ "unbounded copy functions with analogous functions that "
+ "support length arguments such as 'strlcat'. CWE-119.",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Common check for str* functions with no bounds parameters.
+//===----------------------------------------------------------------------===//
+bool WalkAST::checkCall_strCommon(const CallExpr *CE, const FunctionDecl *FD) {
+ const FunctionProtoType *FPT = FD->getType()->getAs<FunctionProtoType>();
+ if (!FPT)
+ return false;
+
+ // Verify the function takes two arguments, three in the _chk version.
+ int numArgs = FPT->getNumParams();
+ if (numArgs != 2 && numArgs != 3)
+ return false;
+
+ // Verify the type for both arguments.
+ for (int i = 0; i < 2; i++) {
+ // Verify that the arguments are pointers.
+ const PointerType *PT = FPT->getParamType(i)->getAs<PointerType>();
+ if (!PT)
+ return false;
+
+ // Verify that the argument is a 'char*'.
+ if (PT->getPointeeType().getUnqualifiedType() != BR.getContext().CharTy)
+ return false;
+ }
+
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Check: Linear congruent random number generators should not be used
+// Originally: <rdar://problem/63371000>
+// CWE-338: Use of cryptographically weak prng
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_rand(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_rand || !CheckRand)
+ return;
+
+ const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
+ if (!FTP)
+ return;
+
+ if (FTP->getNumParams() == 1) {
+ // Is the argument an 'unsigned short *'?
+ // (Actually any integer type is allowed.)
+ const PointerType *PT = FTP->getParamType(0)->getAs<PointerType>();
+ if (!PT)
+ return;
+
+ if (! PT->getPointeeType()->isIntegralOrUnscopedEnumerationType())
+ return;
+ } else if (FTP->getNumParams() != 0)
+ return;
+
+ // Issue a warning.
+ SmallString<256> buf1;
+ llvm::raw_svector_ostream os1(buf1);
+ os1 << '\'' << *FD << "' is a poor random number generator";
+
+ SmallString<256> buf2;
+ llvm::raw_svector_ostream os2(buf2);
+ os2 << "Function '" << *FD
+ << "' is obsolete because it implements a poor random number generator."
+ << " Use 'arc4random' instead";
+
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand, os1.str(),
+ "Security", os2.str(), CELoc,
+ CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Check: 'random' should not be used
+// Originally: <rdar://problem/63371000>
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_random(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!CheckRand || !filter.check_rand)
+ return;
+
+ const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
+ if (!FTP)
+ return;
+
+ // Verify that the function takes no argument.
+ if (FTP->getNumParams() != 0)
+ return;
+
+ // Issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_rand,
+ "'random' is not a secure random number generator",
+ "Security",
+ "The 'random' function produces a sequence of values that "
+ "an adversary may be able to predict. Use 'arc4random' "
+ "instead", CELoc, CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Check: 'vfork' should not be used.
+// POS33-C: Do not use vfork().
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkCall_vfork(const CallExpr *CE, const FunctionDecl *FD) {
+ if (!filter.check_vfork)
+ return;
+
+ // All calls to vfork() are insecure, issue a warning.
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_vfork,
+ "Potential insecure implementation-specific behavior in "
+ "call 'vfork'",
+ "Security",
+ "Call to function 'vfork' is insecure as it can lead to "
+ "denial of service situations in the parent process. "
+ "Replace calls to vfork with calls to the safer "
+ "'posix_spawn' function",
+ CELoc, CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// Check: Should check whether privileges are dropped successfully.
+// Originally: <rdar://problem/6337132>
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkUncheckedReturnValue(CallExpr *CE) {
+ if (!filter.check_UncheckedReturn)
+ return;
+
+ const FunctionDecl *FD = CE->getDirectCallee();
+ if (!FD)
+ return;
+
+ if (II_setid[0] == nullptr) {
+ static const char * const identifiers[num_setids] = {
+ "setuid", "setgid", "seteuid", "setegid",
+ "setreuid", "setregid"
+ };
+
+ for (size_t i = 0; i < num_setids; i++)
+ II_setid[i] = &BR.getContext().Idents.get(identifiers[i]);
+ }
+
+ const IdentifierInfo *id = FD->getIdentifier();
+ size_t identifierid;
+
+ for (identifierid = 0; identifierid < num_setids; identifierid++)
+ if (id == II_setid[identifierid])
+ break;
+
+ if (identifierid >= num_setids)
+ return;
+
+ const FunctionProtoType *FTP = FD->getType()->getAs<FunctionProtoType>();
+ if (!FTP)
+ return;
+
+ // Verify that the function takes one or two arguments (depending on
+ // the function).
+ if (FTP->getNumParams() != (identifierid < 4 ? 1 : 2))
+ return;
+
+ // The arguments must be integers.
+ for (unsigned i = 0; i < FTP->getNumParams(); i++)
+ if (!FTP->getParamType(i)->isIntegralOrUnscopedEnumerationType())
+ return;
+
+ // Issue a warning.
+ SmallString<256> buf1;
+ llvm::raw_svector_ostream os1(buf1);
+ os1 << "Return value is not checked in call to '" << *FD << '\'';
+
+ SmallString<256> buf2;
+ llvm::raw_svector_ostream os2(buf2);
+ os2 << "The return value from the call to '" << *FD
+ << "' is not checked. If an error occurs in '" << *FD
+ << "', the following code may execute with unexpected privileges";
+
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), filter.checkName_UncheckedReturn, os1.str(),
+ "Security", os2.str(), CELoc,
+ CE->getCallee()->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// SecuritySyntaxChecker
+//===----------------------------------------------------------------------===//
+
+namespace {
+class SecuritySyntaxChecker : public Checker<check::ASTCodeBody> {
+public:
+ ChecksFilter filter;
+
+ void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ WalkAST walker(BR, mgr.getAnalysisDeclContext(D), filter);
+ walker.Visit(D->getBody());
+ }
+};
+}
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ SecuritySyntaxChecker *checker = \
+ mgr.registerChecker<SecuritySyntaxChecker>(); \
+ checker->filter.check_##name = true; \
+ checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
+ }
+
+REGISTER_CHECKER(bcmp)
+REGISTER_CHECKER(bcopy)
+REGISTER_CHECKER(bzero)
+REGISTER_CHECKER(gets)
+REGISTER_CHECKER(getpw)
+REGISTER_CHECKER(mkstemp)
+REGISTER_CHECKER(mktemp)
+REGISTER_CHECKER(strcpy)
+REGISTER_CHECKER(rand)
+REGISTER_CHECKER(vfork)
+REGISTER_CHECKER(FloatLoopCounter)
+REGISTER_CHECKER(UncheckedReturn)
+
+
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
new file mode 100644
index 000000000000..7688b713b06b
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
@@ -0,0 +1,93 @@
+//==- CheckSizeofPointer.cpp - Check for sizeof on pointers ------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a check for unintended use of sizeof() on pointer
+// expressions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class WalkAST : public StmtVisitor<WalkAST> {
+ BugReporter &BR;
+ const CheckerBase *Checker;
+ AnalysisDeclContext* AC;
+
+public:
+ WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
+ : BR(br), Checker(checker), AC(ac) {}
+ void VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E);
+ void VisitStmt(Stmt *S) { VisitChildren(S); }
+ void VisitChildren(Stmt *S);
+};
+}
+
+void WalkAST::VisitChildren(Stmt *S) {
+ for (Stmt *Child : S->children())
+ if (Child)
+ Visit(Child);
+}
+
+// CWE-467: Use of sizeof() on a Pointer Type
+void WalkAST::VisitUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *E) {
+ if (E->getKind() != UETT_SizeOf)
+ return;
+
+ // If an explicit type is used in the code, usually the coder knows what they are
+ // doing.
+ if (E->isArgumentType())
+ return;
+
+ QualType T = E->getTypeOfArgument();
+ if (T->isPointerType()) {
+
+ // Many false positives have the form 'sizeof *p'. This is reasonable
+ // because people know what they are doing when they intentionally
+ // dereference the pointer.
+ Expr *ArgEx = E->getArgumentExpr();
+ if (!isa<DeclRefExpr>(ArgEx->IgnoreParens()))
+ return;
+
+ PathDiagnosticLocation ELoc =
+ PathDiagnosticLocation::createBegin(E, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), Checker,
+ "Potential unintended use of sizeof() on pointer type",
+ categories::LogicError,
+ "The code calls sizeof() on a pointer type. "
+ "This can produce an unexpected result.",
+ ELoc, ArgEx->getSourceRange());
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// SizeofPointerChecker
+//===----------------------------------------------------------------------===//
+
+namespace {
+class SizeofPointerChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ WalkAST walker(BR, this, mgr.getAnalysisDeclContext(D));
+ walker.Visit(D->getBody());
+ }
+};
+}
+
+void ento::registerSizeofPointerChecker(CheckerManager &mgr) {
+ mgr.registerChecker<SizeofPointerChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
new file mode 100644
index 000000000000..44fac0278bdd
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
@@ -0,0 +1,335 @@
+//===- CheckerDocumentation.cpp - Documentation checker ---------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker lists all the checker callbacks and provides documentation for
+// checker writers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+
+using namespace clang;
+using namespace ento;
+
+// All checkers should be placed into anonymous namespace.
+// We place the CheckerDocumentation inside ento namespace to make the
+// it visible in doxygen.
+namespace clang {
+namespace ento {
+
+/// This checker documents the callback functions checkers can use to implement
+/// the custom handling of the specific events during path exploration as well
+/// as reporting bugs. Most of the callbacks are targeted at path-sensitive
+/// checking.
+///
+/// \sa CheckerContext
+class CheckerDocumentation : public Checker< check::PreStmt<ReturnStmt>,
+ check::PostStmt<DeclStmt>,
+ check::PreObjCMessage,
+ check::PostObjCMessage,
+ check::ObjCMessageNil,
+ check::PreCall,
+ check::PostCall,
+ check::BranchCondition,
+ check::NewAllocator,
+ check::Location,
+ check::Bind,
+ check::DeadSymbols,
+ check::BeginFunction,
+ check::EndFunction,
+ check::EndAnalysis,
+ check::EndOfTranslationUnit,
+ eval::Call,
+ eval::Assume,
+ check::LiveSymbols,
+ check::RegionChanges,
+ check::PointerEscape,
+ check::ConstPointerEscape,
+ check::Event<ImplicitNullDerefEvent>,
+ check::ASTDecl<FunctionDecl> > {
+public:
+ /// Pre-visit the Statement.
+ ///
+ /// The method will be called before the analyzer core processes the
+ /// statement. The notification is performed for every explored CFGElement,
+ /// which does not include the control flow statements such as IfStmt. The
+ /// callback can be specialized to be called with any subclass of Stmt.
+ ///
+ /// See checkBranchCondition() callback for performing custom processing of
+ /// the branching statements.
+ ///
+ /// check::PreStmt<ReturnStmt>
+ void checkPreStmt(const ReturnStmt *DS, CheckerContext &C) const {}
+
+ /// Post-visit the Statement.
+ ///
+ /// The method will be called after the analyzer core processes the
+ /// statement. The notification is performed for every explored CFGElement,
+ /// which does not include the control flow statements such as IfStmt. The
+ /// callback can be specialized to be called with any subclass of Stmt.
+ ///
+ /// check::PostStmt<DeclStmt>
+ void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
+
+ /// Pre-visit the Objective C message.
+ ///
+ /// This will be called before the analyzer core processes the method call.
+ /// This is called for any action which produces an Objective-C message send,
+ /// including explicit message syntax and property access.
+ ///
+ /// check::PreObjCMessage
+ void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {}
+
+ /// Post-visit the Objective C message.
+ /// \sa checkPreObjCMessage()
+ ///
+ /// check::PostObjCMessage
+ void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const {}
+
+ /// Visit an Objective-C message whose receiver is nil.
+ ///
+ /// This will be called when the analyzer core processes a method call whose
+ /// receiver is definitely nil. In this case, check{Pre/Post}ObjCMessage and
+ /// check{Pre/Post}Call will not be called.
+ ///
+ /// check::ObjCMessageNil
+ void checkObjCMessageNil(const ObjCMethodCall &M, CheckerContext &C) const {}
+
+ /// Pre-visit an abstract "call" event.
+ ///
+ /// This is used for checkers that want to check arguments or attributed
+ /// behavior for functions and methods no matter how they are being invoked.
+ ///
+ /// Note that this includes ALL cross-body invocations, so if you want to
+ /// limit your checks to, say, function calls, you should test for that at the
+ /// beginning of your callback function.
+ ///
+ /// check::PreCall
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const {}
+
+ /// Post-visit an abstract "call" event.
+ /// \sa checkPreObjCMessage()
+ ///
+ /// check::PostCall
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const {}
+
+ /// Pre-visit of the condition statement of a branch (such as IfStmt).
+ void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const {}
+
+ /// Post-visit the C++ operator new's allocation call.
+ ///
+ /// Execution of C++ operator new consists of the following phases: (1) call
+ /// default or overridden operator new() to allocate memory (2) cast the
+ /// return value of operator new() from void pointer type to class pointer
+ /// type, (3) assuming that the value is non-null, call the object's
+ /// constructor over this pointer, (4) declare that the value of the
+ /// new-expression is this pointer. This callback is called between steps
+ /// (2) and (3). Post-call for the allocator is called after step (1).
+ /// Pre-statement for the new-expression is called on step (4) when the value
+ /// of the expression is evaluated.
+ /// \param NE The C++ new-expression that triggered the allocation.
+ /// \param Target The allocated region, casted to the class type.
+ void checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+ CheckerContext &) const {}
+
+ /// Called on a load from and a store to a location.
+ ///
+ /// The method will be called each time a location (pointer) value is
+ /// accessed.
+ /// \param Loc The value of the location (pointer).
+ /// \param IsLoad The flag specifying if the location is a store or a load.
+ /// \param S The load is performed while processing the statement.
+ ///
+ /// check::Location
+ void checkLocation(SVal Loc, bool IsLoad, const Stmt *S,
+ CheckerContext &) const {}
+
+ /// Called on binding of a value to a location.
+ ///
+ /// \param Loc The value of the location (pointer).
+ /// \param Val The value which will be stored at the location Loc.
+ /// \param S The bind is performed while processing the statement S.
+ ///
+ /// check::Bind
+ void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &) const {}
+
+ /// Called whenever a symbol becomes dead.
+ ///
+ /// This callback should be used by the checkers to aggressively clean
+ /// up/reduce the checker state, which is important for reducing the overall
+ /// memory usage. Specifically, if a checker keeps symbol specific information
+ /// in the state, it can and should be dropped after the symbol becomes dead.
+ /// In addition, reporting a bug as soon as the checker becomes dead leads to
+ /// more precise diagnostics. (For example, one should report that a malloced
+ /// variable is not freed right after it goes out of scope.)
+ ///
+ /// \param SR The SymbolReaper object can be queried to determine which
+ /// symbols are dead.
+ ///
+ /// check::DeadSymbols
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const {}
+
+
+ /// Called when the analyzer core starts analyzing a function,
+ /// regardless of whether it is analyzed at the top level or is inlined.
+ ///
+ /// check::BeginFunction
+ void checkBeginFunction(CheckerContext &Ctx) const {}
+
+ /// Called when the analyzer core reaches the end of a
+ /// function being analyzed regardless of whether it is analyzed at the top
+ /// level or is inlined.
+ ///
+ /// check::EndFunction
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const {}
+
+ /// Called after all the paths in the ExplodedGraph reach end of path
+ /// - the symbolic execution graph is fully explored.
+ ///
+ /// This callback should be used in cases when a checker needs to have a
+ /// global view of the information generated on all paths. For example, to
+ /// compare execution summary/result several paths.
+ /// See IdempotentOperationChecker for a usage example.
+ ///
+ /// check::EndAnalysis
+ void checkEndAnalysis(ExplodedGraph &G,
+ BugReporter &BR,
+ ExprEngine &Eng) const {}
+
+ /// Called after analysis of a TranslationUnit is complete.
+ ///
+ /// check::EndOfTranslationUnit
+ void checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
+ AnalysisManager &Mgr,
+ BugReporter &BR) const {}
+
+ /// Evaluates function call.
+ ///
+ /// The analysis core threats all function calls in the same way. However, some
+ /// functions have special meaning, which should be reflected in the program
+ /// state. This callback allows a checker to provide domain specific knowledge
+ /// about the particular functions it knows about.
+ ///
+ /// \returns true if the call has been successfully evaluated
+ /// and false otherwise. Note, that only one checker can evaluate a call. If
+ /// more than one checker claims that they can evaluate the same call the
+ /// first one wins.
+ ///
+ /// eval::Call
+ bool evalCall(const CallExpr *CE, CheckerContext &C) const { return true; }
+
+ /// Handles assumptions on symbolic values.
+ ///
+ /// This method is called when a symbolic expression is assumed to be true or
+ /// false. For example, the assumptions are performed when evaluating a
+ /// condition at a branch. The callback allows checkers track the assumptions
+ /// performed on the symbols of interest and change the state accordingly.
+ ///
+ /// eval::Assume
+ ProgramStateRef evalAssume(ProgramStateRef State,
+ SVal Cond,
+ bool Assumption) const { return State; }
+
+ /// Allows modifying SymbolReaper object. For example, checkers can explicitly
+ /// register symbols of interest as live. These symbols will not be marked
+ /// dead and removed.
+ ///
+ /// check::LiveSymbols
+ void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const {}
+
+ /// Called when the contents of one or more regions change.
+ ///
+ /// This can occur in many different ways: an explicit bind, a blanket
+ /// invalidation of the region contents, or by passing a region to a function
+ /// call whose behavior the analyzer cannot model perfectly.
+ ///
+ /// \param State The current program state.
+ /// \param Invalidated A set of all symbols potentially touched by the change.
+ /// \param ExplicitRegions The regions explicitly requested for invalidation.
+ /// For a function call, this would be the arguments. For a bind, this
+ /// would be the region being bound to.
+ /// \param Regions The transitive closure of regions accessible from,
+ /// \p ExplicitRegions, i.e. all regions that may have been touched
+ /// by this change. For a simple bind, this list will be the same as
+ /// \p ExplicitRegions, since a bind does not affect the contents of
+ /// anything accessible through the base region.
+ /// \param LCtx LocationContext that is useful for getting various contextual
+ /// info, like callstack, CFG etc.
+ /// \param Call The opaque call triggering this invalidation. Will be 0 if the
+ /// change was not triggered by a call.
+ ///
+ /// check::RegionChanges
+ ProgramStateRef
+ checkRegionChanges(ProgramStateRef State,
+ const InvalidatedSymbols *Invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call) const {
+ return State;
+ }
+
+ /// Called when pointers escape.
+ ///
+ /// This notifies the checkers about pointer escape, which occurs whenever
+ /// the analyzer cannot track the symbol any more. For example, as a
+ /// result of assigning a pointer into a global or when it's passed to a
+ /// function call the analyzer cannot model.
+ ///
+ /// \param State The state at the point of escape.
+ /// \param Escaped The list of escaped symbols.
+ /// \param Call The corresponding CallEvent, if the symbols escape as
+ /// parameters to the given call.
+ /// \param Kind How the symbols have escaped.
+ /// \returns Checkers can modify the state by returning a new state.
+ ProgramStateRef checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const {
+ return State;
+ }
+
+ /// Called when const pointers escape.
+ ///
+ /// Note: in most cases checkPointerEscape callback is sufficient.
+ /// \sa checkPointerEscape
+ ProgramStateRef checkConstPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const {
+ return State;
+ }
+
+ /// check::Event<ImplicitNullDerefEvent>
+ void checkEvent(ImplicitNullDerefEvent Event) const {}
+
+ /// Check every declaration in the AST.
+ ///
+ /// An AST traversal callback, which should only be used when the checker is
+ /// not path sensitive. It will be called for every Declaration in the AST and
+ /// can be specialized to only be called on subclasses of Decl, for example,
+ /// FunctionDecl.
+ ///
+ /// check::ASTDecl<FunctionDecl>
+ void checkASTDecl(const FunctionDecl *D,
+ AnalysisManager &Mgr,
+ BugReporter &BR) const {}
+};
+
+void CheckerDocumentation::checkPostStmt(const DeclStmt *DS,
+ CheckerContext &C) const {
+}
+
+} // end namespace ento
+} // end namespace clang
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
new file mode 100644
index 000000000000..673608db1a1d
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
@@ -0,0 +1,155 @@
+//===- Chrootchecker.cpp -------- Basic security checks ---------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines chroot checker, which checks improper use of chroot.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+// enum value that represent the jail state
+enum Kind { NO_CHROOT, ROOT_CHANGED, JAIL_ENTERED };
+
+bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; }
+//bool isJailEntered(intptr_t k) { return k == JAIL_ENTERED; }
+
+// This checker checks improper use of chroot.
+// The state transition:
+// NO_CHROOT ---chroot(path)--> ROOT_CHANGED ---chdir(/) --> JAIL_ENTERED
+// | |
+// ROOT_CHANGED<--chdir(..)-- JAIL_ENTERED<--chdir(..)--
+// | |
+// bug<--foo()-- JAIL_ENTERED<--foo()--
+class ChrootChecker : public Checker<eval::Call, check::PreStmt<CallExpr> > {
+ mutable IdentifierInfo *II_chroot, *II_chdir;
+ // This bug refers to possibly break out of a chroot() jail.
+ mutable std::unique_ptr<BuiltinBug> BT_BreakJail;
+
+public:
+ ChrootChecker() : II_chroot(nullptr), II_chdir(nullptr) {}
+
+ static void *getTag() {
+ static int x;
+ return &x;
+ }
+
+ bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+
+private:
+ void Chroot(CheckerContext &C, const CallExpr *CE) const;
+ void Chdir(CheckerContext &C, const CallExpr *CE) const;
+};
+
+} // end anonymous namespace
+
+bool ChrootChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD)
+ return false;
+
+ ASTContext &Ctx = C.getASTContext();
+ if (!II_chroot)
+ II_chroot = &Ctx.Idents.get("chroot");
+ if (!II_chdir)
+ II_chdir = &Ctx.Idents.get("chdir");
+
+ if (FD->getIdentifier() == II_chroot) {
+ Chroot(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_chdir) {
+ Chdir(C, CE);
+ return true;
+ }
+
+ return false;
+}
+
+void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ ProgramStateManager &Mgr = state->getStateManager();
+
+ // Once encouter a chroot(), set the enum value ROOT_CHANGED directly in
+ // the GDM.
+ state = Mgr.addGDM(state, ChrootChecker::getTag(), (void*) ROOT_CHANGED);
+ C.addTransition(state);
+}
+
+void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ ProgramStateManager &Mgr = state->getStateManager();
+
+ // If there are no jail state in the GDM, just return.
+ const void *k = state->FindGDM(ChrootChecker::getTag());
+ if (!k)
+ return;
+
+ // After chdir("/"), enter the jail, set the enum value JAIL_ENTERED.
+ const Expr *ArgExpr = CE->getArg(0);
+ SVal ArgVal = C.getSVal(ArgExpr);
+
+ if (const MemRegion *R = ArgVal.getAsRegion()) {
+ R = R->StripCasts();
+ if (const StringRegion* StrRegion= dyn_cast<StringRegion>(R)) {
+ const StringLiteral* Str = StrRegion->getStringLiteral();
+ if (Str->getString() == "/")
+ state = Mgr.addGDM(state, ChrootChecker::getTag(),
+ (void*) JAIL_ENTERED);
+ }
+ }
+
+ C.addTransition(state);
+}
+
+// Check the jail state before any function call except chroot and chdir().
+void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD)
+ return;
+
+ ASTContext &Ctx = C.getASTContext();
+ if (!II_chroot)
+ II_chroot = &Ctx.Idents.get("chroot");
+ if (!II_chdir)
+ II_chdir = &Ctx.Idents.get("chdir");
+
+ // Ignore chroot and chdir.
+ if (FD->getIdentifier() == II_chroot || FD->getIdentifier() == II_chdir)
+ return;
+
+ // If jail state is ROOT_CHANGED, generate BugReport.
+ void *const* k = C.getState()->FindGDM(ChrootChecker::getTag());
+ if (k)
+ if (isRootChanged((intptr_t) *k))
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ if (!BT_BreakJail)
+ BT_BreakJail.reset(new BuiltinBug(
+ this, "Break out of jail", "No call of chdir(\"/\") immediately "
+ "after chroot"));
+ C.emitReport(llvm::make_unique<BugReport>(
+ *BT_BreakJail, BT_BreakJail->getDescription(), N));
+ }
+}
+
+void ento::registerChrootChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ChrootChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
new file mode 100644
index 000000000000..89354b866004
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
@@ -0,0 +1,204 @@
+//===--- CloneChecker.cpp - Clone detection checker -------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// CloneChecker is a checker that reports clones in the current translation
+/// unit.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/Analysis/CloneDetection.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class CloneChecker
+ : public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> {
+ mutable CloneDetector Detector;
+ mutable std::unique_ptr<BugType> BT_Exact, BT_Suspicious;
+
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const;
+
+ void checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
+ AnalysisManager &Mgr, BugReporter &BR) const;
+
+ /// Reports all clones to the user.
+ void reportClones(BugReporter &BR, AnalysisManager &Mgr,
+ std::vector<CloneDetector::CloneGroup> &CloneGroups) const;
+
+ /// Reports only suspicious clones to the user along with information
+ /// that explain why they are suspicious.
+ void reportSuspiciousClones(
+ BugReporter &BR, AnalysisManager &Mgr,
+ std::vector<CloneDetector::CloneGroup> &CloneGroups) const;
+};
+} // end anonymous namespace
+
+void CloneChecker::checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const {
+ // Every statement that should be included in the search for clones needs to
+ // be passed to the CloneDetector.
+ Detector.analyzeCodeBody(D);
+}
+
+void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
+ AnalysisManager &Mgr,
+ BugReporter &BR) const {
+ // At this point, every statement in the translation unit has been analyzed by
+ // the CloneDetector. The only thing left to do is to report the found clones.
+
+ int MinComplexity = Mgr.getAnalyzerOptions().getCheckerIntegerOption(
+ "MinimumCloneComplexity", 50, this);
+ assert(MinComplexity >= 0);
+
+ bool ReportSuspiciousClones = Mgr.getAnalyzerOptions()
+ .getCheckerBooleanOption("ReportSuspiciousClones", true, this);
+
+ bool ReportNormalClones = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ "ReportNormalClones", true, this);
+
+ StringRef IgnoredFilesPattern = Mgr.getAnalyzerOptions()
+ .getCheckerStringOption("IgnoredFilesPattern", "", this);
+
+ // Let the CloneDetector create a list of clones from all the analyzed
+ // statements. We don't filter for matching variable patterns at this point
+ // because reportSuspiciousClones() wants to search them for errors.
+ std::vector<CloneDetector::CloneGroup> AllCloneGroups;
+
+ Detector.findClones(
+ AllCloneGroups, FilenamePatternConstraint(IgnoredFilesPattern),
+ RecursiveCloneTypeIIHashConstraint(), MinGroupSizeConstraint(2),
+ MinComplexityConstraint(MinComplexity),
+ RecursiveCloneTypeIIVerifyConstraint(), OnlyLargestCloneConstraint());
+
+ if (ReportSuspiciousClones)
+ reportSuspiciousClones(BR, Mgr, AllCloneGroups);
+
+ // We are done for this translation unit unless we also need to report normal
+ // clones.
+ if (!ReportNormalClones)
+ return;
+
+ // Now that the suspicious clone detector has checked for pattern errors,
+ // we also filter all clones who don't have matching patterns
+ CloneDetector::constrainClones(AllCloneGroups,
+ MatchingVariablePatternConstraint(),
+ MinGroupSizeConstraint(2));
+
+ reportClones(BR, Mgr, AllCloneGroups);
+}
+
+static PathDiagnosticLocation makeLocation(const StmtSequence &S,
+ AnalysisManager &Mgr) {
+ ASTContext &ACtx = Mgr.getASTContext();
+ return PathDiagnosticLocation::createBegin(
+ S.front(), ACtx.getSourceManager(),
+ Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl()));
+}
+
+void CloneChecker::reportClones(
+ BugReporter &BR, AnalysisManager &Mgr,
+ std::vector<CloneDetector::CloneGroup> &CloneGroups) const {
+
+ if (!BT_Exact)
+ BT_Exact.reset(new BugType(this, "Exact code clone", "Code clone"));
+
+ for (const CloneDetector::CloneGroup &Group : CloneGroups) {
+ // We group the clones by printing the first as a warning and all others
+ // as a note.
+ auto R = llvm::make_unique<BugReport>(*BT_Exact, "Duplicate code detected",
+ makeLocation(Group.front(), Mgr));
+ R->addRange(Group.front().getSourceRange());
+
+ for (unsigned i = 1; i < Group.size(); ++i)
+ R->addNote("Similar code here", makeLocation(Group[i], Mgr),
+ Group[i].getSourceRange());
+ BR.emitReport(std::move(R));
+ }
+}
+
+void CloneChecker::reportSuspiciousClones(
+ BugReporter &BR, AnalysisManager &Mgr,
+ std::vector<CloneDetector::CloneGroup> &CloneGroups) const {
+ std::vector<VariablePattern::SuspiciousClonePair> Pairs;
+
+ for (const CloneDetector::CloneGroup &Group : CloneGroups) {
+ for (unsigned i = 0; i < Group.size(); ++i) {
+ VariablePattern PatternA(Group[i]);
+
+ for (unsigned j = i + 1; j < Group.size(); ++j) {
+ VariablePattern PatternB(Group[j]);
+
+ VariablePattern::SuspiciousClonePair ClonePair;
+ // For now, we only report clones which break the variable pattern just
+ // once because multiple differences in a pattern are an indicator that
+ // those differences are maybe intended (e.g. because it's actually a
+ // different algorithm).
+ // FIXME: In very big clones even multiple variables can be unintended,
+ // so replacing this number with a percentage could better handle such
+ // cases. On the other hand it could increase the false-positive rate
+ // for all clones if the percentage is too high.
+ if (PatternA.countPatternDifferences(PatternB, &ClonePair) == 1) {
+ Pairs.push_back(ClonePair);
+ break;
+ }
+ }
+ }
+ }
+
+ if (!BT_Suspicious)
+ BT_Suspicious.reset(
+ new BugType(this, "Suspicious code clone", "Code clone"));
+
+ ASTContext &ACtx = BR.getContext();
+ SourceManager &SM = ACtx.getSourceManager();
+ AnalysisDeclContext *ADC =
+ Mgr.getAnalysisDeclContext(ACtx.getTranslationUnitDecl());
+
+ for (VariablePattern::SuspiciousClonePair &Pair : Pairs) {
+ // FIXME: We are ignoring the suggestions currently, because they are
+ // only 50% accurate (even if the second suggestion is unavailable),
+ // which may confuse the user.
+ // Think how to perform more accurate suggestions?
+
+ auto R = llvm::make_unique<BugReport>(
+ *BT_Suspicious,
+ "Potential copy-paste error; did you really mean to use '" +
+ Pair.FirstCloneInfo.Variable->getNameAsString() + "' here?",
+ PathDiagnosticLocation::createBegin(Pair.FirstCloneInfo.Mention, SM,
+ ADC));
+ R->addRange(Pair.FirstCloneInfo.Mention->getSourceRange());
+
+ R->addNote("Similar code using '" +
+ Pair.SecondCloneInfo.Variable->getNameAsString() + "' here",
+ PathDiagnosticLocation::createBegin(Pair.SecondCloneInfo.Mention,
+ SM, ADC),
+ Pair.SecondCloneInfo.Mention->getSourceRange());
+
+ BR.emitReport(std::move(R));
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Register CloneChecker
+//===----------------------------------------------------------------------===//
+
+void ento::registerCloneChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<CloneChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
new file mode 100644
index 000000000000..a5c67c2a5b45
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
@@ -0,0 +1,198 @@
+//=== ConversionChecker.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Check that there is no loss of sign/precision in assignments, comparisons
+// and multiplications.
+//
+// ConversionChecker uses path sensitive analysis to determine possible values
+// of expressions. A warning is reported when:
+// * a negative value is implicitly converted to an unsigned value in an
+// assignment, comparison or multiplication.
+// * assignment / initialization when the source value is greater than the max
+// value of the target integer type
+// * assignment / initialization when the source integer is above the range
+// where the target floating point type can represent all integers
+//
+// Many compilers and tools have similar checks that are based on semantic
+// analysis. Those checks are sound but have poor precision. ConversionChecker
+// is an alternative to those checks.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/APFloat.h"
+
+#include <climits>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ConversionChecker : public Checker<check::PreStmt<ImplicitCastExpr>> {
+public:
+ void checkPreStmt(const ImplicitCastExpr *Cast, CheckerContext &C) const;
+
+private:
+ mutable std::unique_ptr<BuiltinBug> BT;
+
+ bool isLossOfPrecision(const ImplicitCastExpr *Cast, QualType DestType,
+ CheckerContext &C) const;
+
+ bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
+
+ void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const;
+};
+}
+
+void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
+ CheckerContext &C) const {
+ // TODO: For now we only warn about DeclRefExpr, to avoid noise. Warn for
+ // calculations also.
+ if (!isa<DeclRefExpr>(Cast->IgnoreParenImpCasts()))
+ return;
+
+ // Don't warn for loss of sign/precision in macros.
+ if (Cast->getExprLoc().isMacroID())
+ return;
+
+ // Get Parent.
+ const ParentMap &PM = C.getLocationContext()->getParentMap();
+ const Stmt *Parent = PM.getParent(Cast);
+ if (!Parent)
+ return;
+
+ bool LossOfSign = false;
+ bool LossOfPrecision = false;
+
+ // Loss of sign/precision in binary operation.
+ if (const auto *B = dyn_cast<BinaryOperator>(Parent)) {
+ BinaryOperator::Opcode Opc = B->getOpcode();
+ if (Opc == BO_Assign) {
+ LossOfSign = isLossOfSign(Cast, C);
+ LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
+ } else if (Opc == BO_AddAssign || Opc == BO_SubAssign) {
+ // No loss of sign.
+ LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
+ } else if (Opc == BO_MulAssign) {
+ LossOfSign = isLossOfSign(Cast, C);
+ LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
+ } else if (Opc == BO_DivAssign || Opc == BO_RemAssign) {
+ LossOfSign = isLossOfSign(Cast, C);
+ // No loss of precision.
+ } else if (Opc == BO_AndAssign) {
+ LossOfSign = isLossOfSign(Cast, C);
+ // No loss of precision.
+ } else if (Opc == BO_OrAssign || Opc == BO_XorAssign) {
+ LossOfSign = isLossOfSign(Cast, C);
+ LossOfPrecision = isLossOfPrecision(Cast, B->getLHS()->getType(), C);
+ } else if (B->isRelationalOp() || B->isMultiplicativeOp()) {
+ LossOfSign = isLossOfSign(Cast, C);
+ }
+ } else if (isa<DeclStmt>(Parent)) {
+ LossOfSign = isLossOfSign(Cast, C);
+ LossOfPrecision = isLossOfPrecision(Cast, Cast->getType(), C);
+ }
+
+ if (LossOfSign || LossOfPrecision) {
+ // Generate an error node.
+ ExplodedNode *N = C.generateNonFatalErrorNode(C.getState());
+ if (!N)
+ return;
+ if (LossOfSign)
+ reportBug(N, C, "Loss of sign in implicit conversion");
+ if (LossOfPrecision)
+ reportBug(N, C, "Loss of precision in implicit conversion");
+ }
+}
+
+void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
+ const char Msg[]) const {
+ if (!BT)
+ BT.reset(
+ new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
+
+ // Generate a report for this bug.
+ auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
+ C.emitReport(std::move(R));
+}
+
+bool ConversionChecker::isLossOfPrecision(const ImplicitCastExpr *Cast,
+ QualType DestType,
+ CheckerContext &C) const {
+ // Don't warn about explicit loss of precision.
+ if (Cast->isEvaluatable(C.getASTContext()))
+ return false;
+
+ QualType SubType = Cast->IgnoreParenImpCasts()->getType();
+
+ if (!DestType->isRealType() || !SubType->isIntegerType())
+ return false;
+
+ const bool isFloat = DestType->isFloatingType();
+
+ const auto &AC = C.getASTContext();
+
+ // We will find the largest RepresentsUntilExp value such that the DestType
+ // can exactly represent all nonnegative integers below 2^RepresentsUntilExp.
+ unsigned RepresentsUntilExp;
+
+ if (isFloat) {
+ const llvm::fltSemantics &Sema = AC.getFloatTypeSemantics(DestType);
+ RepresentsUntilExp = llvm::APFloat::semanticsPrecision(Sema);
+ } else {
+ RepresentsUntilExp = AC.getIntWidth(DestType);
+ if (RepresentsUntilExp == 1) {
+ // This is just casting a number to bool, probably not a bug.
+ return false;
+ }
+ if (DestType->isSignedIntegerType())
+ RepresentsUntilExp--;
+ }
+
+ if (RepresentsUntilExp >= sizeof(unsigned long long) * CHAR_BIT) {
+ // Avoid overflow in our later calculations.
+ return false;
+ }
+
+ unsigned CorrectedSrcWidth = AC.getIntWidth(SubType);
+ if (SubType->isSignedIntegerType())
+ CorrectedSrcWidth--;
+
+ if (RepresentsUntilExp >= CorrectedSrcWidth) {
+ // Simple case: the destination can store all values of the source type.
+ return false;
+ }
+
+ unsigned long long MaxVal = 1ULL << RepresentsUntilExp;
+ if (isFloat) {
+ // If this is a floating point type, it can also represent MaxVal exactly.
+ MaxVal++;
+ }
+ return C.isGreaterOrEqual(Cast->getSubExpr(), MaxVal);
+ // TODO: maybe also check negative values with too large magnitude.
+}
+
+bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
+ CheckerContext &C) const {
+ QualType CastType = Cast->getType();
+ QualType SubType = Cast->IgnoreParenImpCasts()->getType();
+
+ if (!CastType->isUnsignedIntegerType() || !SubType->isSignedIntegerType())
+ return false;
+
+ return C.isNegative(Cast->getSubExpr());
+}
+
+void ento::registerConversionChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ConversionChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
new file mode 100644
index 000000000000..4e0f6d3bedfd
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -0,0 +1,481 @@
+//==- DeadStoresChecker.cpp - Check for stores to dead variables -*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a DeadStores, a flow-sensitive checker that looks for
+// stores to variables that are no longer live.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Analysis/Analyses/LiveVariables.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/BitVector.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/SaveAndRestore.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+/// A simple visitor to record what VarDecls occur in EH-handling code.
+class EHCodeVisitor : public RecursiveASTVisitor<EHCodeVisitor> {
+public:
+ bool inEH;
+ llvm::DenseSet<const VarDecl *> &S;
+
+ bool TraverseObjCAtFinallyStmt(ObjCAtFinallyStmt *S) {
+ SaveAndRestore<bool> inFinally(inEH, true);
+ return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtFinallyStmt(S);
+ }
+
+ bool TraverseObjCAtCatchStmt(ObjCAtCatchStmt *S) {
+ SaveAndRestore<bool> inCatch(inEH, true);
+ return ::RecursiveASTVisitor<EHCodeVisitor>::TraverseObjCAtCatchStmt(S);
+ }
+
+ bool TraverseCXXCatchStmt(CXXCatchStmt *S) {
+ SaveAndRestore<bool> inCatch(inEH, true);
+ return TraverseStmt(S->getHandlerBlock());
+ }
+
+ bool VisitDeclRefExpr(DeclRefExpr *DR) {
+ if (inEH)
+ if (const VarDecl *D = dyn_cast<VarDecl>(DR->getDecl()))
+ S.insert(D);
+ return true;
+ }
+
+ EHCodeVisitor(llvm::DenseSet<const VarDecl *> &S) :
+ inEH(false), S(S) {}
+};
+
+// FIXME: Eventually migrate into its own file, and have it managed by
+// AnalysisManager.
+class ReachableCode {
+ const CFG &cfg;
+ llvm::BitVector reachable;
+public:
+ ReachableCode(const CFG &cfg)
+ : cfg(cfg), reachable(cfg.getNumBlockIDs(), false) {}
+
+ void computeReachableBlocks();
+
+ bool isReachable(const CFGBlock *block) const {
+ return reachable[block->getBlockID()];
+ }
+};
+}
+
+void ReachableCode::computeReachableBlocks() {
+ if (!cfg.getNumBlockIDs())
+ return;
+
+ SmallVector<const CFGBlock*, 10> worklist;
+ worklist.push_back(&cfg.getEntry());
+
+ while (!worklist.empty()) {
+ const CFGBlock *block = worklist.pop_back_val();
+ llvm::BitVector::reference isReachable = reachable[block->getBlockID()];
+ if (isReachable)
+ continue;
+ isReachable = true;
+ for (CFGBlock::const_succ_iterator i = block->succ_begin(),
+ e = block->succ_end(); i != e; ++i)
+ if (const CFGBlock *succ = *i)
+ worklist.push_back(succ);
+ }
+}
+
+static const Expr *
+LookThroughTransitiveAssignmentsAndCommaOperators(const Expr *Ex) {
+ while (Ex) {
+ const BinaryOperator *BO =
+ dyn_cast<BinaryOperator>(Ex->IgnoreParenCasts());
+ if (!BO)
+ break;
+ if (BO->getOpcode() == BO_Assign) {
+ Ex = BO->getRHS();
+ continue;
+ }
+ if (BO->getOpcode() == BO_Comma) {
+ Ex = BO->getRHS();
+ continue;
+ }
+ break;
+ }
+ return Ex;
+}
+
+namespace {
+class DeadStoreObs : public LiveVariables::Observer {
+ const CFG &cfg;
+ ASTContext &Ctx;
+ BugReporter& BR;
+ const CheckerBase *Checker;
+ AnalysisDeclContext* AC;
+ ParentMap& Parents;
+ llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
+ std::unique_ptr<ReachableCode> reachableCode;
+ const CFGBlock *currentBlock;
+ std::unique_ptr<llvm::DenseSet<const VarDecl *>> InEH;
+
+ enum DeadStoreKind { Standard, Enclosing, DeadIncrement, DeadInit };
+
+public:
+ DeadStoreObs(const CFG &cfg, ASTContext &ctx, BugReporter &br,
+ const CheckerBase *checker, AnalysisDeclContext *ac,
+ ParentMap &parents,
+ llvm::SmallPtrSet<const VarDecl *, 20> &escaped)
+ : cfg(cfg), Ctx(ctx), BR(br), Checker(checker), AC(ac), Parents(parents),
+ Escaped(escaped), currentBlock(nullptr) {}
+
+ ~DeadStoreObs() override {}
+
+ bool isLive(const LiveVariables::LivenessValues &Live, const VarDecl *D) {
+ if (Live.isLive(D))
+ return true;
+ // Lazily construct the set that records which VarDecls are in
+ // EH code.
+ if (!InEH.get()) {
+ InEH.reset(new llvm::DenseSet<const VarDecl *>());
+ EHCodeVisitor V(*InEH.get());
+ V.TraverseStmt(AC->getBody());
+ }
+ // Treat all VarDecls that occur in EH code as being "always live"
+ // when considering to suppress dead stores. Frequently stores
+ // are followed by reads in EH code, but we don't have the ability
+ // to analyze that yet.
+ return InEH->count(D);
+ }
+
+ void Report(const VarDecl *V, DeadStoreKind dsk,
+ PathDiagnosticLocation L, SourceRange R) {
+ if (Escaped.count(V))
+ return;
+
+ // Compute reachable blocks within the CFG for trivial cases
+ // where a bogus dead store can be reported because itself is unreachable.
+ if (!reachableCode.get()) {
+ reachableCode.reset(new ReachableCode(cfg));
+ reachableCode->computeReachableBlocks();
+ }
+
+ if (!reachableCode->isReachable(currentBlock))
+ return;
+
+ SmallString<64> buf;
+ llvm::raw_svector_ostream os(buf);
+ const char *BugType = nullptr;
+
+ switch (dsk) {
+ case DeadInit:
+ BugType = "Dead initialization";
+ os << "Value stored to '" << *V
+ << "' during its initialization is never read";
+ break;
+
+ case DeadIncrement:
+ BugType = "Dead increment";
+ LLVM_FALLTHROUGH;
+ case Standard:
+ if (!BugType) BugType = "Dead assignment";
+ os << "Value stored to '" << *V << "' is never read";
+ break;
+
+ case Enclosing:
+ // Don't report issues in this case, e.g.: "if (x = foo())",
+ // where 'x' is unused later. We have yet to see a case where
+ // this is a real bug.
+ return;
+ }
+
+ BR.EmitBasicReport(AC->getDecl(), Checker, BugType, "Dead store", os.str(),
+ L, R);
+ }
+
+ void CheckVarDecl(const VarDecl *VD, const Expr *Ex, const Expr *Val,
+ DeadStoreKind dsk,
+ const LiveVariables::LivenessValues &Live) {
+
+ if (!VD->hasLocalStorage())
+ return;
+ // Reference types confuse the dead stores checker. Skip them
+ // for now.
+ if (VD->getType()->getAs<ReferenceType>())
+ return;
+
+ if (!isLive(Live, VD) &&
+ !(VD->hasAttr<UnusedAttr>() || VD->hasAttr<BlocksAttr>() ||
+ VD->hasAttr<ObjCPreciseLifetimeAttr>())) {
+
+ PathDiagnosticLocation ExLoc =
+ PathDiagnosticLocation::createBegin(Ex, BR.getSourceManager(), AC);
+ Report(VD, dsk, ExLoc, Val->getSourceRange());
+ }
+ }
+
+ void CheckDeclRef(const DeclRefExpr *DR, const Expr *Val, DeadStoreKind dsk,
+ const LiveVariables::LivenessValues& Live) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
+ CheckVarDecl(VD, DR, Val, dsk, Live);
+ }
+
+ bool isIncrement(VarDecl *VD, const BinaryOperator* B) {
+ if (B->isCompoundAssignmentOp())
+ return true;
+
+ const Expr *RHS = B->getRHS()->IgnoreParenCasts();
+ const BinaryOperator* BRHS = dyn_cast<BinaryOperator>(RHS);
+
+ if (!BRHS)
+ return false;
+
+ const DeclRefExpr *DR;
+
+ if ((DR = dyn_cast<DeclRefExpr>(BRHS->getLHS()->IgnoreParenCasts())))
+ if (DR->getDecl() == VD)
+ return true;
+
+ if ((DR = dyn_cast<DeclRefExpr>(BRHS->getRHS()->IgnoreParenCasts())))
+ if (DR->getDecl() == VD)
+ return true;
+
+ return false;
+ }
+
+ void observeStmt(const Stmt *S, const CFGBlock *block,
+ const LiveVariables::LivenessValues &Live) override {
+
+ currentBlock = block;
+
+ // Skip statements in macros.
+ if (S->getBeginLoc().isMacroID())
+ return;
+
+ // Only cover dead stores from regular assignments. ++/-- dead stores
+ // have never flagged a real bug.
+ if (const BinaryOperator* B = dyn_cast<BinaryOperator>(S)) {
+ if (!B->isAssignmentOp()) return; // Skip non-assignments.
+
+ if (DeclRefExpr *DR = dyn_cast<DeclRefExpr>(B->getLHS()))
+ if (VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ // Special case: check for assigning null to a pointer.
+ // This is a common form of defensive programming.
+ const Expr *RHS =
+ LookThroughTransitiveAssignmentsAndCommaOperators(B->getRHS());
+ RHS = RHS->IgnoreParenCasts();
+
+ QualType T = VD->getType();
+ if (T.isVolatileQualified())
+ return;
+ if (T->isPointerType() || T->isObjCObjectPointerType()) {
+ if (RHS->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNull))
+ return;
+ }
+
+ // Special case: self-assignments. These are often used to shut up
+ // "unused variable" compiler warnings.
+ if (const DeclRefExpr *RhsDR = dyn_cast<DeclRefExpr>(RHS))
+ if (VD == dyn_cast<VarDecl>(RhsDR->getDecl()))
+ return;
+
+ // Otherwise, issue a warning.
+ DeadStoreKind dsk = Parents.isConsumedExpr(B)
+ ? Enclosing
+ : (isIncrement(VD,B) ? DeadIncrement : Standard);
+
+ CheckVarDecl(VD, DR, B->getRHS(), dsk, Live);
+ }
+ }
+ else if (const UnaryOperator* U = dyn_cast<UnaryOperator>(S)) {
+ if (!U->isIncrementOp() || U->isPrefix())
+ return;
+
+ const Stmt *parent = Parents.getParentIgnoreParenCasts(U);
+ if (!parent || !isa<ReturnStmt>(parent))
+ return;
+
+ const Expr *Ex = U->getSubExpr()->IgnoreParenCasts();
+
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(Ex))
+ CheckDeclRef(DR, U, DeadIncrement, Live);
+ }
+ else if (const DeclStmt *DS = dyn_cast<DeclStmt>(S))
+ // Iterate through the decls. Warn if any initializers are complex
+ // expressions that are not live (never used).
+ for (const auto *DI : DS->decls()) {
+ const auto *V = dyn_cast<VarDecl>(DI);
+
+ if (!V)
+ continue;
+
+ if (V->hasLocalStorage()) {
+ // Reference types confuse the dead stores checker. Skip them
+ // for now.
+ if (V->getType()->getAs<ReferenceType>())
+ return;
+
+ if (const Expr *E = V->getInit()) {
+ while (const FullExpr *FE = dyn_cast<FullExpr>(E))
+ E = FE->getSubExpr();
+
+ // Look through transitive assignments, e.g.:
+ // int x = y = 0;
+ E = LookThroughTransitiveAssignmentsAndCommaOperators(E);
+
+ // Don't warn on C++ objects (yet) until we can show that their
+ // constructors/destructors don't have side effects.
+ if (isa<CXXConstructExpr>(E))
+ return;
+
+ // A dead initialization is a variable that is dead after it
+ // is initialized. We don't flag warnings for those variables
+ // marked 'unused' or 'objc_precise_lifetime'.
+ if (!isLive(Live, V) &&
+ !V->hasAttr<UnusedAttr>() &&
+ !V->hasAttr<ObjCPreciseLifetimeAttr>()) {
+ // Special case: check for initializations with constants.
+ //
+ // e.g. : int x = 0;
+ //
+ // If x is EVER assigned a new value later, don't issue
+ // a warning. This is because such initialization can be
+ // due to defensive programming.
+ if (E->isEvaluatable(Ctx))
+ return;
+
+ if (const DeclRefExpr *DRE =
+ dyn_cast<DeclRefExpr>(E->IgnoreParenCasts()))
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
+ // Special case: check for initialization from constant
+ // variables.
+ //
+ // e.g. extern const int MyConstant;
+ // int x = MyConstant;
+ //
+ if (VD->hasGlobalStorage() &&
+ VD->getType().isConstQualified())
+ return;
+ // Special case: check for initialization from scalar
+ // parameters. This is often a form of defensive
+ // programming. Non-scalars are still an error since
+ // because it more likely represents an actual algorithmic
+ // bug.
+ if (isa<ParmVarDecl>(VD) && VD->getType()->isScalarType())
+ return;
+ }
+
+ PathDiagnosticLocation Loc =
+ PathDiagnosticLocation::create(V, BR.getSourceManager());
+ Report(V, DeadInit, Loc, E->getSourceRange());
+ }
+ }
+ }
+ }
+ }
+};
+
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Driver function to invoke the Dead-Stores checker on a CFG.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class FindEscaped {
+public:
+ llvm::SmallPtrSet<const VarDecl*, 20> Escaped;
+
+ void operator()(const Stmt *S) {
+ // Check for '&'. Any VarDecl whose address has been taken we treat as
+ // escaped.
+ // FIXME: What about references?
+ if (auto *LE = dyn_cast<LambdaExpr>(S)) {
+ findLambdaReferenceCaptures(LE);
+ return;
+ }
+
+ const UnaryOperator *U = dyn_cast<UnaryOperator>(S);
+ if (!U)
+ return;
+ if (U->getOpcode() != UO_AddrOf)
+ return;
+
+ const Expr *E = U->getSubExpr()->IgnoreParenCasts();
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
+ Escaped.insert(VD);
+ }
+
+ // Treat local variables captured by reference in C++ lambdas as escaped.
+ void findLambdaReferenceCaptures(const LambdaExpr *LE) {
+ const CXXRecordDecl *LambdaClass = LE->getLambdaClass();
+ llvm::DenseMap<const VarDecl *, FieldDecl *> CaptureFields;
+ FieldDecl *ThisCaptureField;
+ LambdaClass->getCaptureFields(CaptureFields, ThisCaptureField);
+
+ for (const LambdaCapture &C : LE->captures()) {
+ if (!C.capturesVariable())
+ continue;
+
+ VarDecl *VD = C.getCapturedVar();
+ const FieldDecl *FD = CaptureFields[VD];
+ if (!FD)
+ continue;
+
+ // If the capture field is a reference type, it is capture-by-reference.
+ if (FD->getType()->isReferenceType())
+ Escaped.insert(VD);
+ }
+ }
+};
+} // end anonymous namespace
+
+
+//===----------------------------------------------------------------------===//
+// DeadStoresChecker
+//===----------------------------------------------------------------------===//
+
+namespace {
+class DeadStoresChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+
+ // Don't do anything for template instantiations.
+ // Proving that code in a template instantiation is "dead"
+ // means proving that it is dead in all instantiations.
+ // This same problem exists with -Wunreachable-code.
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
+ if (FD->isTemplateInstantiation())
+ return;
+
+ if (LiveVariables *L = mgr.getAnalysis<LiveVariables>(D)) {
+ CFG &cfg = *mgr.getCFG(D);
+ AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D);
+ ParentMap &pmap = mgr.getParentMap(D);
+ FindEscaped FS;
+ cfg.VisitBlockStmts(FS);
+ DeadStoreObs A(cfg, BR.getContext(), BR, this, AC, pmap, FS.Escaped);
+ L->runOnAllBlocks(A);
+ }
+ }
+};
+}
+
+void ento::registerDeadStoresChecker(CheckerManager &mgr) {
+ mgr.registerChecker<DeadStoresChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
new file mode 100644
index 000000000000..90b1111aff0f
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
@@ -0,0 +1,235 @@
+//==- DebugCheckers.cpp - Debugging Checkers ---------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines checkers that display debugging information.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/Analysis/Analyses/Dominators.h"
+#include "clang/Analysis/Analyses/LiveVariables.h"
+#include "clang/Analysis/CallGraph.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "llvm/Support/Process.h"
+
+using namespace clang;
+using namespace ento;
+
+//===----------------------------------------------------------------------===//
+// DominatorsTreeDumper
+//===----------------------------------------------------------------------===//
+
+namespace {
+class DominatorsTreeDumper : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ if (AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D)) {
+ DominatorTree dom;
+ dom.buildDominatorTree(*AC);
+ dom.dump();
+ }
+ }
+};
+}
+
+void ento::registerDominatorsTreeDumper(CheckerManager &mgr) {
+ mgr.registerChecker<DominatorsTreeDumper>();
+}
+
+//===----------------------------------------------------------------------===//
+// LiveVariablesDumper
+//===----------------------------------------------------------------------===//
+
+namespace {
+class LiveVariablesDumper : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ if (LiveVariables* L = mgr.getAnalysis<LiveVariables>(D)) {
+ L->dumpBlockLiveness(mgr.getSourceManager());
+ }
+ }
+};
+}
+
+void ento::registerLiveVariablesDumper(CheckerManager &mgr) {
+ mgr.registerChecker<LiveVariablesDumper>();
+}
+
+//===----------------------------------------------------------------------===//
+// LiveStatementsDumper
+//===----------------------------------------------------------------------===//
+
+namespace {
+class LiveStatementsDumper : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
+ BugReporter &BR) const {
+ if (LiveVariables *L = Mgr.getAnalysis<RelaxedLiveVariables>(D))
+ L->dumpStmtLiveness(Mgr.getSourceManager());
+ }
+};
+}
+
+void ento::registerLiveStatementsDumper(CheckerManager &mgr) {
+ mgr.registerChecker<LiveStatementsDumper>();
+}
+
+//===----------------------------------------------------------------------===//
+// CFGViewer
+//===----------------------------------------------------------------------===//
+
+namespace {
+class CFGViewer : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ if (CFG *cfg = mgr.getCFG(D)) {
+ cfg->viewCFG(mgr.getLangOpts());
+ }
+ }
+};
+}
+
+void ento::registerCFGViewer(CheckerManager &mgr) {
+ mgr.registerChecker<CFGViewer>();
+}
+
+//===----------------------------------------------------------------------===//
+// CFGDumper
+//===----------------------------------------------------------------------===//
+
+namespace {
+class CFGDumper : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ PrintingPolicy Policy(mgr.getLangOpts());
+ Policy.TerseOutput = true;
+ Policy.PolishForDeclaration = true;
+ D->print(llvm::errs(), Policy);
+
+ if (CFG *cfg = mgr.getCFG(D)) {
+ cfg->dump(mgr.getLangOpts(),
+ llvm::sys::Process::StandardErrHasColors());
+ }
+ }
+};
+}
+
+void ento::registerCFGDumper(CheckerManager &mgr) {
+ mgr.registerChecker<CFGDumper>();
+}
+
+//===----------------------------------------------------------------------===//
+// CallGraphViewer
+//===----------------------------------------------------------------------===//
+
+namespace {
+class CallGraphViewer : public Checker< check::ASTDecl<TranslationUnitDecl> > {
+public:
+ void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ CallGraph CG;
+ CG.addToCallGraph(const_cast<TranslationUnitDecl*>(TU));
+ CG.viewGraph();
+ }
+};
+}
+
+void ento::registerCallGraphViewer(CheckerManager &mgr) {
+ mgr.registerChecker<CallGraphViewer>();
+}
+
+//===----------------------------------------------------------------------===//
+// CallGraphDumper
+//===----------------------------------------------------------------------===//
+
+namespace {
+class CallGraphDumper : public Checker< check::ASTDecl<TranslationUnitDecl> > {
+public:
+ void checkASTDecl(const TranslationUnitDecl *TU, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ CallGraph CG;
+ CG.addToCallGraph(const_cast<TranslationUnitDecl*>(TU));
+ CG.dump();
+ }
+};
+}
+
+void ento::registerCallGraphDumper(CheckerManager &mgr) {
+ mgr.registerChecker<CallGraphDumper>();
+}
+
+
+//===----------------------------------------------------------------------===//
+// ConfigDumper
+//===----------------------------------------------------------------------===//
+
+namespace {
+class ConfigDumper : public Checker< check::EndOfTranslationUnit > {
+ typedef AnalyzerOptions::ConfigTable Table;
+
+ static int compareEntry(const Table::MapEntryTy *const *LHS,
+ const Table::MapEntryTy *const *RHS) {
+ return (*LHS)->getKey().compare((*RHS)->getKey());
+ }
+
+public:
+ void checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
+ AnalysisManager& mgr,
+ BugReporter &BR) const {
+ const Table &Config = mgr.options.Config;
+
+ SmallVector<const Table::MapEntryTy *, 32> Keys;
+ for (Table::const_iterator I = Config.begin(), E = Config.end(); I != E;
+ ++I)
+ Keys.push_back(&*I);
+ llvm::array_pod_sort(Keys.begin(), Keys.end(), compareEntry);
+
+ llvm::errs() << "[config]\n";
+ for (unsigned I = 0, E = Keys.size(); I != E; ++I)
+ llvm::errs() << Keys[I]->getKey() << " = "
+ << (Keys[I]->second.empty() ? "\"\"" : Keys[I]->second)
+ << '\n';
+
+ llvm::errs() << "[stats]\n" << "num-entries = " << Keys.size() << '\n';
+ }
+};
+}
+
+void ento::registerConfigDumper(CheckerManager &mgr) {
+ mgr.registerChecker<ConfigDumper>();
+}
+
+//===----------------------------------------------------------------------===//
+// ExplodedGraph Viewer
+//===----------------------------------------------------------------------===//
+
+namespace {
+class ExplodedGraphViewer : public Checker< check::EndAnalysis > {
+public:
+ ExplodedGraphViewer() {}
+ void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,ExprEngine &Eng) const {
+ Eng.ViewGraph(0);
+ }
+};
+
+}
+
+void ento::registerExplodedGraphViewer(CheckerManager &mgr) {
+ mgr.registerChecker<ExplodedGraphViewer>();
+}
+
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
new file mode 100644
index 000000000000..adf5a8e77a74
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
@@ -0,0 +1,150 @@
+//===-- DeleteWithNonVirtualDtorChecker.cpp -----------------------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for the OOP52-CPP CERT rule: Do not delete a polymorphic
+// object without a virtual destructor.
+//
+// Diagnostic flags -Wnon-virtual-dtor and -Wdelete-non-virtual-dtor report if
+// an object with a virtual function but a non-virtual destructor exists or is
+// deleted, respectively.
+//
+// This check exceeds them by comparing the dynamic and static types of the
+// object at the point of destruction and only warns if it happens through a
+// pointer to a base type without a virtual destructor. The check places a note
+// at the last point where the conversion from derived to base happened.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class DeleteWithNonVirtualDtorChecker
+ : public Checker<check::PreStmt<CXXDeleteExpr>> {
+ mutable std::unique_ptr<BugType> BT;
+
+ class DeleteBugVisitor : public BugReporterVisitor {
+ public:
+ DeleteBugVisitor() : Satisfied(false) {}
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int X = 0;
+ ID.AddPointer(&X);
+ }
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ private:
+ bool Satisfied;
+ };
+
+public:
+ void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+void DeleteWithNonVirtualDtorChecker::checkPreStmt(const CXXDeleteExpr *DE,
+ CheckerContext &C) const {
+ const Expr *DeletedObj = DE->getArgument();
+ const MemRegion *MR = C.getSVal(DeletedObj).getAsRegion();
+ if (!MR)
+ return;
+
+ const auto *BaseClassRegion = MR->getAs<TypedValueRegion>();
+ const auto *DerivedClassRegion = MR->getBaseRegion()->getAs<SymbolicRegion>();
+ if (!BaseClassRegion || !DerivedClassRegion)
+ return;
+
+ const auto *BaseClass = BaseClassRegion->getValueType()->getAsCXXRecordDecl();
+ const auto *DerivedClass =
+ DerivedClassRegion->getSymbol()->getType()->getPointeeCXXRecordDecl();
+ if (!BaseClass || !DerivedClass)
+ return;
+
+ if (!BaseClass->hasDefinition() || !DerivedClass->hasDefinition())
+ return;
+
+ if (BaseClass->getDestructor()->isVirtual())
+ return;
+
+ if (!DerivedClass->isDerivedFrom(BaseClass))
+ return;
+
+ if (!BT)
+ BT.reset(new BugType(this,
+ "Destruction of a polymorphic object with no "
+ "virtual destructor",
+ "Logic error"));
+
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
+
+ // Mark region of problematic base class for later use in the BugVisitor.
+ R->markInteresting(BaseClassRegion);
+ R->addVisitor(llvm::make_unique<DeleteBugVisitor>());
+ C.emitReport(std::move(R));
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BRC,
+ BugReport &BR) {
+ // Stop traversal after the first conversion was found on a path.
+ if (Satisfied)
+ return nullptr;
+
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ if (!S)
+ return nullptr;
+
+ const auto *CastE = dyn_cast<CastExpr>(S);
+ if (!CastE)
+ return nullptr;
+
+ // Only interested in DerivedToBase implicit casts.
+ // Explicit casts can have different CastKinds.
+ if (const auto *ImplCastE = dyn_cast<ImplicitCastExpr>(CastE)) {
+ if (ImplCastE->getCastKind() != CK_DerivedToBase)
+ return nullptr;
+ }
+
+ // Region associated with the current cast expression.
+ const MemRegion *M = N->getSVal(CastE).getAsRegion();
+ if (!M)
+ return nullptr;
+
+ // Check if target region was marked as problematic previously.
+ if (!BR.isInteresting(M))
+ return nullptr;
+
+ // Stop traversal on this path.
+ Satisfied = true;
+
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+ OS << "Conversion from derived to base happened here";
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
+ nullptr);
+}
+
+void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
+ mgr.registerChecker<DeleteWithNonVirtualDtorChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
new file mode 100644
index 000000000000..d01a889d256a
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -0,0 +1,306 @@
+//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines NullDerefChecker, a builtin check in ExprEngine that performs
+// checks for null pointers at loads and stores.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/ExprOpenMP.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class DereferenceChecker
+ : public Checker< check::Location,
+ check::Bind,
+ EventDispatcher<ImplicitNullDerefEvent> > {
+ mutable std::unique_ptr<BuiltinBug> BT_null;
+ mutable std::unique_ptr<BuiltinBug> BT_undef;
+
+ void reportBug(ProgramStateRef State, const Stmt *S, CheckerContext &C) const;
+
+public:
+ void checkLocation(SVal location, bool isLoad, const Stmt* S,
+ CheckerContext &C) const;
+ void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
+
+ static void AddDerefSource(raw_ostream &os,
+ SmallVectorImpl<SourceRange> &Ranges,
+ const Expr *Ex, const ProgramState *state,
+ const LocationContext *LCtx,
+ bool loadedFrom = false);
+};
+} // end anonymous namespace
+
+void
+DereferenceChecker::AddDerefSource(raw_ostream &os,
+ SmallVectorImpl<SourceRange> &Ranges,
+ const Expr *Ex,
+ const ProgramState *state,
+ const LocationContext *LCtx,
+ bool loadedFrom) {
+ Ex = Ex->IgnoreParenLValueCasts();
+ switch (Ex->getStmtClass()) {
+ default:
+ break;
+ case Stmt::DeclRefExprClass: {
+ const DeclRefExpr *DR = cast<DeclRefExpr>(Ex);
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ os << " (" << (loadedFrom ? "loaded from" : "from")
+ << " variable '" << VD->getName() << "')";
+ Ranges.push_back(DR->getSourceRange());
+ }
+ break;
+ }
+ case Stmt::MemberExprClass: {
+ const MemberExpr *ME = cast<MemberExpr>(Ex);
+ os << " (" << (loadedFrom ? "loaded from" : "via")
+ << " field '" << ME->getMemberNameInfo() << "')";
+ SourceLocation L = ME->getMemberLoc();
+ Ranges.push_back(SourceRange(L, L));
+ break;
+ }
+ case Stmt::ObjCIvarRefExprClass: {
+ const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(Ex);
+ os << " (" << (loadedFrom ? "loaded from" : "via")
+ << " ivar '" << IV->getDecl()->getName() << "')";
+ SourceLocation L = IV->getLocation();
+ Ranges.push_back(SourceRange(L, L));
+ break;
+ }
+ }
+}
+
+static const Expr *getDereferenceExpr(const Stmt *S, bool IsBind=false){
+ const Expr *E = nullptr;
+
+ // Walk through lvalue casts to get the original expression
+ // that syntactically caused the load.
+ if (const Expr *expr = dyn_cast<Expr>(S))
+ E = expr->IgnoreParenLValueCasts();
+
+ if (IsBind) {
+ const VarDecl *VD;
+ const Expr *Init;
+ std::tie(VD, Init) = parseAssignment(S);
+ if (VD && Init)
+ E = Init;
+ }
+ return E;
+}
+
+static bool suppressReport(const Expr *E) {
+ // Do not report dereferences on memory in non-default address spaces.
+ return E->getType().getQualifiers().hasAddressSpace();
+}
+
+static bool isDeclRefExprToReference(const Expr *E) {
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(E))
+ return DRE->getDecl()->getType()->isReferenceType();
+ return false;
+}
+
+void DereferenceChecker::reportBug(ProgramStateRef State, const Stmt *S,
+ CheckerContext &C) const {
+ // Generate an error node.
+ ExplodedNode *N = C.generateErrorNode(State);
+ if (!N)
+ return;
+
+ // We know that 'location' cannot be non-null. This is what
+ // we call an "explicit" null dereference.
+ if (!BT_null)
+ BT_null.reset(new BuiltinBug(this, "Dereference of null pointer"));
+
+ SmallString<100> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ SmallVector<SourceRange, 2> Ranges;
+
+ switch (S->getStmtClass()) {
+ case Stmt::ArraySubscriptExprClass: {
+ os << "Array access";
+ const ArraySubscriptExpr *AE = cast<ArraySubscriptExpr>(S);
+ AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
+ State.get(), N->getLocationContext());
+ os << " results in a null pointer dereference";
+ break;
+ }
+ case Stmt::OMPArraySectionExprClass: {
+ os << "Array access";
+ const OMPArraySectionExpr *AE = cast<OMPArraySectionExpr>(S);
+ AddDerefSource(os, Ranges, AE->getBase()->IgnoreParenCasts(),
+ State.get(), N->getLocationContext());
+ os << " results in a null pointer dereference";
+ break;
+ }
+ case Stmt::UnaryOperatorClass: {
+ os << "Dereference of null pointer";
+ const UnaryOperator *U = cast<UnaryOperator>(S);
+ AddDerefSource(os, Ranges, U->getSubExpr()->IgnoreParens(),
+ State.get(), N->getLocationContext(), true);
+ break;
+ }
+ case Stmt::MemberExprClass: {
+ const MemberExpr *M = cast<MemberExpr>(S);
+ if (M->isArrow() || isDeclRefExprToReference(M->getBase())) {
+ os << "Access to field '" << M->getMemberNameInfo()
+ << "' results in a dereference of a null pointer";
+ AddDerefSource(os, Ranges, M->getBase()->IgnoreParenCasts(),
+ State.get(), N->getLocationContext(), true);
+ }
+ break;
+ }
+ case Stmt::ObjCIvarRefExprClass: {
+ const ObjCIvarRefExpr *IV = cast<ObjCIvarRefExpr>(S);
+ os << "Access to instance variable '" << *IV->getDecl()
+ << "' results in a dereference of a null pointer";
+ AddDerefSource(os, Ranges, IV->getBase()->IgnoreParenCasts(),
+ State.get(), N->getLocationContext(), true);
+ break;
+ }
+ default:
+ break;
+ }
+
+ auto report = llvm::make_unique<BugReport>(
+ *BT_null, buf.empty() ? BT_null->getDescription() : StringRef(buf), N);
+
+ bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
+
+ for (SmallVectorImpl<SourceRange>::iterator
+ I = Ranges.begin(), E = Ranges.end(); I!=E; ++I)
+ report->addRange(*I);
+
+ C.emitReport(std::move(report));
+}
+
+void DereferenceChecker::checkLocation(SVal l, bool isLoad, const Stmt* S,
+ CheckerContext &C) const {
+ // Check for dereference of an undefined value.
+ if (l.isUndef()) {
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT_undef)
+ BT_undef.reset(
+ new BuiltinBug(this, "Dereference of undefined pointer value"));
+
+ auto report =
+ llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N);
+ bugreporter::trackExpressionValue(N, bugreporter::getDerefExpr(S), *report);
+ C.emitReport(std::move(report));
+ }
+ return;
+ }
+
+ DefinedOrUnknownSVal location = l.castAs<DefinedOrUnknownSVal>();
+
+ // Check for null dereferences.
+ if (!location.getAs<Loc>())
+ return;
+
+ ProgramStateRef state = C.getState();
+
+ ProgramStateRef notNullState, nullState;
+ std::tie(notNullState, nullState) = state->assume(location);
+
+ // The explicit NULL case.
+ if (nullState) {
+ if (!notNullState) {
+ const Expr *expr = getDereferenceExpr(S);
+ if (!suppressReport(expr)) {
+ reportBug(nullState, expr, C);
+ return;
+ }
+ }
+
+ // Otherwise, we have the case where the location could either be
+ // null or not-null. Record the error node as an "implicit" null
+ // dereference.
+ if (ExplodedNode *N = C.generateSink(nullState, C.getPredecessor())) {
+ ImplicitNullDerefEvent event = {l, isLoad, N, &C.getBugReporter(),
+ /*IsDirectDereference=*/true};
+ dispatchEvent(event);
+ }
+ }
+
+ // From this point forward, we know that the location is not null.
+ C.addTransition(notNullState);
+}
+
+void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
+ CheckerContext &C) const {
+ // If we're binding to a reference, check if the value is known to be null.
+ if (V.isUndef())
+ return;
+
+ const MemRegion *MR = L.getAsRegion();
+ const TypedValueRegion *TVR = dyn_cast_or_null<TypedValueRegion>(MR);
+ if (!TVR)
+ return;
+
+ if (!TVR->getValueType()->isReferenceType())
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ ProgramStateRef StNonNull, StNull;
+ std::tie(StNonNull, StNull) = State->assume(V.castAs<DefinedOrUnknownSVal>());
+
+ if (StNull) {
+ if (!StNonNull) {
+ const Expr *expr = getDereferenceExpr(S, /*IsBind=*/true);
+ if (!suppressReport(expr)) {
+ reportBug(StNull, expr, C);
+ return;
+ }
+ }
+
+ // At this point the value could be either null or non-null.
+ // Record this as an "implicit" null dereference.
+ if (ExplodedNode *N = C.generateSink(StNull, C.getPredecessor())) {
+ ImplicitNullDerefEvent event = {V, /*isLoad=*/true, N,
+ &C.getBugReporter(),
+ /*IsDirectDereference=*/true};
+ dispatchEvent(event);
+ }
+ }
+
+ // Unlike a regular null dereference, initializing a reference with a
+ // dereferenced null pointer does not actually cause a runtime exception in
+ // Clang's implementation of references.
+ //
+ // int &r = *p; // safe??
+ // if (p != NULL) return; // uh-oh
+ // r = 5; // trap here
+ //
+ // The standard says this is invalid as soon as we try to create a "null
+ // reference" (there is no such thing), but turning this into an assumption
+ // that 'p' is never null will not match our actual runtime behavior.
+ // So we do not record this assumption, allowing us to warn on the last line
+ // of this example.
+ //
+ // We do need to add a transition because we may have generated a sink for
+ // the "implicit" null dereference.
+ C.addTransition(State, this);
+}
+
+void ento::registerDereferenceChecker(CheckerManager &mgr) {
+ mgr.registerChecker<DereferenceChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
new file mode 100644
index 000000000000..2a559422df34
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
@@ -0,0 +1,227 @@
+//=- DirectIvarAssignment.cpp - Check rules on ObjC properties -*- C++ ----*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Check that Objective C properties are set with the setter, not though a
+// direct assignment.
+//
+// Two versions of a checker exist: one that checks all methods and the other
+// that only checks the methods annotated with
+// __attribute__((annotate("objc_no_direct_instance_variable_assignment")))
+//
+// The checker does not warn about assignments to Ivars, annotated with
+// __attribute__((objc_allow_direct_instance_variable_assignment"))). This
+// annotation serves as a false positive suppression mechanism for the
+// checker. The annotation is allowed on properties and Ivars.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/DenseMap.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+/// The default method filter, which is used to filter out the methods on which
+/// the check should not be performed.
+///
+/// Checks for the init, dealloc, and any other functions that might be allowed
+/// to perform direct instance variable assignment based on their name.
+static bool DefaultMethodFilter(const ObjCMethodDecl *M) {
+ return M->getMethodFamily() == OMF_init ||
+ M->getMethodFamily() == OMF_dealloc ||
+ M->getMethodFamily() == OMF_copy ||
+ M->getMethodFamily() == OMF_mutableCopy ||
+ M->getSelector().getNameForSlot(0).find("init") != StringRef::npos ||
+ M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos;
+}
+
+class DirectIvarAssignment :
+ public Checker<check::ASTDecl<ObjCImplementationDecl> > {
+
+ typedef llvm::DenseMap<const ObjCIvarDecl*,
+ const ObjCPropertyDecl*> IvarToPropertyMapTy;
+
+ /// A helper class, which walks the AST and locates all assignments to ivars
+ /// in the given function.
+ class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
+ const IvarToPropertyMapTy &IvarToPropMap;
+ const ObjCMethodDecl *MD;
+ const ObjCInterfaceDecl *InterfD;
+ BugReporter &BR;
+ const CheckerBase *Checker;
+ LocationOrAnalysisDeclContext DCtx;
+
+ public:
+ MethodCrawler(const IvarToPropertyMapTy &InMap, const ObjCMethodDecl *InMD,
+ const ObjCInterfaceDecl *InID, BugReporter &InBR,
+ const CheckerBase *Checker, AnalysisDeclContext *InDCtx)
+ : IvarToPropMap(InMap), MD(InMD), InterfD(InID), BR(InBR),
+ Checker(Checker), DCtx(InDCtx) {}
+
+ void VisitStmt(const Stmt *S) { VisitChildren(S); }
+
+ void VisitBinaryOperator(const BinaryOperator *BO);
+
+ void VisitChildren(const Stmt *S) {
+ for (const Stmt *Child : S->children())
+ if (Child)
+ this->Visit(Child);
+ }
+ };
+
+public:
+ bool (*ShouldSkipMethod)(const ObjCMethodDecl *);
+
+ DirectIvarAssignment() : ShouldSkipMethod(&DefaultMethodFilter) {}
+
+ void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
+ BugReporter &BR) const;
+};
+
+static const ObjCIvarDecl *findPropertyBackingIvar(const ObjCPropertyDecl *PD,
+ const ObjCInterfaceDecl *InterD,
+ ASTContext &Ctx) {
+ // Check for synthesized ivars.
+ ObjCIvarDecl *ID = PD->getPropertyIvarDecl();
+ if (ID)
+ return ID;
+
+ ObjCInterfaceDecl *NonConstInterD = const_cast<ObjCInterfaceDecl*>(InterD);
+
+ // Check for existing "_PropName".
+ ID = NonConstInterD->lookupInstanceVariable(PD->getDefaultSynthIvarName(Ctx));
+ if (ID)
+ return ID;
+
+ // Check for existing "PropName".
+ IdentifierInfo *PropIdent = PD->getIdentifier();
+ ID = NonConstInterD->lookupInstanceVariable(PropIdent);
+
+ return ID;
+}
+
+void DirectIvarAssignment::checkASTDecl(const ObjCImplementationDecl *D,
+ AnalysisManager& Mgr,
+ BugReporter &BR) const {
+ const ObjCInterfaceDecl *InterD = D->getClassInterface();
+
+
+ IvarToPropertyMapTy IvarToPropMap;
+
+ // Find all properties for this class.
+ for (const auto *PD : InterD->instance_properties()) {
+ // Find the corresponding IVar.
+ const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterD,
+ Mgr.getASTContext());
+
+ if (!ID)
+ continue;
+
+ // Store the IVar to property mapping.
+ IvarToPropMap[ID] = PD;
+ }
+
+ if (IvarToPropMap.empty())
+ return;
+
+ for (const auto *M : D->instance_methods()) {
+ AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);
+
+ if ((*ShouldSkipMethod)(M))
+ continue;
+
+ const Stmt *Body = M->getBody();
+ assert(Body);
+
+ MethodCrawler MC(IvarToPropMap, M->getCanonicalDecl(), InterD, BR, this,
+ DCtx);
+ MC.VisitStmt(Body);
+ }
+}
+
+static bool isAnnotatedToAllowDirectAssignment(const Decl *D) {
+ for (const auto *Ann : D->specific_attrs<AnnotateAttr>())
+ if (Ann->getAnnotation() ==
+ "objc_allow_direct_instance_variable_assignment")
+ return true;
+ return false;
+}
+
+void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(
+ const BinaryOperator *BO) {
+ if (!BO->isAssignmentOp())
+ return;
+
+ const ObjCIvarRefExpr *IvarRef =
+ dyn_cast<ObjCIvarRefExpr>(BO->getLHS()->IgnoreParenCasts());
+
+ if (!IvarRef)
+ return;
+
+ if (const ObjCIvarDecl *D = IvarRef->getDecl()) {
+ IvarToPropertyMapTy::const_iterator I = IvarToPropMap.find(D);
+
+ if (I != IvarToPropMap.end()) {
+ const ObjCPropertyDecl *PD = I->second;
+ // Skip warnings on Ivars, annotated with
+ // objc_allow_direct_instance_variable_assignment. This annotation serves
+ // as a false positive suppression mechanism for the checker. The
+ // annotation is allowed on properties and ivars.
+ if (isAnnotatedToAllowDirectAssignment(PD) ||
+ isAnnotatedToAllowDirectAssignment(D))
+ return;
+
+ ObjCMethodDecl *GetterMethod =
+ InterfD->getInstanceMethod(PD->getGetterName());
+ ObjCMethodDecl *SetterMethod =
+ InterfD->getInstanceMethod(PD->getSetterName());
+
+ if (SetterMethod && SetterMethod->getCanonicalDecl() == MD)
+ return;
+
+ if (GetterMethod && GetterMethod->getCanonicalDecl() == MD)
+ return;
+
+ BR.EmitBasicReport(
+ MD, Checker, "Property access", categories::CoreFoundationObjectiveC,
+ "Direct assignment to an instance variable backing a property; "
+ "use the setter instead",
+ PathDiagnosticLocation(IvarRef, BR.getSourceManager(), DCtx));
+ }
+ }
+}
+}
+
+// Register the checker that checks for direct accesses in all functions,
+// except for the initialization and copy routines.
+void ento::registerDirectIvarAssignment(CheckerManager &mgr) {
+ mgr.registerChecker<DirectIvarAssignment>();
+}
+
+// Register the checker that checks for direct accesses in functions annotated
+// with __attribute__((annotate("objc_no_direct_instance_variable_assignment"))).
+static bool AttrFilter(const ObjCMethodDecl *M) {
+ for (const auto *Ann : M->specific_attrs<AnnotateAttr>())
+ if (Ann->getAnnotation() == "objc_no_direct_instance_variable_assignment")
+ return false;
+ return true;
+}
+
+void ento::registerDirectIvarAssignmentForAnnotatedFunctions(
+ CheckerManager &mgr) {
+ mgr.registerChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
new file mode 100644
index 000000000000..a220a0513e28
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
@@ -0,0 +1,101 @@
+//== DivZeroChecker.cpp - Division by zero checker --------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines DivZeroChecker, a builtin check in ExprEngine that performs
+// checks for division by zeros.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > {
+ mutable std::unique_ptr<BuiltinBug> BT;
+ void reportBug(const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
+ std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
+
+public:
+ void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+static const Expr *getDenomExpr(const ExplodedNode *N) {
+ const Stmt *S = N->getLocationAs<PreStmt>()->getStmt();
+ if (const auto *BE = dyn_cast<BinaryOperator>(S))
+ return BE->getRHS();
+ return nullptr;
+}
+
+void DivZeroChecker::reportBug(
+ const char *Msg, ProgramStateRef StateZero, CheckerContext &C,
+ std::unique_ptr<BugReporterVisitor> Visitor) const {
+ if (ExplodedNode *N = C.generateErrorNode(StateZero)) {
+ if (!BT)
+ BT.reset(new BuiltinBug(this, "Division by zero"));
+
+ auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
+ R->addVisitor(std::move(Visitor));
+ bugreporter::trackExpressionValue(N, getDenomExpr(N), *R);
+ C.emitReport(std::move(R));
+ }
+}
+
+void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
+ CheckerContext &C) const {
+ BinaryOperator::Opcode Op = B->getOpcode();
+ if (Op != BO_Div &&
+ Op != BO_Rem &&
+ Op != BO_DivAssign &&
+ Op != BO_RemAssign)
+ return;
+
+ if (!B->getRHS()->getType()->isScalarType())
+ return;
+
+ SVal Denom = C.getSVal(B->getRHS());
+ Optional<DefinedSVal> DV = Denom.getAs<DefinedSVal>();
+
+ // Divide-by-undefined handled in the generic checking for uses of
+ // undefined values.
+ if (!DV)
+ return;
+
+ // Check for divide by zero.
+ ConstraintManager &CM = C.getConstraintManager();
+ ProgramStateRef stateNotZero, stateZero;
+ std::tie(stateNotZero, stateZero) = CM.assumeDual(C.getState(), *DV);
+
+ if (!stateNotZero) {
+ assert(stateZero);
+ reportBug("Division by zero", stateZero, C);
+ return;
+ }
+
+ bool TaintedD = C.getState()->isTainted(*DV);
+ if ((stateNotZero && stateZero && TaintedD)) {
+ reportBug("Division by a tainted value, possibly zero", stateZero, C,
+ llvm::make_unique<TaintBugVisitor>(*DV));
+ return;
+ }
+
+ // If we get here, then the denom should not be zero. We abandon the implicit
+ // zero denom case for now.
+ C.addTransition(stateNotZero);
+}
+
+void ento::registerDivZeroChecker(CheckerManager &mgr) {
+ mgr.registerChecker<DivZeroChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
new file mode 100644
index 000000000000..803d7ae22a71
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
@@ -0,0 +1,208 @@
+//== DynamicTypeChecker.cpp ------------------------------------ -*- C++ -*--=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker looks for cases where the dynamic type of an object is unrelated
+// to its static type. The type information utilized by this check is collected
+// by the DynamicTypePropagation checker. This check does not report any type
+// error for ObjC Generic types, in order to avoid duplicate erros from the
+// ObjC Generics checker. This checker is not supposed to modify the program
+// state, it is just the observer of the type information provided by other
+// checkers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class DynamicTypeChecker : public Checker<check::PostStmt<ImplicitCastExpr>> {
+ mutable std::unique_ptr<BugType> BT;
+ void initBugType() const {
+ if (!BT)
+ BT.reset(
+ new BugType(this, "Dynamic and static type mismatch", "Type Error"));
+ }
+
+ class DynamicTypeBugVisitor : public BugReporterVisitor {
+ public:
+ DynamicTypeBugVisitor(const MemRegion *Reg) : Reg(Reg) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int X = 0;
+ ID.AddPointer(&X);
+ ID.AddPointer(Reg);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ private:
+ // The tracked region.
+ const MemRegion *Reg;
+ };
+
+ void reportTypeError(QualType DynamicType, QualType StaticType,
+ const MemRegion *Reg, const Stmt *ReportedNode,
+ CheckerContext &C) const;
+
+public:
+ void checkPostStmt(const ImplicitCastExpr *CE, CheckerContext &C) const;
+};
+}
+
+void DynamicTypeChecker::reportTypeError(QualType DynamicType,
+ QualType StaticType,
+ const MemRegion *Reg,
+ const Stmt *ReportedNode,
+ CheckerContext &C) const {
+ initBugType();
+ SmallString<192> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+ OS << "Object has a dynamic type '";
+ QualType::print(DynamicType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
+ llvm::Twine());
+ OS << "' which is incompatible with static type '";
+ QualType::print(StaticType.getTypePtr(), Qualifiers(), OS, C.getLangOpts(),
+ llvm::Twine());
+ OS << "'";
+ std::unique_ptr<BugReport> R(
+ new BugReport(*BT, OS.str(), C.generateNonFatalErrorNode()));
+ R->markInteresting(Reg);
+ R->addVisitor(llvm::make_unique<DynamicTypeBugVisitor>(Reg));
+ R->addRange(ReportedNode->getSourceRange());
+ C.emitReport(std::move(R));
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+DynamicTypeChecker::DynamicTypeBugVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &) {
+ ProgramStateRef State = N->getState();
+ ProgramStateRef StatePrev = N->getFirstPred()->getState();
+
+ DynamicTypeInfo TrackedType = getDynamicTypeInfo(State, Reg);
+ DynamicTypeInfo TrackedTypePrev = getDynamicTypeInfo(StatePrev, Reg);
+ if (!TrackedType.isValid())
+ return nullptr;
+
+ if (TrackedTypePrev.isValid() &&
+ TrackedTypePrev.getType() == TrackedType.getType())
+ return nullptr;
+
+ // Retrieve the associated statement.
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ if (!S)
+ return nullptr;
+
+ const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
+
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+ OS << "Type '";
+ QualType::print(TrackedType.getType().getTypePtr(), Qualifiers(), OS,
+ LangOpts, llvm::Twine());
+ OS << "' is inferred from ";
+
+ if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
+ OS << "explicit cast (from '";
+ QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
+ Qualifiers(), OS, LangOpts, llvm::Twine());
+ OS << "' to '";
+ QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
+ LangOpts, llvm::Twine());
+ OS << "')";
+ } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
+ OS << "implicit cast (from '";
+ QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
+ Qualifiers(), OS, LangOpts, llvm::Twine());
+ OS << "' to '";
+ QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
+ LangOpts, llvm::Twine());
+ OS << "')";
+ } else {
+ OS << "this context";
+ }
+
+ // Generate the extra diagnostic.
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
+ nullptr);
+}
+
+static bool hasDefinition(const ObjCObjectPointerType *ObjPtr) {
+ const ObjCInterfaceDecl *Decl = ObjPtr->getInterfaceDecl();
+ if (!Decl)
+ return false;
+
+ return Decl->getDefinition();
+}
+
+// TODO: consider checking explicit casts?
+void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
+ CheckerContext &C) const {
+ // TODO: C++ support.
+ if (CE->getCastKind() != CK_BitCast)
+ return;
+
+ const MemRegion *Region = C.getSVal(CE).getAsRegion();
+ if (!Region)
+ return;
+
+ ProgramStateRef State = C.getState();
+ DynamicTypeInfo DynTypeInfo = getDynamicTypeInfo(State, Region);
+
+ if (!DynTypeInfo.isValid())
+ return;
+
+ QualType DynType = DynTypeInfo.getType();
+ QualType StaticType = CE->getType();
+
+ const auto *DynObjCType = DynType->getAs<ObjCObjectPointerType>();
+ const auto *StaticObjCType = StaticType->getAs<ObjCObjectPointerType>();
+
+ if (!DynObjCType || !StaticObjCType)
+ return;
+
+ if (!hasDefinition(DynObjCType) || !hasDefinition(StaticObjCType))
+ return;
+
+ ASTContext &ASTCtxt = C.getASTContext();
+
+ // Strip kindeofness to correctly detect subtyping relationships.
+ DynObjCType = DynObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
+ StaticObjCType = StaticObjCType->stripObjCKindOfTypeAndQuals(ASTCtxt);
+
+ // Specialized objects are handled by the generics checker.
+ if (StaticObjCType->isSpecialized())
+ return;
+
+ if (ASTCtxt.canAssignObjCInterfaces(StaticObjCType, DynObjCType))
+ return;
+
+ if (DynTypeInfo.canBeASubClass() &&
+ ASTCtxt.canAssignObjCInterfaces(DynObjCType, StaticObjCType))
+ return;
+
+ reportTypeError(DynType, StaticType, Region, CE, C);
+}
+
+void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
+ mgr.registerChecker<DynamicTypeChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
new file mode 100644
index 000000000000..31d4eebe8968
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -0,0 +1,998 @@
+//===- DynamicTypePropagation.cpp ------------------------------*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains two checkers. One helps the static analyzer core to track
+// types, the other does type inference on Obj-C generics and report type
+// errors.
+//
+// Dynamic Type Propagation:
+// This checker defines the rules for dynamic type gathering and propagation.
+//
+// Generics Checker for Objective-C:
+// This checker tries to find type errors that the compiler is not able to catch
+// due to the implicit conversions that were introduced for backward
+// compatibility.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+
+using namespace clang;
+using namespace ento;
+
+// ProgramState trait - The type inflation is tracked by DynamicTypeMap. This is
+// an auxiliary map that tracks more information about generic types, because in
+// some cases the most derived type is not the most informative one about the
+// type parameters. This types that are stored for each symbol in this map must
+// be specialized.
+// TODO: In some case the type stored in this map is exactly the same that is
+// stored in DynamicTypeMap. We should no store duplicated information in those
+// cases.
+REGISTER_MAP_WITH_PROGRAMSTATE(MostSpecializedTypeArgsMap, SymbolRef,
+ const ObjCObjectPointerType *)
+
+namespace {
+class DynamicTypePropagation:
+ public Checker< check::PreCall,
+ check::PostCall,
+ check::DeadSymbols,
+ check::PostStmt<CastExpr>,
+ check::PostStmt<CXXNewExpr>,
+ check::PreObjCMessage,
+ check::PostObjCMessage > {
+ const ObjCObjectType *getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
+ CheckerContext &C) const;
+
+ /// Return a better dynamic type if one can be derived from the cast.
+ const ObjCObjectPointerType *getBetterObjCType(const Expr *CastE,
+ CheckerContext &C) const;
+
+ ExplodedNode *dynamicTypePropagationOnCasts(const CastExpr *CE,
+ ProgramStateRef &State,
+ CheckerContext &C) const;
+
+ mutable std::unique_ptr<BugType> ObjCGenericsBugType;
+ void initBugType() const {
+ if (!ObjCGenericsBugType)
+ ObjCGenericsBugType.reset(
+ new BugType(this, "Generics", categories::CoreFoundationObjectiveC));
+ }
+
+ class GenericsBugVisitor : public BugReporterVisitor {
+ public:
+ GenericsBugVisitor(SymbolRef S) : Sym(S) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int X = 0;
+ ID.AddPointer(&X);
+ ID.AddPointer(Sym);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ private:
+ // The tracked symbol.
+ SymbolRef Sym;
+ };
+
+ void reportGenericsBug(const ObjCObjectPointerType *From,
+ const ObjCObjectPointerType *To, ExplodedNode *N,
+ SymbolRef Sym, CheckerContext &C,
+ const Stmt *ReportedNode = nullptr) const;
+
+public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostStmt(const CastExpr *CastE, CheckerContext &C) const;
+ void checkPostStmt(const CXXNewExpr *NewE, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+ void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+
+ /// This value is set to true, when the Generics checker is turned on.
+ DefaultBool CheckGenerics;
+};
+} // end anonymous namespace
+
+void DynamicTypePropagation::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ DynamicTypeMapImpl TypeMap = State->get<DynamicTypeMap>();
+ for (DynamicTypeMapImpl::iterator I = TypeMap.begin(), E = TypeMap.end();
+ I != E; ++I) {
+ if (!SR.isLiveRegion(I->first)) {
+ State = State->remove<DynamicTypeMap>(I->first);
+ }
+ }
+
+ MostSpecializedTypeArgsMapTy TyArgMap =
+ State->get<MostSpecializedTypeArgsMap>();
+ for (MostSpecializedTypeArgsMapTy::iterator I = TyArgMap.begin(),
+ E = TyArgMap.end();
+ I != E; ++I) {
+ if (SR.isDead(I->first)) {
+ State = State->remove<MostSpecializedTypeArgsMap>(I->first);
+ }
+ }
+
+ C.addTransition(State);
+}
+
+static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD,
+ CheckerContext &C) {
+ assert(Region);
+ assert(MD);
+
+ ASTContext &Ctx = C.getASTContext();
+ QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent()));
+
+ ProgramStateRef State = C.getState();
+ State = setDynamicTypeInfo(State, Region, Ty, /*CanBeSubclass=*/false);
+ C.addTransition(State);
+}
+
+void DynamicTypePropagation::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
+ // C++11 [class.cdtor]p4: When a virtual function is called directly or
+ // indirectly from a constructor or from a destructor, including during
+ // the construction or destruction of the class's non-static data members,
+ // and the object to which the call applies is the object under
+ // construction or destruction, the function called is the final overrider
+ // in the constructor's or destructor's class and not one overriding it in
+ // a more-derived class.
+
+ switch (Ctor->getOriginExpr()->getConstructionKind()) {
+ case CXXConstructExpr::CK_Complete:
+ case CXXConstructExpr::CK_Delegating:
+ // No additional type info necessary.
+ return;
+ case CXXConstructExpr::CK_NonVirtualBase:
+ case CXXConstructExpr::CK_VirtualBase:
+ if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion())
+ recordFixedType(Target, Ctor->getDecl(), C);
+ return;
+ }
+
+ return;
+ }
+
+ if (const CXXDestructorCall *Dtor = dyn_cast<CXXDestructorCall>(&Call)) {
+ // C++11 [class.cdtor]p4 (see above)
+ if (!Dtor->isBaseDestructor())
+ return;
+
+ const MemRegion *Target = Dtor->getCXXThisVal().getAsRegion();
+ if (!Target)
+ return;
+
+ const Decl *D = Dtor->getDecl();
+ if (!D)
+ return;
+
+ recordFixedType(Target, cast<CXXDestructorDecl>(D), C);
+ return;
+ }
+}
+
+void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // We can obtain perfect type info for return values from some calls.
+ if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {
+
+ // Get the returned value if it's a region.
+ const MemRegion *RetReg = Call.getReturnValue().getAsRegion();
+ if (!RetReg)
+ return;
+
+ ProgramStateRef State = C.getState();
+ const ObjCMethodDecl *D = Msg->getDecl();
+
+ if (D && D->hasRelatedResultType()) {
+ switch (Msg->getMethodFamily()) {
+ default:
+ break;
+
+ // We assume that the type of the object returned by alloc and new are the
+ // pointer to the object of the class specified in the receiver of the
+ // message.
+ case OMF_alloc:
+ case OMF_new: {
+ // Get the type of object that will get created.
+ const ObjCMessageExpr *MsgE = Msg->getOriginExpr();
+ const ObjCObjectType *ObjTy = getObjectTypeForAllocAndNew(MsgE, C);
+ if (!ObjTy)
+ return;
+ QualType DynResTy =
+ C.getASTContext().getObjCObjectPointerType(QualType(ObjTy, 0));
+ C.addTransition(setDynamicTypeInfo(State, RetReg, DynResTy, false));
+ break;
+ }
+ case OMF_init: {
+ // Assume, the result of the init method has the same dynamic type as
+ // the receiver and propagate the dynamic type info.
+ const MemRegion *RecReg = Msg->getReceiverSVal().getAsRegion();
+ if (!RecReg)
+ return;
+ DynamicTypeInfo RecDynType = getDynamicTypeInfo(State, RecReg);
+ C.addTransition(setDynamicTypeInfo(State, RetReg, RecDynType));
+ break;
+ }
+ }
+ }
+ return;
+ }
+
+ if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
+ // We may need to undo the effects of our pre-call check.
+ switch (Ctor->getOriginExpr()->getConstructionKind()) {
+ case CXXConstructExpr::CK_Complete:
+ case CXXConstructExpr::CK_Delegating:
+ // No additional work necessary.
+ // Note: This will leave behind the actual type of the object for
+ // complete constructors, but arguably that's a good thing, since it
+ // means the dynamic type info will be correct even for objects
+ // constructed with operator new.
+ return;
+ case CXXConstructExpr::CK_NonVirtualBase:
+ case CXXConstructExpr::CK_VirtualBase:
+ if (const MemRegion *Target = Ctor->getCXXThisVal().getAsRegion()) {
+ // We just finished a base constructor. Now we can use the subclass's
+ // type when resolving virtual calls.
+ const LocationContext *LCtx = C.getLocationContext();
+
+ // FIXME: In C++17 classes with non-virtual bases may be treated as
+ // aggregates, and in such case no top-frame constructor will be called.
+ // Figure out if we need to do anything in this case.
+ // FIXME: Instead of relying on the ParentMap, we should have the
+ // trigger-statement (InitListExpr in this case) available in this
+ // callback, ideally as part of CallEvent.
+ if (dyn_cast_or_null<InitListExpr>(
+ LCtx->getParentMap().getParent(Ctor->getOriginExpr())))
+ return;
+
+ recordFixedType(Target, cast<CXXConstructorDecl>(LCtx->getDecl()), C);
+ }
+ return;
+ }
+ }
+}
+
+/// TODO: Handle explicit casts.
+/// Handle C++ casts.
+///
+/// Precondition: the cast is between ObjCObjectPointers.
+ExplodedNode *DynamicTypePropagation::dynamicTypePropagationOnCasts(
+ const CastExpr *CE, ProgramStateRef &State, CheckerContext &C) const {
+ // We only track type info for regions.
+ const MemRegion *ToR = C.getSVal(CE).getAsRegion();
+ if (!ToR)
+ return C.getPredecessor();
+
+ if (isa<ExplicitCastExpr>(CE))
+ return C.getPredecessor();
+
+ if (const Type *NewTy = getBetterObjCType(CE, C)) {
+ State = setDynamicTypeInfo(State, ToR, QualType(NewTy, 0));
+ return C.addTransition(State);
+ }
+ return C.getPredecessor();
+}
+
+void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE,
+ CheckerContext &C) const {
+ if (NewE->isArray())
+ return;
+
+ // We only track dynamic type info for regions.
+ const MemRegion *MR = C.getSVal(NewE).getAsRegion();
+ if (!MR)
+ return;
+
+ C.addTransition(setDynamicTypeInfo(C.getState(), MR, NewE->getType(),
+ /*CanBeSubclass=*/false));
+}
+
+const ObjCObjectType *
+DynamicTypePropagation::getObjectTypeForAllocAndNew(const ObjCMessageExpr *MsgE,
+ CheckerContext &C) const {
+ if (MsgE->getReceiverKind() == ObjCMessageExpr::Class) {
+ if (const ObjCObjectType *ObjTy
+ = MsgE->getClassReceiver()->getAs<ObjCObjectType>())
+ return ObjTy;
+ }
+
+ if (MsgE->getReceiverKind() == ObjCMessageExpr::SuperClass) {
+ if (const ObjCObjectType *ObjTy
+ = MsgE->getSuperType()->getAs<ObjCObjectType>())
+ return ObjTy;
+ }
+
+ const Expr *RecE = MsgE->getInstanceReceiver();
+ if (!RecE)
+ return nullptr;
+
+ RecE= RecE->IgnoreParenImpCasts();
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(RecE)) {
+ const StackFrameContext *SFCtx = C.getStackFrame();
+ // Are we calling [self alloc]? If this is self, get the type of the
+ // enclosing ObjC class.
+ if (DRE->getDecl() == SFCtx->getSelfDecl()) {
+ if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(SFCtx->getDecl()))
+ if (const ObjCObjectType *ObjTy =
+ dyn_cast<ObjCObjectType>(MD->getClassInterface()->getTypeForDecl()))
+ return ObjTy;
+ }
+ }
+ return nullptr;
+}
+
+// Return a better dynamic type if one can be derived from the cast.
+// Compare the current dynamic type of the region and the new type to which we
+// are casting. If the new type is lower in the inheritance hierarchy, pick it.
+const ObjCObjectPointerType *
+DynamicTypePropagation::getBetterObjCType(const Expr *CastE,
+ CheckerContext &C) const {
+ const MemRegion *ToR = C.getSVal(CastE).getAsRegion();
+ assert(ToR);
+
+ // Get the old and new types.
+ const ObjCObjectPointerType *NewTy =
+ CastE->getType()->getAs<ObjCObjectPointerType>();
+ if (!NewTy)
+ return nullptr;
+ QualType OldDTy = getDynamicTypeInfo(C.getState(), ToR).getType();
+ if (OldDTy.isNull()) {
+ return NewTy;
+ }
+ const ObjCObjectPointerType *OldTy =
+ OldDTy->getAs<ObjCObjectPointerType>();
+ if (!OldTy)
+ return nullptr;
+
+ // Id the old type is 'id', the new one is more precise.
+ if (OldTy->isObjCIdType() && !NewTy->isObjCIdType())
+ return NewTy;
+
+ // Return new if it's a subclass of old.
+ const ObjCInterfaceDecl *ToI = NewTy->getInterfaceDecl();
+ const ObjCInterfaceDecl *FromI = OldTy->getInterfaceDecl();
+ if (ToI && FromI && FromI->isSuperClassOf(ToI))
+ return NewTy;
+
+ return nullptr;
+}
+
+static const ObjCObjectPointerType *getMostInformativeDerivedClassImpl(
+ const ObjCObjectPointerType *From, const ObjCObjectPointerType *To,
+ const ObjCObjectPointerType *MostInformativeCandidate, ASTContext &C) {
+ // Checking if from and to are the same classes modulo specialization.
+ if (From->getInterfaceDecl()->getCanonicalDecl() ==
+ To->getInterfaceDecl()->getCanonicalDecl()) {
+ if (To->isSpecialized()) {
+ assert(MostInformativeCandidate->isSpecialized());
+ return MostInformativeCandidate;
+ }
+ return From;
+ }
+
+ if (To->getObjectType()->getSuperClassType().isNull()) {
+ // If To has no super class and From and To aren't the same then
+ // To was not actually a descendent of From. In this case the best we can
+ // do is 'From'.
+ return From;
+ }
+
+ const auto *SuperOfTo =
+ To->getObjectType()->getSuperClassType()->getAs<ObjCObjectType>();
+ assert(SuperOfTo);
+ QualType SuperPtrOfToQual =
+ C.getObjCObjectPointerType(QualType(SuperOfTo, 0));
+ const auto *SuperPtrOfTo = SuperPtrOfToQual->getAs<ObjCObjectPointerType>();
+ if (To->isUnspecialized())
+ return getMostInformativeDerivedClassImpl(From, SuperPtrOfTo, SuperPtrOfTo,
+ C);
+ else
+ return getMostInformativeDerivedClassImpl(From, SuperPtrOfTo,
+ MostInformativeCandidate, C);
+}
+
+/// A downcast may loose specialization information. E. g.:
+/// MutableMap<T, U> : Map
+/// The downcast to MutableMap looses the information about the types of the
+/// Map (due to the type parameters are not being forwarded to Map), and in
+/// general there is no way to recover that information from the
+/// declaration. In order to have to most information, lets find the most
+/// derived type that has all the type parameters forwarded.
+///
+/// Get the a subclass of \p From (which has a lower bound \p To) that do not
+/// loose information about type parameters. \p To has to be a subclass of
+/// \p From. From has to be specialized.
+static const ObjCObjectPointerType *
+getMostInformativeDerivedClass(const ObjCObjectPointerType *From,
+ const ObjCObjectPointerType *To, ASTContext &C) {
+ return getMostInformativeDerivedClassImpl(From, To, To, C);
+}
+
+/// Inputs:
+/// \param StaticLowerBound Static lower bound for a symbol. The dynamic lower
+/// bound might be the subclass of this type.
+/// \param StaticUpperBound A static upper bound for a symbol.
+/// \p StaticLowerBound expected to be the subclass of \p StaticUpperBound.
+/// \param Current The type that was inferred for a symbol in a previous
+/// context. Might be null when this is the first time that inference happens.
+/// Precondition:
+/// \p StaticLowerBound or \p StaticUpperBound is specialized. If \p Current
+/// is not null, it is specialized.
+/// Possible cases:
+/// (1) The \p Current is null and \p StaticLowerBound <: \p StaticUpperBound
+/// (2) \p StaticLowerBound <: \p Current <: \p StaticUpperBound
+/// (3) \p Current <: \p StaticLowerBound <: \p StaticUpperBound
+/// (4) \p StaticLowerBound <: \p StaticUpperBound <: \p Current
+/// Effect:
+/// Use getMostInformativeDerivedClass with the upper and lower bound of the
+/// set {\p StaticLowerBound, \p Current, \p StaticUpperBound}. The computed
+/// lower bound must be specialized. If the result differs from \p Current or
+/// \p Current is null, store the result.
+static bool
+storeWhenMoreInformative(ProgramStateRef &State, SymbolRef Sym,
+ const ObjCObjectPointerType *const *Current,
+ const ObjCObjectPointerType *StaticLowerBound,
+ const ObjCObjectPointerType *StaticUpperBound,
+ ASTContext &C) {
+ // TODO: The above 4 cases are not exhaustive. In particular, it is possible
+ // for Current to be incomparable with StaticLowerBound, StaticUpperBound,
+ // or both.
+ //
+ // For example, suppose Foo<T> and Bar<T> are unrelated types.
+ //
+ // Foo<T> *f = ...
+ // Bar<T> *b = ...
+ //
+ // id t1 = b;
+ // f = t1;
+ // id t2 = f; // StaticLowerBound is Foo<T>, Current is Bar<T>
+ //
+ // We should either constrain the callers of this function so that the stated
+ // preconditions hold (and assert it) or rewrite the function to expicitly
+ // handle the additional cases.
+
+ // Precondition
+ assert(StaticUpperBound->isSpecialized() ||
+ StaticLowerBound->isSpecialized());
+ assert(!Current || (*Current)->isSpecialized());
+
+ // Case (1)
+ if (!Current) {
+ if (StaticUpperBound->isUnspecialized()) {
+ State = State->set<MostSpecializedTypeArgsMap>(Sym, StaticLowerBound);
+ return true;
+ }
+ // Upper bound is specialized.
+ const ObjCObjectPointerType *WithMostInfo =
+ getMostInformativeDerivedClass(StaticUpperBound, StaticLowerBound, C);
+ State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo);
+ return true;
+ }
+
+ // Case (3)
+ if (C.canAssignObjCInterfaces(StaticLowerBound, *Current)) {
+ return false;
+ }
+
+ // Case (4)
+ if (C.canAssignObjCInterfaces(*Current, StaticUpperBound)) {
+ // The type arguments might not be forwarded at any point of inheritance.
+ const ObjCObjectPointerType *WithMostInfo =
+ getMostInformativeDerivedClass(*Current, StaticUpperBound, C);
+ WithMostInfo =
+ getMostInformativeDerivedClass(WithMostInfo, StaticLowerBound, C);
+ if (WithMostInfo == *Current)
+ return false;
+ State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo);
+ return true;
+ }
+
+ // Case (2)
+ const ObjCObjectPointerType *WithMostInfo =
+ getMostInformativeDerivedClass(*Current, StaticLowerBound, C);
+ if (WithMostInfo != *Current) {
+ State = State->set<MostSpecializedTypeArgsMap>(Sym, WithMostInfo);
+ return true;
+ }
+
+ return false;
+}
+
+/// Type inference based on static type information that is available for the
+/// cast and the tracked type information for the given symbol. When the tracked
+/// symbol and the destination type of the cast are unrelated, report an error.
+void DynamicTypePropagation::checkPostStmt(const CastExpr *CE,
+ CheckerContext &C) const {
+ if (CE->getCastKind() != CK_BitCast)
+ return;
+
+ QualType OriginType = CE->getSubExpr()->getType();
+ QualType DestType = CE->getType();
+
+ const auto *OrigObjectPtrType = OriginType->getAs<ObjCObjectPointerType>();
+ const auto *DestObjectPtrType = DestType->getAs<ObjCObjectPointerType>();
+
+ if (!OrigObjectPtrType || !DestObjectPtrType)
+ return;
+
+ ProgramStateRef State = C.getState();
+ ExplodedNode *AfterTypeProp = dynamicTypePropagationOnCasts(CE, State, C);
+
+ ASTContext &ASTCtxt = C.getASTContext();
+
+ // This checker detects the subtyping relationships using the assignment
+ // rules. In order to be able to do this the kindofness must be stripped
+ // first. The checker treats every type as kindof type anyways: when the
+ // tracked type is the subtype of the static type it tries to look up the
+ // methods in the tracked type first.
+ OrigObjectPtrType = OrigObjectPtrType->stripObjCKindOfTypeAndQuals(ASTCtxt);
+ DestObjectPtrType = DestObjectPtrType->stripObjCKindOfTypeAndQuals(ASTCtxt);
+
+ if (OrigObjectPtrType->isUnspecialized() &&
+ DestObjectPtrType->isUnspecialized())
+ return;
+
+ SymbolRef Sym = C.getSVal(CE).getAsSymbol();
+ if (!Sym)
+ return;
+
+ const ObjCObjectPointerType *const *TrackedType =
+ State->get<MostSpecializedTypeArgsMap>(Sym);
+
+ if (isa<ExplicitCastExpr>(CE)) {
+ // Treat explicit casts as an indication from the programmer that the
+ // Objective-C type system is not rich enough to express the needed
+ // invariant. In such cases, forget any existing information inferred
+ // about the type arguments. We don't assume the casted-to specialized
+ // type here because the invariant the programmer specifies in the cast
+ // may only hold at this particular program point and not later ones.
+ // We don't want a suppressing cast to require a cascade of casts down the
+ // line.
+ if (TrackedType) {
+ State = State->remove<MostSpecializedTypeArgsMap>(Sym);
+ C.addTransition(State, AfterTypeProp);
+ }
+ return;
+ }
+
+ // Check which assignments are legal.
+ bool OrigToDest =
+ ASTCtxt.canAssignObjCInterfaces(DestObjectPtrType, OrigObjectPtrType);
+ bool DestToOrig =
+ ASTCtxt.canAssignObjCInterfaces(OrigObjectPtrType, DestObjectPtrType);
+
+ // The tracked type should be the sub or super class of the static destination
+ // type. When an (implicit) upcast or a downcast happens according to static
+ // types, and there is no subtyping relationship between the tracked and the
+ // static destination types, it indicates an error.
+ if (TrackedType &&
+ !ASTCtxt.canAssignObjCInterfaces(DestObjectPtrType, *TrackedType) &&
+ !ASTCtxt.canAssignObjCInterfaces(*TrackedType, DestObjectPtrType)) {
+ static CheckerProgramPointTag IllegalConv(this, "IllegalConversion");
+ ExplodedNode *N = C.addTransition(State, AfterTypeProp, &IllegalConv);
+ reportGenericsBug(*TrackedType, DestObjectPtrType, N, Sym, C);
+ return;
+ }
+
+ // Handle downcasts and upcasts.
+
+ const ObjCObjectPointerType *LowerBound = DestObjectPtrType;
+ const ObjCObjectPointerType *UpperBound = OrigObjectPtrType;
+ if (OrigToDest && !DestToOrig)
+ std::swap(LowerBound, UpperBound);
+
+ // The id type is not a real bound. Eliminate it.
+ LowerBound = LowerBound->isObjCIdType() ? UpperBound : LowerBound;
+ UpperBound = UpperBound->isObjCIdType() ? LowerBound : UpperBound;
+
+ if (storeWhenMoreInformative(State, Sym, TrackedType, LowerBound, UpperBound,
+ ASTCtxt)) {
+ C.addTransition(State, AfterTypeProp);
+ }
+}
+
+static const Expr *stripCastsAndSugar(const Expr *E) {
+ E = E->IgnoreParenImpCasts();
+ if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
+ E = POE->getSyntacticForm()->IgnoreParenImpCasts();
+ if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
+ E = OVE->getSourceExpr()->IgnoreParenImpCasts();
+ return E;
+}
+
+static bool isObjCTypeParamDependent(QualType Type) {
+ // It is illegal to typedef parameterized types inside an interface. Therefore
+ // an Objective-C type can only be dependent on a type parameter when the type
+ // parameter structurally present in the type itself.
+ class IsObjCTypeParamDependentTypeVisitor
+ : public RecursiveASTVisitor<IsObjCTypeParamDependentTypeVisitor> {
+ public:
+ IsObjCTypeParamDependentTypeVisitor() : Result(false) {}
+ bool VisitObjCTypeParamType(const ObjCTypeParamType *Type) {
+ if (isa<ObjCTypeParamDecl>(Type->getDecl())) {
+ Result = true;
+ return false;
+ }
+ return true;
+ }
+
+ bool Result;
+ };
+
+ IsObjCTypeParamDependentTypeVisitor Visitor;
+ Visitor.TraverseType(Type);
+ return Visitor.Result;
+}
+
+/// A method might not be available in the interface indicated by the static
+/// type. However it might be available in the tracked type. In order to
+/// properly substitute the type parameters we need the declaration context of
+/// the method. The more specialized the enclosing class of the method is, the
+/// more likely that the parameter substitution will be successful.
+static const ObjCMethodDecl *
+findMethodDecl(const ObjCMessageExpr *MessageExpr,
+ const ObjCObjectPointerType *TrackedType, ASTContext &ASTCtxt) {
+ const ObjCMethodDecl *Method = nullptr;
+
+ QualType ReceiverType = MessageExpr->getReceiverType();
+ const auto *ReceiverObjectPtrType =
+ ReceiverType->getAs<ObjCObjectPointerType>();
+
+ // Do this "devirtualization" on instance and class methods only. Trust the
+ // static type on super and super class calls.
+ if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Instance ||
+ MessageExpr->getReceiverKind() == ObjCMessageExpr::Class) {
+ // When the receiver type is id, Class, or some super class of the tracked
+ // type, look up the method in the tracked type, not in the receiver type.
+ // This way we preserve more information.
+ if (ReceiverType->isObjCIdType() || ReceiverType->isObjCClassType() ||
+ ASTCtxt.canAssignObjCInterfaces(ReceiverObjectPtrType, TrackedType)) {
+ const ObjCInterfaceDecl *InterfaceDecl = TrackedType->getInterfaceDecl();
+ // The method might not be found.
+ Selector Sel = MessageExpr->getSelector();
+ Method = InterfaceDecl->lookupInstanceMethod(Sel);
+ if (!Method)
+ Method = InterfaceDecl->lookupClassMethod(Sel);
+ }
+ }
+
+ // Fallback to statick method lookup when the one based on the tracked type
+ // failed.
+ return Method ? Method : MessageExpr->getMethodDecl();
+}
+
+/// Get the returned ObjCObjectPointerType by a method based on the tracked type
+/// information, or null pointer when the returned type is not an
+/// ObjCObjectPointerType.
+static QualType getReturnTypeForMethod(
+ const ObjCMethodDecl *Method, ArrayRef<QualType> TypeArgs,
+ const ObjCObjectPointerType *SelfType, ASTContext &C) {
+ QualType StaticResultType = Method->getReturnType();
+
+ // Is the return type declared as instance type?
+ if (StaticResultType == C.getObjCInstanceType())
+ return QualType(SelfType, 0);
+
+ // Check whether the result type depends on a type parameter.
+ if (!isObjCTypeParamDependent(StaticResultType))
+ return QualType();
+
+ QualType ResultType = StaticResultType.substObjCTypeArgs(
+ C, TypeArgs, ObjCSubstitutionContext::Result);
+
+ return ResultType;
+}
+
+/// When the receiver has a tracked type, use that type to validate the
+/// argumments of the message expression and the return value.
+void DynamicTypePropagation::checkPreObjCMessage(const ObjCMethodCall &M,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SymbolRef Sym = M.getReceiverSVal().getAsSymbol();
+ if (!Sym)
+ return;
+
+ const ObjCObjectPointerType *const *TrackedType =
+ State->get<MostSpecializedTypeArgsMap>(Sym);
+ if (!TrackedType)
+ return;
+
+ // Get the type arguments from tracked type and substitute type arguments
+ // before do the semantic check.
+
+ ASTContext &ASTCtxt = C.getASTContext();
+ const ObjCMessageExpr *MessageExpr = M.getOriginExpr();
+ const ObjCMethodDecl *Method =
+ findMethodDecl(MessageExpr, *TrackedType, ASTCtxt);
+
+ // It is possible to call non-existent methods in Obj-C.
+ if (!Method)
+ return;
+
+ // If the method is declared on a class that has a non-invariant
+ // type parameter, don't warn about parameter mismatches after performing
+ // substitution. This prevents warning when the programmer has purposely
+ // casted the receiver to a super type or unspecialized type but the analyzer
+ // has a more precise tracked type than the programmer intends at the call
+ // site.
+ //
+ // For example, consider NSArray (which has a covariant type parameter)
+ // and NSMutableArray (a subclass of NSArray where the type parameter is
+ // invariant):
+ // NSMutableArray *a = [[NSMutableArray<NSString *> alloc] init;
+ //
+ // [a containsObject:number]; // Safe: -containsObject is defined on NSArray.
+ // NSArray<NSObject *> *other = [a arrayByAddingObject:number] // Safe
+ //
+ // [a addObject:number] // Unsafe: -addObject: is defined on NSMutableArray
+ //
+
+ const ObjCInterfaceDecl *Interface = Method->getClassInterface();
+ if (!Interface)
+ return;
+
+ ObjCTypeParamList *TypeParams = Interface->getTypeParamList();
+ if (!TypeParams)
+ return;
+
+ for (ObjCTypeParamDecl *TypeParam : *TypeParams) {
+ if (TypeParam->getVariance() != ObjCTypeParamVariance::Invariant)
+ return;
+ }
+
+ Optional<ArrayRef<QualType>> TypeArgs =
+ (*TrackedType)->getObjCSubstitutions(Method->getDeclContext());
+ // This case might happen when there is an unspecialized override of a
+ // specialized method.
+ if (!TypeArgs)
+ return;
+
+ for (unsigned i = 0; i < Method->param_size(); i++) {
+ const Expr *Arg = MessageExpr->getArg(i);
+ const ParmVarDecl *Param = Method->parameters()[i];
+
+ QualType OrigParamType = Param->getType();
+ if (!isObjCTypeParamDependent(OrigParamType))
+ continue;
+
+ QualType ParamType = OrigParamType.substObjCTypeArgs(
+ ASTCtxt, *TypeArgs, ObjCSubstitutionContext::Parameter);
+ // Check if it can be assigned
+ const auto *ParamObjectPtrType = ParamType->getAs<ObjCObjectPointerType>();
+ const auto *ArgObjectPtrType =
+ stripCastsAndSugar(Arg)->getType()->getAs<ObjCObjectPointerType>();
+ if (!ParamObjectPtrType || !ArgObjectPtrType)
+ continue;
+
+ // Check if we have more concrete tracked type that is not a super type of
+ // the static argument type.
+ SVal ArgSVal = M.getArgSVal(i);
+ SymbolRef ArgSym = ArgSVal.getAsSymbol();
+ if (ArgSym) {
+ const ObjCObjectPointerType *const *TrackedArgType =
+ State->get<MostSpecializedTypeArgsMap>(ArgSym);
+ if (TrackedArgType &&
+ ASTCtxt.canAssignObjCInterfaces(ArgObjectPtrType, *TrackedArgType)) {
+ ArgObjectPtrType = *TrackedArgType;
+ }
+ }
+
+ // Warn when argument is incompatible with the parameter.
+ if (!ASTCtxt.canAssignObjCInterfaces(ParamObjectPtrType,
+ ArgObjectPtrType)) {
+ static CheckerProgramPointTag Tag(this, "ArgTypeMismatch");
+ ExplodedNode *N = C.addTransition(State, &Tag);
+ reportGenericsBug(ArgObjectPtrType, ParamObjectPtrType, N, Sym, C, Arg);
+ return;
+ }
+ }
+}
+
+/// This callback is used to infer the types for Class variables. This info is
+/// used later to validate messages that sent to classes. Class variables are
+/// initialized with by invoking the 'class' method on a class.
+/// This method is also used to infer the type information for the return
+/// types.
+// TODO: right now it only tracks generic types. Extend this to track every
+// type in the DynamicTypeMap and diagnose type errors!
+void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M,
+ CheckerContext &C) const {
+ const ObjCMessageExpr *MessageExpr = M.getOriginExpr();
+
+ SymbolRef RetSym = M.getReturnValue().getAsSymbol();
+ if (!RetSym)
+ return;
+
+ Selector Sel = MessageExpr->getSelector();
+ ProgramStateRef State = C.getState();
+ // Inference for class variables.
+ // We are only interested in cases where the class method is invoked on a
+ // class. This method is provided by the runtime and available on all classes.
+ if (MessageExpr->getReceiverKind() == ObjCMessageExpr::Class &&
+ Sel.getAsString() == "class") {
+ QualType ReceiverType = MessageExpr->getClassReceiver();
+ const auto *ReceiverClassType = ReceiverType->getAs<ObjCObjectType>();
+ QualType ReceiverClassPointerType =
+ C.getASTContext().getObjCObjectPointerType(
+ QualType(ReceiverClassType, 0));
+
+ if (!ReceiverClassType->isSpecialized())
+ return;
+ const auto *InferredType =
+ ReceiverClassPointerType->getAs<ObjCObjectPointerType>();
+ assert(InferredType);
+
+ State = State->set<MostSpecializedTypeArgsMap>(RetSym, InferredType);
+ C.addTransition(State);
+ return;
+ }
+
+ // Tracking for return types.
+ SymbolRef RecSym = M.getReceiverSVal().getAsSymbol();
+ if (!RecSym)
+ return;
+
+ const ObjCObjectPointerType *const *TrackedType =
+ State->get<MostSpecializedTypeArgsMap>(RecSym);
+ if (!TrackedType)
+ return;
+
+ ASTContext &ASTCtxt = C.getASTContext();
+ const ObjCMethodDecl *Method =
+ findMethodDecl(MessageExpr, *TrackedType, ASTCtxt);
+ if (!Method)
+ return;
+
+ Optional<ArrayRef<QualType>> TypeArgs =
+ (*TrackedType)->getObjCSubstitutions(Method->getDeclContext());
+ if (!TypeArgs)
+ return;
+
+ QualType ResultType =
+ getReturnTypeForMethod(Method, *TypeArgs, *TrackedType, ASTCtxt);
+ // The static type is the same as the deduced type.
+ if (ResultType.isNull())
+ return;
+
+ const MemRegion *RetRegion = M.getReturnValue().getAsRegion();
+ ExplodedNode *Pred = C.getPredecessor();
+ // When there is an entry available for the return symbol in DynamicTypeMap,
+ // the call was inlined, and the information in the DynamicTypeMap is should
+ // be precise.
+ if (RetRegion && !State->get<DynamicTypeMap>(RetRegion)) {
+ // TODO: we have duplicated information in DynamicTypeMap and
+ // MostSpecializedTypeArgsMap. We should only store anything in the later if
+ // the stored data differs from the one stored in the former.
+ State = setDynamicTypeInfo(State, RetRegion, ResultType,
+ /*CanBeSubclass=*/true);
+ Pred = C.addTransition(State);
+ }
+
+ const auto *ResultPtrType = ResultType->getAs<ObjCObjectPointerType>();
+
+ if (!ResultPtrType || ResultPtrType->isUnspecialized())
+ return;
+
+ // When the result is a specialized type and it is not tracked yet, track it
+ // for the result symbol.
+ if (!State->get<MostSpecializedTypeArgsMap>(RetSym)) {
+ State = State->set<MostSpecializedTypeArgsMap>(RetSym, ResultPtrType);
+ C.addTransition(State, Pred);
+ }
+}
+
+void DynamicTypePropagation::reportGenericsBug(
+ const ObjCObjectPointerType *From, const ObjCObjectPointerType *To,
+ ExplodedNode *N, SymbolRef Sym, CheckerContext &C,
+ const Stmt *ReportedNode) const {
+ if (!CheckGenerics)
+ return;
+
+ initBugType();
+ SmallString<192> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+ OS << "Conversion from value of type '";
+ QualType::print(From, Qualifiers(), OS, C.getLangOpts(), llvm::Twine());
+ OS << "' to incompatible type '";
+ QualType::print(To, Qualifiers(), OS, C.getLangOpts(), llvm::Twine());
+ OS << "'";
+ std::unique_ptr<BugReport> R(
+ new BugReport(*ObjCGenericsBugType, OS.str(), N));
+ R->markInteresting(Sym);
+ R->addVisitor(llvm::make_unique<GenericsBugVisitor>(Sym));
+ if (ReportedNode)
+ R->addRange(ReportedNode->getSourceRange());
+ C.emitReport(std::move(R));
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+DynamicTypePropagation::GenericsBugVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+ ProgramStateRef state = N->getState();
+ ProgramStateRef statePrev = N->getFirstPred()->getState();
+
+ const ObjCObjectPointerType *const *TrackedType =
+ state->get<MostSpecializedTypeArgsMap>(Sym);
+ const ObjCObjectPointerType *const *TrackedTypePrev =
+ statePrev->get<MostSpecializedTypeArgsMap>(Sym);
+ if (!TrackedType)
+ return nullptr;
+
+ if (TrackedTypePrev && *TrackedTypePrev == *TrackedType)
+ return nullptr;
+
+ // Retrieve the associated statement.
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ if (!S)
+ return nullptr;
+
+ const LangOptions &LangOpts = BRC.getASTContext().getLangOpts();
+
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+ OS << "Type '";
+ QualType::print(*TrackedType, Qualifiers(), OS, LangOpts, llvm::Twine());
+ OS << "' is inferred from ";
+
+ if (const auto *ExplicitCast = dyn_cast<ExplicitCastExpr>(S)) {
+ OS << "explicit cast (from '";
+ QualType::print(ExplicitCast->getSubExpr()->getType().getTypePtr(),
+ Qualifiers(), OS, LangOpts, llvm::Twine());
+ OS << "' to '";
+ QualType::print(ExplicitCast->getType().getTypePtr(), Qualifiers(), OS,
+ LangOpts, llvm::Twine());
+ OS << "')";
+ } else if (const auto *ImplicitCast = dyn_cast<ImplicitCastExpr>(S)) {
+ OS << "implicit cast (from '";
+ QualType::print(ImplicitCast->getSubExpr()->getType().getTypePtr(),
+ Qualifiers(), OS, LangOpts, llvm::Twine());
+ OS << "' to '";
+ QualType::print(ImplicitCast->getType().getTypePtr(), Qualifiers(), OS,
+ LangOpts, llvm::Twine());
+ OS << "')";
+ } else {
+ OS << "this context";
+ }
+
+ // Generate the extra diagnostic.
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
+ nullptr);
+}
+
+/// Register checkers.
+void ento::registerObjCGenericsChecker(CheckerManager &mgr) {
+ DynamicTypePropagation *checker =
+ mgr.registerChecker<DynamicTypePropagation>();
+ checker->CheckGenerics = true;
+}
+
+void ento::registerDynamicTypePropagation(CheckerManager &mgr) {
+ mgr.registerChecker<DynamicTypePropagation>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
new file mode 100644
index 000000000000..4e51cffaa744
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
@@ -0,0 +1,128 @@
+//===- EnumCastOutOfRangeChecker.cpp ---------------------------*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// The EnumCastOutOfRangeChecker is responsible for checking integer to
+// enumeration casts that could result in undefined values. This could happen
+// if the value that we cast from is out of the value range of the enumeration.
+// Reference:
+// [ISO/IEC 14882-2014] ISO/IEC 14882-2014.
+// Programming Languages — C++, Fourth Edition. 2014.
+// C++ Standard, [dcl.enum], in paragraph 8, which defines the range of an enum
+// C++ Standard, [expr.static.cast], paragraph 10, which defines the behaviour
+// of casting an integer value that is out of range
+// SEI CERT C++ Coding Standard, INT50-CPP. Do not cast to an out-of-range
+// enumeration value
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+// This evaluator checks two SVals for equality. The first SVal is provided via
+// the constructor, the second is the parameter of the overloaded () operator.
+// It uses the in-built ConstraintManager to resolve the equlity to possible or
+// not possible ProgramStates.
+class ConstraintBasedEQEvaluator {
+ const DefinedOrUnknownSVal CompareValue;
+ const ProgramStateRef PS;
+ SValBuilder &SVB;
+
+public:
+ ConstraintBasedEQEvaluator(CheckerContext &C,
+ const DefinedOrUnknownSVal CompareValue)
+ : CompareValue(CompareValue), PS(C.getState()), SVB(C.getSValBuilder()) {}
+
+ bool operator()(const llvm::APSInt &EnumDeclInitValue) {
+ DefinedOrUnknownSVal EnumDeclValue = SVB.makeIntVal(EnumDeclInitValue);
+ DefinedOrUnknownSVal ElemEqualsValueToCast =
+ SVB.evalEQ(PS, EnumDeclValue, CompareValue);
+
+ return static_cast<bool>(PS->assume(ElemEqualsValueToCast, true));
+ }
+};
+
+// This checker checks CastExpr statements.
+// If the value provided to the cast is one of the values the enumeration can
+// represent, the said value matches the enumeration. If the checker can
+// establish the impossibility of matching it gives a warning.
+// Being conservative, it does not warn if there is slight possibility the
+// value can be matching.
+class EnumCastOutOfRangeChecker : public Checker<check::PreStmt<CastExpr>> {
+ mutable std::unique_ptr<BuiltinBug> EnumValueCastOutOfRange;
+ void reportWarning(CheckerContext &C) const;
+
+public:
+ void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
+};
+
+using EnumValueVector = llvm::SmallVector<llvm::APSInt, 6>;
+
+// Collects all of the values an enum can represent (as SVals).
+EnumValueVector getDeclValuesForEnum(const EnumDecl *ED) {
+ EnumValueVector DeclValues(
+ std::distance(ED->enumerator_begin(), ED->enumerator_end()));
+ llvm::transform(ED->enumerators(), DeclValues.begin(),
+ [](const EnumConstantDecl *D) { return D->getInitVal(); });
+ return DeclValues;
+}
+} // namespace
+
+void EnumCastOutOfRangeChecker::reportWarning(CheckerContext &C) const {
+ if (const ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ if (!EnumValueCastOutOfRange)
+ EnumValueCastOutOfRange.reset(
+ new BuiltinBug(this, "Enum cast out of range",
+ "The value provided to the cast expression is not in "
+ "the valid range of values for the enum"));
+ C.emitReport(llvm::make_unique<BugReport>(
+ *EnumValueCastOutOfRange, EnumValueCastOutOfRange->getDescription(),
+ N));
+ }
+}
+
+void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
+ CheckerContext &C) const {
+ // Get the value of the expression to cast.
+ const llvm::Optional<DefinedOrUnknownSVal> ValueToCast =
+ C.getSVal(CE->getSubExpr()).getAs<DefinedOrUnknownSVal>();
+
+ // If the value cannot be reasoned about (not even a DefinedOrUnknownSVal),
+ // don't analyze further.
+ if (!ValueToCast)
+ return;
+
+ const QualType T = CE->getType();
+ // Check whether the cast type is an enum.
+ if (!T->isEnumeralType())
+ return;
+
+ // If the cast is an enum, get its declaration.
+ // If the isEnumeralType() returned true, then the declaration must exist
+ // even if it is a stub declaration. It is up to the getDeclValuesForEnum()
+ // function to handle this.
+ const EnumDecl *ED = T->castAs<EnumType>()->getDecl();
+
+ EnumValueVector DeclValues = getDeclValuesForEnum(ED);
+ // Check if any of the enum values possibly match.
+ bool PossibleValueMatch = llvm::any_of(
+ DeclValues, ConstraintBasedEQEvaluator(C, *ValueToCast));
+
+ // If there is no value that can possibly match any of the enum values, then
+ // warn.
+ if (!PossibleValueMatch)
+ reportWarning(C);
+}
+
+void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) {
+ mgr.registerChecker<EnumCastOutOfRangeChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
new file mode 100644
index 000000000000..2553f54bbcac
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -0,0 +1,411 @@
+//==- ExprInspectionChecker.cpp - Used for regression tests ------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Checkers/SValExplainer.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/IssueHash.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols,
+ check::EndAnalysis> {
+ mutable std::unique_ptr<BugType> BT;
+
+ // These stats are per-analysis, not per-branch, hence they shouldn't
+ // stay inside the program state.
+ struct ReachedStat {
+ ExplodedNode *ExampleNode;
+ unsigned NumTimesReached;
+ };
+ mutable llvm::DenseMap<const CallExpr *, ReachedStat> ReachedStats;
+
+ void analyzerEval(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerCheckInlined(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerWarnIfReached(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerNumTimesReached(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerCrash(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerWarnOnDeadSymbol(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerDump(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerExplain(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerPrintState(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerGetExtent(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerHashDump(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerDenote(const CallExpr *CE, CheckerContext &C) const;
+ void analyzerExpress(const CallExpr *CE, CheckerContext &C) const;
+
+ typedef void (ExprInspectionChecker::*FnCheck)(const CallExpr *,
+ CheckerContext &C) const;
+
+ ExplodedNode *reportBug(llvm::StringRef Msg, CheckerContext &C) const;
+ ExplodedNode *reportBug(llvm::StringRef Msg, BugReporter &BR,
+ ExplodedNode *N) const;
+
+public:
+ bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
+ ExprEngine &Eng) const;
+};
+}
+
+REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef)
+REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *)
+
+bool ExprInspectionChecker::evalCall(const CallExpr *CE,
+ CheckerContext &C) const {
+ // These checks should have no effect on the surrounding environment
+ // (globals should not be invalidated, etc), hence the use of evalCall.
+ FnCheck Handler = llvm::StringSwitch<FnCheck>(C.getCalleeName(CE))
+ .Case("clang_analyzer_eval", &ExprInspectionChecker::analyzerEval)
+ .Case("clang_analyzer_checkInlined",
+ &ExprInspectionChecker::analyzerCheckInlined)
+ .Case("clang_analyzer_crash", &ExprInspectionChecker::analyzerCrash)
+ .Case("clang_analyzer_warnIfReached",
+ &ExprInspectionChecker::analyzerWarnIfReached)
+ .Case("clang_analyzer_warnOnDeadSymbol",
+ &ExprInspectionChecker::analyzerWarnOnDeadSymbol)
+ .StartsWith("clang_analyzer_explain", &ExprInspectionChecker::analyzerExplain)
+ .StartsWith("clang_analyzer_dump", &ExprInspectionChecker::analyzerDump)
+ .Case("clang_analyzer_getExtent", &ExprInspectionChecker::analyzerGetExtent)
+ .Case("clang_analyzer_printState",
+ &ExprInspectionChecker::analyzerPrintState)
+ .Case("clang_analyzer_numTimesReached",
+ &ExprInspectionChecker::analyzerNumTimesReached)
+ .Case("clang_analyzer_hashDump", &ExprInspectionChecker::analyzerHashDump)
+ .Case("clang_analyzer_denote", &ExprInspectionChecker::analyzerDenote)
+ .Case("clang_analyzer_express", &ExprInspectionChecker::analyzerExpress)
+ .Default(nullptr);
+
+ if (!Handler)
+ return false;
+
+ (this->*Handler)(CE, C);
+ return true;
+}
+
+static const char *getArgumentValueString(const CallExpr *CE,
+ CheckerContext &C) {
+ if (CE->getNumArgs() == 0)
+ return "Missing assertion argument";
+
+ ExplodedNode *N = C.getPredecessor();
+ const LocationContext *LC = N->getLocationContext();
+ ProgramStateRef State = N->getState();
+
+ const Expr *Assertion = CE->getArg(0);
+ SVal AssertionVal = State->getSVal(Assertion, LC);
+
+ if (AssertionVal.isUndef())
+ return "UNDEFINED";
+
+ ProgramStateRef StTrue, StFalse;
+ std::tie(StTrue, StFalse) =
+ State->assume(AssertionVal.castAs<DefinedOrUnknownSVal>());
+
+ if (StTrue) {
+ if (StFalse)
+ return "UNKNOWN";
+ else
+ return "TRUE";
+ } else {
+ if (StFalse)
+ return "FALSE";
+ else
+ llvm_unreachable("Invalid constraint; neither true or false.");
+ }
+}
+
+ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
+ CheckerContext &C) const {
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ reportBug(Msg, C.getBugReporter(), N);
+ return N;
+}
+
+ExplodedNode *ExprInspectionChecker::reportBug(llvm::StringRef Msg,
+ BugReporter &BR,
+ ExplodedNode *N) const {
+ if (!N)
+ return nullptr;
+
+ if (!BT)
+ BT.reset(new BugType(this, "Checking analyzer assumptions", "debug"));
+
+ BR.emitReport(llvm::make_unique<BugReport>(*BT, Msg, N));
+ return N;
+}
+
+void ExprInspectionChecker::analyzerEval(const CallExpr *CE,
+ CheckerContext &C) const {
+ const LocationContext *LC = C.getPredecessor()->getLocationContext();
+
+ // A specific instantiation of an inlined function may have more constrained
+ // values than can generally be assumed. Skip the check.
+ if (LC->getStackFrame()->getParent() != nullptr)
+ return;
+
+ reportBug(getArgumentValueString(CE, C), C);
+}
+
+void ExprInspectionChecker::analyzerWarnIfReached(const CallExpr *CE,
+ CheckerContext &C) const {
+ reportBug("REACHABLE", C);
+}
+
+void ExprInspectionChecker::analyzerNumTimesReached(const CallExpr *CE,
+ CheckerContext &C) const {
+ ++ReachedStats[CE].NumTimesReached;
+ if (!ReachedStats[CE].ExampleNode) {
+ // Later, in checkEndAnalysis, we'd throw a report against it.
+ ReachedStats[CE].ExampleNode = C.generateNonFatalErrorNode();
+ }
+}
+
+void ExprInspectionChecker::analyzerCheckInlined(const CallExpr *CE,
+ CheckerContext &C) const {
+ const LocationContext *LC = C.getPredecessor()->getLocationContext();
+
+ // An inlined function could conceivably also be analyzed as a top-level
+ // function. We ignore this case and only emit a message (TRUE or FALSE)
+ // when we are analyzing it as an inlined function. This means that
+ // clang_analyzer_checkInlined(true) should always print TRUE, but
+ // clang_analyzer_checkInlined(false) should never actually print anything.
+ if (LC->getStackFrame()->getParent() == nullptr)
+ return;
+
+ reportBug(getArgumentValueString(CE, C), C);
+}
+
+void ExprInspectionChecker::analyzerExplain(const CallExpr *CE,
+ CheckerContext &C) const {
+ if (CE->getNumArgs() == 0) {
+ reportBug("Missing argument for explaining", C);
+ return;
+ }
+
+ SVal V = C.getSVal(CE->getArg(0));
+ SValExplainer Ex(C.getASTContext());
+ reportBug(Ex.Visit(V), C);
+}
+
+void ExprInspectionChecker::analyzerDump(const CallExpr *CE,
+ CheckerContext &C) const {
+ if (CE->getNumArgs() == 0) {
+ reportBug("Missing argument for dumping", C);
+ return;
+ }
+
+ SVal V = C.getSVal(CE->getArg(0));
+
+ llvm::SmallString<32> Str;
+ llvm::raw_svector_ostream OS(Str);
+ V.dumpToStream(OS);
+ reportBug(OS.str(), C);
+}
+
+void ExprInspectionChecker::analyzerGetExtent(const CallExpr *CE,
+ CheckerContext &C) const {
+ if (CE->getNumArgs() == 0) {
+ reportBug("Missing region for obtaining extent", C);
+ return;
+ }
+
+ auto MR = dyn_cast_or_null<SubRegion>(C.getSVal(CE->getArg(0)).getAsRegion());
+ if (!MR) {
+ reportBug("Obtaining extent of a non-region", C);
+ return;
+ }
+
+ ProgramStateRef State = C.getState();
+ State = State->BindExpr(CE, C.getLocationContext(),
+ MR->getExtent(C.getSValBuilder()));
+ C.addTransition(State);
+}
+
+void ExprInspectionChecker::analyzerPrintState(const CallExpr *CE,
+ CheckerContext &C) const {
+ C.getState()->dump();
+}
+
+void ExprInspectionChecker::analyzerWarnOnDeadSymbol(const CallExpr *CE,
+ CheckerContext &C) const {
+ if (CE->getNumArgs() == 0)
+ return;
+ SVal Val = C.getSVal(CE->getArg(0));
+ SymbolRef Sym = Val.getAsSymbol();
+ if (!Sym)
+ return;
+
+ ProgramStateRef State = C.getState();
+ State = State->add<MarkedSymbols>(Sym);
+ C.addTransition(State);
+}
+
+void ExprInspectionChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ const MarkedSymbolsTy &Syms = State->get<MarkedSymbols>();
+ ExplodedNode *N = C.getPredecessor();
+ for (auto I = Syms.begin(), E = Syms.end(); I != E; ++I) {
+ SymbolRef Sym = *I;
+ if (!SymReaper.isDead(Sym))
+ continue;
+
+ // The non-fatal error node should be the same for all reports.
+ if (ExplodedNode *BugNode = reportBug("SYMBOL DEAD", C))
+ N = BugNode;
+ State = State->remove<MarkedSymbols>(Sym);
+ }
+
+ for (auto I : State->get<DenotedSymbols>()) {
+ SymbolRef Sym = I.first;
+ if (!SymReaper.isLive(Sym))
+ State = State->remove<DenotedSymbols>(Sym);
+ }
+
+ C.addTransition(State, N);
+}
+
+void ExprInspectionChecker::checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
+ ExprEngine &Eng) const {
+ for (auto Item: ReachedStats) {
+ unsigned NumTimesReached = Item.second.NumTimesReached;
+ ExplodedNode *N = Item.second.ExampleNode;
+
+ reportBug(llvm::to_string(NumTimesReached), BR, N);
+ }
+ ReachedStats.clear();
+}
+
+void ExprInspectionChecker::analyzerCrash(const CallExpr *CE,
+ CheckerContext &C) const {
+ LLVM_BUILTIN_TRAP;
+}
+
+void ExprInspectionChecker::analyzerHashDump(const CallExpr *CE,
+ CheckerContext &C) const {
+ const LangOptions &Opts = C.getLangOpts();
+ const SourceManager &SM = C.getSourceManager();
+ FullSourceLoc FL(CE->getArg(0)->getBeginLoc(), SM);
+ std::string HashContent =
+ GetIssueString(SM, FL, getCheckName().getName(), "Category",
+ C.getLocationContext()->getDecl(), Opts);
+
+ reportBug(HashContent, C);
+}
+
+void ExprInspectionChecker::analyzerDenote(const CallExpr *CE,
+ CheckerContext &C) const {
+ if (CE->getNumArgs() < 2) {
+ reportBug("clang_analyzer_denote() requires a symbol and a string literal",
+ C);
+ return;
+ }
+
+ SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
+ if (!Sym) {
+ reportBug("Not a symbol", C);
+ return;
+ }
+
+ const auto *E = dyn_cast<StringLiteral>(CE->getArg(1)->IgnoreParenCasts());
+ if (!E) {
+ reportBug("Not a string literal", C);
+ return;
+ }
+
+ ProgramStateRef State = C.getState();
+
+ C.addTransition(C.getState()->set<DenotedSymbols>(Sym, E));
+}
+
+namespace {
+class SymbolExpressor
+ : public SymExprVisitor<SymbolExpressor, Optional<std::string>> {
+ ProgramStateRef State;
+
+public:
+ SymbolExpressor(ProgramStateRef State) : State(State) {}
+
+ Optional<std::string> lookup(const SymExpr *S) {
+ if (const StringLiteral *const *SLPtr = State->get<DenotedSymbols>(S)) {
+ const StringLiteral *SL = *SLPtr;
+ return std::string(SL->getBytes());
+ }
+ return None;
+ }
+
+ Optional<std::string> VisitSymExpr(const SymExpr *S) {
+ return lookup(S);
+ }
+
+ Optional<std::string> VisitSymIntExpr(const SymIntExpr *S) {
+ if (Optional<std::string> Str = lookup(S))
+ return Str;
+ if (Optional<std::string> Str = Visit(S->getLHS()))
+ return (*Str + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) + " " +
+ std::to_string(S->getRHS().getLimitedValue()) +
+ (S->getRHS().isUnsigned() ? "U" : ""))
+ .str();
+ return None;
+ }
+
+ Optional<std::string> VisitSymSymExpr(const SymSymExpr *S) {
+ if (Optional<std::string> Str = lookup(S))
+ return Str;
+ if (Optional<std::string> Str1 = Visit(S->getLHS()))
+ if (Optional<std::string> Str2 = Visit(S->getRHS()))
+ return (*Str1 + " " + BinaryOperator::getOpcodeStr(S->getOpcode()) +
+ " " + *Str2).str();
+ return None;
+ }
+
+ Optional<std::string> VisitSymbolCast(const SymbolCast *S) {
+ if (Optional<std::string> Str = lookup(S))
+ return Str;
+ if (Optional<std::string> Str = Visit(S->getOperand()))
+ return (Twine("(") + S->getType().getAsString() + ")" + *Str).str();
+ return None;
+ }
+};
+} // namespace
+
+void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
+ CheckerContext &C) const {
+ if (CE->getNumArgs() == 0) {
+ reportBug("clang_analyzer_express() requires a symbol", C);
+ return;
+ }
+
+ SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
+ if (!Sym) {
+ reportBug("Not a symbol", C);
+ return;
+ }
+
+ SymbolExpressor V(C.getState());
+ auto Str = V.Visit(Sym);
+ if (!Str) {
+ reportBug("Unable to express", C);
+ return;
+ }
+
+ reportBug(*Str, C);
+}
+
+void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<ExprInspectionChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
new file mode 100644
index 000000000000..165a4e4490eb
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
@@ -0,0 +1,67 @@
+//=== FixedAddressChecker.cpp - Fixed address usage checker ----*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This files defines FixedAddressChecker, a builtin checker that checks for
+// assignment of a fixed address to a pointer.
+// This check corresponds to CWE-587.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class FixedAddressChecker
+ : public Checker< check::PreStmt<BinaryOperator> > {
+ mutable std::unique_ptr<BuiltinBug> BT;
+
+public:
+ void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
+};
+}
+
+void FixedAddressChecker::checkPreStmt(const BinaryOperator *B,
+ CheckerContext &C) const {
+ // Using a fixed address is not portable because that address will probably
+ // not be valid in all environments or platforms.
+
+ if (B->getOpcode() != BO_Assign)
+ return;
+
+ QualType T = B->getType();
+ if (!T->isPointerType())
+ return;
+
+ SVal RV = C.getSVal(B->getRHS());
+
+ if (!RV.isConstant() || RV.isZeroConstant())
+ return;
+
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ if (!BT)
+ BT.reset(
+ new BuiltinBug(this, "Use fixed address",
+ "Using a fixed address is not portable because that "
+ "address will probably not be valid in all "
+ "environments or platforms."));
+ auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N);
+ R->addRange(B->getRHS()->getSourceRange());
+ C.emitReport(std::move(R));
+ }
+}
+
+void ento::registerFixedAddressChecker(CheckerManager &mgr) {
+ mgr.registerChecker<FixedAddressChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
new file mode 100644
index 000000000000..248b9c3f7693
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
@@ -0,0 +1,229 @@
+//===- GCDAntipatternChecker.cpp ---------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines GCDAntipatternChecker which checks against a common
+// antipattern when synchronous API is emulated from asynchronous callbacks
+// using a semaphore:
+//
+// dispatch_semaphore_t sema = dispatch_semaphore_create(0);
+//
+// AnyCFunctionCall(^{
+// // code…
+// dispatch_semaphore_signal(sema);
+// })
+// dispatch_semaphore_wait(sema, *)
+//
+// Such code is a common performance problem, due to inability of GCD to
+// properly handle QoS when a combination of queues and semaphores is used.
+// Good code would either use asynchronous API (when available), or perform
+// the necessary action in asynchronous callback.
+//
+// Currently, the check is performed using a simple heuristical AST pattern
+// matching.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/Support/Debug.h"
+
+using namespace clang;
+using namespace ento;
+using namespace ast_matchers;
+
+namespace {
+
+// ID of a node at which the diagnostic would be emitted.
+const char *WarnAtNode = "waitcall";
+
+class GCDAntipatternChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const;
+};
+
+auto callsName(const char *FunctionName)
+ -> decltype(callee(functionDecl())) {
+ return callee(functionDecl(hasName(FunctionName)));
+}
+
+auto equalsBoundArgDecl(int ArgIdx, const char *DeclName)
+ -> decltype(hasArgument(0, expr())) {
+ return hasArgument(ArgIdx, ignoringParenCasts(declRefExpr(
+ to(varDecl(equalsBoundNode(DeclName))))));
+}
+
+auto bindAssignmentToDecl(const char *DeclName) -> decltype(hasLHS(expr())) {
+ return hasLHS(ignoringParenImpCasts(
+ declRefExpr(to(varDecl().bind(DeclName)))));
+}
+
+/// The pattern is very common in tests, and it is OK to use it there.
+/// We have to heuristics for detecting tests: method name starts with "test"
+/// (used in XCTest), and a class name contains "mock" or "test" (used in
+/// helpers which are not tests themselves, but used exclusively in tests).
+static bool isTest(const Decl *D) {
+ if (const auto* ND = dyn_cast<NamedDecl>(D)) {
+ std::string DeclName = ND->getNameAsString();
+ if (StringRef(DeclName).startswith("test"))
+ return true;
+ }
+ if (const auto *OD = dyn_cast<ObjCMethodDecl>(D)) {
+ if (const auto *CD = dyn_cast<ObjCContainerDecl>(OD->getParent())) {
+ std::string ContainerName = CD->getNameAsString();
+ StringRef CN(ContainerName);
+ if (CN.contains_lower("test") || CN.contains_lower("mock"))
+ return true;
+ }
+ }
+ return false;
+}
+
+static auto findGCDAntiPatternWithSemaphore() -> decltype(compoundStmt()) {
+
+ const char *SemaphoreBinding = "semaphore_name";
+ auto SemaphoreCreateM = callExpr(allOf(
+ callsName("dispatch_semaphore_create"),
+ hasArgument(0, ignoringParenCasts(integerLiteral(equals(0))))));
+
+ auto SemaphoreBindingM = anyOf(
+ forEachDescendant(
+ varDecl(hasDescendant(SemaphoreCreateM)).bind(SemaphoreBinding)),
+ forEachDescendant(binaryOperator(bindAssignmentToDecl(SemaphoreBinding),
+ hasRHS(SemaphoreCreateM))));
+
+ auto HasBlockArgumentM = hasAnyArgument(hasType(
+ hasCanonicalType(blockPointerType())
+ ));
+
+ auto ArgCallsSignalM = hasAnyArgument(stmt(hasDescendant(callExpr(
+ allOf(
+ callsName("dispatch_semaphore_signal"),
+ equalsBoundArgDecl(0, SemaphoreBinding)
+ )))));
+
+ auto HasBlockAndCallsSignalM = allOf(HasBlockArgumentM, ArgCallsSignalM);
+
+ auto HasBlockCallingSignalM =
+ forEachDescendant(
+ stmt(anyOf(
+ callExpr(HasBlockAndCallsSignalM),
+ objcMessageExpr(HasBlockAndCallsSignalM)
+ )));
+
+ auto SemaphoreWaitM = forEachDescendant(
+ callExpr(
+ allOf(
+ callsName("dispatch_semaphore_wait"),
+ equalsBoundArgDecl(0, SemaphoreBinding)
+ )
+ ).bind(WarnAtNode));
+
+ return compoundStmt(
+ SemaphoreBindingM, HasBlockCallingSignalM, SemaphoreWaitM);
+}
+
+static auto findGCDAntiPatternWithGroup() -> decltype(compoundStmt()) {
+
+ const char *GroupBinding = "group_name";
+ auto DispatchGroupCreateM = callExpr(callsName("dispatch_group_create"));
+
+ auto GroupBindingM = anyOf(
+ forEachDescendant(
+ varDecl(hasDescendant(DispatchGroupCreateM)).bind(GroupBinding)),
+ forEachDescendant(binaryOperator(bindAssignmentToDecl(GroupBinding),
+ hasRHS(DispatchGroupCreateM))));
+
+ auto GroupEnterM = forEachDescendant(
+ stmt(callExpr(allOf(callsName("dispatch_group_enter"),
+ equalsBoundArgDecl(0, GroupBinding)))));
+
+ auto HasBlockArgumentM = hasAnyArgument(hasType(
+ hasCanonicalType(blockPointerType())
+ ));
+
+ auto ArgCallsSignalM = hasAnyArgument(stmt(hasDescendant(callExpr(
+ allOf(
+ callsName("dispatch_group_leave"),
+ equalsBoundArgDecl(0, GroupBinding)
+ )))));
+
+ auto HasBlockAndCallsLeaveM = allOf(HasBlockArgumentM, ArgCallsSignalM);
+
+ auto AcceptsBlockM =
+ forEachDescendant(
+ stmt(anyOf(
+ callExpr(HasBlockAndCallsLeaveM),
+ objcMessageExpr(HasBlockAndCallsLeaveM)
+ )));
+
+ auto GroupWaitM = forEachDescendant(
+ callExpr(
+ allOf(
+ callsName("dispatch_group_wait"),
+ equalsBoundArgDecl(0, GroupBinding)
+ )
+ ).bind(WarnAtNode));
+
+ return compoundStmt(GroupBindingM, GroupEnterM, AcceptsBlockM, GroupWaitM);
+}
+
+static void emitDiagnostics(const BoundNodes &Nodes,
+ const char* Type,
+ BugReporter &BR,
+ AnalysisDeclContext *ADC,
+ const GCDAntipatternChecker *Checker) {
+ const auto *SW = Nodes.getNodeAs<CallExpr>(WarnAtNode);
+ assert(SW);
+
+ std::string Diagnostics;
+ llvm::raw_string_ostream OS(Diagnostics);
+ OS << "Waiting on a callback using a " << Type << " creates useless threads "
+ << "and is subject to priority inversion; consider "
+ << "using a synchronous API or changing the caller to be asynchronous";
+
+ BR.EmitBasicReport(
+ ADC->getDecl(),
+ Checker,
+ /*Name=*/"GCD performance anti-pattern",
+ /*Category=*/"Performance",
+ OS.str(),
+ PathDiagnosticLocation::createBegin(SW, BR.getSourceManager(), ADC),
+ SW->getSourceRange());
+}
+
+void GCDAntipatternChecker::checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const {
+ if (isTest(D))
+ return;
+
+ AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
+
+ auto SemaphoreMatcherM = findGCDAntiPatternWithSemaphore();
+ auto Matches = match(SemaphoreMatcherM, *D->getBody(), AM.getASTContext());
+ for (BoundNodes Match : Matches)
+ emitDiagnostics(Match, "semaphore", BR, ADC, this);
+
+ auto GroupMatcherM = findGCDAntiPatternWithGroup();
+ Matches = match(GroupMatcherM, *D->getBody(), AM.getASTContext());
+ for (BoundNodes Match : Matches)
+ emitDiagnostics(Match, "group", BR, ADC, this);
+}
+
+}
+
+void ento::registerGCDAntipattern(CheckerManager &Mgr) {
+ Mgr.registerChecker<GCDAntipatternChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
new file mode 100644
index 000000000000..818716dd6070
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
@@ -0,0 +1,299 @@
+//==- GTestChecker.cpp - Model gtest API --*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker models the behavior of un-inlined APIs from the gtest
+// unit-testing library to avoid false positives when using assertions from
+// that library.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Expr.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+// Modeling of un-inlined AssertionResult constructors
+//
+// The gtest unit testing API provides macros for assertions that expand
+// into an if statement that calls a series of constructors and returns
+// when the "assertion" is false.
+//
+// For example,
+//
+// ASSERT_TRUE(a == b)
+//
+// expands into:
+//
+// switch (0)
+// case 0:
+// default:
+// if (const ::testing::AssertionResult gtest_ar_ =
+// ::testing::AssertionResult((a == b)))
+// ;
+// else
+// return ::testing::internal::AssertHelper(
+// ::testing::TestPartResult::kFatalFailure,
+// "<path to project>",
+// <line number>,
+// ::testing::internal::GetBoolAssertionFailureMessage(
+// gtest_ar_, "a == b", "false", "true")
+// .c_str()) = ::testing::Message();
+//
+// where AssertionResult is defined similarly to
+//
+// class AssertionResult {
+// public:
+// AssertionResult(const AssertionResult& other);
+// explicit AssertionResult(bool success) : success_(success) {}
+// operator bool() const { return success_; }
+// ...
+// private:
+// bool success_;
+// };
+//
+// In order for the analyzer to correctly handle this assertion, it needs to
+// know that the boolean value of the expression "a == b" is stored the
+// 'success_' field of the original AssertionResult temporary and propagated
+// (via the copy constructor) into the 'success_' field of the object stored
+// in 'gtest_ar_'. That boolean value will then be returned from the bool
+// conversion method in the if statement. This guarantees that the assertion
+// holds when the return path is not taken.
+//
+// If the success value is not properly propagated, then the eager case split
+// on evaluating the expression can cause pernicious false positives
+// on the non-return path:
+//
+// ASSERT(ptr != NULL)
+// *ptr = 7; // False positive null pointer dereference here
+//
+// Unfortunately, the bool constructor cannot be inlined (because its
+// implementation is not present in the headers) and the copy constructor is
+// not inlined (because it is constructed into a temporary and the analyzer
+// does not inline these since it does not yet reliably call temporary
+// destructors).
+//
+// This checker compensates for the missing inlining by propagating the
+// _success value across the bool and copy constructors so the assertion behaves
+// as expected.
+
+namespace {
+class GTestChecker : public Checker<check::PostCall> {
+
+ mutable IdentifierInfo *AssertionResultII;
+ mutable IdentifierInfo *SuccessII;
+
+public:
+ GTestChecker();
+
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+private:
+ void modelAssertionResultBoolConstructor(const CXXConstructorCall *Call,
+ bool IsRef, CheckerContext &C) const;
+
+ void modelAssertionResultCopyConstructor(const CXXConstructorCall *Call,
+ CheckerContext &C) const;
+
+ void initIdentifierInfo(ASTContext &Ctx) const;
+
+ SVal
+ getAssertionResultSuccessFieldValue(const CXXRecordDecl *AssertionResultDecl,
+ SVal Instance,
+ ProgramStateRef State) const;
+
+ static ProgramStateRef assumeValuesEqual(SVal Val1, SVal Val2,
+ ProgramStateRef State,
+ CheckerContext &C);
+};
+} // End anonymous namespace.
+
+GTestChecker::GTestChecker() : AssertionResultII(nullptr), SuccessII(nullptr) {}
+
+/// Model a call to an un-inlined AssertionResult(bool) or
+/// AssertionResult(bool &, ...).
+/// To do so, constrain the value of the newly-constructed instance's 'success_'
+/// field to be equal to the passed-in boolean value.
+///
+/// \param IsRef Whether the boolean parameter is a reference or not.
+void GTestChecker::modelAssertionResultBoolConstructor(
+ const CXXConstructorCall *Call, bool IsRef, CheckerContext &C) const {
+ assert(Call->getNumArgs() >= 1 && Call->getNumArgs() <= 2);
+
+ ProgramStateRef State = C.getState();
+ SVal BooleanArgVal = Call->getArgSVal(0);
+ if (IsRef) {
+ // The argument is a reference, so load from it to get the boolean value.
+ if (!BooleanArgVal.getAs<Loc>())
+ return;
+ BooleanArgVal = C.getState()->getSVal(BooleanArgVal.castAs<Loc>());
+ }
+
+ SVal ThisVal = Call->getCXXThisVal();
+
+ SVal ThisSuccess = getAssertionResultSuccessFieldValue(
+ Call->getDecl()->getParent(), ThisVal, State);
+
+ State = assumeValuesEqual(ThisSuccess, BooleanArgVal, State, C);
+ C.addTransition(State);
+}
+
+/// Model a call to an un-inlined AssertionResult copy constructor:
+///
+/// AssertionResult(const &AssertionResult other)
+///
+/// To do so, constrain the value of the newly-constructed instance's
+/// 'success_' field to be equal to the value of the pass-in instance's
+/// 'success_' field.
+void GTestChecker::modelAssertionResultCopyConstructor(
+ const CXXConstructorCall *Call, CheckerContext &C) const {
+ assert(Call->getNumArgs() == 1);
+
+ // The first parameter of the copy constructor must be the other
+ // instance to initialize this instances fields from.
+ SVal OtherVal = Call->getArgSVal(0);
+ SVal ThisVal = Call->getCXXThisVal();
+
+ const CXXRecordDecl *AssertResultClassDecl = Call->getDecl()->getParent();
+ ProgramStateRef State = C.getState();
+
+ SVal ThisSuccess = getAssertionResultSuccessFieldValue(AssertResultClassDecl,
+ ThisVal, State);
+ SVal OtherSuccess = getAssertionResultSuccessFieldValue(AssertResultClassDecl,
+ OtherVal, State);
+
+ State = assumeValuesEqual(ThisSuccess, OtherSuccess, State, C);
+ C.addTransition(State);
+}
+
+/// Model calls to AssertionResult constructors that are not inlined.
+void GTestChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ /// If the constructor was inlined, there is no need model it.
+ if (C.wasInlined)
+ return;
+
+ initIdentifierInfo(C.getASTContext());
+
+ auto *CtorCall = dyn_cast<CXXConstructorCall>(&Call);
+ if (!CtorCall)
+ return;
+
+ const CXXConstructorDecl *CtorDecl = CtorCall->getDecl();
+ const CXXRecordDecl *CtorParent = CtorDecl->getParent();
+ if (CtorParent->getIdentifier() != AssertionResultII)
+ return;
+
+ unsigned ParamCount = CtorDecl->getNumParams();
+
+ // Call the appropriate modeling method based the parameters and their
+ // types.
+
+ // We have AssertionResult(const &AssertionResult)
+ if (CtorDecl->isCopyConstructor() && ParamCount == 1) {
+ modelAssertionResultCopyConstructor(CtorCall, C);
+ return;
+ }
+
+ // There are two possible boolean constructors, depending on which
+ // version of gtest is being used:
+ //
+ // v1.7 and earlier:
+ // AssertionResult(bool success)
+ //
+ // v1.8 and greater:
+ // template <typename T>
+ // AssertionResult(const T& success,
+ // typename internal::EnableIf<
+ // !internal::ImplicitlyConvertible<T,
+ // AssertionResult>::value>::type*)
+ //
+ CanQualType BoolTy = C.getASTContext().BoolTy;
+ if (ParamCount == 1 && CtorDecl->getParamDecl(0)->getType() == BoolTy) {
+ // We have AssertionResult(bool)
+ modelAssertionResultBoolConstructor(CtorCall, /*IsRef=*/false, C);
+ return;
+ }
+ if (ParamCount == 2){
+ auto *RefTy = CtorDecl->getParamDecl(0)->getType()->getAs<ReferenceType>();
+ if (RefTy &&
+ RefTy->getPointeeType()->getCanonicalTypeUnqualified() == BoolTy) {
+ // We have AssertionResult(bool &, ...)
+ modelAssertionResultBoolConstructor(CtorCall, /*IsRef=*/true, C);
+ return;
+ }
+ }
+}
+
+void GTestChecker::initIdentifierInfo(ASTContext &Ctx) const {
+ if (AssertionResultII)
+ return;
+
+ AssertionResultII = &Ctx.Idents.get("AssertionResult");
+ SuccessII = &Ctx.Idents.get("success_");
+}
+
+/// Returns the value stored in the 'success_' field of the passed-in
+/// AssertionResult instance.
+SVal GTestChecker::getAssertionResultSuccessFieldValue(
+ const CXXRecordDecl *AssertionResultDecl, SVal Instance,
+ ProgramStateRef State) const {
+
+ DeclContext::lookup_result Result = AssertionResultDecl->lookup(SuccessII);
+ if (Result.empty())
+ return UnknownVal();
+
+ auto *SuccessField = dyn_cast<FieldDecl>(Result.front());
+ if (!SuccessField)
+ return UnknownVal();
+
+ Optional<Loc> FieldLoc =
+ State->getLValue(SuccessField, Instance).getAs<Loc>();
+ if (!FieldLoc.hasValue())
+ return UnknownVal();
+
+ return State->getSVal(*FieldLoc);
+}
+
+/// Constrain the passed-in state to assume two values are equal.
+ProgramStateRef GTestChecker::assumeValuesEqual(SVal Val1, SVal Val2,
+ ProgramStateRef State,
+ CheckerContext &C) {
+ if (!Val1.getAs<DefinedOrUnknownSVal>() ||
+ !Val2.getAs<DefinedOrUnknownSVal>())
+ return State;
+
+ auto ValuesEqual =
+ C.getSValBuilder().evalEQ(State, Val1.castAs<DefinedOrUnknownSVal>(),
+ Val2.castAs<DefinedOrUnknownSVal>());
+
+ if (!ValuesEqual.getAs<DefinedSVal>())
+ return State;
+
+ State = C.getConstraintManager().assume(
+ State, ValuesEqual.castAs<DefinedSVal>(), true);
+
+ return State;
+}
+
+void ento::registerGTestChecker(CheckerManager &Mgr) {
+ const LangOptions &LangOpts = Mgr.getLangOpts();
+ // gtest is a C++ API so there is no sense running the checker
+ // if not compiling for C++.
+ if (!LangOpts.CPlusPlus)
+ return;
+
+ Mgr.registerChecker<GTestChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
new file mode 100644
index 000000000000..32fed202d3ab
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -0,0 +1,748 @@
+//== GenericTaintChecker.cpp ----------------------------------- -*- C++ -*--=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker defines the attack surface for generic taint propagation.
+//
+// The taint information produced by it might be useful to other checkers. For
+// example, checkers should report errors which involve tainted data more
+// aggressively, even if the involved symbols are under constrained.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Attr.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include <climits>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class GenericTaintChecker
+ : public Checker<check::PostStmt<CallExpr>, check::PreStmt<CallExpr>> {
+public:
+ static void *getTag() {
+ static int Tag;
+ return &Tag;
+ }
+
+ void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
+
+ void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+
+private:
+ static const unsigned InvalidArgIndex = UINT_MAX;
+ /// Denotes the return vale.
+ static const unsigned ReturnValueIndex = UINT_MAX - 1;
+
+ mutable std::unique_ptr<BugType> BT;
+ inline void initBugType() const {
+ if (!BT)
+ BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data"));
+ }
+
+ /// Catch taint related bugs. Check if tainted data is passed to a
+ /// system call etc.
+ bool checkPre(const CallExpr *CE, CheckerContext &C) const;
+
+ /// Add taint sources on a pre-visit.
+ void addSourcesPre(const CallExpr *CE, CheckerContext &C) const;
+
+ /// Propagate taint generated at pre-visit.
+ bool propagateFromPre(const CallExpr *CE, CheckerContext &C) const;
+
+ /// Add taint sources on a post visit.
+ void addSourcesPost(const CallExpr *CE, CheckerContext &C) const;
+
+ /// Check if the region the expression evaluates to is the standard input,
+ /// and thus, is tainted.
+ static bool isStdin(const Expr *E, CheckerContext &C);
+
+ /// Given a pointer argument, return the value it points to.
+ static Optional<SVal> getPointedToSVal(CheckerContext &C, const Expr *Arg);
+
+ /// Functions defining the attack surface.
+ typedef ProgramStateRef (GenericTaintChecker::*FnCheck)(
+ const CallExpr *, CheckerContext &C) const;
+ ProgramStateRef postScanf(const CallExpr *CE, CheckerContext &C) const;
+ ProgramStateRef postSocket(const CallExpr *CE, CheckerContext &C) const;
+ ProgramStateRef postRetTaint(const CallExpr *CE, CheckerContext &C) const;
+
+ /// Taint the scanned input if the file is tainted.
+ ProgramStateRef preFscanf(const CallExpr *CE, CheckerContext &C) const;
+
+ /// Check for CWE-134: Uncontrolled Format String.
+ static const char MsgUncontrolledFormatString[];
+ bool checkUncontrolledFormatString(const CallExpr *CE,
+ CheckerContext &C) const;
+
+ /// Check for:
+ /// CERT/STR02-C. "Sanitize data passed to complex subsystems"
+ /// CWE-78, "Failure to Sanitize Data into an OS Command"
+ static const char MsgSanitizeSystemArgs[];
+ bool checkSystemCall(const CallExpr *CE, StringRef Name,
+ CheckerContext &C) const;
+
+ /// Check if tainted data is used as a buffer size ins strn.. functions,
+ /// and allocators.
+ static const char MsgTaintedBufferSize[];
+ bool checkTaintedBufferSize(const CallExpr *CE, const FunctionDecl *FDecl,
+ CheckerContext &C) const;
+
+ /// Generate a report if the expression is tainted or points to tainted data.
+ bool generateReportIfTainted(const Expr *E, const char Msg[],
+ CheckerContext &C) const;
+
+ typedef SmallVector<unsigned, 2> ArgVector;
+
+ /// A struct used to specify taint propagation rules for a function.
+ ///
+ /// If any of the possible taint source arguments is tainted, all of the
+ /// destination arguments should also be tainted. Use InvalidArgIndex in the
+ /// src list to specify that all of the arguments can introduce taint. Use
+ /// InvalidArgIndex in the dst arguments to signify that all the non-const
+ /// pointer and reference arguments might be tainted on return. If
+ /// ReturnValueIndex is added to the dst list, the return value will be
+ /// tainted.
+ struct TaintPropagationRule {
+ /// List of arguments which can be taint sources and should be checked.
+ ArgVector SrcArgs;
+ /// List of arguments which should be tainted on function return.
+ ArgVector DstArgs;
+ // TODO: Check if using other data structures would be more optimal.
+
+ TaintPropagationRule() {}
+
+ TaintPropagationRule(unsigned SArg, unsigned DArg, bool TaintRet = false) {
+ SrcArgs.push_back(SArg);
+ DstArgs.push_back(DArg);
+ if (TaintRet)
+ DstArgs.push_back(ReturnValueIndex);
+ }
+
+ TaintPropagationRule(unsigned SArg1, unsigned SArg2, unsigned DArg,
+ bool TaintRet = false) {
+ SrcArgs.push_back(SArg1);
+ SrcArgs.push_back(SArg2);
+ DstArgs.push_back(DArg);
+ if (TaintRet)
+ DstArgs.push_back(ReturnValueIndex);
+ }
+
+ /// Get the propagation rule for a given function.
+ static TaintPropagationRule
+ getTaintPropagationRule(const FunctionDecl *FDecl, StringRef Name,
+ CheckerContext &C);
+
+ inline void addSrcArg(unsigned A) { SrcArgs.push_back(A); }
+ inline void addDstArg(unsigned A) { DstArgs.push_back(A); }
+
+ inline bool isNull() const { return SrcArgs.empty(); }
+
+ inline bool isDestinationArgument(unsigned ArgNum) const {
+ return (std::find(DstArgs.begin(), DstArgs.end(), ArgNum) !=
+ DstArgs.end());
+ }
+
+ static inline bool isTaintedOrPointsToTainted(const Expr *E,
+ ProgramStateRef State,
+ CheckerContext &C) {
+ if (State->isTainted(E, C.getLocationContext()) || isStdin(E, C))
+ return true;
+
+ if (!E->getType().getTypePtr()->isPointerType())
+ return false;
+
+ Optional<SVal> V = getPointedToSVal(C, E);
+ return (V && State->isTainted(*V));
+ }
+
+ /// Pre-process a function which propagates taint according to the
+ /// taint rule.
+ ProgramStateRef process(const CallExpr *CE, CheckerContext &C) const;
+ };
+};
+
+const unsigned GenericTaintChecker::ReturnValueIndex;
+const unsigned GenericTaintChecker::InvalidArgIndex;
+
+const char GenericTaintChecker::MsgUncontrolledFormatString[] =
+ "Untrusted data is used as a format string "
+ "(CWE-134: Uncontrolled Format String)";
+
+const char GenericTaintChecker::MsgSanitizeSystemArgs[] =
+ "Untrusted data is passed to a system call "
+ "(CERT/STR02-C. Sanitize data passed to complex subsystems)";
+
+const char GenericTaintChecker::MsgTaintedBufferSize[] =
+ "Untrusted data is used to specify the buffer size "
+ "(CERT/STR31-C. Guarantee that storage for strings has sufficient space "
+ "for "
+ "character data and the null terminator)";
+
+} // end of anonymous namespace
+
+/// A set which is used to pass information from call pre-visit instruction
+/// to the call post-visit. The values are unsigned integers, which are either
+/// ReturnValueIndex, or indexes of the pointer/reference argument, which
+/// points to data, which should be tainted on return.
+REGISTER_SET_WITH_PROGRAMSTATE(TaintArgsOnPostVisit, unsigned)
+
+GenericTaintChecker::TaintPropagationRule
+GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
+ const FunctionDecl *FDecl, StringRef Name, CheckerContext &C) {
+ // TODO: Currently, we might lose precision here: we always mark a return
+ // value as tainted even if it's just a pointer, pointing to tainted data.
+
+ // Check for exact name match for functions without builtin substitutes.
+ TaintPropagationRule Rule =
+ llvm::StringSwitch<TaintPropagationRule>(Name)
+ .Case("atoi", TaintPropagationRule(0, ReturnValueIndex))
+ .Case("atol", TaintPropagationRule(0, ReturnValueIndex))
+ .Case("atoll", TaintPropagationRule(0, ReturnValueIndex))
+ .Case("getc", TaintPropagationRule(0, ReturnValueIndex))
+ .Case("fgetc", TaintPropagationRule(0, ReturnValueIndex))
+ .Case("getc_unlocked", TaintPropagationRule(0, ReturnValueIndex))
+ .Case("getw", TaintPropagationRule(0, ReturnValueIndex))
+ .Case("toupper", TaintPropagationRule(0, ReturnValueIndex))
+ .Case("tolower", TaintPropagationRule(0, ReturnValueIndex))
+ .Case("strchr", TaintPropagationRule(0, ReturnValueIndex))
+ .Case("strrchr", TaintPropagationRule(0, ReturnValueIndex))
+ .Case("read", TaintPropagationRule(0, 2, 1, true))
+ .Case("pread", TaintPropagationRule(InvalidArgIndex, 1, true))
+ .Case("gets", TaintPropagationRule(InvalidArgIndex, 0, true))
+ .Case("fgets", TaintPropagationRule(2, 0, true))
+ .Case("getline", TaintPropagationRule(2, 0))
+ .Case("getdelim", TaintPropagationRule(3, 0))
+ .Case("fgetln", TaintPropagationRule(0, ReturnValueIndex))
+ .Default(TaintPropagationRule());
+
+ if (!Rule.isNull())
+ return Rule;
+
+ // Check if it's one of the memory setting/copying functions.
+ // This check is specialized but faster then calling isCLibraryFunction.
+ unsigned BId = 0;
+ if ((BId = FDecl->getMemoryFunctionKind()))
+ switch (BId) {
+ case Builtin::BImemcpy:
+ case Builtin::BImemmove:
+ case Builtin::BIstrncpy:
+ case Builtin::BIstrncat:
+ return TaintPropagationRule(1, 2, 0, true);
+ case Builtin::BIstrlcpy:
+ case Builtin::BIstrlcat:
+ return TaintPropagationRule(1, 2, 0, false);
+ case Builtin::BIstrndup:
+ return TaintPropagationRule(0, 1, ReturnValueIndex);
+
+ default:
+ break;
+ };
+
+ // Process all other functions which could be defined as builtins.
+ if (Rule.isNull()) {
+ if (C.isCLibraryFunction(FDecl, "snprintf") ||
+ C.isCLibraryFunction(FDecl, "sprintf"))
+ return TaintPropagationRule(InvalidArgIndex, 0, true);
+ else if (C.isCLibraryFunction(FDecl, "strcpy") ||
+ C.isCLibraryFunction(FDecl, "stpcpy") ||
+ C.isCLibraryFunction(FDecl, "strcat"))
+ return TaintPropagationRule(1, 0, true);
+ else if (C.isCLibraryFunction(FDecl, "bcopy"))
+ return TaintPropagationRule(0, 2, 1, false);
+ else if (C.isCLibraryFunction(FDecl, "strdup") ||
+ C.isCLibraryFunction(FDecl, "strdupa"))
+ return TaintPropagationRule(0, ReturnValueIndex);
+ else if (C.isCLibraryFunction(FDecl, "wcsdup"))
+ return TaintPropagationRule(0, ReturnValueIndex);
+ }
+
+ // Skipping the following functions, since they might be used for cleansing
+ // or smart memory copy:
+ // - memccpy - copying until hitting a special character.
+
+ return TaintPropagationRule();
+}
+
+void GenericTaintChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ // Check for errors first.
+ if (checkPre(CE, C))
+ return;
+
+ // Add taint second.
+ addSourcesPre(CE, C);
+}
+
+void GenericTaintChecker::checkPostStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ if (propagateFromPre(CE, C))
+ return;
+ addSourcesPost(CE, C);
+}
+
+void GenericTaintChecker::addSourcesPre(const CallExpr *CE,
+ CheckerContext &C) const {
+ ProgramStateRef State = nullptr;
+ const FunctionDecl *FDecl = C.getCalleeDecl(CE);
+ if (!FDecl || FDecl->getKind() != Decl::Function)
+ return;
+
+ StringRef Name = C.getCalleeName(FDecl);
+ if (Name.empty())
+ return;
+
+ // First, try generating a propagation rule for this function.
+ TaintPropagationRule Rule =
+ TaintPropagationRule::getTaintPropagationRule(FDecl, Name, C);
+ if (!Rule.isNull()) {
+ State = Rule.process(CE, C);
+ if (!State)
+ return;
+ C.addTransition(State);
+ return;
+ }
+
+ // Otherwise, check if we have custom pre-processing implemented.
+ FnCheck evalFunction = llvm::StringSwitch<FnCheck>(Name)
+ .Case("fscanf", &GenericTaintChecker::preFscanf)
+ .Default(nullptr);
+ // Check and evaluate the call.
+ if (evalFunction)
+ State = (this->*evalFunction)(CE, C);
+ if (!State)
+ return;
+ C.addTransition(State);
+}
+
+bool GenericTaintChecker::propagateFromPre(const CallExpr *CE,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // Depending on what was tainted at pre-visit, we determined a set of
+ // arguments which should be tainted after the function returns. These are
+ // stored in the state as TaintArgsOnPostVisit set.
+ TaintArgsOnPostVisitTy TaintArgs = State->get<TaintArgsOnPostVisit>();
+ if (TaintArgs.isEmpty())
+ return false;
+
+ for (llvm::ImmutableSet<unsigned>::iterator I = TaintArgs.begin(),
+ E = TaintArgs.end();
+ I != E; ++I) {
+ unsigned ArgNum = *I;
+
+ // Special handling for the tainted return value.
+ if (ArgNum == ReturnValueIndex) {
+ State = State->addTaint(CE, C.getLocationContext());
+ continue;
+ }
+
+ // The arguments are pointer arguments. The data they are pointing at is
+ // tainted after the call.
+ if (CE->getNumArgs() < (ArgNum + 1))
+ return false;
+ const Expr *Arg = CE->getArg(ArgNum);
+ Optional<SVal> V = getPointedToSVal(C, Arg);
+ if (V)
+ State = State->addTaint(*V);
+ }
+
+ // Clear up the taint info from the state.
+ State = State->remove<TaintArgsOnPostVisit>();
+
+ if (State != C.getState()) {
+ C.addTransition(State);
+ return true;
+ }
+ return false;
+}
+
+void GenericTaintChecker::addSourcesPost(const CallExpr *CE,
+ CheckerContext &C) const {
+ // Define the attack surface.
+ // Set the evaluation function by switching on the callee name.
+ const FunctionDecl *FDecl = C.getCalleeDecl(CE);
+ if (!FDecl || FDecl->getKind() != Decl::Function)
+ return;
+
+ StringRef Name = C.getCalleeName(FDecl);
+ if (Name.empty())
+ return;
+ FnCheck evalFunction =
+ llvm::StringSwitch<FnCheck>(Name)
+ .Case("scanf", &GenericTaintChecker::postScanf)
+ // TODO: Add support for vfscanf & family.
+ .Case("getchar", &GenericTaintChecker::postRetTaint)
+ .Case("getchar_unlocked", &GenericTaintChecker::postRetTaint)
+ .Case("getenv", &GenericTaintChecker::postRetTaint)
+ .Case("fopen", &GenericTaintChecker::postRetTaint)
+ .Case("fdopen", &GenericTaintChecker::postRetTaint)
+ .Case("freopen", &GenericTaintChecker::postRetTaint)
+ .Case("getch", &GenericTaintChecker::postRetTaint)
+ .Case("wgetch", &GenericTaintChecker::postRetTaint)
+ .Case("socket", &GenericTaintChecker::postSocket)
+ .Default(nullptr);
+
+ // If the callee isn't defined, it is not of security concern.
+ // Check and evaluate the call.
+ ProgramStateRef State = nullptr;
+ if (evalFunction)
+ State = (this->*evalFunction)(CE, C);
+ if (!State)
+ return;
+
+ C.addTransition(State);
+}
+
+bool GenericTaintChecker::checkPre(const CallExpr *CE,
+ CheckerContext &C) const {
+
+ if (checkUncontrolledFormatString(CE, C))
+ return true;
+
+ const FunctionDecl *FDecl = C.getCalleeDecl(CE);
+ if (!FDecl || FDecl->getKind() != Decl::Function)
+ return false;
+
+ StringRef Name = C.getCalleeName(FDecl);
+ if (Name.empty())
+ return false;
+
+ if (checkSystemCall(CE, Name, C))
+ return true;
+
+ if (checkTaintedBufferSize(CE, FDecl, C))
+ return true;
+
+ return false;
+}
+
+Optional<SVal> GenericTaintChecker::getPointedToSVal(CheckerContext &C,
+ const Expr *Arg) {
+ ProgramStateRef State = C.getState();
+ SVal AddrVal = C.getSVal(Arg->IgnoreParens());
+ if (AddrVal.isUnknownOrUndef())
+ return None;
+
+ Optional<Loc> AddrLoc = AddrVal.getAs<Loc>();
+ if (!AddrLoc)
+ return None;
+
+ QualType ArgTy = Arg->getType().getCanonicalType();
+ if (!ArgTy->isPointerType())
+ return None;
+
+ QualType ValTy = ArgTy->getPointeeType();
+
+ // Do not dereference void pointers. Treat them as byte pointers instead.
+ // FIXME: we might want to consider more than just the first byte.
+ if (ValTy->isVoidType())
+ ValTy = C.getASTContext().CharTy;
+
+ return State->getSVal(*AddrLoc, ValTy);
+}
+
+ProgramStateRef
+GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // Check for taint in arguments.
+ bool IsTainted = false;
+ for (ArgVector::const_iterator I = SrcArgs.begin(), E = SrcArgs.end(); I != E;
+ ++I) {
+ unsigned ArgNum = *I;
+
+ if (ArgNum == InvalidArgIndex) {
+ // Check if any of the arguments is tainted, but skip the
+ // destination arguments.
+ for (unsigned int i = 0; i < CE->getNumArgs(); ++i) {
+ if (isDestinationArgument(i))
+ continue;
+ if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(i), State, C)))
+ break;
+ }
+ break;
+ }
+
+ if (CE->getNumArgs() < (ArgNum + 1))
+ return State;
+ if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(ArgNum), State, C)))
+ break;
+ }
+ if (!IsTainted)
+ return State;
+
+ // Mark the arguments which should be tainted after the function returns.
+ for (ArgVector::const_iterator I = DstArgs.begin(), E = DstArgs.end(); I != E;
+ ++I) {
+ unsigned ArgNum = *I;
+
+ // Should we mark all arguments as tainted?
+ if (ArgNum == InvalidArgIndex) {
+ // For all pointer and references that were passed in:
+ // If they are not pointing to const data, mark data as tainted.
+ // TODO: So far we are just going one level down; ideally we'd need to
+ // recurse here.
+ for (unsigned int i = 0; i < CE->getNumArgs(); ++i) {
+ const Expr *Arg = CE->getArg(i);
+ // Process pointer argument.
+ const Type *ArgTy = Arg->getType().getTypePtr();
+ QualType PType = ArgTy->getPointeeType();
+ if ((!PType.isNull() && !PType.isConstQualified()) ||
+ (ArgTy->isReferenceType() && !Arg->getType().isConstQualified()))
+ State = State->add<TaintArgsOnPostVisit>(i);
+ }
+ continue;
+ }
+
+ // Should mark the return value?
+ if (ArgNum == ReturnValueIndex) {
+ State = State->add<TaintArgsOnPostVisit>(ReturnValueIndex);
+ continue;
+ }
+
+ // Mark the given argument.
+ assert(ArgNum < CE->getNumArgs());
+ State = State->add<TaintArgsOnPostVisit>(ArgNum);
+ }
+
+ return State;
+}
+
+// If argument 0 (file descriptor) is tainted, all arguments except for arg 0
+// and arg 1 should get taint.
+ProgramStateRef GenericTaintChecker::preFscanf(const CallExpr *CE,
+ CheckerContext &C) const {
+ assert(CE->getNumArgs() >= 2);
+ ProgramStateRef State = C.getState();
+
+ // Check is the file descriptor is tainted.
+ if (State->isTainted(CE->getArg(0), C.getLocationContext()) ||
+ isStdin(CE->getArg(0), C)) {
+ // All arguments except for the first two should get taint.
+ for (unsigned int i = 2; i < CE->getNumArgs(); ++i)
+ State = State->add<TaintArgsOnPostVisit>(i);
+ return State;
+ }
+
+ return nullptr;
+}
+
+// If argument 0(protocol domain) is network, the return value should get taint.
+ProgramStateRef GenericTaintChecker::postSocket(const CallExpr *CE,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ if (CE->getNumArgs() < 3)
+ return State;
+
+ SourceLocation DomLoc = CE->getArg(0)->getExprLoc();
+ StringRef DomName = C.getMacroNameOrSpelling(DomLoc);
+ // White list the internal communication protocols.
+ if (DomName.equals("AF_SYSTEM") || DomName.equals("AF_LOCAL") ||
+ DomName.equals("AF_UNIX") || DomName.equals("AF_RESERVED_36"))
+ return State;
+ State = State->addTaint(CE, C.getLocationContext());
+ return State;
+}
+
+ProgramStateRef GenericTaintChecker::postScanf(const CallExpr *CE,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ if (CE->getNumArgs() < 2)
+ return State;
+
+ // All arguments except for the very first one should get taint.
+ for (unsigned int i = 1; i < CE->getNumArgs(); ++i) {
+ // The arguments are pointer arguments. The data they are pointing at is
+ // tainted after the call.
+ const Expr *Arg = CE->getArg(i);
+ Optional<SVal> V = getPointedToSVal(C, Arg);
+ if (V)
+ State = State->addTaint(*V);
+ }
+ return State;
+}
+
+ProgramStateRef GenericTaintChecker::postRetTaint(const CallExpr *CE,
+ CheckerContext &C) const {
+ return C.getState()->addTaint(CE, C.getLocationContext());
+}
+
+bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) {
+ ProgramStateRef State = C.getState();
+ SVal Val = C.getSVal(E);
+
+ // stdin is a pointer, so it would be a region.
+ const MemRegion *MemReg = Val.getAsRegion();
+
+ // The region should be symbolic, we do not know it's value.
+ const SymbolicRegion *SymReg = dyn_cast_or_null<SymbolicRegion>(MemReg);
+ if (!SymReg)
+ return false;
+
+ // Get it's symbol and find the declaration region it's pointing to.
+ const SymbolRegionValue *Sm =
+ dyn_cast<SymbolRegionValue>(SymReg->getSymbol());
+ if (!Sm)
+ return false;
+ const DeclRegion *DeclReg = dyn_cast_or_null<DeclRegion>(Sm->getRegion());
+ if (!DeclReg)
+ return false;
+
+ // This region corresponds to a declaration, find out if it's a global/extern
+ // variable named stdin with the proper type.
+ if (const VarDecl *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) {
+ D = D->getCanonicalDecl();
+ if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC())
+ if (const PointerType *PtrTy =
+ dyn_cast<PointerType>(D->getType().getTypePtr()))
+ if (PtrTy->getPointeeType().getCanonicalType() ==
+ C.getASTContext().getFILEType().getCanonicalType())
+ return true;
+ }
+ return false;
+}
+
+static bool getPrintfFormatArgumentNum(const CallExpr *CE,
+ const CheckerContext &C,
+ unsigned int &ArgNum) {
+ // Find if the function contains a format string argument.
+ // Handles: fprintf, printf, sprintf, snprintf, vfprintf, vprintf, vsprintf,
+ // vsnprintf, syslog, custom annotated functions.
+ const FunctionDecl *FDecl = C.getCalleeDecl(CE);
+ if (!FDecl)
+ return false;
+ for (const auto *Format : FDecl->specific_attrs<FormatAttr>()) {
+ ArgNum = Format->getFormatIdx() - 1;
+ if ((Format->getType()->getName() == "printf") && CE->getNumArgs() > ArgNum)
+ return true;
+ }
+
+ // Or if a function is named setproctitle (this is a heuristic).
+ if (C.getCalleeName(CE).find("setproctitle") != StringRef::npos) {
+ ArgNum = 0;
+ return true;
+ }
+
+ return false;
+}
+
+bool GenericTaintChecker::generateReportIfTainted(const Expr *E,
+ const char Msg[],
+ CheckerContext &C) const {
+ assert(E);
+
+ // Check for taint.
+ ProgramStateRef State = C.getState();
+ Optional<SVal> PointedToSVal = getPointedToSVal(C, E);
+ SVal TaintedSVal;
+ if (PointedToSVal && State->isTainted(*PointedToSVal))
+ TaintedSVal = *PointedToSVal;
+ else if (State->isTainted(E, C.getLocationContext()))
+ TaintedSVal = C.getSVal(E);
+ else
+ return false;
+
+ // Generate diagnostic.
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ initBugType();
+ auto report = llvm::make_unique<BugReport>(*BT, Msg, N);
+ report->addRange(E->getSourceRange());
+ report->addVisitor(llvm::make_unique<TaintBugVisitor>(TaintedSVal));
+ C.emitReport(std::move(report));
+ return true;
+ }
+ return false;
+}
+
+bool GenericTaintChecker::checkUncontrolledFormatString(
+ const CallExpr *CE, CheckerContext &C) const {
+ // Check if the function contains a format string argument.
+ unsigned int ArgNum = 0;
+ if (!getPrintfFormatArgumentNum(CE, C, ArgNum))
+ return false;
+
+ // If either the format string content or the pointer itself are tainted,
+ // warn.
+ return generateReportIfTainted(CE->getArg(ArgNum),
+ MsgUncontrolledFormatString, C);
+}
+
+bool GenericTaintChecker::checkSystemCall(const CallExpr *CE, StringRef Name,
+ CheckerContext &C) const {
+ // TODO: It might make sense to run this check on demand. In some cases,
+ // we should check if the environment has been cleansed here. We also might
+ // need to know if the user was reset before these calls(seteuid).
+ unsigned ArgNum = llvm::StringSwitch<unsigned>(Name)
+ .Case("system", 0)
+ .Case("popen", 0)
+ .Case("execl", 0)
+ .Case("execle", 0)
+ .Case("execlp", 0)
+ .Case("execv", 0)
+ .Case("execvp", 0)
+ .Case("execvP", 0)
+ .Case("execve", 0)
+ .Case("dlopen", 0)
+ .Default(UINT_MAX);
+
+ if (ArgNum == UINT_MAX || CE->getNumArgs() < (ArgNum + 1))
+ return false;
+
+ return generateReportIfTainted(CE->getArg(ArgNum), MsgSanitizeSystemArgs, C);
+}
+
+// TODO: Should this check be a part of the CString checker?
+// If yes, should taint be a global setting?
+bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE,
+ const FunctionDecl *FDecl,
+ CheckerContext &C) const {
+ // If the function has a buffer size argument, set ArgNum.
+ unsigned ArgNum = InvalidArgIndex;
+ unsigned BId = 0;
+ if ((BId = FDecl->getMemoryFunctionKind()))
+ switch (BId) {
+ case Builtin::BImemcpy:
+ case Builtin::BImemmove:
+ case Builtin::BIstrncpy:
+ ArgNum = 2;
+ break;
+ case Builtin::BIstrndup:
+ ArgNum = 1;
+ break;
+ default:
+ break;
+ };
+
+ if (ArgNum == InvalidArgIndex) {
+ if (C.isCLibraryFunction(FDecl, "malloc") ||
+ C.isCLibraryFunction(FDecl, "calloc") ||
+ C.isCLibraryFunction(FDecl, "alloca"))
+ ArgNum = 0;
+ else if (C.isCLibraryFunction(FDecl, "memccpy"))
+ ArgNum = 3;
+ else if (C.isCLibraryFunction(FDecl, "realloc"))
+ ArgNum = 1;
+ else if (C.isCLibraryFunction(FDecl, "bcopy"))
+ ArgNum = 2;
+ }
+
+ return ArgNum != InvalidArgIndex && CE->getNumArgs() > ArgNum &&
+ generateReportIfTainted(CE->getArg(ArgNum), MsgTaintedBufferSize, C);
+}
+
+void ento::registerGenericTaintChecker(CheckerManager &mgr) {
+ mgr.registerChecker<GenericTaintChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
new file mode 100644
index 000000000000..4c2a229428d9
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
@@ -0,0 +1,515 @@
+//== IdenticalExprChecker.cpp - Identical expression checker----------------==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This defines IdenticalExprChecker, a check that warns about
+/// unintended use of identical expressions.
+///
+/// It checks for use of identical expressions with comparison operators and
+/// inside conditional expressions.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
+ const Stmt *Stmt2, bool IgnoreSideEffects = false);
+//===----------------------------------------------------------------------===//
+// FindIdenticalExprVisitor - Identify nodes using identical expressions.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class FindIdenticalExprVisitor
+ : public RecursiveASTVisitor<FindIdenticalExprVisitor> {
+ BugReporter &BR;
+ const CheckerBase *Checker;
+ AnalysisDeclContext *AC;
+public:
+ explicit FindIdenticalExprVisitor(BugReporter &B,
+ const CheckerBase *Checker,
+ AnalysisDeclContext *A)
+ : BR(B), Checker(Checker), AC(A) {}
+ // FindIdenticalExprVisitor only visits nodes
+ // that are binary operators, if statements or
+ // conditional operators.
+ bool VisitBinaryOperator(const BinaryOperator *B);
+ bool VisitIfStmt(const IfStmt *I);
+ bool VisitConditionalOperator(const ConditionalOperator *C);
+
+private:
+ void reportIdenticalExpr(const BinaryOperator *B, bool CheckBitwise,
+ ArrayRef<SourceRange> Sr);
+ void checkBitwiseOrLogicalOp(const BinaryOperator *B, bool CheckBitwise);
+ void checkComparisonOp(const BinaryOperator *B);
+};
+} // end anonymous namespace
+
+void FindIdenticalExprVisitor::reportIdenticalExpr(const BinaryOperator *B,
+ bool CheckBitwise,
+ ArrayRef<SourceRange> Sr) {
+ StringRef Message;
+ if (CheckBitwise)
+ Message = "identical expressions on both sides of bitwise operator";
+ else
+ Message = "identical expressions on both sides of logical operator";
+
+ PathDiagnosticLocation ELoc =
+ PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
+ BR.EmitBasicReport(AC->getDecl(), Checker,
+ "Use of identical expressions",
+ categories::LogicError,
+ Message, ELoc, Sr);
+}
+
+void FindIdenticalExprVisitor::checkBitwiseOrLogicalOp(const BinaryOperator *B,
+ bool CheckBitwise) {
+ SourceRange Sr[2];
+
+ const Expr *LHS = B->getLHS();
+ const Expr *RHS = B->getRHS();
+
+ // Split operators as long as we still have operators to split on. We will
+ // get called for every binary operator in an expression so there is no need
+ // to check every one against each other here, just the right most one with
+ // the others.
+ while (const BinaryOperator *B2 = dyn_cast<BinaryOperator>(LHS)) {
+ if (B->getOpcode() != B2->getOpcode())
+ break;
+ if (isIdenticalStmt(AC->getASTContext(), RHS, B2->getRHS())) {
+ Sr[0] = RHS->getSourceRange();
+ Sr[1] = B2->getRHS()->getSourceRange();
+ reportIdenticalExpr(B, CheckBitwise, Sr);
+ }
+ LHS = B2->getLHS();
+ }
+
+ if (isIdenticalStmt(AC->getASTContext(), RHS, LHS)) {
+ Sr[0] = RHS->getSourceRange();
+ Sr[1] = LHS->getSourceRange();
+ reportIdenticalExpr(B, CheckBitwise, Sr);
+ }
+}
+
+bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) {
+ const Stmt *Stmt1 = I->getThen();
+ const Stmt *Stmt2 = I->getElse();
+
+ // Check for identical inner condition:
+ //
+ // if (x<10) {
+ // if (x<10) {
+ // ..
+ if (const CompoundStmt *CS = dyn_cast<CompoundStmt>(Stmt1)) {
+ if (!CS->body_empty()) {
+ const IfStmt *InnerIf = dyn_cast<IfStmt>(*CS->body_begin());
+ if (InnerIf && isIdenticalStmt(AC->getASTContext(), I->getCond(), InnerIf->getCond(), /*ignoreSideEffects=*/ false)) {
+ PathDiagnosticLocation ELoc(InnerIf->getCond(), BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions",
+ categories::LogicError,
+ "conditions of the inner and outer statements are identical",
+ ELoc);
+ }
+ }
+ }
+
+ // Check for identical conditions:
+ //
+ // if (b) {
+ // foo1();
+ // } else if (b) {
+ // foo2();
+ // }
+ if (Stmt1 && Stmt2) {
+ const Expr *Cond1 = I->getCond();
+ const Stmt *Else = Stmt2;
+ while (const IfStmt *I2 = dyn_cast_or_null<IfStmt>(Else)) {
+ const Expr *Cond2 = I2->getCond();
+ if (isIdenticalStmt(AC->getASTContext(), Cond1, Cond2, false)) {
+ SourceRange Sr = Cond1->getSourceRange();
+ PathDiagnosticLocation ELoc(Cond2, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), Checker, "Identical conditions",
+ categories::LogicError,
+ "expression is identical to previous condition",
+ ELoc, Sr);
+ }
+ Else = I2->getElse();
+ }
+ }
+
+ if (!Stmt1 || !Stmt2)
+ return true;
+
+ // Special handling for code like:
+ //
+ // if (b) {
+ // i = 1;
+ // } else
+ // i = 1;
+ if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt1)) {
+ if (CompStmt->size() == 1)
+ Stmt1 = CompStmt->body_back();
+ }
+ if (const CompoundStmt *CompStmt = dyn_cast<CompoundStmt>(Stmt2)) {
+ if (CompStmt->size() == 1)
+ Stmt2 = CompStmt->body_back();
+ }
+
+ if (isIdenticalStmt(AC->getASTContext(), Stmt1, Stmt2, true)) {
+ PathDiagnosticLocation ELoc =
+ PathDiagnosticLocation::createBegin(I, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), Checker,
+ "Identical branches",
+ categories::LogicError,
+ "true and false branches are identical", ELoc);
+ }
+ return true;
+}
+
+bool FindIdenticalExprVisitor::VisitBinaryOperator(const BinaryOperator *B) {
+ BinaryOperator::Opcode Op = B->getOpcode();
+
+ if (BinaryOperator::isBitwiseOp(Op))
+ checkBitwiseOrLogicalOp(B, true);
+
+ if (BinaryOperator::isLogicalOp(Op))
+ checkBitwiseOrLogicalOp(B, false);
+
+ if (BinaryOperator::isComparisonOp(Op))
+ checkComparisonOp(B);
+
+ // We want to visit ALL nodes (subexpressions of binary comparison
+ // expressions too) that contains comparison operators.
+ // True is always returned to traverse ALL nodes.
+ return true;
+}
+
+void FindIdenticalExprVisitor::checkComparisonOp(const BinaryOperator *B) {
+ BinaryOperator::Opcode Op = B->getOpcode();
+
+ //
+ // Special case for floating-point representation.
+ //
+ // If expressions on both sides of comparison operator are of type float,
+ // then for some comparison operators no warning shall be
+ // reported even if the expressions are identical from a symbolic point of
+ // view. Comparison between expressions, declared variables and literals
+ // are treated differently.
+ //
+ // != and == between float literals that have the same value should NOT warn.
+ // < > between float literals that have the same value SHOULD warn.
+ //
+ // != and == between the same float declaration should NOT warn.
+ // < > between the same float declaration SHOULD warn.
+ //
+ // != and == between eq. expressions that evaluates into float
+ // should NOT warn.
+ // < > between eq. expressions that evaluates into float
+ // should NOT warn.
+ //
+ const Expr *LHS = B->getLHS()->IgnoreParenImpCasts();
+ const Expr *RHS = B->getRHS()->IgnoreParenImpCasts();
+
+ const DeclRefExpr *DeclRef1 = dyn_cast<DeclRefExpr>(LHS);
+ const DeclRefExpr *DeclRef2 = dyn_cast<DeclRefExpr>(RHS);
+ const FloatingLiteral *FloatLit1 = dyn_cast<FloatingLiteral>(LHS);
+ const FloatingLiteral *FloatLit2 = dyn_cast<FloatingLiteral>(RHS);
+ if ((DeclRef1) && (DeclRef2)) {
+ if ((DeclRef1->getType()->hasFloatingRepresentation()) &&
+ (DeclRef2->getType()->hasFloatingRepresentation())) {
+ if (DeclRef1->getDecl() == DeclRef2->getDecl()) {
+ if ((Op == BO_EQ) || (Op == BO_NE)) {
+ return;
+ }
+ }
+ }
+ } else if ((FloatLit1) && (FloatLit2)) {
+ if (FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue())) {
+ if ((Op == BO_EQ) || (Op == BO_NE)) {
+ return;
+ }
+ }
+ } else if (LHS->getType()->hasFloatingRepresentation()) {
+ // If any side of comparison operator still has floating-point
+ // representation, then it's an expression. Don't warn.
+ // Here only LHS is checked since RHS will be implicit casted to float.
+ return;
+ } else {
+ // No special case with floating-point representation, report as usual.
+ }
+
+ if (isIdenticalStmt(AC->getASTContext(), B->getLHS(), B->getRHS())) {
+ PathDiagnosticLocation ELoc =
+ PathDiagnosticLocation::createOperatorLoc(B, BR.getSourceManager());
+ StringRef Message;
+ if (Op == BO_Cmp)
+ Message = "comparison of identical expressions always evaluates to "
+ "'equal'";
+ else if (((Op == BO_EQ) || (Op == BO_LE) || (Op == BO_GE)))
+ Message = "comparison of identical expressions always evaluates to true";
+ else
+ Message = "comparison of identical expressions always evaluates to false";
+ BR.EmitBasicReport(AC->getDecl(), Checker,
+ "Compare of identical expressions",
+ categories::LogicError, Message, ELoc);
+ }
+}
+
+bool FindIdenticalExprVisitor::VisitConditionalOperator(
+ const ConditionalOperator *C) {
+
+ // Check if expressions in conditional expression are identical
+ // from a symbolic point of view.
+
+ if (isIdenticalStmt(AC->getASTContext(), C->getTrueExpr(),
+ C->getFalseExpr(), true)) {
+ PathDiagnosticLocation ELoc =
+ PathDiagnosticLocation::createConditionalColonLoc(
+ C, BR.getSourceManager());
+
+ SourceRange Sr[2];
+ Sr[0] = C->getTrueExpr()->getSourceRange();
+ Sr[1] = C->getFalseExpr()->getSourceRange();
+ BR.EmitBasicReport(
+ AC->getDecl(), Checker,
+ "Identical expressions in conditional expression",
+ categories::LogicError,
+ "identical expressions on both sides of ':' in conditional expression",
+ ELoc, Sr);
+ }
+ // We want to visit ALL nodes (expressions in conditional
+ // expressions too) that contains conditional operators,
+ // thus always return true to traverse ALL nodes.
+ return true;
+}
+
+/// Determines whether two statement trees are identical regarding
+/// operators and symbols.
+///
+/// Exceptions: expressions containing macros or functions with possible side
+/// effects are never considered identical.
+/// Limitations: (t + u) and (u + t) are not considered identical.
+/// t*(u + t) and t*u + t*t are not considered identical.
+///
+static bool isIdenticalStmt(const ASTContext &Ctx, const Stmt *Stmt1,
+ const Stmt *Stmt2, bool IgnoreSideEffects) {
+
+ if (!Stmt1 || !Stmt2) {
+ return !Stmt1 && !Stmt2;
+ }
+
+ // If Stmt1 & Stmt2 are of different class then they are not
+ // identical statements.
+ if (Stmt1->getStmtClass() != Stmt2->getStmtClass())
+ return false;
+
+ const Expr *Expr1 = dyn_cast<Expr>(Stmt1);
+ const Expr *Expr2 = dyn_cast<Expr>(Stmt2);
+
+ if (Expr1 && Expr2) {
+ // If Stmt1 has side effects then don't warn even if expressions
+ // are identical.
+ if (!IgnoreSideEffects && Expr1->HasSideEffects(Ctx))
+ return false;
+ // If either expression comes from a macro then don't warn even if
+ // the expressions are identical.
+ if ((Expr1->getExprLoc().isMacroID()) || (Expr2->getExprLoc().isMacroID()))
+ return false;
+
+ // If all children of two expressions are identical, return true.
+ Expr::const_child_iterator I1 = Expr1->child_begin();
+ Expr::const_child_iterator I2 = Expr2->child_begin();
+ while (I1 != Expr1->child_end() && I2 != Expr2->child_end()) {
+ if (!*I1 || !*I2 || !isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
+ return false;
+ ++I1;
+ ++I2;
+ }
+ // If there are different number of children in the statements, return
+ // false.
+ if (I1 != Expr1->child_end())
+ return false;
+ if (I2 != Expr2->child_end())
+ return false;
+ }
+
+ switch (Stmt1->getStmtClass()) {
+ default:
+ return false;
+ case Stmt::CallExprClass:
+ case Stmt::ArraySubscriptExprClass:
+ case Stmt::OMPArraySectionExprClass:
+ case Stmt::ImplicitCastExprClass:
+ case Stmt::ParenExprClass:
+ case Stmt::BreakStmtClass:
+ case Stmt::ContinueStmtClass:
+ case Stmt::NullStmtClass:
+ return true;
+ case Stmt::CStyleCastExprClass: {
+ const CStyleCastExpr* CastExpr1 = cast<CStyleCastExpr>(Stmt1);
+ const CStyleCastExpr* CastExpr2 = cast<CStyleCastExpr>(Stmt2);
+
+ return CastExpr1->getTypeAsWritten() == CastExpr2->getTypeAsWritten();
+ }
+ case Stmt::ReturnStmtClass: {
+ const ReturnStmt *ReturnStmt1 = cast<ReturnStmt>(Stmt1);
+ const ReturnStmt *ReturnStmt2 = cast<ReturnStmt>(Stmt2);
+
+ return isIdenticalStmt(Ctx, ReturnStmt1->getRetValue(),
+ ReturnStmt2->getRetValue(), IgnoreSideEffects);
+ }
+ case Stmt::ForStmtClass: {
+ const ForStmt *ForStmt1 = cast<ForStmt>(Stmt1);
+ const ForStmt *ForStmt2 = cast<ForStmt>(Stmt2);
+
+ if (!isIdenticalStmt(Ctx, ForStmt1->getInit(), ForStmt2->getInit(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, ForStmt1->getCond(), ForStmt2->getCond(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, ForStmt1->getInc(), ForStmt2->getInc(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, ForStmt1->getBody(), ForStmt2->getBody(),
+ IgnoreSideEffects))
+ return false;
+ return true;
+ }
+ case Stmt::DoStmtClass: {
+ const DoStmt *DStmt1 = cast<DoStmt>(Stmt1);
+ const DoStmt *DStmt2 = cast<DoStmt>(Stmt2);
+
+ if (!isIdenticalStmt(Ctx, DStmt1->getCond(), DStmt2->getCond(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, DStmt1->getBody(), DStmt2->getBody(),
+ IgnoreSideEffects))
+ return false;
+ return true;
+ }
+ case Stmt::WhileStmtClass: {
+ const WhileStmt *WStmt1 = cast<WhileStmt>(Stmt1);
+ const WhileStmt *WStmt2 = cast<WhileStmt>(Stmt2);
+
+ if (!isIdenticalStmt(Ctx, WStmt1->getCond(), WStmt2->getCond(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, WStmt1->getBody(), WStmt2->getBody(),
+ IgnoreSideEffects))
+ return false;
+ return true;
+ }
+ case Stmt::IfStmtClass: {
+ const IfStmt *IStmt1 = cast<IfStmt>(Stmt1);
+ const IfStmt *IStmt2 = cast<IfStmt>(Stmt2);
+
+ if (!isIdenticalStmt(Ctx, IStmt1->getCond(), IStmt2->getCond(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, IStmt1->getThen(), IStmt2->getThen(),
+ IgnoreSideEffects))
+ return false;
+ if (!isIdenticalStmt(Ctx, IStmt1->getElse(), IStmt2->getElse(),
+ IgnoreSideEffects))
+ return false;
+ return true;
+ }
+ case Stmt::CompoundStmtClass: {
+ const CompoundStmt *CompStmt1 = cast<CompoundStmt>(Stmt1);
+ const CompoundStmt *CompStmt2 = cast<CompoundStmt>(Stmt2);
+
+ if (CompStmt1->size() != CompStmt2->size())
+ return false;
+
+ CompoundStmt::const_body_iterator I1 = CompStmt1->body_begin();
+ CompoundStmt::const_body_iterator I2 = CompStmt2->body_begin();
+ while (I1 != CompStmt1->body_end() && I2 != CompStmt2->body_end()) {
+ if (!isIdenticalStmt(Ctx, *I1, *I2, IgnoreSideEffects))
+ return false;
+ ++I1;
+ ++I2;
+ }
+
+ return true;
+ }
+ case Stmt::CompoundAssignOperatorClass:
+ case Stmt::BinaryOperatorClass: {
+ const BinaryOperator *BinOp1 = cast<BinaryOperator>(Stmt1);
+ const BinaryOperator *BinOp2 = cast<BinaryOperator>(Stmt2);
+ return BinOp1->getOpcode() == BinOp2->getOpcode();
+ }
+ case Stmt::CharacterLiteralClass: {
+ const CharacterLiteral *CharLit1 = cast<CharacterLiteral>(Stmt1);
+ const CharacterLiteral *CharLit2 = cast<CharacterLiteral>(Stmt2);
+ return CharLit1->getValue() == CharLit2->getValue();
+ }
+ case Stmt::DeclRefExprClass: {
+ const DeclRefExpr *DeclRef1 = cast<DeclRefExpr>(Stmt1);
+ const DeclRefExpr *DeclRef2 = cast<DeclRefExpr>(Stmt2);
+ return DeclRef1->getDecl() == DeclRef2->getDecl();
+ }
+ case Stmt::IntegerLiteralClass: {
+ const IntegerLiteral *IntLit1 = cast<IntegerLiteral>(Stmt1);
+ const IntegerLiteral *IntLit2 = cast<IntegerLiteral>(Stmt2);
+
+ llvm::APInt I1 = IntLit1->getValue();
+ llvm::APInt I2 = IntLit2->getValue();
+ if (I1.getBitWidth() != I2.getBitWidth())
+ return false;
+ return I1 == I2;
+ }
+ case Stmt::FloatingLiteralClass: {
+ const FloatingLiteral *FloatLit1 = cast<FloatingLiteral>(Stmt1);
+ const FloatingLiteral *FloatLit2 = cast<FloatingLiteral>(Stmt2);
+ return FloatLit1->getValue().bitwiseIsEqual(FloatLit2->getValue());
+ }
+ case Stmt::StringLiteralClass: {
+ const StringLiteral *StringLit1 = cast<StringLiteral>(Stmt1);
+ const StringLiteral *StringLit2 = cast<StringLiteral>(Stmt2);
+ return StringLit1->getBytes() == StringLit2->getBytes();
+ }
+ case Stmt::MemberExprClass: {
+ const MemberExpr *MemberStmt1 = cast<MemberExpr>(Stmt1);
+ const MemberExpr *MemberStmt2 = cast<MemberExpr>(Stmt2);
+ return MemberStmt1->getMemberDecl() == MemberStmt2->getMemberDecl();
+ }
+ case Stmt::UnaryOperatorClass: {
+ const UnaryOperator *UnaryOp1 = cast<UnaryOperator>(Stmt1);
+ const UnaryOperator *UnaryOp2 = cast<UnaryOperator>(Stmt2);
+ return UnaryOp1->getOpcode() == UnaryOp2->getOpcode();
+ }
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// FindIdenticalExprChecker
+//===----------------------------------------------------------------------===//
+
+namespace {
+class FindIdenticalExprChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const {
+ FindIdenticalExprVisitor Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
+ Visitor.TraverseDecl(const_cast<Decl *>(D));
+ }
+};
+} // end anonymous namespace
+
+void ento::registerIdenticalExprChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<FindIdenticalExprChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
new file mode 100644
index 000000000000..a4f47d727a8f
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -0,0 +1,312 @@
+//=== InnerPointerChecker.cpp -------------------------------------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a check that marks a raw pointer to a C++ container's
+// inner buffer released when the object is destroyed. This information can
+// be used by MallocChecker to detect use-after-free problems.
+//
+//===----------------------------------------------------------------------===//
+
+#include "AllocationState.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "InterCheckerAPI.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+// Associate container objects with a set of raw pointer symbols.
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(PtrSet, SymbolRef)
+REGISTER_MAP_WITH_PROGRAMSTATE(RawPtrMap, const MemRegion *, PtrSet)
+
+
+namespace {
+
+class InnerPointerChecker
+ : public Checker<check::DeadSymbols, check::PostCall> {
+
+ CallDescription AppendFn, AssignFn, ClearFn, CStrFn, DataFn, EraseFn,
+ InsertFn, PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
+ ShrinkToFitFn, SwapFn;
+
+public:
+ class InnerPointerBRVisitor : public BugReporterVisitor {
+ SymbolRef PtrToBuf;
+
+ public:
+ InnerPointerBRVisitor(SymbolRef Sym) : PtrToBuf(Sym) {}
+
+ static void *getTag() {
+ static int Tag = 0;
+ return &Tag;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddPointer(getTag());
+ }
+
+ virtual std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ // FIXME: Scan the map once in the visitor's constructor and do a direct
+ // lookup by region.
+ bool isSymbolTracked(ProgramStateRef State, SymbolRef Sym) {
+ RawPtrMapTy Map = State->get<RawPtrMap>();
+ for (const auto Entry : Map) {
+ if (Entry.second.contains(Sym))
+ return true;
+ }
+ return false;
+ }
+ };
+
+ InnerPointerChecker()
+ : AppendFn({"std", "basic_string", "append"}),
+ AssignFn({"std", "basic_string", "assign"}),
+ ClearFn({"std", "basic_string", "clear"}),
+ CStrFn({"std", "basic_string", "c_str"}),
+ DataFn({"std", "basic_string", "data"}),
+ EraseFn({"std", "basic_string", "erase"}),
+ InsertFn({"std", "basic_string", "insert"}),
+ PopBackFn({"std", "basic_string", "pop_back"}),
+ PushBackFn({"std", "basic_string", "push_back"}),
+ ReplaceFn({"std", "basic_string", "replace"}),
+ ReserveFn({"std", "basic_string", "reserve"}),
+ ResizeFn({"std", "basic_string", "resize"}),
+ ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}),
+ SwapFn({"std", "basic_string", "swap"}) {}
+
+ /// Check whether the called member function potentially invalidates
+ /// pointers referring to the container object's inner buffer.
+ bool isInvalidatingMemberFunction(const CallEvent &Call) const;
+
+ /// Mark pointer symbols associated with the given memory region released
+ /// in the program state.
+ void markPtrSymbolsReleased(const CallEvent &Call, ProgramStateRef State,
+ const MemRegion *ObjRegion,
+ CheckerContext &C) const;
+
+ /// Standard library functions that take a non-const `basic_string` argument by
+ /// reference may invalidate its inner pointers. Check for these cases and
+ /// mark the pointers released.
+ void checkFunctionArguments(const CallEvent &Call, ProgramStateRef State,
+ CheckerContext &C) const;
+
+ /// Record the connection between raw pointers referring to a container
+ /// object's inner buffer and the object's memory region in the program state.
+ /// Mark potentially invalidated pointers released.
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+ /// Clean up the program state map.
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+};
+
+} // end anonymous namespace
+
+bool InnerPointerChecker::isInvalidatingMemberFunction(
+ const CallEvent &Call) const {
+ if (const auto *MemOpCall = dyn_cast<CXXMemberOperatorCall>(&Call)) {
+ OverloadedOperatorKind Opc = MemOpCall->getOriginExpr()->getOperator();
+ if (Opc == OO_Equal || Opc == OO_PlusEqual)
+ return true;
+ return false;
+ }
+ return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) ||
+ Call.isCalled(AssignFn) || Call.isCalled(ClearFn) ||
+ Call.isCalled(EraseFn) || Call.isCalled(InsertFn) ||
+ Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) ||
+ Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) ||
+ Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) ||
+ Call.isCalled(SwapFn));
+}
+
+void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
+ ProgramStateRef State,
+ const MemRegion *MR,
+ CheckerContext &C) const {
+ if (const PtrSet *PS = State->get<RawPtrMap>(MR)) {
+ const Expr *Origin = Call.getOriginExpr();
+ for (const auto Symbol : *PS) {
+ // NOTE: `Origin` may be null, and will be stored so in the symbol's
+ // `RefState` in MallocChecker's `RegionState` program state map.
+ State = allocation_state::markReleased(State, Symbol, Origin);
+ }
+ State = State->remove<RawPtrMap>(MR);
+ C.addTransition(State);
+ return;
+ }
+}
+
+void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,
+ ProgramStateRef State,
+ CheckerContext &C) const {
+ if (const auto *FC = dyn_cast<AnyFunctionCall>(&Call)) {
+ const FunctionDecl *FD = FC->getDecl();
+ if (!FD || !FD->isInStdNamespace())
+ return;
+
+ for (unsigned I = 0, E = FD->getNumParams(); I != E; ++I) {
+ QualType ParamTy = FD->getParamDecl(I)->getType();
+ if (!ParamTy->isReferenceType() ||
+ ParamTy->getPointeeType().isConstQualified())
+ continue;
+
+ // In case of member operator calls, `this` is counted as an
+ // argument but not as a parameter.
+ bool isaMemberOpCall = isa<CXXMemberOperatorCall>(FC);
+ unsigned ArgI = isaMemberOpCall ? I+1 : I;
+
+ SVal Arg = FC->getArgSVal(ArgI);
+ const auto *ArgRegion =
+ dyn_cast_or_null<TypedValueRegion>(Arg.getAsRegion());
+ if (!ArgRegion)
+ continue;
+
+ markPtrSymbolsReleased(Call, State, ArgRegion, C);
+ }
+ }
+}
+
+// [string.require]
+//
+// "References, pointers, and iterators referring to the elements of a
+// basic_string sequence may be invalidated by the following uses of that
+// basic_string object:
+//
+// -- As an argument to any standard library function taking a reference
+// to non-const basic_string as an argument. For example, as an argument to
+// non-member functions swap(), operator>>(), and getline(), or as an argument
+// to basic_string::swap().
+//
+// -- Calling non-const member functions, except operator[], at, front, back,
+// begin, rbegin, end, and rend."
+
+void InnerPointerChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ if (const auto *ICall = dyn_cast<CXXInstanceCall>(&Call)) {
+ // TODO: Do we need these to be typed?
+ const auto *ObjRegion = dyn_cast_or_null<TypedValueRegion>(
+ ICall->getCXXThisVal().getAsRegion());
+ if (!ObjRegion)
+ return;
+
+ if (Call.isCalled(CStrFn) || Call.isCalled(DataFn)) {
+ SVal RawPtr = Call.getReturnValue();
+ if (SymbolRef Sym = RawPtr.getAsSymbol(/*IncludeBaseRegions=*/true)) {
+ // Start tracking this raw pointer by adding it to the set of symbols
+ // associated with this container object in the program state map.
+
+ PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
+ const PtrSet *SetPtr = State->get<RawPtrMap>(ObjRegion);
+ PtrSet Set = SetPtr ? *SetPtr : F.getEmptySet();
+ assert(C.wasInlined || !Set.contains(Sym));
+ Set = F.add(Set, Sym);
+
+ State = State->set<RawPtrMap>(ObjRegion, Set);
+ C.addTransition(State);
+ }
+ return;
+ }
+
+ // Check [string.require] / second point.
+ if (isInvalidatingMemberFunction(Call)) {
+ markPtrSymbolsReleased(Call, State, ObjRegion, C);
+ return;
+ }
+ }
+
+ // Check [string.require] / first point.
+ checkFunctionArguments(Call, State, C);
+}
+
+void InnerPointerChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ PtrSet::Factory &F = State->getStateManager().get_context<PtrSet>();
+ RawPtrMapTy RPM = State->get<RawPtrMap>();
+ for (const auto Entry : RPM) {
+ if (!SymReaper.isLiveRegion(Entry.first)) {
+ // Due to incomplete destructor support, some dead regions might
+ // remain in the program state map. Clean them up.
+ State = State->remove<RawPtrMap>(Entry.first);
+ }
+ if (const PtrSet *OldSet = State->get<RawPtrMap>(Entry.first)) {
+ PtrSet CleanedUpSet = *OldSet;
+ for (const auto Symbol : Entry.second) {
+ if (!SymReaper.isLive(Symbol))
+ CleanedUpSet = F.remove(CleanedUpSet, Symbol);
+ }
+ State = CleanedUpSet.isEmpty()
+ ? State->remove<RawPtrMap>(Entry.first)
+ : State->set<RawPtrMap>(Entry.first, CleanedUpSet);
+ }
+ }
+ C.addTransition(State);
+}
+
+namespace clang {
+namespace ento {
+namespace allocation_state {
+
+std::unique_ptr<BugReporterVisitor> getInnerPointerBRVisitor(SymbolRef Sym) {
+ return llvm::make_unique<InnerPointerChecker::InnerPointerBRVisitor>(Sym);
+}
+
+const MemRegion *getContainerObjRegion(ProgramStateRef State, SymbolRef Sym) {
+ RawPtrMapTy Map = State->get<RawPtrMap>();
+ for (const auto Entry : Map) {
+ if (Entry.second.contains(Sym)) {
+ return Entry.first;
+ }
+ }
+ return nullptr;
+}
+
+} // end namespace allocation_state
+} // end namespace ento
+} // end namespace clang
+
+std::shared_ptr<PathDiagnosticPiece>
+InnerPointerChecker::InnerPointerBRVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &) {
+ if (!isSymbolTracked(N->getState(), PtrToBuf) ||
+ isSymbolTracked(N->getFirstPred()->getState(), PtrToBuf))
+ return nullptr;
+
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ if (!S)
+ return nullptr;
+
+ const MemRegion *ObjRegion =
+ allocation_state::getContainerObjRegion(N->getState(), PtrToBuf);
+ const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
+ QualType ObjTy = TypedRegion->getValueType();
+
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+ OS << "Pointer to inner buffer of '" << ObjTy.getAsString()
+ << "' obtained here";
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true,
+ nullptr);
+}
+
+void ento::registerInnerPointerChecker(CheckerManager &Mgr) {
+ registerInnerPointerCheckerAux(Mgr);
+ Mgr.registerChecker<InnerPointerChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
new file mode 100644
index 000000000000..81c95a4813a6
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
@@ -0,0 +1,27 @@
+//==--- InterCheckerAPI.h ---------------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This file allows introduction of checker dependencies. It contains APIs for
+// inter-checker communications.
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_INTERCHECKERAPI_H
+namespace clang {
+class CheckerManager;
+
+namespace ento {
+
+/// Register the checker which evaluates CString API calls.
+void registerCStringCheckerBasic(CheckerManager &Mgr);
+
+/// Register the part of MallocChecker connected to InnerPointerChecker.
+void registerInnerPointerCheckerAux(CheckerManager &Mgr);
+
+}}
+#endif /* INTERCHECKERAPI_H_ */
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
new file mode 100644
index 000000000000..e719e19d68e9
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -0,0 +1,2408 @@
+//===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for using iterators outside their range (past end). Usage
+// means here dereferencing, incrementing etc.
+//
+//===----------------------------------------------------------------------===//
+//
+// In the code, iterator can be represented as a:
+// * type-I: typedef-ed pointer. Operations over such iterator, such as
+// comparisons or increments, are modeled straightforwardly by the
+// analyzer.
+// * type-II: structure with its method bodies available. Operations over such
+// iterator are inlined by the analyzer, and results of modeling
+// these operations are exposing implementation details of the
+// iterators, which is not necessarily helping.
+// * type-III: completely opaque structure. Operations over such iterator are
+// modeled conservatively, producing conjured symbols everywhere.
+//
+// To handle all these types in a common way we introduce a structure called
+// IteratorPosition which is an abstraction of the position the iterator
+// represents using symbolic expressions. The checker handles all the
+// operations on this structure.
+//
+// Additionally, depending on the circumstances, operators of types II and III
+// can be represented as:
+// * type-IIa, type-IIIa: conjured structure symbols - when returned by value
+// from conservatively evaluated methods such as
+// `.begin()`.
+// * type-IIb, type-IIIb: memory regions of iterator-typed objects, such as
+// variables or temporaries, when the iterator object is
+// currently treated as an lvalue.
+// * type-IIc, type-IIIc: compound values of iterator-typed objects, when the
+// iterator object is treated as an rvalue taken of a
+// particular lvalue, eg. a copy of "type-a" iterator
+// object, or an iterator that existed before the
+// analysis has started.
+//
+// To handle any of these three different representations stored in an SVal we
+// use setter and getters functions which separate the three cases. To store
+// them we use a pointer union of symbol and memory region.
+//
+// The checker works the following way: We record the begin and the
+// past-end iterator for all containers whenever their `.begin()` and `.end()`
+// are called. Since the Constraint Manager cannot handle such SVals we need
+// to take over its role. We post-check equality and non-equality comparisons
+// and record that the two sides are equal if we are in the 'equal' branch
+// (true-branch for `==` and false-branch for `!=`).
+//
+// In case of type-I or type-II iterators we get a concrete integer as a result
+// of the comparison (1 or 0) but in case of type-III we only get a Symbol. In
+// this latter case we record the symbol and reload it in evalAssume() and do
+// the propagation there. We also handle (maybe double) negated comparisons
+// which are represented in the form of (x == 0 or x != 0) where x is the
+// comparison itself.
+//
+// Since `SimpleConstraintManager` cannot handle complex symbolic expressions
+// we only use expressions of the format S, S+n or S-n for iterator positions
+// where S is a conjured symbol and n is an unsigned concrete integer. When
+// making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
+// a constraint which we later retrieve when doing an actual comparison.
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+// Abstract position of an iterator. This helps to handle all three kinds
+// of operators in a common way by using a symbolic position.
+struct IteratorPosition {
+private:
+
+ // Container the iterator belongs to
+ const MemRegion *Cont;
+
+ // Whether iterator is valid
+ const bool Valid;
+
+ // Abstract offset
+ const SymbolRef Offset;
+
+ IteratorPosition(const MemRegion *C, bool V, SymbolRef Of)
+ : Cont(C), Valid(V), Offset(Of) {}
+
+public:
+ const MemRegion *getContainer() const { return Cont; }
+ bool isValid() const { return Valid; }
+ SymbolRef getOffset() const { return Offset; }
+
+ IteratorPosition invalidate() const {
+ return IteratorPosition(Cont, false, Offset);
+ }
+
+ static IteratorPosition getPosition(const MemRegion *C, SymbolRef Of) {
+ return IteratorPosition(C, true, Of);
+ }
+
+ IteratorPosition setTo(SymbolRef NewOf) const {
+ return IteratorPosition(Cont, Valid, NewOf);
+ }
+
+ IteratorPosition reAssign(const MemRegion *NewCont) const {
+ return IteratorPosition(NewCont, Valid, Offset);
+ }
+
+ bool operator==(const IteratorPosition &X) const {
+ return Cont == X.Cont && Valid == X.Valid && Offset == X.Offset;
+ }
+
+ bool operator!=(const IteratorPosition &X) const {
+ return Cont != X.Cont || Valid != X.Valid || Offset != X.Offset;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddPointer(Cont);
+ ID.AddInteger(Valid);
+ ID.Add(Offset);
+ }
+};
+
+typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
+
+// Structure to record the symbolic begin and end position of a container
+struct ContainerData {
+private:
+ const SymbolRef Begin, End;
+
+ ContainerData(SymbolRef B, SymbolRef E) : Begin(B), End(E) {}
+
+public:
+ static ContainerData fromBegin(SymbolRef B) {
+ return ContainerData(B, nullptr);
+ }
+
+ static ContainerData fromEnd(SymbolRef E) {
+ return ContainerData(nullptr, E);
+ }
+
+ SymbolRef getBegin() const { return Begin; }
+ SymbolRef getEnd() const { return End; }
+
+ ContainerData newBegin(SymbolRef B) const { return ContainerData(B, End); }
+
+ ContainerData newEnd(SymbolRef E) const { return ContainerData(Begin, E); }
+
+ bool operator==(const ContainerData &X) const {
+ return Begin == X.Begin && End == X.End;
+ }
+
+ bool operator!=(const ContainerData &X) const {
+ return Begin != X.Begin || End != X.End;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.Add(Begin);
+ ID.Add(End);
+ }
+};
+
+// Structure fo recording iterator comparisons. We needed to retrieve the
+// original comparison expression in assumptions.
+struct IteratorComparison {
+private:
+ RegionOrSymbol Left, Right;
+ bool Equality;
+
+public:
+ IteratorComparison(RegionOrSymbol L, RegionOrSymbol R, bool Eq)
+ : Left(L), Right(R), Equality(Eq) {}
+
+ RegionOrSymbol getLeft() const { return Left; }
+ RegionOrSymbol getRight() const { return Right; }
+ bool isEquality() const { return Equality; }
+ bool operator==(const IteratorComparison &X) const {
+ return Left == X.Left && Right == X.Right && Equality == X.Equality;
+ }
+ bool operator!=(const IteratorComparison &X) const {
+ return Left != X.Left || Right != X.Right || Equality != X.Equality;
+ }
+ void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(Equality); }
+};
+
+class IteratorChecker
+ : public Checker<check::PreCall, check::PostCall,
+ check::PostStmt<MaterializeTemporaryExpr>, check::Bind,
+ check::LiveSymbols, check::DeadSymbols,
+ eval::Assume> {
+
+ std::unique_ptr<BugType> OutOfRangeBugType;
+ std::unique_ptr<BugType> MismatchedBugType;
+ std::unique_ptr<BugType> InvalidatedBugType;
+
+ void handleComparison(CheckerContext &C, const SVal &RetVal, const SVal &LVal,
+ const SVal &RVal, OverloadedOperatorKind Op) const;
+ void verifyAccess(CheckerContext &C, const SVal &Val) const;
+ void verifyDereference(CheckerContext &C, const SVal &Val) const;
+ void handleIncrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
+ bool Postfix) const;
+ void handleDecrement(CheckerContext &C, const SVal &RetVal, const SVal &Iter,
+ bool Postfix) const;
+ void handleRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
+ const SVal &RetVal, const SVal &LHS,
+ const SVal &RHS) const;
+ void handleBegin(CheckerContext &C, const Expr *CE, const SVal &RetVal,
+ const SVal &Cont) const;
+ void handleEnd(CheckerContext &C, const Expr *CE, const SVal &RetVal,
+ const SVal &Cont) const;
+ void assignToContainer(CheckerContext &C, const Expr *CE, const SVal &RetVal,
+ const MemRegion *Cont) const;
+ void handleAssign(CheckerContext &C, const SVal &Cont,
+ const Expr *CE = nullptr,
+ const SVal &OldCont = UndefinedVal()) const;
+ void handleClear(CheckerContext &C, const SVal &Cont) const;
+ void handlePushBack(CheckerContext &C, const SVal &Cont) const;
+ void handlePopBack(CheckerContext &C, const SVal &Cont) const;
+ void handlePushFront(CheckerContext &C, const SVal &Cont) const;
+ void handlePopFront(CheckerContext &C, const SVal &Cont) const;
+ void handleInsert(CheckerContext &C, const SVal &Iter) const;
+ void handleErase(CheckerContext &C, const SVal &Iter) const;
+ void handleErase(CheckerContext &C, const SVal &Iter1,
+ const SVal &Iter2) const;
+ void handleEraseAfter(CheckerContext &C, const SVal &Iter) const;
+ void handleEraseAfter(CheckerContext &C, const SVal &Iter1,
+ const SVal &Iter2) const;
+ void verifyIncrement(CheckerContext &C, const SVal &Iter) const;
+ void verifyDecrement(CheckerContext &C, const SVal &Iter) const;
+ void verifyRandomIncrOrDecr(CheckerContext &C, OverloadedOperatorKind Op,
+ const SVal &LHS, const SVal &RHS) const;
+ void verifyMatch(CheckerContext &C, const SVal &Iter,
+ const MemRegion *Cont) const;
+ void verifyMatch(CheckerContext &C, const SVal &Iter1,
+ const SVal &Iter2) const;
+ IteratorPosition advancePosition(CheckerContext &C, OverloadedOperatorKind Op,
+ const IteratorPosition &Pos,
+ const SVal &Distance) const;
+ void reportOutOfRangeBug(const StringRef &Message, const SVal &Val,
+ CheckerContext &C, ExplodedNode *ErrNode) const;
+ void reportMismatchedBug(const StringRef &Message, const SVal &Val1,
+ const SVal &Val2, CheckerContext &C,
+ ExplodedNode *ErrNode) const;
+ void reportMismatchedBug(const StringRef &Message, const SVal &Val,
+ const MemRegion *Reg, CheckerContext &C,
+ ExplodedNode *ErrNode) const;
+ void reportInvalidatedBug(const StringRef &Message, const SVal &Val,
+ CheckerContext &C, ExplodedNode *ErrNode) const;
+
+public:
+ IteratorChecker();
+
+ enum CheckKind {
+ CK_IteratorRangeChecker,
+ CK_MismatchedIteratorChecker,
+ CK_InvalidatedIteratorChecker,
+ CK_NumCheckKinds
+ };
+
+ DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ CheckName CheckNames[CK_NumCheckKinds];
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const;
+ void checkPostStmt(const CXXConstructExpr *CCE, CheckerContext &C) const;
+ void checkPostStmt(const DeclStmt *DS, CheckerContext &C) const;
+ void checkPostStmt(const MaterializeTemporaryExpr *MTE,
+ CheckerContext &C) const;
+ void checkLiveSymbols(ProgramStateRef State, SymbolReaper &SR) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+ ProgramStateRef evalAssume(ProgramStateRef State, SVal Cond,
+ bool Assumption) const;
+};
+} // namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(IteratorSymbolMap, SymbolRef, IteratorPosition)
+REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
+ IteratorPosition)
+
+REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
+
+REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
+ IteratorComparison)
+
+namespace {
+
+bool isIteratorType(const QualType &Type);
+bool isIterator(const CXXRecordDecl *CRD);
+bool isComparisonOperator(OverloadedOperatorKind OK);
+bool isBeginCall(const FunctionDecl *Func);
+bool isEndCall(const FunctionDecl *Func);
+bool isAssignCall(const FunctionDecl *Func);
+bool isClearCall(const FunctionDecl *Func);
+bool isPushBackCall(const FunctionDecl *Func);
+bool isEmplaceBackCall(const FunctionDecl *Func);
+bool isPopBackCall(const FunctionDecl *Func);
+bool isPushFrontCall(const FunctionDecl *Func);
+bool isEmplaceFrontCall(const FunctionDecl *Func);
+bool isPopFrontCall(const FunctionDecl *Func);
+bool isInsertCall(const FunctionDecl *Func);
+bool isEraseCall(const FunctionDecl *Func);
+bool isEraseAfterCall(const FunctionDecl *Func);
+bool isEmplaceCall(const FunctionDecl *Func);
+bool isAssignmentOperator(OverloadedOperatorKind OK);
+bool isSimpleComparisonOperator(OverloadedOperatorKind OK);
+bool isAccessOperator(OverloadedOperatorKind OK);
+bool isDereferenceOperator(OverloadedOperatorKind OK);
+bool isIncrementOperator(OverloadedOperatorKind OK);
+bool isDecrementOperator(OverloadedOperatorKind OK);
+bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK);
+bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg);
+bool frontModifiable(ProgramStateRef State, const MemRegion *Reg);
+bool backModifiable(ProgramStateRef State, const MemRegion *Reg);
+BinaryOperator::Opcode getOpcode(const SymExpr *SE);
+const RegionOrSymbol getRegionOrSymbol(const SVal &Val);
+const ProgramStateRef processComparison(ProgramStateRef State,
+ RegionOrSymbol LVal,
+ RegionOrSymbol RVal, bool Equal);
+const ProgramStateRef saveComparison(ProgramStateRef State,
+ const SymExpr *Condition, const SVal &LVal,
+ const SVal &RVal, bool Eq);
+const IteratorComparison *loadComparison(ProgramStateRef State,
+ const SymExpr *Condition);
+SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont);
+SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont);
+ProgramStateRef createContainerBegin(ProgramStateRef State,
+ const MemRegion *Cont,
+ const SymbolRef Sym);
+ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
+ const SymbolRef Sym);
+const IteratorPosition *getIteratorPosition(ProgramStateRef State,
+ const SVal &Val);
+const IteratorPosition *getIteratorPosition(ProgramStateRef State,
+ RegionOrSymbol RegOrSym);
+ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
+ const IteratorPosition &Pos);
+ProgramStateRef setIteratorPosition(ProgramStateRef State,
+ RegionOrSymbol RegOrSym,
+ const IteratorPosition &Pos);
+ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val);
+ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
+ RegionOrSymbol RegOrSym,
+ const IteratorPosition &Pos, bool Equal);
+ProgramStateRef relateIteratorPositions(ProgramStateRef State,
+ const IteratorPosition &Pos1,
+ const IteratorPosition &Pos2,
+ bool Equal);
+ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
+ const MemRegion *Cont);
+ProgramStateRef
+invalidateAllIteratorPositionsExcept(ProgramStateRef State,
+ const MemRegion *Cont, SymbolRef Offset,
+ BinaryOperator::Opcode Opc);
+ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
+ SymbolRef Offset,
+ BinaryOperator::Opcode Opc);
+ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
+ SymbolRef Offset1,
+ BinaryOperator::Opcode Opc1,
+ SymbolRef Offset2,
+ BinaryOperator::Opcode Opc2);
+ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
+ const MemRegion *Cont,
+ const MemRegion *NewCont);
+ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
+ const MemRegion *Cont,
+ const MemRegion *NewCont,
+ SymbolRef Offset,
+ BinaryOperator::Opcode Opc);
+ProgramStateRef rebaseSymbolInIteratorPositionsIf(
+ ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
+ SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
+const ContainerData *getContainerData(ProgramStateRef State,
+ const MemRegion *Cont);
+ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
+ const ContainerData &CData);
+bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont);
+bool isBoundThroughLazyCompoundVal(const Environment &Env,
+ const MemRegion *Reg);
+bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
+bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos);
+bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos);
+bool isZero(ProgramStateRef State, const NonLoc &Val);
+} // namespace
+
+IteratorChecker::IteratorChecker() {
+ OutOfRangeBugType.reset(
+ new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
+ OutOfRangeBugType->setSuppressOnSink(true);
+ MismatchedBugType.reset(
+ new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs"));
+ MismatchedBugType->setSuppressOnSink(true);
+ InvalidatedBugType.reset(
+ new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
+ InvalidatedBugType->setSuppressOnSink(true);
+}
+
+void IteratorChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // Check for out of range access or access of invalidated position and
+ // iterator mismatches
+ const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!Func)
+ return;
+
+ if (Func->isOverloadedOperator()) {
+ if (ChecksEnabled[CK_InvalidatedIteratorChecker] &&
+ isAccessOperator(Func->getOverloadedOperator())) {
+ // Check for any kind of access of invalidated iterator positions
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ verifyAccess(C, InstCall->getCXXThisVal());
+ } else {
+ verifyAccess(C, Call.getArgSVal(0));
+ }
+ }
+ if (ChecksEnabled[CK_IteratorRangeChecker]) {
+ if (isIncrementOperator(Func->getOverloadedOperator())) {
+ // Check for out-of-range incrementions
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ verifyIncrement(C, InstCall->getCXXThisVal());
+ } else {
+ if (Call.getNumArgs() >= 1) {
+ verifyIncrement(C, Call.getArgSVal(0));
+ }
+ }
+ } else if (isDecrementOperator(Func->getOverloadedOperator())) {
+ // Check for out-of-range decrementions
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ verifyDecrement(C, InstCall->getCXXThisVal());
+ } else {
+ if (Call.getNumArgs() >= 1) {
+ verifyDecrement(C, Call.getArgSVal(0));
+ }
+ }
+ } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ // Check for out-of-range incrementions and decrementions
+ if (Call.getNumArgs() >= 1) {
+ verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ InstCall->getCXXThisVal(),
+ Call.getArgSVal(0));
+ }
+ } else {
+ if (Call.getNumArgs() >= 2) {
+ verifyRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ Call.getArgSVal(0), Call.getArgSVal(1));
+ }
+ }
+ } else if (isDereferenceOperator(Func->getOverloadedOperator())) {
+ // Check for dereference of out-of-range iterators
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ verifyDereference(C, InstCall->getCXXThisVal());
+ } else {
+ verifyDereference(C, Call.getArgSVal(0));
+ }
+ }
+ } else if (ChecksEnabled[CK_MismatchedIteratorChecker] &&
+ isComparisonOperator(Func->getOverloadedOperator())) {
+ // Check for comparisons of iterators of different containers
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ if (Call.getNumArgs() < 1)
+ return;
+
+ if (!isIteratorType(InstCall->getCXXThisExpr()->getType()) ||
+ !isIteratorType(Call.getArgExpr(0)->getType()))
+ return;
+
+ verifyMatch(C, InstCall->getCXXThisVal(), Call.getArgSVal(0));
+ } else {
+ if (Call.getNumArgs() < 2)
+ return;
+
+ if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
+ !isIteratorType(Call.getArgExpr(1)->getType()))
+ return;
+
+ verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ }
+ }
+ } else if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ if (!ChecksEnabled[CK_MismatchedIteratorChecker])
+ return;
+
+ const auto *ContReg = InstCall->getCXXThisVal().getAsRegion();
+ if (!ContReg)
+ return;
+ // Check for erase, insert and emplace using iterator of another container
+ if (isEraseCall(Func) || isEraseAfterCall(Func)) {
+ verifyMatch(C, Call.getArgSVal(0),
+ InstCall->getCXXThisVal().getAsRegion());
+ if (Call.getNumArgs() == 2) {
+ verifyMatch(C, Call.getArgSVal(1),
+ InstCall->getCXXThisVal().getAsRegion());
+ }
+ } else if (isInsertCall(Func)) {
+ verifyMatch(C, Call.getArgSVal(0),
+ InstCall->getCXXThisVal().getAsRegion());
+ if (Call.getNumArgs() == 3 &&
+ isIteratorType(Call.getArgExpr(1)->getType()) &&
+ isIteratorType(Call.getArgExpr(2)->getType())) {
+ verifyMatch(C, Call.getArgSVal(1), Call.getArgSVal(2));
+ }
+ } else if (isEmplaceCall(Func)) {
+ verifyMatch(C, Call.getArgSVal(0),
+ InstCall->getCXXThisVal().getAsRegion());
+ }
+ } else if (isa<CXXConstructorCall>(&Call)) {
+ // Check match of first-last iterator pair in a constructor of a container
+ if (Call.getNumArgs() < 2)
+ return;
+
+ const auto *Ctr = cast<CXXConstructorDecl>(Call.getDecl());
+ if (Ctr->getNumParams() < 2)
+ return;
+
+ if (Ctr->getParamDecl(0)->getName() != "first" ||
+ Ctr->getParamDecl(1)->getName() != "last")
+ return;
+
+ if (!isIteratorType(Call.getArgExpr(0)->getType()) ||
+ !isIteratorType(Call.getArgExpr(1)->getType()))
+ return;
+
+ verifyMatch(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ } else {
+ // The main purpose of iterators is to abstract away from different
+ // containers and provide a (maybe limited) uniform access to them.
+ // This implies that any correctly written template function that
+ // works on multiple containers using iterators takes different
+ // template parameters for different containers. So we can safely
+ // assume that passing iterators of different containers as arguments
+ // whose type replaces the same template parameter is a bug.
+ //
+ // Example:
+ // template<typename I1, typename I2>
+ // void f(I1 first1, I1 last1, I2 first2, I2 last2);
+ //
+ // In this case the first two arguments to f() must be iterators must belong
+ // to the same container and the last to also to the same container but
+ // not necessarily to the same as the first two.
+
+ if (!ChecksEnabled[CK_MismatchedIteratorChecker])
+ return;
+
+ const auto *Templ = Func->getPrimaryTemplate();
+ if (!Templ)
+ return;
+
+ const auto *TParams = Templ->getTemplateParameters();
+ const auto *TArgs = Func->getTemplateSpecializationArgs();
+
+ // Iterate over all the template parameters
+ for (size_t I = 0; I < TParams->size(); ++I) {
+ const auto *TPDecl = dyn_cast<TemplateTypeParmDecl>(TParams->getParam(I));
+ if (!TPDecl)
+ continue;
+
+ if (TPDecl->isParameterPack())
+ continue;
+
+ const auto TAType = TArgs->get(I).getAsType();
+ if (!isIteratorType(TAType))
+ continue;
+
+ SVal LHS = UndefinedVal();
+
+ // For every template parameter which is an iterator type in the
+ // instantiation look for all functions' parameters' type by it and
+ // check whether they belong to the same container
+ for (auto J = 0U; J < Func->getNumParams(); ++J) {
+ const auto *Param = Func->getParamDecl(J);
+ const auto *ParamType =
+ Param->getType()->getAs<SubstTemplateTypeParmType>();
+ if (!ParamType ||
+ ParamType->getReplacedParameter()->getDecl() != TPDecl)
+ continue;
+ if (LHS.isUndef()) {
+ LHS = Call.getArgSVal(J);
+ } else {
+ verifyMatch(C, LHS, Call.getArgSVal(J));
+ }
+ }
+ }
+ }
+}
+
+void IteratorChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // Record new iterator positions and iterator position changes
+ const auto *Func = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!Func)
+ return;
+
+ if (Func->isOverloadedOperator()) {
+ const auto Op = Func->getOverloadedOperator();
+ if (isAssignmentOperator(Op)) {
+ const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call);
+ if (Func->getParamDecl(0)->getType()->isRValueReferenceType()) {
+ handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
+ Call.getArgSVal(0));
+ } else {
+ handleAssign(C, InstCall->getCXXThisVal());
+ }
+ } else if (isSimpleComparisonOperator(Op)) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleComparison(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
+ Call.getArgSVal(0), Op);
+ } else {
+ handleComparison(C, Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getArgSVal(1), Op);
+ }
+ } else if (isRandomIncrOrDecrOperator(Func->getOverloadedOperator())) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ if (Call.getNumArgs() >= 1) {
+ handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ Call.getReturnValue(),
+ InstCall->getCXXThisVal(), Call.getArgSVal(0));
+ }
+ } else {
+ if (Call.getNumArgs() >= 2) {
+ handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
+ Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getArgSVal(1));
+ }
+ }
+ } else if (isIncrementOperator(Func->getOverloadedOperator())) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleIncrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
+ Call.getNumArgs());
+ } else {
+ handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getNumArgs());
+ }
+ } else if (isDecrementOperator(Func->getOverloadedOperator())) {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ handleDecrement(C, Call.getReturnValue(), InstCall->getCXXThisVal(),
+ Call.getNumArgs());
+ } else {
+ handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getNumArgs());
+ }
+ }
+ } else {
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ if (isAssignCall(Func)) {
+ handleAssign(C, InstCall->getCXXThisVal());
+ } else if (isClearCall(Func)) {
+ handleClear(C, InstCall->getCXXThisVal());
+ } else if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
+ handlePushBack(C, InstCall->getCXXThisVal());
+ } else if (isPopBackCall(Func)) {
+ handlePopBack(C, InstCall->getCXXThisVal());
+ } else if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
+ handlePushFront(C, InstCall->getCXXThisVal());
+ } else if (isPopFrontCall(Func)) {
+ handlePopFront(C, InstCall->getCXXThisVal());
+ } else if (isInsertCall(Func) || isEmplaceCall(Func)) {
+ handleInsert(C, Call.getArgSVal(0));
+ } else if (isEraseCall(Func)) {
+ if (Call.getNumArgs() == 1) {
+ handleErase(C, Call.getArgSVal(0));
+ } else if (Call.getNumArgs() == 2) {
+ handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ }
+ } else if (isEraseAfterCall(Func)) {
+ if (Call.getNumArgs() == 1) {
+ handleEraseAfter(C, Call.getArgSVal(0));
+ } else if (Call.getNumArgs() == 2) {
+ handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ }
+ }
+ }
+
+ const auto *OrigExpr = Call.getOriginExpr();
+ if (!OrigExpr)
+ return;
+
+ if (!isIteratorType(Call.getResultType()))
+ return;
+
+ auto State = C.getState();
+
+ if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
+ if (isBeginCall(Func)) {
+ handleBegin(C, OrigExpr, Call.getReturnValue(),
+ InstCall->getCXXThisVal());
+ return;
+ }
+ if (isEndCall(Func)) {
+ handleEnd(C, OrigExpr, Call.getReturnValue(),
+ InstCall->getCXXThisVal());
+ return;
+ }
+ }
+
+ // Already bound to container?
+ if (getIteratorPosition(State, Call.getReturnValue()))
+ return;
+
+ // Copy-like and move constructors
+ if (isa<CXXConstructorCall>(&Call) && Call.getNumArgs() == 1) {
+ if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(0))) {
+ State = setIteratorPosition(State, Call.getReturnValue(), *Pos);
+ if (cast<CXXConstructorDecl>(Func)->isMoveConstructor()) {
+ State = removeIteratorPosition(State, Call.getArgSVal(0));
+ }
+ C.addTransition(State);
+ return;
+ }
+ }
+
+ // Assumption: if return value is an iterator which is not yet bound to a
+ // container, then look for the first iterator argument, and
+ // bind the return value to the same container. This approach
+ // works for STL algorithms.
+ // FIXME: Add a more conservative mode
+ for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
+ if (isIteratorType(Call.getArgExpr(i)->getType())) {
+ if (const auto *Pos = getIteratorPosition(State, Call.getArgSVal(i))) {
+ assignToContainer(C, OrigExpr, Call.getReturnValue(),
+ Pos->getContainer());
+ return;
+ }
+ }
+ }
+ }
+}
+
+void IteratorChecker::checkBind(SVal Loc, SVal Val, const Stmt *S,
+ CheckerContext &C) const {
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Val);
+ if (Pos) {
+ State = setIteratorPosition(State, Loc, *Pos);
+ C.addTransition(State);
+ } else {
+ const auto *OldPos = getIteratorPosition(State, Loc);
+ if (OldPos) {
+ State = removeIteratorPosition(State, Loc);
+ C.addTransition(State);
+ }
+ }
+}
+
+void IteratorChecker::checkPostStmt(const MaterializeTemporaryExpr *MTE,
+ CheckerContext &C) const {
+ /* Transfer iterator state to temporary objects */
+ auto State = C.getState();
+ const auto *Pos =
+ getIteratorPosition(State, C.getSVal(MTE->GetTemporaryExpr()));
+ if (!Pos)
+ return;
+ State = setIteratorPosition(State, C.getSVal(MTE), *Pos);
+ C.addTransition(State);
+}
+
+void IteratorChecker::checkLiveSymbols(ProgramStateRef State,
+ SymbolReaper &SR) const {
+ // Keep symbolic expressions of iterator positions, container begins and ends
+ // alive
+ auto RegionMap = State->get<IteratorRegionMap>();
+ for (const auto Reg : RegionMap) {
+ const auto Offset = Reg.second.getOffset();
+ for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
+ if (isa<SymbolData>(*i))
+ SR.markLive(*i);
+ }
+
+ auto SymbolMap = State->get<IteratorSymbolMap>();
+ for (const auto Sym : SymbolMap) {
+ const auto Offset = Sym.second.getOffset();
+ for (auto i = Offset->symbol_begin(); i != Offset->symbol_end(); ++i)
+ if (isa<SymbolData>(*i))
+ SR.markLive(*i);
+ }
+
+ auto ContMap = State->get<ContainerMap>();
+ for (const auto Cont : ContMap) {
+ const auto CData = Cont.second;
+ if (CData.getBegin()) {
+ SR.markLive(CData.getBegin());
+ if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getBegin()))
+ SR.markLive(SIE->getLHS());
+ }
+ if (CData.getEnd()) {
+ SR.markLive(CData.getEnd());
+ if(const auto *SIE = dyn_cast<SymIntExpr>(CData.getEnd()))
+ SR.markLive(SIE->getLHS());
+ }
+ }
+}
+
+void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
+ // Cleanup
+ auto State = C.getState();
+
+ auto RegionMap = State->get<IteratorRegionMap>();
+ for (const auto Reg : RegionMap) {
+ if (!SR.isLiveRegion(Reg.first)) {
+ // The region behind the `LazyCompoundVal` is often cleaned up before
+ // the `LazyCompoundVal` itself. If there are iterator positions keyed
+ // by these regions their cleanup must be deferred.
+ if (!isBoundThroughLazyCompoundVal(State->getEnvironment(), Reg.first)) {
+ State = State->remove<IteratorRegionMap>(Reg.first);
+ }
+ }
+ }
+
+ auto SymbolMap = State->get<IteratorSymbolMap>();
+ for (const auto Sym : SymbolMap) {
+ if (!SR.isLive(Sym.first)) {
+ State = State->remove<IteratorSymbolMap>(Sym.first);
+ }
+ }
+
+ auto ContMap = State->get<ContainerMap>();
+ for (const auto Cont : ContMap) {
+ if (!SR.isLiveRegion(Cont.first)) {
+ // We must keep the container data while it has live iterators to be able
+ // to compare them to the begin and the end of the container.
+ if (!hasLiveIterators(State, Cont.first)) {
+ State = State->remove<ContainerMap>(Cont.first);
+ }
+ }
+ }
+
+ auto ComparisonMap = State->get<IteratorComparisonMap>();
+ for (const auto Comp : ComparisonMap) {
+ if (!SR.isLive(Comp.first)) {
+ State = State->remove<IteratorComparisonMap>(Comp.first);
+ }
+ }
+
+ C.addTransition(State);
+}
+
+ProgramStateRef IteratorChecker::evalAssume(ProgramStateRef State, SVal Cond,
+ bool Assumption) const {
+ // Load recorded comparison and transfer iterator state between sides
+ // according to comparison operator and assumption
+ const auto *SE = Cond.getAsSymExpr();
+ if (!SE)
+ return State;
+
+ auto Opc = getOpcode(SE);
+ if (Opc != BO_EQ && Opc != BO_NE)
+ return State;
+
+ bool Negated = false;
+ const auto *Comp = loadComparison(State, SE);
+ if (!Comp) {
+ // Try negated comparison, which is a SymExpr to 0 integer comparison
+ const auto *SIE = dyn_cast<SymIntExpr>(SE);
+ if (!SIE)
+ return State;
+
+ if (SIE->getRHS() != 0)
+ return State;
+
+ SE = SIE->getLHS();
+ Negated = SIE->getOpcode() == BO_EQ; // Equal to zero means negation
+ Opc = getOpcode(SE);
+ if (Opc != BO_EQ && Opc != BO_NE)
+ return State;
+
+ Comp = loadComparison(State, SE);
+ if (!Comp)
+ return State;
+ }
+
+ return processComparison(State, Comp->getLeft(), Comp->getRight(),
+ (Comp->isEquality() == Assumption) != Negated);
+}
+
+void IteratorChecker::handleComparison(CheckerContext &C, const SVal &RetVal,
+ const SVal &LVal, const SVal &RVal,
+ OverloadedOperatorKind Op) const {
+ // Record the operands and the operator of the comparison for the next
+ // evalAssume, if the result is a symbolic expression. If it is a concrete
+ // value (only one branch is possible), then transfer the state between
+ // the operands according to the operator and the result
+ auto State = C.getState();
+ if (const auto *Condition = RetVal.getAsSymbolicExpression()) {
+ const auto *LPos = getIteratorPosition(State, LVal);
+ const auto *RPos = getIteratorPosition(State, RVal);
+ if (!LPos && !RPos)
+ return;
+ State = saveComparison(State, Condition, LVal, RVal, Op == OO_EqualEqual);
+ C.addTransition(State);
+ } else if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
+ if ((State = processComparison(
+ State, getRegionOrSymbol(LVal), getRegionOrSymbol(RVal),
+ (Op == OO_EqualEqual) == (TruthVal->getValue() != 0)))) {
+ C.addTransition(State);
+ } else {
+ C.generateSink(State, C.getPredecessor());
+ }
+ }
+}
+
+void IteratorChecker::verifyDereference(CheckerContext &C,
+ const SVal &Val) const {
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Val);
+ if (Pos && isPastTheEnd(State, *Pos)) {
+ auto *N = C.generateNonFatalErrorNode(State);
+ if (!N)
+ return;
+ reportOutOfRangeBug("Past-the-end iterator dereferenced.", Val, C, N);
+ return;
+ }
+}
+
+void IteratorChecker::verifyAccess(CheckerContext &C, const SVal &Val) const {
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Val);
+ if (Pos && !Pos->isValid()) {
+ auto *N = C.generateNonFatalErrorNode(State);
+ if (!N) {
+ return;
+ }
+ reportInvalidatedBug("Invalidated iterator accessed.", Val, C, N);
+ }
+}
+
+void IteratorChecker::handleIncrement(CheckerContext &C, const SVal &RetVal,
+ const SVal &Iter, bool Postfix) const {
+ // Increment the symbolic expressions which represents the position of the
+ // iterator
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (Pos) {
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ const auto NewPos =
+ advancePosition(C, OO_Plus, *Pos,
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
+ State = setIteratorPosition(State, Iter, NewPos);
+ State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
+ C.addTransition(State);
+ }
+}
+
+void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
+ const SVal &Iter, bool Postfix) const {
+ // Decrement the symbolic expressions which represents the position of the
+ // iterator
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (Pos) {
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ const auto NewPos =
+ advancePosition(C, OO_Minus, *Pos,
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
+ State = setIteratorPosition(State, Iter, NewPos);
+ State = setIteratorPosition(State, RetVal, Postfix ? *Pos : NewPos);
+ C.addTransition(State);
+ }
+}
+
+// This function tells the analyzer's engine that symbols produced by our
+// checker, most notably iterator positions, are relatively small.
+// A distance between items in the container should not be very large.
+// By assuming that it is within around 1/8 of the address space,
+// we can help the analyzer perform operations on these symbols
+// without being afraid of integer overflows.
+// FIXME: Should we provide it as an API, so that all checkers could use it?
+static ProgramStateRef assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
+ long Scale) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ BasicValueFactory &BV = SVB.getBasicValueFactory();
+
+ QualType T = Sym->getType();
+ assert(T->isSignedIntegerOrEnumerationType());
+ APSIntType AT = BV.getAPSIntType(T);
+
+ ProgramStateRef NewState = State;
+
+ llvm::APSInt Max = AT.getMaxValue() / AT.getValue(Scale);
+ SVal IsCappedFromAbove =
+ SVB.evalBinOpNN(State, BO_LE, nonloc::SymbolVal(Sym),
+ nonloc::ConcreteInt(Max), SVB.getConditionType());
+ if (auto DV = IsCappedFromAbove.getAs<DefinedSVal>()) {
+ NewState = NewState->assume(*DV, true);
+ if (!NewState)
+ return State;
+ }
+
+ llvm::APSInt Min = -Max;
+ SVal IsCappedFromBelow =
+ SVB.evalBinOpNN(State, BO_GE, nonloc::SymbolVal(Sym),
+ nonloc::ConcreteInt(Min), SVB.getConditionType());
+ if (auto DV = IsCappedFromBelow.getAs<DefinedSVal>()) {
+ NewState = NewState->assume(*DV, true);
+ if (!NewState)
+ return State;
+ }
+
+ return NewState;
+}
+
+void IteratorChecker::handleRandomIncrOrDecr(CheckerContext &C,
+ OverloadedOperatorKind Op,
+ const SVal &RetVal,
+ const SVal &LHS,
+ const SVal &RHS) const {
+ // Increment or decrement the symbolic expressions which represents the
+ // position of the iterator
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, LHS);
+ if (!Pos)
+ return;
+
+ const auto *value = &RHS;
+ if (auto loc = RHS.getAs<Loc>()) {
+ const auto val = State->getRawSVal(*loc);
+ value = &val;
+ }
+
+ auto &TgtVal = (Op == OO_PlusEqual || Op == OO_MinusEqual) ? LHS : RetVal;
+ State =
+ setIteratorPosition(State, TgtVal, advancePosition(C, Op, *Pos, *value));
+ C.addTransition(State);
+}
+
+void IteratorChecker::verifyIncrement(CheckerContext &C,
+ const SVal &Iter) const {
+ auto &BVF = C.getSValBuilder().getBasicValueFactory();
+ verifyRandomIncrOrDecr(C, OO_Plus, Iter,
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
+}
+
+void IteratorChecker::verifyDecrement(CheckerContext &C,
+ const SVal &Iter) const {
+ auto &BVF = C.getSValBuilder().getBasicValueFactory();
+ verifyRandomIncrOrDecr(C, OO_Minus, Iter,
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))));
+}
+
+void IteratorChecker::verifyRandomIncrOrDecr(CheckerContext &C,
+ OverloadedOperatorKind Op,
+ const SVal &LHS,
+ const SVal &RHS) const {
+ auto State = C.getState();
+
+ // If the iterator is initially inside its range, then the operation is valid
+ const auto *Pos = getIteratorPosition(State, LHS);
+ if (!Pos)
+ return;
+
+ auto Value = RHS;
+ if (auto ValAsLoc = RHS.getAs<Loc>()) {
+ Value = State->getRawSVal(*ValAsLoc);
+ }
+
+ if (Value.isUnknown())
+ return;
+
+ // Incremention or decremention by 0 is never a bug.
+ if (isZero(State, Value.castAs<NonLoc>()))
+ return;
+
+ // The result may be the past-end iterator of the container, but any other
+ // out of range position is undefined behaviour
+ if (isAheadOfRange(State, advancePosition(C, Op, *Pos, Value))) {
+ auto *N = C.generateNonFatalErrorNode(State);
+ if (!N)
+ return;
+ reportOutOfRangeBug("Iterator decremented ahead of its valid range.", LHS,
+ C, N);
+ }
+ if (isBehindPastTheEnd(State, advancePosition(C, Op, *Pos, Value))) {
+ auto *N = C.generateNonFatalErrorNode(State);
+ if (!N)
+ return;
+ reportOutOfRangeBug("Iterator incremented behind the past-the-end "
+ "iterator.", LHS, C, N);
+ }
+}
+
+void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
+ const MemRegion *Cont) const {
+ // Verify match between a container and the container of an iterator
+ Cont = Cont->getMostDerivedObjectRegion();
+
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (Pos && Pos->getContainer() != Cont) {
+ auto *N = C.generateNonFatalErrorNode(State);
+ if (!N) {
+ return;
+ }
+ reportMismatchedBug("Container accessed using foreign iterator argument.", Iter, Cont, C, N);
+ }
+}
+
+void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1,
+ const SVal &Iter2) const {
+ // Verify match between the containers of two iterators
+ auto State = C.getState();
+ const auto *Pos1 = getIteratorPosition(State, Iter1);
+ const auto *Pos2 = getIteratorPosition(State, Iter2);
+ if (Pos1 && Pos2 && Pos1->getContainer() != Pos2->getContainer()) {
+ auto *N = C.generateNonFatalErrorNode(State);
+ if (!N)
+ return;
+ reportMismatchedBug("Iterators of different containers used where the "
+ "same container is expected.", Iter1, Iter2, C, N);
+ }
+}
+
+void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
+ const SVal &RetVal, const SVal &Cont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // If the container already has a begin symbol then use it. Otherwise first
+ // create a new one.
+ auto State = C.getState();
+ auto BeginSym = getContainerBegin(State, ContReg);
+ if (!BeginSym) {
+ auto &SymMgr = C.getSymbolManager();
+ BeginSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
+ C.getASTContext().LongTy, C.blockCount());
+ State = assumeNoOverflow(State, BeginSym, 4);
+ State = createContainerBegin(State, ContReg, BeginSym);
+ }
+ State = setIteratorPosition(State, RetVal,
+ IteratorPosition::getPosition(ContReg, BeginSym));
+ C.addTransition(State);
+}
+
+void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
+ const SVal &RetVal, const SVal &Cont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // If the container already has an end symbol then use it. Otherwise first
+ // create a new one.
+ auto State = C.getState();
+ auto EndSym = getContainerEnd(State, ContReg);
+ if (!EndSym) {
+ auto &SymMgr = C.getSymbolManager();
+ EndSym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
+ C.getASTContext().LongTy, C.blockCount());
+ State = assumeNoOverflow(State, EndSym, 4);
+ State = createContainerEnd(State, ContReg, EndSym);
+ }
+ State = setIteratorPosition(State, RetVal,
+ IteratorPosition::getPosition(ContReg, EndSym));
+ C.addTransition(State);
+}
+
+void IteratorChecker::assignToContainer(CheckerContext &C, const Expr *CE,
+ const SVal &RetVal,
+ const MemRegion *Cont) const {
+ Cont = Cont->getMostDerivedObjectRegion();
+
+ auto State = C.getState();
+ auto &SymMgr = C.getSymbolManager();
+ auto Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
+ C.getASTContext().LongTy, C.blockCount());
+ State = assumeNoOverflow(State, Sym, 4);
+ State = setIteratorPosition(State, RetVal,
+ IteratorPosition::getPosition(Cont, Sym));
+ C.addTransition(State);
+}
+
+void IteratorChecker::handleAssign(CheckerContext &C, const SVal &Cont,
+ const Expr *CE, const SVal &OldCont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // Assignment of a new value to a container always invalidates all its
+ // iterators
+ auto State = C.getState();
+ const auto CData = getContainerData(State, ContReg);
+ if (CData) {
+ State = invalidateAllIteratorPositions(State, ContReg);
+ }
+
+ // In case of move, iterators of the old container (except the past-end
+ // iterators) remain valid but refer to the new container
+ if (!OldCont.isUndef()) {
+ const auto *OldContReg = OldCont.getAsRegion();
+ if (OldContReg) {
+ OldContReg = OldContReg->getMostDerivedObjectRegion();
+ const auto OldCData = getContainerData(State, OldContReg);
+ if (OldCData) {
+ if (const auto OldEndSym = OldCData->getEnd()) {
+ // If we already assigned an "end" symbol to the old container, then
+ // first reassign all iterator positions to the new container which
+ // are not past the container (thus not greater or equal to the
+ // current "end" symbol).
+ State = reassignAllIteratorPositionsUnless(State, OldContReg, ContReg,
+ OldEndSym, BO_GE);
+ auto &SymMgr = C.getSymbolManager();
+ auto &SVB = C.getSValBuilder();
+ // Then generate and assign a new "end" symbol for the new container.
+ auto NewEndSym =
+ SymMgr.conjureSymbol(CE, C.getLocationContext(),
+ C.getASTContext().LongTy, C.blockCount());
+ State = assumeNoOverflow(State, NewEndSym, 4);
+ if (CData) {
+ State = setContainerData(State, ContReg, CData->newEnd(NewEndSym));
+ } else {
+ State = setContainerData(State, ContReg,
+ ContainerData::fromEnd(NewEndSym));
+ }
+ // Finally, replace the old "end" symbol in the already reassigned
+ // iterator positions with the new "end" symbol.
+ State = rebaseSymbolInIteratorPositionsIf(
+ State, SVB, OldEndSym, NewEndSym, OldEndSym, BO_LT);
+ } else {
+ // There was no "end" symbol assigned yet to the old container,
+ // so reassign all iterator positions to the new container.
+ State = reassignAllIteratorPositions(State, OldContReg, ContReg);
+ }
+ if (const auto OldBeginSym = OldCData->getBegin()) {
+ // If we already assigned a "begin" symbol to the old container, then
+ // assign it to the new container and remove it from the old one.
+ if (CData) {
+ State =
+ setContainerData(State, ContReg, CData->newBegin(OldBeginSym));
+ } else {
+ State = setContainerData(State, ContReg,
+ ContainerData::fromBegin(OldBeginSym));
+ }
+ State =
+ setContainerData(State, OldContReg, OldCData->newEnd(nullptr));
+ }
+ } else {
+ // There was neither "begin" nor "end" symbol assigned yet to the old
+ // container, so reassign all iterator positions to the new container.
+ State = reassignAllIteratorPositions(State, OldContReg, ContReg);
+ }
+ }
+ }
+ C.addTransition(State);
+}
+
+void IteratorChecker::handleClear(CheckerContext &C, const SVal &Cont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // The clear() operation invalidates all the iterators, except the past-end
+ // iterators of list-like containers
+ auto State = C.getState();
+ if (!hasSubscriptOperator(State, ContReg) ||
+ !backModifiable(State, ContReg)) {
+ const auto CData = getContainerData(State, ContReg);
+ if (CData) {
+ if (const auto EndSym = CData->getEnd()) {
+ State =
+ invalidateAllIteratorPositionsExcept(State, ContReg, EndSym, BO_GE);
+ C.addTransition(State);
+ return;
+ }
+ }
+ }
+ State = invalidateAllIteratorPositions(State, ContReg);
+ C.addTransition(State);
+}
+
+void IteratorChecker::handlePushBack(CheckerContext &C,
+ const SVal &Cont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // For deque-like containers invalidate all iterator positions
+ auto State = C.getState();
+ if (hasSubscriptOperator(State, ContReg) && frontModifiable(State, ContReg)) {
+ State = invalidateAllIteratorPositions(State, ContReg);
+ C.addTransition(State);
+ return;
+ }
+
+ const auto CData = getContainerData(State, ContReg);
+ if (!CData)
+ return;
+
+ // For vector-like containers invalidate the past-end iterator positions
+ if (const auto EndSym = CData->getEnd()) {
+ if (hasSubscriptOperator(State, ContReg)) {
+ State = invalidateIteratorPositions(State, EndSym, BO_GE);
+ }
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto newEndSym =
+ SVB.evalBinOp(State, BO_Add,
+ nonloc::SymbolVal(EndSym),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(EndSym)).getAsSymbol();
+ State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
+ }
+ C.addTransition(State);
+}
+
+void IteratorChecker::handlePopBack(CheckerContext &C, const SVal &Cont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ auto State = C.getState();
+ const auto CData = getContainerData(State, ContReg);
+ if (!CData)
+ return;
+
+ if (const auto EndSym = CData->getEnd()) {
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto BackSym =
+ SVB.evalBinOp(State, BO_Sub,
+ nonloc::SymbolVal(EndSym),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(EndSym)).getAsSymbol();
+ // For vector-like and deque-like containers invalidate the last and the
+ // past-end iterator positions. For list-like containers only invalidate
+ // the last position
+ if (hasSubscriptOperator(State, ContReg) &&
+ backModifiable(State, ContReg)) {
+ State = invalidateIteratorPositions(State, BackSym, BO_GE);
+ State = setContainerData(State, ContReg, CData->newEnd(nullptr));
+ } else {
+ State = invalidateIteratorPositions(State, BackSym, BO_EQ);
+ }
+ auto newEndSym = BackSym;
+ State = setContainerData(State, ContReg, CData->newEnd(newEndSym));
+ C.addTransition(State);
+ }
+}
+
+void IteratorChecker::handlePushFront(CheckerContext &C,
+ const SVal &Cont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ // For deque-like containers invalidate all iterator positions
+ auto State = C.getState();
+ if (hasSubscriptOperator(State, ContReg)) {
+ State = invalidateAllIteratorPositions(State, ContReg);
+ C.addTransition(State);
+ } else {
+ const auto CData = getContainerData(State, ContReg);
+ if (!CData)
+ return;
+
+ if (const auto BeginSym = CData->getBegin()) {
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto newBeginSym =
+ SVB.evalBinOp(State, BO_Sub,
+ nonloc::SymbolVal(BeginSym),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(BeginSym)).getAsSymbol();
+ State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
+ C.addTransition(State);
+ }
+ }
+}
+
+void IteratorChecker::handlePopFront(CheckerContext &C,
+ const SVal &Cont) const {
+ const auto *ContReg = Cont.getAsRegion();
+ if (!ContReg)
+ return;
+
+ ContReg = ContReg->getMostDerivedObjectRegion();
+
+ auto State = C.getState();
+ const auto CData = getContainerData(State, ContReg);
+ if (!CData)
+ return;
+
+ // For deque-like containers invalidate all iterator positions. For list-like
+ // iterators only invalidate the first position
+ if (const auto BeginSym = CData->getBegin()) {
+ if (hasSubscriptOperator(State, ContReg)) {
+ State = invalidateIteratorPositions(State, BeginSym, BO_LE);
+ } else {
+ State = invalidateIteratorPositions(State, BeginSym, BO_EQ);
+ }
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto newBeginSym =
+ SVB.evalBinOp(State, BO_Add,
+ nonloc::SymbolVal(BeginSym),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(BeginSym)).getAsSymbol();
+ State = setContainerData(State, ContReg, CData->newBegin(newBeginSym));
+ C.addTransition(State);
+ }
+}
+
+void IteratorChecker::handleInsert(CheckerContext &C, const SVal &Iter) const {
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (!Pos)
+ return;
+
+ // For deque-like containers invalidate all iterator positions. For
+ // vector-like containers invalidate iterator positions after the insertion.
+ const auto *Cont = Pos->getContainer();
+ if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
+ if (frontModifiable(State, Cont)) {
+ State = invalidateAllIteratorPositions(State, Cont);
+ } else {
+ State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
+ }
+ if (const auto *CData = getContainerData(State, Cont)) {
+ if (const auto EndSym = CData->getEnd()) {
+ State = invalidateIteratorPositions(State, EndSym, BO_GE);
+ State = setContainerData(State, Cont, CData->newEnd(nullptr));
+ }
+ }
+ C.addTransition(State);
+ }
+}
+
+void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter) const {
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (!Pos)
+ return;
+
+ // For deque-like containers invalidate all iterator positions. For
+ // vector-like containers invalidate iterator positions at and after the
+ // deletion. For list-like containers only invalidate the deleted position.
+ const auto *Cont = Pos->getContainer();
+ if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
+ if (frontModifiable(State, Cont)) {
+ State = invalidateAllIteratorPositions(State, Cont);
+ } else {
+ State = invalidateIteratorPositions(State, Pos->getOffset(), BO_GE);
+ }
+ if (const auto *CData = getContainerData(State, Cont)) {
+ if (const auto EndSym = CData->getEnd()) {
+ State = invalidateIteratorPositions(State, EndSym, BO_GE);
+ State = setContainerData(State, Cont, CData->newEnd(nullptr));
+ }
+ }
+ } else {
+ State = invalidateIteratorPositions(State, Pos->getOffset(), BO_EQ);
+ }
+ C.addTransition(State);
+}
+
+void IteratorChecker::handleErase(CheckerContext &C, const SVal &Iter1,
+ const SVal &Iter2) const {
+ auto State = C.getState();
+ const auto *Pos1 = getIteratorPosition(State, Iter1);
+ const auto *Pos2 = getIteratorPosition(State, Iter2);
+ if (!Pos1 || !Pos2)
+ return;
+
+ // For deque-like containers invalidate all iterator positions. For
+ // vector-like containers invalidate iterator positions at and after the
+ // deletion range. For list-like containers only invalidate the deleted
+ // position range [first..last].
+ const auto *Cont = Pos1->getContainer();
+ if (hasSubscriptOperator(State, Cont) && backModifiable(State, Cont)) {
+ if (frontModifiable(State, Cont)) {
+ State = invalidateAllIteratorPositions(State, Cont);
+ } else {
+ State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE);
+ }
+ if (const auto *CData = getContainerData(State, Cont)) {
+ if (const auto EndSym = CData->getEnd()) {
+ State = invalidateIteratorPositions(State, EndSym, BO_GE);
+ State = setContainerData(State, Cont, CData->newEnd(nullptr));
+ }
+ }
+ } else {
+ State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GE,
+ Pos2->getOffset(), BO_LT);
+ }
+ C.addTransition(State);
+}
+
+void IteratorChecker::handleEraseAfter(CheckerContext &C,
+ const SVal &Iter) const {
+ auto State = C.getState();
+ const auto *Pos = getIteratorPosition(State, Iter);
+ if (!Pos)
+ return;
+
+ // Invalidate the deleted iterator position, which is the position of the
+ // parameter plus one.
+ auto &SymMgr = C.getSymbolManager();
+ auto &BVF = SymMgr.getBasicVals();
+ auto &SVB = C.getSValBuilder();
+ const auto NextSym =
+ SVB.evalBinOp(State, BO_Add,
+ nonloc::SymbolVal(Pos->getOffset()),
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(1))),
+ SymMgr.getType(Pos->getOffset())).getAsSymbol();
+ State = invalidateIteratorPositions(State, NextSym, BO_EQ);
+ C.addTransition(State);
+}
+
+void IteratorChecker::handleEraseAfter(CheckerContext &C, const SVal &Iter1,
+ const SVal &Iter2) const {
+ auto State = C.getState();
+ const auto *Pos1 = getIteratorPosition(State, Iter1);
+ const auto *Pos2 = getIteratorPosition(State, Iter2);
+ if (!Pos1 || !Pos2)
+ return;
+
+ // Invalidate the deleted iterator position range (first..last)
+ State = invalidateIteratorPositions(State, Pos1->getOffset(), BO_GT,
+ Pos2->getOffset(), BO_LT);
+ C.addTransition(State);
+}
+
+IteratorPosition IteratorChecker::advancePosition(CheckerContext &C,
+ OverloadedOperatorKind Op,
+ const IteratorPosition &Pos,
+ const SVal &Distance) const {
+ auto State = C.getState();
+ auto &SymMgr = C.getSymbolManager();
+ auto &SVB = C.getSValBuilder();
+
+ assert ((Op == OO_Plus || Op == OO_PlusEqual ||
+ Op == OO_Minus || Op == OO_MinusEqual) &&
+ "Advance operator must be one of +, -, += and -=.");
+ auto BinOp = (Op == OO_Plus || Op == OO_PlusEqual) ? BO_Add : BO_Sub;
+ if (const auto IntDist = Distance.getAs<nonloc::ConcreteInt>()) {
+ // For concrete integers we can calculate the new position
+ return Pos.setTo(SVB.evalBinOp(State, BinOp,
+ nonloc::SymbolVal(Pos.getOffset()), *IntDist,
+ SymMgr.getType(Pos.getOffset()))
+ .getAsSymbol());
+ } else {
+ // For other symbols create a new symbol to keep expressions simple
+ const auto &LCtx = C.getLocationContext();
+ const auto NewPosSym = SymMgr.conjureSymbol(nullptr, LCtx,
+ SymMgr.getType(Pos.getOffset()),
+ C.blockCount());
+ State = assumeNoOverflow(State, NewPosSym, 4);
+ return Pos.setTo(NewPosSym);
+ }
+}
+
+void IteratorChecker::reportOutOfRangeBug(const StringRef &Message,
+ const SVal &Val, CheckerContext &C,
+ ExplodedNode *ErrNode) const {
+ auto R = llvm::make_unique<BugReport>(*OutOfRangeBugType, Message, ErrNode);
+ R->markInteresting(Val);
+ C.emitReport(std::move(R));
+}
+
+void IteratorChecker::reportMismatchedBug(const StringRef &Message,
+ const SVal &Val1, const SVal &Val2,
+ CheckerContext &C,
+ ExplodedNode *ErrNode) const {
+ auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
+ R->markInteresting(Val1);
+ R->markInteresting(Val2);
+ C.emitReport(std::move(R));
+}
+
+void IteratorChecker::reportMismatchedBug(const StringRef &Message,
+ const SVal &Val, const MemRegion *Reg,
+ CheckerContext &C,
+ ExplodedNode *ErrNode) const {
+ auto R = llvm::make_unique<BugReport>(*MismatchedBugType, Message, ErrNode);
+ R->markInteresting(Val);
+ R->markInteresting(Reg);
+ C.emitReport(std::move(R));
+}
+
+void IteratorChecker::reportInvalidatedBug(const StringRef &Message,
+ const SVal &Val, CheckerContext &C,
+ ExplodedNode *ErrNode) const {
+ auto R = llvm::make_unique<BugReport>(*InvalidatedBugType, Message, ErrNode);
+ R->markInteresting(Val);
+ C.emitReport(std::move(R));
+}
+
+namespace {
+
+bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
+bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
+bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2);
+bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
+ BinaryOperator::Opcode Opc);
+bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
+ BinaryOperator::Opcode Opc);
+const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
+ const MemRegion *Reg);
+SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB, SymbolRef Expr,
+ SymbolRef OldSym, SymbolRef NewSym);
+
+bool isIteratorType(const QualType &Type) {
+ if (Type->isPointerType())
+ return true;
+
+ const auto *CRD = Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
+ return isIterator(CRD);
+}
+
+bool isIterator(const CXXRecordDecl *CRD) {
+ if (!CRD)
+ return false;
+
+ const auto Name = CRD->getName();
+ if (!(Name.endswith_lower("iterator") || Name.endswith_lower("iter") ||
+ Name.endswith_lower("it")))
+ return false;
+
+ bool HasCopyCtor = false, HasCopyAssign = true, HasDtor = false,
+ HasPreIncrOp = false, HasPostIncrOp = false, HasDerefOp = false;
+ for (const auto *Method : CRD->methods()) {
+ if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(Method)) {
+ if (Ctor->isCopyConstructor()) {
+ HasCopyCtor = !Ctor->isDeleted() && Ctor->getAccess() == AS_public;
+ }
+ continue;
+ }
+ if (const auto *Dtor = dyn_cast<CXXDestructorDecl>(Method)) {
+ HasDtor = !Dtor->isDeleted() && Dtor->getAccess() == AS_public;
+ continue;
+ }
+ if (Method->isCopyAssignmentOperator()) {
+ HasCopyAssign = !Method->isDeleted() && Method->getAccess() == AS_public;
+ continue;
+ }
+ if (!Method->isOverloadedOperator())
+ continue;
+ const auto OPK = Method->getOverloadedOperator();
+ if (OPK == OO_PlusPlus) {
+ HasPreIncrOp = HasPreIncrOp || (Method->getNumParams() == 0);
+ HasPostIncrOp = HasPostIncrOp || (Method->getNumParams() == 1);
+ continue;
+ }
+ if (OPK == OO_Star) {
+ HasDerefOp = (Method->getNumParams() == 0);
+ continue;
+ }
+ }
+
+ return HasCopyCtor && HasCopyAssign && HasDtor && HasPreIncrOp &&
+ HasPostIncrOp && HasDerefOp;
+}
+
+bool isComparisonOperator(OverloadedOperatorKind OK) {
+ return OK == OO_EqualEqual || OK == OO_ExclaimEqual || OK == OO_Less ||
+ OK == OO_LessEqual || OK == OO_Greater || OK == OO_GreaterEqual;
+}
+
+bool isBeginCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ return IdInfo->getName().endswith_lower("begin");
+}
+
+bool isEndCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ return IdInfo->getName().endswith_lower("end");
+}
+
+bool isAssignCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() > 2)
+ return false;
+ return IdInfo->getName() == "assign";
+}
+
+bool isClearCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() > 0)
+ return false;
+ return IdInfo->getName() == "clear";
+}
+
+bool isPushBackCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() != 1)
+ return false;
+ return IdInfo->getName() == "push_back";
+}
+
+bool isEmplaceBackCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() < 1)
+ return false;
+ return IdInfo->getName() == "emplace_back";
+}
+
+bool isPopBackCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() > 0)
+ return false;
+ return IdInfo->getName() == "pop_back";
+}
+
+bool isPushFrontCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() != 1)
+ return false;
+ return IdInfo->getName() == "push_front";
+}
+
+bool isEmplaceFrontCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() < 1)
+ return false;
+ return IdInfo->getName() == "emplace_front";
+}
+
+bool isPopFrontCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() > 0)
+ return false;
+ return IdInfo->getName() == "pop_front";
+}
+
+bool isInsertCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() < 2 || Func->getNumParams() > 3)
+ return false;
+ if (!isIteratorType(Func->getParamDecl(0)->getType()))
+ return false;
+ return IdInfo->getName() == "insert";
+}
+
+bool isEmplaceCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() < 2)
+ return false;
+ if (!isIteratorType(Func->getParamDecl(0)->getType()))
+ return false;
+ return IdInfo->getName() == "emplace";
+}
+
+bool isEraseCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
+ return false;
+ if (!isIteratorType(Func->getParamDecl(0)->getType()))
+ return false;
+ if (Func->getNumParams() == 2 &&
+ !isIteratorType(Func->getParamDecl(1)->getType()))
+ return false;
+ return IdInfo->getName() == "erase";
+}
+
+bool isEraseAfterCall(const FunctionDecl *Func) {
+ const auto *IdInfo = Func->getIdentifier();
+ if (!IdInfo)
+ return false;
+ if (Func->getNumParams() < 1 || Func->getNumParams() > 2)
+ return false;
+ if (!isIteratorType(Func->getParamDecl(0)->getType()))
+ return false;
+ if (Func->getNumParams() == 2 &&
+ !isIteratorType(Func->getParamDecl(1)->getType()))
+ return false;
+ return IdInfo->getName() == "erase_after";
+}
+
+bool isAssignmentOperator(OverloadedOperatorKind OK) { return OK == OO_Equal; }
+
+bool isSimpleComparisonOperator(OverloadedOperatorKind OK) {
+ return OK == OO_EqualEqual || OK == OO_ExclaimEqual;
+}
+
+bool isAccessOperator(OverloadedOperatorKind OK) {
+ return isDereferenceOperator(OK) || isIncrementOperator(OK) ||
+ isDecrementOperator(OK) || isRandomIncrOrDecrOperator(OK);
+}
+
+bool isDereferenceOperator(OverloadedOperatorKind OK) {
+ return OK == OO_Star || OK == OO_Arrow || OK == OO_ArrowStar ||
+ OK == OO_Subscript;
+}
+
+bool isIncrementOperator(OverloadedOperatorKind OK) {
+ return OK == OO_PlusPlus;
+}
+
+bool isDecrementOperator(OverloadedOperatorKind OK) {
+ return OK == OO_MinusMinus;
+}
+
+bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
+ return OK == OO_Plus || OK == OO_PlusEqual || OK == OO_Minus ||
+ OK == OO_MinusEqual;
+}
+
+BinaryOperator::Opcode getOpcode(const SymExpr *SE) {
+ if (const auto *BSE = dyn_cast<BinarySymExpr>(SE)) {
+ return BSE->getOpcode();
+ } else if (const auto *SC = dyn_cast<SymbolConjured>(SE)) {
+ const auto *COE = dyn_cast_or_null<CXXOperatorCallExpr>(SC->getStmt());
+ if (!COE)
+ return BO_Comma; // Extremal value, neither EQ nor NE
+ if (COE->getOperator() == OO_EqualEqual) {
+ return BO_EQ;
+ } else if (COE->getOperator() == OO_ExclaimEqual) {
+ return BO_NE;
+ }
+ return BO_Comma; // Extremal value, neither EQ nor NE
+ }
+ return BO_Comma; // Extremal value, neither EQ nor NE
+}
+
+bool hasSubscriptOperator(ProgramStateRef State, const MemRegion *Reg) {
+ const auto *CRD = getCXXRecordDecl(State, Reg);
+ if (!CRD)
+ return false;
+
+ for (const auto *Method : CRD->methods()) {
+ if (!Method->isOverloadedOperator())
+ continue;
+ const auto OPK = Method->getOverloadedOperator();
+ if (OPK == OO_Subscript) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool frontModifiable(ProgramStateRef State, const MemRegion *Reg) {
+ const auto *CRD = getCXXRecordDecl(State, Reg);
+ if (!CRD)
+ return false;
+
+ for (const auto *Method : CRD->methods()) {
+ if (!Method->getDeclName().isIdentifier())
+ continue;
+ if (Method->getName() == "push_front" || Method->getName() == "pop_front") {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool backModifiable(ProgramStateRef State, const MemRegion *Reg) {
+ const auto *CRD = getCXXRecordDecl(State, Reg);
+ if (!CRD)
+ return false;
+
+ for (const auto *Method : CRD->methods()) {
+ if (!Method->getDeclName().isIdentifier())
+ continue;
+ if (Method->getName() == "push_back" || Method->getName() == "pop_back") {
+ return true;
+ }
+ }
+ return false;
+}
+
+const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
+ const MemRegion *Reg) {
+ auto TI = getDynamicTypeInfo(State, Reg);
+ if (!TI.isValid())
+ return nullptr;
+
+ auto Type = TI.getType();
+ if (const auto *RefT = Type->getAs<ReferenceType>()) {
+ Type = RefT->getPointeeType();
+ }
+
+ return Type->getUnqualifiedDesugaredType()->getAsCXXRecordDecl();
+}
+
+const RegionOrSymbol getRegionOrSymbol(const SVal &Val) {
+ if (const auto Reg = Val.getAsRegion()) {
+ return Reg;
+ } else if (const auto Sym = Val.getAsSymbol()) {
+ return Sym;
+ } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+ return LCVal->getRegion();
+ }
+ return RegionOrSymbol();
+}
+
+const ProgramStateRef processComparison(ProgramStateRef State,
+ RegionOrSymbol LVal,
+ RegionOrSymbol RVal, bool Equal) {
+ const auto *LPos = getIteratorPosition(State, LVal);
+ const auto *RPos = getIteratorPosition(State, RVal);
+ if (LPos && !RPos) {
+ State = adjustIteratorPosition(State, RVal, *LPos, Equal);
+ } else if (!LPos && RPos) {
+ State = adjustIteratorPosition(State, LVal, *RPos, Equal);
+ } else if (LPos && RPos) {
+ State = relateIteratorPositions(State, *LPos, *RPos, Equal);
+ }
+ return State;
+}
+
+const ProgramStateRef saveComparison(ProgramStateRef State,
+ const SymExpr *Condition, const SVal &LVal,
+ const SVal &RVal, bool Eq) {
+ const auto Left = getRegionOrSymbol(LVal);
+ const auto Right = getRegionOrSymbol(RVal);
+ if (!Left || !Right)
+ return State;
+ return State->set<IteratorComparisonMap>(Condition,
+ IteratorComparison(Left, Right, Eq));
+}
+
+const IteratorComparison *loadComparison(ProgramStateRef State,
+ const SymExpr *Condition) {
+ return State->get<IteratorComparisonMap>(Condition);
+}
+
+SymbolRef getContainerBegin(ProgramStateRef State, const MemRegion *Cont) {
+ const auto *CDataPtr = getContainerData(State, Cont);
+ if (!CDataPtr)
+ return nullptr;
+
+ return CDataPtr->getBegin();
+}
+
+SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
+ const auto *CDataPtr = getContainerData(State, Cont);
+ if (!CDataPtr)
+ return nullptr;
+
+ return CDataPtr->getEnd();
+}
+
+ProgramStateRef createContainerBegin(ProgramStateRef State,
+ const MemRegion *Cont,
+ const SymbolRef Sym) {
+ // Only create if it does not exist
+ const auto *CDataPtr = getContainerData(State, Cont);
+ if (CDataPtr) {
+ if (CDataPtr->getBegin()) {
+ return State;
+ }
+ const auto CData = CDataPtr->newBegin(Sym);
+ return setContainerData(State, Cont, CData);
+ }
+ const auto CData = ContainerData::fromBegin(Sym);
+ return setContainerData(State, Cont, CData);
+}
+
+ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
+ const SymbolRef Sym) {
+ // Only create if it does not exist
+ const auto *CDataPtr = getContainerData(State, Cont);
+ if (CDataPtr) {
+ if (CDataPtr->getEnd()) {
+ return State;
+ }
+ const auto CData = CDataPtr->newEnd(Sym);
+ return setContainerData(State, Cont, CData);
+ }
+ const auto CData = ContainerData::fromEnd(Sym);
+ return setContainerData(State, Cont, CData);
+}
+
+const ContainerData *getContainerData(ProgramStateRef State,
+ const MemRegion *Cont) {
+ return State->get<ContainerMap>(Cont);
+}
+
+ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
+ const ContainerData &CData) {
+ return State->set<ContainerMap>(Cont, CData);
+}
+
+const IteratorPosition *getIteratorPosition(ProgramStateRef State,
+ const SVal &Val) {
+ if (auto Reg = Val.getAsRegion()) {
+ Reg = Reg->getMostDerivedObjectRegion();
+ return State->get<IteratorRegionMap>(Reg);
+ } else if (const auto Sym = Val.getAsSymbol()) {
+ return State->get<IteratorSymbolMap>(Sym);
+ } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+ return State->get<IteratorRegionMap>(LCVal->getRegion());
+ }
+ return nullptr;
+}
+
+const IteratorPosition *getIteratorPosition(ProgramStateRef State,
+ RegionOrSymbol RegOrSym) {
+ if (RegOrSym.is<const MemRegion *>()) {
+ auto Reg = RegOrSym.get<const MemRegion *>()->getMostDerivedObjectRegion();
+ return State->get<IteratorRegionMap>(Reg);
+ } else if (RegOrSym.is<SymbolRef>()) {
+ return State->get<IteratorSymbolMap>(RegOrSym.get<SymbolRef>());
+ }
+ return nullptr;
+}
+
+ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
+ const IteratorPosition &Pos) {
+ if (auto Reg = Val.getAsRegion()) {
+ Reg = Reg->getMostDerivedObjectRegion();
+ return State->set<IteratorRegionMap>(Reg, Pos);
+ } else if (const auto Sym = Val.getAsSymbol()) {
+ return State->set<IteratorSymbolMap>(Sym, Pos);
+ } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+ return State->set<IteratorRegionMap>(LCVal->getRegion(), Pos);
+ }
+ return nullptr;
+}
+
+ProgramStateRef setIteratorPosition(ProgramStateRef State,
+ RegionOrSymbol RegOrSym,
+ const IteratorPosition &Pos) {
+ if (RegOrSym.is<const MemRegion *>()) {
+ auto Reg = RegOrSym.get<const MemRegion *>()->getMostDerivedObjectRegion();
+ return State->set<IteratorRegionMap>(Reg, Pos);
+ } else if (RegOrSym.is<SymbolRef>()) {
+ return State->set<IteratorSymbolMap>(RegOrSym.get<SymbolRef>(), Pos);
+ }
+ return nullptr;
+}
+
+ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
+ if (auto Reg = Val.getAsRegion()) {
+ Reg = Reg->getMostDerivedObjectRegion();
+ return State->remove<IteratorRegionMap>(Reg);
+ } else if (const auto Sym = Val.getAsSymbol()) {
+ return State->remove<IteratorSymbolMap>(Sym);
+ } else if (const auto LCVal = Val.getAs<nonloc::LazyCompoundVal>()) {
+ return State->remove<IteratorRegionMap>(LCVal->getRegion());
+ }
+ return nullptr;
+}
+
+ProgramStateRef adjustIteratorPosition(ProgramStateRef State,
+ RegionOrSymbol RegOrSym,
+ const IteratorPosition &Pos,
+ bool Equal) {
+ if (Equal) {
+ return setIteratorPosition(State, RegOrSym, Pos);
+ } else {
+ return State;
+ }
+}
+
+ProgramStateRef relateIteratorPositions(ProgramStateRef State,
+ const IteratorPosition &Pos1,
+ const IteratorPosition &Pos2,
+ bool Equal) {
+ auto &SVB = State->getStateManager().getSValBuilder();
+
+ // FIXME: This code should be reworked as follows:
+ // 1. Subtract the operands using evalBinOp().
+ // 2. Assume that the result doesn't overflow.
+ // 3. Compare the result to 0.
+ // 4. Assume the result of the comparison.
+ const auto comparison =
+ SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Pos1.getOffset()),
+ nonloc::SymbolVal(Pos2.getOffset()),
+ SVB.getConditionType());
+
+ assert(comparison.getAs<DefinedSVal>() &&
+ "Symbol comparison must be a `DefinedSVal`");
+
+ auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
+ if (const auto CompSym = comparison.getAsSymbol()) {
+ assert(isa<SymIntExpr>(CompSym) &&
+ "Symbol comparison must be a `SymIntExpr`");
+ assert(BinaryOperator::isComparisonOp(
+ cast<SymIntExpr>(CompSym)->getOpcode()) &&
+ "Symbol comparison must be a comparison");
+ return assumeNoOverflow(NewState, cast<SymIntExpr>(CompSym)->getLHS(), 2);
+ }
+
+ return NewState;
+}
+
+bool hasLiveIterators(ProgramStateRef State, const MemRegion *Cont) {
+ auto RegionMap = State->get<IteratorRegionMap>();
+ for (const auto Reg : RegionMap) {
+ if (Reg.second.getContainer() == Cont)
+ return true;
+ }
+
+ auto SymbolMap = State->get<IteratorSymbolMap>();
+ for (const auto Sym : SymbolMap) {
+ if (Sym.second.getContainer() == Cont)
+ return true;
+ }
+
+ return false;
+}
+
+bool isBoundThroughLazyCompoundVal(const Environment &Env,
+ const MemRegion *Reg) {
+ for (const auto Binding: Env) {
+ if (const auto LCVal = Binding.second.getAs<nonloc::LazyCompoundVal>()) {
+ if (LCVal->getRegion() == Reg)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+template <typename Condition, typename Process>
+ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
+ Process Proc) {
+ auto &RegionMapFactory = State->get_context<IteratorRegionMap>();
+ auto RegionMap = State->get<IteratorRegionMap>();
+ bool Changed = false;
+ for (const auto Reg : RegionMap) {
+ if (Cond(Reg.second)) {
+ RegionMap = RegionMapFactory.add(RegionMap, Reg.first, Proc(Reg.second));
+ Changed = true;
+ }
+ }
+
+ if (Changed)
+ State = State->set<IteratorRegionMap>(RegionMap);
+
+ auto &SymbolMapFactory = State->get_context<IteratorSymbolMap>();
+ auto SymbolMap = State->get<IteratorSymbolMap>();
+ Changed = false;
+ for (const auto Sym : SymbolMap) {
+ if (Cond(Sym.second)) {
+ SymbolMap = SymbolMapFactory.add(SymbolMap, Sym.first, Proc(Sym.second));
+ Changed = true;
+ }
+ }
+
+ if (Changed)
+ State = State->set<IteratorSymbolMap>(SymbolMap);
+
+ return State;
+}
+
+ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
+ const MemRegion *Cont) {
+ auto MatchCont = [&](const IteratorPosition &Pos) {
+ return Pos.getContainer() == Cont;
+ };
+ auto Invalidate = [&](const IteratorPosition &Pos) {
+ return Pos.invalidate();
+ };
+ return processIteratorPositions(State, MatchCont, Invalidate);
+}
+
+ProgramStateRef
+invalidateAllIteratorPositionsExcept(ProgramStateRef State,
+ const MemRegion *Cont, SymbolRef Offset,
+ BinaryOperator::Opcode Opc) {
+ auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
+ return Pos.getContainer() == Cont &&
+ !compare(State, Pos.getOffset(), Offset, Opc);
+ };
+ auto Invalidate = [&](const IteratorPosition &Pos) {
+ return Pos.invalidate();
+ };
+ return processIteratorPositions(State, MatchContAndCompare, Invalidate);
+}
+
+ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
+ SymbolRef Offset,
+ BinaryOperator::Opcode Opc) {
+ auto Compare = [&](const IteratorPosition &Pos) {
+ return compare(State, Pos.getOffset(), Offset, Opc);
+ };
+ auto Invalidate = [&](const IteratorPosition &Pos) {
+ return Pos.invalidate();
+ };
+ return processIteratorPositions(State, Compare, Invalidate);
+}
+
+ProgramStateRef invalidateIteratorPositions(ProgramStateRef State,
+ SymbolRef Offset1,
+ BinaryOperator::Opcode Opc1,
+ SymbolRef Offset2,
+ BinaryOperator::Opcode Opc2) {
+ auto Compare = [&](const IteratorPosition &Pos) {
+ return compare(State, Pos.getOffset(), Offset1, Opc1) &&
+ compare(State, Pos.getOffset(), Offset2, Opc2);
+ };
+ auto Invalidate = [&](const IteratorPosition &Pos) {
+ return Pos.invalidate();
+ };
+ return processIteratorPositions(State, Compare, Invalidate);
+}
+
+ProgramStateRef reassignAllIteratorPositions(ProgramStateRef State,
+ const MemRegion *Cont,
+ const MemRegion *NewCont) {
+ auto MatchCont = [&](const IteratorPosition &Pos) {
+ return Pos.getContainer() == Cont;
+ };
+ auto ReAssign = [&](const IteratorPosition &Pos) {
+ return Pos.reAssign(NewCont);
+ };
+ return processIteratorPositions(State, MatchCont, ReAssign);
+}
+
+ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
+ const MemRegion *Cont,
+ const MemRegion *NewCont,
+ SymbolRef Offset,
+ BinaryOperator::Opcode Opc) {
+ auto MatchContAndCompare = [&](const IteratorPosition &Pos) {
+ return Pos.getContainer() == Cont &&
+ !compare(State, Pos.getOffset(), Offset, Opc);
+ };
+ auto ReAssign = [&](const IteratorPosition &Pos) {
+ return Pos.reAssign(NewCont);
+ };
+ return processIteratorPositions(State, MatchContAndCompare, ReAssign);
+}
+
+// This function rebases symbolic expression `OldSym + Int` to `NewSym + Int`,
+// `OldSym - Int` to `NewSym - Int` and `OldSym` to `NewSym` in any iterator
+// position offsets where `CondSym` is true.
+ProgramStateRef rebaseSymbolInIteratorPositionsIf(
+ ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
+ SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc) {
+ auto LessThanEnd = [&](const IteratorPosition &Pos) {
+ return compare(State, Pos.getOffset(), CondSym, Opc);
+ };
+ auto RebaseSymbol = [&](const IteratorPosition &Pos) {
+ return Pos.setTo(rebaseSymbol(State, SVB, Pos.getOffset(), OldSym,
+ NewSym));
+ };
+ return processIteratorPositions(State, LessThanEnd, RebaseSymbol);
+}
+
+// This function rebases symbolic expression `OldExpr + Int` to `NewExpr + Int`,
+// `OldExpr - Int` to `NewExpr - Int` and `OldExpr` to `NewExpr` in expression
+// `OrigExpr`.
+SymbolRef rebaseSymbol(ProgramStateRef State, SValBuilder &SVB,
+ SymbolRef OrigExpr, SymbolRef OldExpr,
+ SymbolRef NewSym) {
+ auto &SymMgr = SVB.getSymbolManager();
+ auto Diff = SVB.evalBinOpNN(State, BO_Sub, nonloc::SymbolVal(OrigExpr),
+ nonloc::SymbolVal(OldExpr),
+ SymMgr.getType(OrigExpr));
+
+ const auto DiffInt = Diff.getAs<nonloc::ConcreteInt>();
+ if (!DiffInt)
+ return OrigExpr;
+
+ return SVB.evalBinOpNN(State, BO_Add, *DiffInt, nonloc::SymbolVal(NewSym),
+ SymMgr.getType(OrigExpr)).getAsSymbol();
+}
+
+bool isZero(ProgramStateRef State, const NonLoc &Val) {
+ auto &BVF = State->getBasicVals();
+ return compare(State, Val,
+ nonloc::ConcreteInt(BVF.getValue(llvm::APSInt::get(0))),
+ BO_EQ);
+}
+
+bool isPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
+ const auto *Cont = Pos.getContainer();
+ const auto *CData = getContainerData(State, Cont);
+ if (!CData)
+ return false;
+
+ const auto End = CData->getEnd();
+ if (End) {
+ if (isEqual(State, Pos.getOffset(), End)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool isAheadOfRange(ProgramStateRef State, const IteratorPosition &Pos) {
+ const auto *Cont = Pos.getContainer();
+ const auto *CData = getContainerData(State, Cont);
+ if (!CData)
+ return false;
+
+ const auto Beg = CData->getBegin();
+ if (Beg) {
+ if (isLess(State, Pos.getOffset(), Beg)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool isBehindPastTheEnd(ProgramStateRef State, const IteratorPosition &Pos) {
+ const auto *Cont = Pos.getContainer();
+ const auto *CData = getContainerData(State, Cont);
+ if (!CData)
+ return false;
+
+ const auto End = CData->getEnd();
+ if (End) {
+ if (isGreater(State, Pos.getOffset(), End)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool isLess(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
+ return compare(State, Sym1, Sym2, BO_LT);
+}
+
+bool isGreater(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
+ return compare(State, Sym1, Sym2, BO_GT);
+}
+
+bool isEqual(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2) {
+ return compare(State, Sym1, Sym2, BO_EQ);
+}
+
+bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
+ BinaryOperator::Opcode Opc) {
+ return compare(State, nonloc::SymbolVal(Sym1), nonloc::SymbolVal(Sym2), Opc);
+}
+
+
+bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
+ BinaryOperator::Opcode Opc) {
+ auto &SVB = State->getStateManager().getSValBuilder();
+
+ const auto comparison =
+ SVB.evalBinOp(State, Opc, NL1, NL2, SVB.getConditionType());
+
+ assert(comparison.getAs<DefinedSVal>() &&
+ "Symbol comparison must be a `DefinedSVal`");
+
+ return !State->assume(comparison.castAs<DefinedSVal>(), false);
+}
+
+} // namespace
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &Mgr) { \
+ auto *checker = Mgr.registerChecker<IteratorChecker>(); \
+ checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
+ checker->CheckNames[IteratorChecker::CK_##name] = \
+ Mgr.getCurrentCheckName(); \
+ }
+
+REGISTER_CHECKER(IteratorRangeChecker)
+REGISTER_CHECKER(MismatchedIteratorChecker)
+REGISTER_CHECKER(InvalidatedIteratorChecker)
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
new file mode 100644
index 000000000000..aade62fd7491
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -0,0 +1,748 @@
+//===- IvarInvalidationChecker.cpp ------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker implements annotation driven invalidation checking. If a class
+// contains a method annotated with 'objc_instance_variable_invalidator',
+// - (void) foo
+// __attribute__((annotate("objc_instance_variable_invalidator")));
+// all the "ivalidatable" instance variables of this class should be
+// invalidated. We call an instance variable ivalidatable if it is an object of
+// a class which contains an invalidation method. There could be multiple
+// methods annotated with such annotations per class, either one can be used
+// to invalidate the ivar. An ivar or property are considered to be
+// invalidated if they are being assigned 'nil' or an invalidation method has
+// been called on them. An invalidation method should either invalidate all
+// the ivars or call another invalidation method (on self).
+//
+// Partial invalidor annotation allows to address cases when ivars are
+// invalidated by other methods, which might or might not be called from
+// the invalidation method. The checker checks that each invalidation
+// method and all the partial methods cumulatively invalidate all ivars.
+// __attribute__((annotate("objc_instance_variable_invalidator_partial")));
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallString.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+struct ChecksFilter {
+ /// Check for missing invalidation method declarations.
+ DefaultBool check_MissingInvalidationMethod;
+ /// Check that all ivars are invalidated.
+ DefaultBool check_InstanceVariableInvalidation;
+
+ CheckName checkName_MissingInvalidationMethod;
+ CheckName checkName_InstanceVariableInvalidation;
+};
+
+class IvarInvalidationCheckerImpl {
+ typedef llvm::SmallSetVector<const ObjCMethodDecl*, 2> MethodSet;
+ typedef llvm::DenseMap<const ObjCMethodDecl*,
+ const ObjCIvarDecl*> MethToIvarMapTy;
+ typedef llvm::DenseMap<const ObjCPropertyDecl*,
+ const ObjCIvarDecl*> PropToIvarMapTy;
+ typedef llvm::DenseMap<const ObjCIvarDecl*,
+ const ObjCPropertyDecl*> IvarToPropMapTy;
+
+ struct InvalidationInfo {
+ /// Has the ivar been invalidated?
+ bool IsInvalidated;
+
+ /// The methods which can be used to invalidate the ivar.
+ MethodSet InvalidationMethods;
+
+ InvalidationInfo() : IsInvalidated(false) {}
+ void addInvalidationMethod(const ObjCMethodDecl *MD) {
+ InvalidationMethods.insert(MD);
+ }
+
+ bool needsInvalidation() const {
+ return !InvalidationMethods.empty();
+ }
+
+ bool hasMethod(const ObjCMethodDecl *MD) {
+ if (IsInvalidated)
+ return true;
+ for (MethodSet::iterator I = InvalidationMethods.begin(),
+ E = InvalidationMethods.end(); I != E; ++I) {
+ if (*I == MD) {
+ IsInvalidated = true;
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ typedef llvm::DenseMap<const ObjCIvarDecl*, InvalidationInfo> IvarSet;
+
+ /// Statement visitor, which walks the method body and flags the ivars
+ /// referenced in it (either directly or via property).
+ class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
+ /// The set of Ivars which need to be invalidated.
+ IvarSet &IVars;
+
+ /// Flag is set as the result of a message send to another
+ /// invalidation method.
+ bool &CalledAnotherInvalidationMethod;
+
+ /// Property setter to ivar mapping.
+ const MethToIvarMapTy &PropertySetterToIvarMap;
+
+ /// Property getter to ivar mapping.
+ const MethToIvarMapTy &PropertyGetterToIvarMap;
+
+ /// Property to ivar mapping.
+ const PropToIvarMapTy &PropertyToIvarMap;
+
+ /// The invalidation method being currently processed.
+ const ObjCMethodDecl *InvalidationMethod;
+
+ ASTContext &Ctx;
+
+ /// Peel off parens, casts, OpaqueValueExpr, and PseudoObjectExpr.
+ const Expr *peel(const Expr *E) const;
+
+ /// Does this expression represent zero: '0'?
+ bool isZero(const Expr *E) const;
+
+ /// Mark the given ivar as invalidated.
+ void markInvalidated(const ObjCIvarDecl *Iv);
+
+ /// Checks if IvarRef refers to the tracked IVar, if yes, marks it as
+ /// invalidated.
+ void checkObjCIvarRefExpr(const ObjCIvarRefExpr *IvarRef);
+
+ /// Checks if ObjCPropertyRefExpr refers to the tracked IVar, if yes, marks
+ /// it as invalidated.
+ void checkObjCPropertyRefExpr(const ObjCPropertyRefExpr *PA);
+
+ /// Checks if ObjCMessageExpr refers to (is a getter for) the tracked IVar,
+ /// if yes, marks it as invalidated.
+ void checkObjCMessageExpr(const ObjCMessageExpr *ME);
+
+ /// Checks if the Expr refers to an ivar, if yes, marks it as invalidated.
+ void check(const Expr *E);
+
+ public:
+ MethodCrawler(IvarSet &InIVars,
+ bool &InCalledAnotherInvalidationMethod,
+ const MethToIvarMapTy &InPropertySetterToIvarMap,
+ const MethToIvarMapTy &InPropertyGetterToIvarMap,
+ const PropToIvarMapTy &InPropertyToIvarMap,
+ ASTContext &InCtx)
+ : IVars(InIVars),
+ CalledAnotherInvalidationMethod(InCalledAnotherInvalidationMethod),
+ PropertySetterToIvarMap(InPropertySetterToIvarMap),
+ PropertyGetterToIvarMap(InPropertyGetterToIvarMap),
+ PropertyToIvarMap(InPropertyToIvarMap),
+ InvalidationMethod(nullptr),
+ Ctx(InCtx) {}
+
+ void VisitStmt(const Stmt *S) { VisitChildren(S); }
+
+ void VisitBinaryOperator(const BinaryOperator *BO);
+
+ void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
+
+ void VisitChildren(const Stmt *S) {
+ for (const auto *Child : S->children()) {
+ if (Child)
+ this->Visit(Child);
+ if (CalledAnotherInvalidationMethod)
+ return;
+ }
+ }
+ };
+
+ /// Check if the any of the methods inside the interface are annotated with
+ /// the invalidation annotation, update the IvarInfo accordingly.
+ /// \param LookForPartial is set when we are searching for partial
+ /// invalidators.
+ static void containsInvalidationMethod(const ObjCContainerDecl *D,
+ InvalidationInfo &Out,
+ bool LookForPartial);
+
+ /// Check if ivar should be tracked and add to TrackedIvars if positive.
+ /// Returns true if ivar should be tracked.
+ static bool trackIvar(const ObjCIvarDecl *Iv, IvarSet &TrackedIvars,
+ const ObjCIvarDecl **FirstIvarDecl);
+
+ /// Given the property declaration, and the list of tracked ivars, finds
+ /// the ivar backing the property when possible. Returns '0' when no such
+ /// ivar could be found.
+ static const ObjCIvarDecl *findPropertyBackingIvar(
+ const ObjCPropertyDecl *Prop,
+ const ObjCInterfaceDecl *InterfaceD,
+ IvarSet &TrackedIvars,
+ const ObjCIvarDecl **FirstIvarDecl);
+
+ /// Print ivar name or the property if the given ivar backs a property.
+ static void printIvar(llvm::raw_svector_ostream &os,
+ const ObjCIvarDecl *IvarDecl,
+ const IvarToPropMapTy &IvarToPopertyMap);
+
+ void reportNoInvalidationMethod(CheckName CheckName,
+ const ObjCIvarDecl *FirstIvarDecl,
+ const IvarToPropMapTy &IvarToPopertyMap,
+ const ObjCInterfaceDecl *InterfaceD,
+ bool MissingDeclaration) const;
+
+ void reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
+ const IvarToPropMapTy &IvarToPopertyMap,
+ const ObjCMethodDecl *MethodD) const;
+
+ AnalysisManager& Mgr;
+ BugReporter &BR;
+ /// Filter on the checks performed.
+ const ChecksFilter &Filter;
+
+public:
+ IvarInvalidationCheckerImpl(AnalysisManager& InMgr,
+ BugReporter &InBR,
+ const ChecksFilter &InFilter) :
+ Mgr (InMgr), BR(InBR), Filter(InFilter) {}
+
+ void visit(const ObjCImplementationDecl *D) const;
+};
+
+static bool isInvalidationMethod(const ObjCMethodDecl *M, bool LookForPartial) {
+ for (const auto *Ann : M->specific_attrs<AnnotateAttr>()) {
+ if (!LookForPartial &&
+ Ann->getAnnotation() == "objc_instance_variable_invalidator")
+ return true;
+ if (LookForPartial &&
+ Ann->getAnnotation() == "objc_instance_variable_invalidator_partial")
+ return true;
+ }
+ return false;
+}
+
+void IvarInvalidationCheckerImpl::containsInvalidationMethod(
+ const ObjCContainerDecl *D, InvalidationInfo &OutInfo, bool Partial) {
+
+ if (!D)
+ return;
+
+ assert(!isa<ObjCImplementationDecl>(D));
+ // TODO: Cache the results.
+
+ // Check all methods.
+ for (const auto *MDI : D->methods())
+ if (isInvalidationMethod(MDI, Partial))
+ OutInfo.addInvalidationMethod(
+ cast<ObjCMethodDecl>(MDI->getCanonicalDecl()));
+
+ // If interface, check all parent protocols and super.
+ if (const ObjCInterfaceDecl *InterfD = dyn_cast<ObjCInterfaceDecl>(D)) {
+
+ // Visit all protocols.
+ for (const auto *I : InterfD->protocols())
+ containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
+
+ // Visit all categories in case the invalidation method is declared in
+ // a category.
+ for (const auto *Ext : InterfD->visible_extensions())
+ containsInvalidationMethod(Ext, OutInfo, Partial);
+
+ containsInvalidationMethod(InterfD->getSuperClass(), OutInfo, Partial);
+ return;
+ }
+
+ // If protocol, check all parent protocols.
+ if (const ObjCProtocolDecl *ProtD = dyn_cast<ObjCProtocolDecl>(D)) {
+ for (const auto *I : ProtD->protocols()) {
+ containsInvalidationMethod(I->getDefinition(), OutInfo, Partial);
+ }
+ return;
+ }
+}
+
+bool IvarInvalidationCheckerImpl::trackIvar(const ObjCIvarDecl *Iv,
+ IvarSet &TrackedIvars,
+ const ObjCIvarDecl **FirstIvarDecl) {
+ QualType IvQTy = Iv->getType();
+ const ObjCObjectPointerType *IvTy = IvQTy->getAs<ObjCObjectPointerType>();
+ if (!IvTy)
+ return false;
+ const ObjCInterfaceDecl *IvInterf = IvTy->getInterfaceDecl();
+
+ InvalidationInfo Info;
+ containsInvalidationMethod(IvInterf, Info, /*LookForPartial*/ false);
+ if (Info.needsInvalidation()) {
+ const ObjCIvarDecl *I = cast<ObjCIvarDecl>(Iv->getCanonicalDecl());
+ TrackedIvars[I] = Info;
+ if (!*FirstIvarDecl)
+ *FirstIvarDecl = I;
+ return true;
+ }
+ return false;
+}
+
+const ObjCIvarDecl *IvarInvalidationCheckerImpl::findPropertyBackingIvar(
+ const ObjCPropertyDecl *Prop,
+ const ObjCInterfaceDecl *InterfaceD,
+ IvarSet &TrackedIvars,
+ const ObjCIvarDecl **FirstIvarDecl) {
+ const ObjCIvarDecl *IvarD = nullptr;
+
+ // Lookup for the synthesized case.
+ IvarD = Prop->getPropertyIvarDecl();
+ // We only track the ivars/properties that are defined in the current
+ // class (not the parent).
+ if (IvarD && IvarD->getContainingInterface() == InterfaceD) {
+ if (TrackedIvars.count(IvarD)) {
+ return IvarD;
+ }
+ // If the ivar is synthesized we still want to track it.
+ if (trackIvar(IvarD, TrackedIvars, FirstIvarDecl))
+ return IvarD;
+ }
+
+ // Lookup IVars named "_PropName"or "PropName" among the tracked Ivars.
+ StringRef PropName = Prop->getIdentifier()->getName();
+ for (IvarSet::const_iterator I = TrackedIvars.begin(),
+ E = TrackedIvars.end(); I != E; ++I) {
+ const ObjCIvarDecl *Iv = I->first;
+ StringRef IvarName = Iv->getName();
+
+ if (IvarName == PropName)
+ return Iv;
+
+ SmallString<128> PropNameWithUnderscore;
+ {
+ llvm::raw_svector_ostream os(PropNameWithUnderscore);
+ os << '_' << PropName;
+ }
+ if (IvarName == PropNameWithUnderscore)
+ return Iv;
+ }
+
+ // Note, this is a possible source of false positives. We could look at the
+ // getter implementation to find the ivar when its name is not derived from
+ // the property name.
+ return nullptr;
+}
+
+void IvarInvalidationCheckerImpl::printIvar(llvm::raw_svector_ostream &os,
+ const ObjCIvarDecl *IvarDecl,
+ const IvarToPropMapTy &IvarToPopertyMap) {
+ if (IvarDecl->getSynthesize()) {
+ const ObjCPropertyDecl *PD = IvarToPopertyMap.lookup(IvarDecl);
+ assert(PD &&"Do we synthesize ivars for something other than properties?");
+ os << "Property "<< PD->getName() << " ";
+ } else {
+ os << "Instance variable "<< IvarDecl->getName() << " ";
+ }
+}
+
+// Check that the invalidatable interfaces with ivars/properties implement the
+// invalidation methods.
+void IvarInvalidationCheckerImpl::
+visit(const ObjCImplementationDecl *ImplD) const {
+ // Collect all ivars that need cleanup.
+ IvarSet Ivars;
+ // Record the first Ivar needing invalidation; used in reporting when only
+ // one ivar is sufficient. Cannot grab the first on the Ivars set to ensure
+ // deterministic output.
+ const ObjCIvarDecl *FirstIvarDecl = nullptr;
+ const ObjCInterfaceDecl *InterfaceD = ImplD->getClassInterface();
+
+ // Collect ivars declared in this class, its extensions and its implementation
+ ObjCInterfaceDecl *IDecl = const_cast<ObjCInterfaceDecl *>(InterfaceD);
+ for (const ObjCIvarDecl *Iv = IDecl->all_declared_ivar_begin(); Iv;
+ Iv= Iv->getNextIvar())
+ trackIvar(Iv, Ivars, &FirstIvarDecl);
+
+ // Construct Property/Property Accessor to Ivar maps to assist checking if an
+ // ivar which is backing a property has been reset.
+ MethToIvarMapTy PropSetterToIvarMap;
+ MethToIvarMapTy PropGetterToIvarMap;
+ PropToIvarMapTy PropertyToIvarMap;
+ IvarToPropMapTy IvarToPopertyMap;
+
+ ObjCInterfaceDecl::PropertyMap PropMap;
+ ObjCInterfaceDecl::PropertyDeclOrder PropOrder;
+ InterfaceD->collectPropertiesToImplement(PropMap, PropOrder);
+
+ for (ObjCInterfaceDecl::PropertyMap::iterator
+ I = PropMap.begin(), E = PropMap.end(); I != E; ++I) {
+ const ObjCPropertyDecl *PD = I->second;
+ if (PD->isClassProperty())
+ continue;
+
+ const ObjCIvarDecl *ID = findPropertyBackingIvar(PD, InterfaceD, Ivars,
+ &FirstIvarDecl);
+ if (!ID)
+ continue;
+
+ // Store the mappings.
+ PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
+ PropertyToIvarMap[PD] = ID;
+ IvarToPopertyMap[ID] = PD;
+
+ // Find the setter and the getter.
+ const ObjCMethodDecl *SetterD = PD->getSetterMethodDecl();
+ if (SetterD) {
+ SetterD = SetterD->getCanonicalDecl();
+ PropSetterToIvarMap[SetterD] = ID;
+ }
+
+ const ObjCMethodDecl *GetterD = PD->getGetterMethodDecl();
+ if (GetterD) {
+ GetterD = GetterD->getCanonicalDecl();
+ PropGetterToIvarMap[GetterD] = ID;
+ }
+ }
+
+ // If no ivars need invalidation, there is nothing to check here.
+ if (Ivars.empty())
+ return;
+
+ // Find all partial invalidation methods.
+ InvalidationInfo PartialInfo;
+ containsInvalidationMethod(InterfaceD, PartialInfo, /*LookForPartial*/ true);
+
+ // Remove ivars invalidated by the partial invalidation methods. They do not
+ // need to be invalidated in the regular invalidation methods.
+ bool AtImplementationContainsAtLeastOnePartialInvalidationMethod = false;
+ for (MethodSet::iterator
+ I = PartialInfo.InvalidationMethods.begin(),
+ E = PartialInfo.InvalidationMethods.end(); I != E; ++I) {
+ const ObjCMethodDecl *InterfD = *I;
+
+ // Get the corresponding method in the @implementation.
+ const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
+ InterfD->isInstanceMethod());
+ if (D && D->hasBody()) {
+ AtImplementationContainsAtLeastOnePartialInvalidationMethod = true;
+
+ bool CalledAnotherInvalidationMethod = false;
+ // The MethodCrowler is going to remove the invalidated ivars.
+ MethodCrawler(Ivars,
+ CalledAnotherInvalidationMethod,
+ PropSetterToIvarMap,
+ PropGetterToIvarMap,
+ PropertyToIvarMap,
+ BR.getContext()).VisitStmt(D->getBody());
+ // If another invalidation method was called, trust that full invalidation
+ // has occurred.
+ if (CalledAnotherInvalidationMethod)
+ Ivars.clear();
+ }
+ }
+
+ // If all ivars have been invalidated by partial invalidators, there is
+ // nothing to check here.
+ if (Ivars.empty())
+ return;
+
+ // Find all invalidation methods in this @interface declaration and parents.
+ InvalidationInfo Info;
+ containsInvalidationMethod(InterfaceD, Info, /*LookForPartial*/ false);
+
+ // Report an error in case none of the invalidation methods are declared.
+ if (!Info.needsInvalidation() && !PartialInfo.needsInvalidation()) {
+ if (Filter.check_MissingInvalidationMethod)
+ reportNoInvalidationMethod(Filter.checkName_MissingInvalidationMethod,
+ FirstIvarDecl, IvarToPopertyMap, InterfaceD,
+ /*MissingDeclaration*/ true);
+ // If there are no invalidation methods, there is no ivar validation work
+ // to be done.
+ return;
+ }
+
+ // Only check if Ivars are invalidated when InstanceVariableInvalidation
+ // has been requested.
+ if (!Filter.check_InstanceVariableInvalidation)
+ return;
+
+ // Check that all ivars are invalidated by the invalidation methods.
+ bool AtImplementationContainsAtLeastOneInvalidationMethod = false;
+ for (MethodSet::iterator I = Info.InvalidationMethods.begin(),
+ E = Info.InvalidationMethods.end(); I != E; ++I) {
+ const ObjCMethodDecl *InterfD = *I;
+
+ // Get the corresponding method in the @implementation.
+ const ObjCMethodDecl *D = ImplD->getMethod(InterfD->getSelector(),
+ InterfD->isInstanceMethod());
+ if (D && D->hasBody()) {
+ AtImplementationContainsAtLeastOneInvalidationMethod = true;
+
+ // Get a copy of ivars needing invalidation.
+ IvarSet IvarsI = Ivars;
+
+ bool CalledAnotherInvalidationMethod = false;
+ MethodCrawler(IvarsI,
+ CalledAnotherInvalidationMethod,
+ PropSetterToIvarMap,
+ PropGetterToIvarMap,
+ PropertyToIvarMap,
+ BR.getContext()).VisitStmt(D->getBody());
+ // If another invalidation method was called, trust that full invalidation
+ // has occurred.
+ if (CalledAnotherInvalidationMethod)
+ continue;
+
+ // Warn on the ivars that were not invalidated by the method.
+ for (IvarSet::const_iterator
+ I = IvarsI.begin(), E = IvarsI.end(); I != E; ++I)
+ reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, D);
+ }
+ }
+
+ // Report an error in case none of the invalidation methods are implemented.
+ if (!AtImplementationContainsAtLeastOneInvalidationMethod) {
+ if (AtImplementationContainsAtLeastOnePartialInvalidationMethod) {
+ // Warn on the ivars that were not invalidated by the prrtial
+ // invalidation methods.
+ for (IvarSet::const_iterator
+ I = Ivars.begin(), E = Ivars.end(); I != E; ++I)
+ reportIvarNeedsInvalidation(I->first, IvarToPopertyMap, nullptr);
+ } else {
+ // Otherwise, no invalidation methods were implemented.
+ reportNoInvalidationMethod(Filter.checkName_InstanceVariableInvalidation,
+ FirstIvarDecl, IvarToPopertyMap, InterfaceD,
+ /*MissingDeclaration*/ false);
+ }
+ }
+}
+
+void IvarInvalidationCheckerImpl::reportNoInvalidationMethod(
+ CheckName CheckName, const ObjCIvarDecl *FirstIvarDecl,
+ const IvarToPropMapTy &IvarToPopertyMap,
+ const ObjCInterfaceDecl *InterfaceD, bool MissingDeclaration) const {
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ assert(FirstIvarDecl);
+ printIvar(os, FirstIvarDecl, IvarToPopertyMap);
+ os << "needs to be invalidated; ";
+ if (MissingDeclaration)
+ os << "no invalidation method is declared for ";
+ else
+ os << "no invalidation method is defined in the @implementation for ";
+ os << InterfaceD->getName();
+
+ PathDiagnosticLocation IvarDecLocation =
+ PathDiagnosticLocation::createBegin(FirstIvarDecl, BR.getSourceManager());
+
+ BR.EmitBasicReport(FirstIvarDecl, CheckName, "Incomplete invalidation",
+ categories::CoreFoundationObjectiveC, os.str(),
+ IvarDecLocation);
+}
+
+void IvarInvalidationCheckerImpl::
+reportIvarNeedsInvalidation(const ObjCIvarDecl *IvarD,
+ const IvarToPropMapTy &IvarToPopertyMap,
+ const ObjCMethodDecl *MethodD) const {
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ printIvar(os, IvarD, IvarToPopertyMap);
+ os << "needs to be invalidated or set to nil";
+ if (MethodD) {
+ PathDiagnosticLocation MethodDecLocation =
+ PathDiagnosticLocation::createEnd(MethodD->getBody(),
+ BR.getSourceManager(),
+ Mgr.getAnalysisDeclContext(MethodD));
+ BR.EmitBasicReport(MethodD, Filter.checkName_InstanceVariableInvalidation,
+ "Incomplete invalidation",
+ categories::CoreFoundationObjectiveC, os.str(),
+ MethodDecLocation);
+ } else {
+ BR.EmitBasicReport(
+ IvarD, Filter.checkName_InstanceVariableInvalidation,
+ "Incomplete invalidation", categories::CoreFoundationObjectiveC,
+ os.str(),
+ PathDiagnosticLocation::createBegin(IvarD, BR.getSourceManager()));
+ }
+}
+
+void IvarInvalidationCheckerImpl::MethodCrawler::markInvalidated(
+ const ObjCIvarDecl *Iv) {
+ IvarSet::iterator I = IVars.find(Iv);
+ if (I != IVars.end()) {
+ // If InvalidationMethod is present, we are processing the message send and
+ // should ensure we are invalidating with the appropriate method,
+ // otherwise, we are processing setting to 'nil'.
+ if (!InvalidationMethod || I->second.hasMethod(InvalidationMethod))
+ IVars.erase(I);
+ }
+}
+
+const Expr *IvarInvalidationCheckerImpl::MethodCrawler::peel(const Expr *E) const {
+ E = E->IgnoreParenCasts();
+ if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(E))
+ E = POE->getSyntacticForm()->IgnoreParenCasts();
+ if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(E))
+ E = OVE->getSourceExpr()->IgnoreParenCasts();
+ return E;
+}
+
+void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCIvarRefExpr(
+ const ObjCIvarRefExpr *IvarRef) {
+ if (const Decl *D = IvarRef->getDecl())
+ markInvalidated(cast<ObjCIvarDecl>(D->getCanonicalDecl()));
+}
+
+void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCMessageExpr(
+ const ObjCMessageExpr *ME) {
+ const ObjCMethodDecl *MD = ME->getMethodDecl();
+ if (MD) {
+ MD = MD->getCanonicalDecl();
+ MethToIvarMapTy::const_iterator IvI = PropertyGetterToIvarMap.find(MD);
+ if (IvI != PropertyGetterToIvarMap.end())
+ markInvalidated(IvI->second);
+ }
+}
+
+void IvarInvalidationCheckerImpl::MethodCrawler::checkObjCPropertyRefExpr(
+ const ObjCPropertyRefExpr *PA) {
+
+ if (PA->isExplicitProperty()) {
+ const ObjCPropertyDecl *PD = PA->getExplicitProperty();
+ if (PD) {
+ PD = cast<ObjCPropertyDecl>(PD->getCanonicalDecl());
+ PropToIvarMapTy::const_iterator IvI = PropertyToIvarMap.find(PD);
+ if (IvI != PropertyToIvarMap.end())
+ markInvalidated(IvI->second);
+ return;
+ }
+ }
+
+ if (PA->isImplicitProperty()) {
+ const ObjCMethodDecl *MD = PA->getImplicitPropertySetter();
+ if (MD) {
+ MD = MD->getCanonicalDecl();
+ MethToIvarMapTy::const_iterator IvI =PropertyGetterToIvarMap.find(MD);
+ if (IvI != PropertyGetterToIvarMap.end())
+ markInvalidated(IvI->second);
+ return;
+ }
+ }
+}
+
+bool IvarInvalidationCheckerImpl::MethodCrawler::isZero(const Expr *E) const {
+ E = peel(E);
+
+ return (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull)
+ != Expr::NPCK_NotNull);
+}
+
+void IvarInvalidationCheckerImpl::MethodCrawler::check(const Expr *E) {
+ E = peel(E);
+
+ if (const ObjCIvarRefExpr *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
+ checkObjCIvarRefExpr(IvarRef);
+ return;
+ }
+
+ if (const ObjCPropertyRefExpr *PropRef = dyn_cast<ObjCPropertyRefExpr>(E)) {
+ checkObjCPropertyRefExpr(PropRef);
+ return;
+ }
+
+ if (const ObjCMessageExpr *MsgExpr = dyn_cast<ObjCMessageExpr>(E)) {
+ checkObjCMessageExpr(MsgExpr);
+ return;
+ }
+}
+
+void IvarInvalidationCheckerImpl::MethodCrawler::VisitBinaryOperator(
+ const BinaryOperator *BO) {
+ VisitStmt(BO);
+
+ // Do we assign/compare against zero? If yes, check the variable we are
+ // assigning to.
+ BinaryOperatorKind Opcode = BO->getOpcode();
+ if (Opcode != BO_Assign &&
+ Opcode != BO_EQ &&
+ Opcode != BO_NE)
+ return;
+
+ if (isZero(BO->getRHS())) {
+ check(BO->getLHS());
+ return;
+ }
+
+ if (Opcode != BO_Assign && isZero(BO->getLHS())) {
+ check(BO->getRHS());
+ return;
+ }
+}
+
+void IvarInvalidationCheckerImpl::MethodCrawler::VisitObjCMessageExpr(
+ const ObjCMessageExpr *ME) {
+ const ObjCMethodDecl *MD = ME->getMethodDecl();
+ const Expr *Receiver = ME->getInstanceReceiver();
+
+ // Stop if we are calling '[self invalidate]'.
+ if (Receiver && isInvalidationMethod(MD, /*LookForPartial*/ false))
+ if (Receiver->isObjCSelfExpr()) {
+ CalledAnotherInvalidationMethod = true;
+ return;
+ }
+
+ // Check if we call a setter and set the property to 'nil'.
+ if (MD && (ME->getNumArgs() == 1) && isZero(ME->getArg(0))) {
+ MD = MD->getCanonicalDecl();
+ MethToIvarMapTy::const_iterator IvI = PropertySetterToIvarMap.find(MD);
+ if (IvI != PropertySetterToIvarMap.end()) {
+ markInvalidated(IvI->second);
+ return;
+ }
+ }
+
+ // Check if we call the 'invalidation' routine on the ivar.
+ if (Receiver) {
+ InvalidationMethod = MD;
+ check(Receiver->IgnoreParenCasts());
+ InvalidationMethod = nullptr;
+ }
+
+ VisitStmt(ME);
+}
+} // end anonymous namespace
+
+// Register the checkers.
+namespace {
+class IvarInvalidationChecker :
+ public Checker<check::ASTDecl<ObjCImplementationDecl> > {
+public:
+ ChecksFilter Filter;
+public:
+ void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& Mgr,
+ BugReporter &BR) const {
+ IvarInvalidationCheckerImpl Walker(Mgr, BR, Filter);
+ Walker.visit(D);
+ }
+};
+} // end anonymous namespace
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ IvarInvalidationChecker *checker = \
+ mgr.registerChecker<IvarInvalidationChecker>(); \
+ checker->Filter.check_##name = true; \
+ checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \
+ }
+
+REGISTER_CHECKER(InstanceVariableInvalidation)
+REGISTER_CHECKER(MissingInvalidationMethod)
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
new file mode 100644
index 000000000000..df238f2b2e45
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
@@ -0,0 +1,316 @@
+//=== LLVMConventionsChecker.cpp - Check LLVM codebase conventions ---*- C++ -*-
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines LLVMConventionsChecker, a bunch of small little checks
+// for checking specific coding conventions in the LLVM/Clang codebase.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+//===----------------------------------------------------------------------===//
+// Generic type checking routines.
+//===----------------------------------------------------------------------===//
+
+static bool IsLLVMStringRef(QualType T) {
+ const RecordType *RT = T->getAs<RecordType>();
+ if (!RT)
+ return false;
+
+ return StringRef(QualType(RT, 0).getAsString()) == "class StringRef";
+}
+
+/// Check whether the declaration is semantically inside the top-level
+/// namespace named by ns.
+static bool InNamespace(const Decl *D, StringRef NS) {
+ const NamespaceDecl *ND = dyn_cast<NamespaceDecl>(D->getDeclContext());
+ if (!ND)
+ return false;
+ const IdentifierInfo *II = ND->getIdentifier();
+ if (!II || !II->getName().equals(NS))
+ return false;
+ return isa<TranslationUnitDecl>(ND->getDeclContext());
+}
+
+static bool IsStdString(QualType T) {
+ if (const ElaboratedType *QT = T->getAs<ElaboratedType>())
+ T = QT->getNamedType();
+
+ const TypedefType *TT = T->getAs<TypedefType>();
+ if (!TT)
+ return false;
+
+ const TypedefNameDecl *TD = TT->getDecl();
+
+ if (!TD->isInStdNamespace())
+ return false;
+
+ return TD->getName() == "string";
+}
+
+static bool IsClangType(const RecordDecl *RD) {
+ return RD->getName() == "Type" && InNamespace(RD, "clang");
+}
+
+static bool IsClangDecl(const RecordDecl *RD) {
+ return RD->getName() == "Decl" && InNamespace(RD, "clang");
+}
+
+static bool IsClangStmt(const RecordDecl *RD) {
+ return RD->getName() == "Stmt" && InNamespace(RD, "clang");
+}
+
+static bool IsClangAttr(const RecordDecl *RD) {
+ return RD->getName() == "Attr" && InNamespace(RD, "clang");
+}
+
+static bool IsStdVector(QualType T) {
+ const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>();
+ if (!TS)
+ return false;
+
+ TemplateName TM = TS->getTemplateName();
+ TemplateDecl *TD = TM.getAsTemplateDecl();
+
+ if (!TD || !InNamespace(TD, "std"))
+ return false;
+
+ return TD->getName() == "vector";
+}
+
+static bool IsSmallVector(QualType T) {
+ const TemplateSpecializationType *TS = T->getAs<TemplateSpecializationType>();
+ if (!TS)
+ return false;
+
+ TemplateName TM = TS->getTemplateName();
+ TemplateDecl *TD = TM.getAsTemplateDecl();
+
+ if (!TD || !InNamespace(TD, "llvm"))
+ return false;
+
+ return TD->getName() == "SmallVector";
+}
+
+//===----------------------------------------------------------------------===//
+// CHECK: a StringRef should not be bound to a temporary std::string whose
+// lifetime is shorter than the StringRef's.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class StringRefCheckerVisitor : public StmtVisitor<StringRefCheckerVisitor> {
+ const Decl *DeclWithIssue;
+ BugReporter &BR;
+ const CheckerBase *Checker;
+
+public:
+ StringRefCheckerVisitor(const Decl *declWithIssue, BugReporter &br,
+ const CheckerBase *checker)
+ : DeclWithIssue(declWithIssue), BR(br), Checker(checker) {}
+ void VisitChildren(Stmt *S) {
+ for (Stmt *Child : S->children())
+ if (Child)
+ Visit(Child);
+ }
+ void VisitStmt(Stmt *S) { VisitChildren(S); }
+ void VisitDeclStmt(DeclStmt *DS);
+private:
+ void VisitVarDecl(VarDecl *VD);
+};
+} // end anonymous namespace
+
+static void CheckStringRefAssignedTemporary(const Decl *D, BugReporter &BR,
+ const CheckerBase *Checker) {
+ StringRefCheckerVisitor walker(D, BR, Checker);
+ walker.Visit(D->getBody());
+}
+
+void StringRefCheckerVisitor::VisitDeclStmt(DeclStmt *S) {
+ VisitChildren(S);
+
+ for (auto *I : S->decls())
+ if (VarDecl *VD = dyn_cast<VarDecl>(I))
+ VisitVarDecl(VD);
+}
+
+void StringRefCheckerVisitor::VisitVarDecl(VarDecl *VD) {
+ Expr *Init = VD->getInit();
+ if (!Init)
+ return;
+
+ // Pattern match for:
+ // StringRef x = call() (where call returns std::string)
+ if (!IsLLVMStringRef(VD->getType()))
+ return;
+ ExprWithCleanups *Ex1 = dyn_cast<ExprWithCleanups>(Init);
+ if (!Ex1)
+ return;
+ CXXConstructExpr *Ex2 = dyn_cast<CXXConstructExpr>(Ex1->getSubExpr());
+ if (!Ex2 || Ex2->getNumArgs() != 1)
+ return;
+ ImplicitCastExpr *Ex3 = dyn_cast<ImplicitCastExpr>(Ex2->getArg(0));
+ if (!Ex3)
+ return;
+ CXXConstructExpr *Ex4 = dyn_cast<CXXConstructExpr>(Ex3->getSubExpr());
+ if (!Ex4 || Ex4->getNumArgs() != 1)
+ return;
+ ImplicitCastExpr *Ex5 = dyn_cast<ImplicitCastExpr>(Ex4->getArg(0));
+ if (!Ex5)
+ return;
+ CXXBindTemporaryExpr *Ex6 = dyn_cast<CXXBindTemporaryExpr>(Ex5->getSubExpr());
+ if (!Ex6 || !IsStdString(Ex6->getType()))
+ return;
+
+ // Okay, badness! Report an error.
+ const char *desc = "StringRef should not be bound to temporary "
+ "std::string that it outlives";
+ PathDiagnosticLocation VDLoc =
+ PathDiagnosticLocation::createBegin(VD, BR.getSourceManager());
+ BR.EmitBasicReport(DeclWithIssue, Checker, desc, "LLVM Conventions", desc,
+ VDLoc, Init->getSourceRange());
+}
+
+//===----------------------------------------------------------------------===//
+// CHECK: Clang AST nodes should not have fields that can allocate
+// memory.
+//===----------------------------------------------------------------------===//
+
+static bool AllocatesMemory(QualType T) {
+ return IsStdVector(T) || IsStdString(T) || IsSmallVector(T);
+}
+
+// This type checking could be sped up via dynamic programming.
+static bool IsPartOfAST(const CXXRecordDecl *R) {
+ if (IsClangStmt(R) || IsClangType(R) || IsClangDecl(R) || IsClangAttr(R))
+ return true;
+
+ for (const auto &BS : R->bases()) {
+ QualType T = BS.getType();
+ if (const RecordType *baseT = T->getAs<RecordType>()) {
+ CXXRecordDecl *baseD = cast<CXXRecordDecl>(baseT->getDecl());
+ if (IsPartOfAST(baseD))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+namespace {
+class ASTFieldVisitor {
+ SmallVector<FieldDecl*, 10> FieldChain;
+ const CXXRecordDecl *Root;
+ BugReporter &BR;
+ const CheckerBase *Checker;
+
+public:
+ ASTFieldVisitor(const CXXRecordDecl *root, BugReporter &br,
+ const CheckerBase *checker)
+ : Root(root), BR(br), Checker(checker) {}
+
+ void Visit(FieldDecl *D);
+ void ReportError(QualType T);
+};
+} // end anonymous namespace
+
+static void CheckASTMemory(const CXXRecordDecl *R, BugReporter &BR,
+ const CheckerBase *Checker) {
+ if (!IsPartOfAST(R))
+ return;
+
+ for (auto *I : R->fields()) {
+ ASTFieldVisitor walker(R, BR, Checker);
+ walker.Visit(I);
+ }
+}
+
+void ASTFieldVisitor::Visit(FieldDecl *D) {
+ FieldChain.push_back(D);
+
+ QualType T = D->getType();
+
+ if (AllocatesMemory(T))
+ ReportError(T);
+
+ if (const RecordType *RT = T->getAs<RecordType>()) {
+ const RecordDecl *RD = RT->getDecl()->getDefinition();
+ for (auto *I : RD->fields())
+ Visit(I);
+ }
+
+ FieldChain.pop_back();
+}
+
+void ASTFieldVisitor::ReportError(QualType T) {
+ SmallString<1024> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << "AST class '" << Root->getName() << "' has a field '"
+ << FieldChain.front()->getName() << "' that allocates heap memory";
+ if (FieldChain.size() > 1) {
+ os << " via the following chain: ";
+ bool isFirst = true;
+ for (SmallVectorImpl<FieldDecl*>::iterator I=FieldChain.begin(),
+ E=FieldChain.end(); I!=E; ++I) {
+ if (!isFirst)
+ os << '.';
+ else
+ isFirst = false;
+ os << (*I)->getName();
+ }
+ }
+ os << " (type " << FieldChain.back()->getType().getAsString() << ")";
+
+ // Note that this will fire for every translation unit that uses this
+ // class. This is suboptimal, but at least scan-build will merge
+ // duplicate HTML reports. In the future we need a unified way of merging
+ // duplicate reports across translation units. For C++ classes we cannot
+ // just report warnings when we see an out-of-line method definition for a
+ // class, as that heuristic doesn't always work (the complete definition of
+ // the class may be in the header file, for example).
+ PathDiagnosticLocation L = PathDiagnosticLocation::createBegin(
+ FieldChain.front(), BR.getSourceManager());
+ BR.EmitBasicReport(Root, Checker, "AST node allocates heap memory",
+ "LLVM Conventions", os.str(), L);
+}
+
+//===----------------------------------------------------------------------===//
+// LLVMConventionsChecker
+//===----------------------------------------------------------------------===//
+
+namespace {
+class LLVMConventionsChecker : public Checker<
+ check::ASTDecl<CXXRecordDecl>,
+ check::ASTCodeBody > {
+public:
+ void checkASTDecl(const CXXRecordDecl *R, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ if (R->isCompleteDefinition())
+ CheckASTMemory(R, BR, this);
+ }
+
+ void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ CheckStringRefAssignedTemporary(D, BR, this);
+ }
+};
+}
+
+void ento::registerLLVMConventionsChecker(CheckerManager &mgr) {
+ mgr.registerChecker<LLVMConventionsChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
new file mode 100644
index 000000000000..eda39efeca17
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
@@ -0,0 +1,1411 @@
+//=- LocalizationChecker.cpp -------------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a set of checks for localizability including:
+// 1) A checker that warns about uses of non-localized NSStrings passed to
+// UI methods expecting localized strings
+// 2) A syntactic checker that warns against the bad practice of
+// not including a comment in NSLocalizedString macros.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "llvm/Support/Unicode.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+struct LocalizedState {
+private:
+ enum Kind { NonLocalized, Localized } K;
+ LocalizedState(Kind InK) : K(InK) {}
+
+public:
+ bool isLocalized() const { return K == Localized; }
+ bool isNonLocalized() const { return K == NonLocalized; }
+
+ static LocalizedState getLocalized() { return LocalizedState(Localized); }
+ static LocalizedState getNonLocalized() {
+ return LocalizedState(NonLocalized);
+ }
+
+ // Overload the == operator
+ bool operator==(const LocalizedState &X) const { return K == X.K; }
+
+ // LLVMs equivalent of a hash function
+ void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
+};
+
+class NonLocalizedStringChecker
+ : public Checker<check::PreCall, check::PostCall, check::PreObjCMessage,
+ check::PostObjCMessage,
+ check::PostStmt<ObjCStringLiteral>> {
+
+ mutable std::unique_ptr<BugType> BT;
+
+ // Methods that require a localized string
+ mutable llvm::DenseMap<const IdentifierInfo *,
+ llvm::DenseMap<Selector, uint8_t>> UIMethods;
+ // Methods that return a localized string
+ mutable llvm::SmallSet<std::pair<const IdentifierInfo *, Selector>, 12> LSM;
+ // C Functions that return a localized string
+ mutable llvm::SmallSet<const IdentifierInfo *, 5> LSF;
+
+ void initUIMethods(ASTContext &Ctx) const;
+ void initLocStringsMethods(ASTContext &Ctx) const;
+
+ bool hasNonLocalizedState(SVal S, CheckerContext &C) const;
+ bool hasLocalizedState(SVal S, CheckerContext &C) const;
+ void setNonLocalizedState(SVal S, CheckerContext &C) const;
+ void setLocalizedState(SVal S, CheckerContext &C) const;
+
+ bool isAnnotatedAsReturningLocalized(const Decl *D) const;
+ bool isAnnotatedAsTakingLocalized(const Decl *D) const;
+ void reportLocalizationError(SVal S, const CallEvent &M, CheckerContext &C,
+ int argumentNumber = 0) const;
+
+ int getLocalizedArgumentForSelector(const IdentifierInfo *Receiver,
+ Selector S) const;
+
+public:
+ NonLocalizedStringChecker();
+
+ // When this parameter is set to true, the checker assumes all
+ // methods that return NSStrings are unlocalized. Thus, more false
+ // positives will be reported.
+ DefaultBool IsAggressive;
+
+ void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
+ void checkPostStmt(const ObjCStringLiteral *SL, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+};
+
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(LocalizedMemMap, const MemRegion *,
+ LocalizedState)
+
+NonLocalizedStringChecker::NonLocalizedStringChecker() {
+ BT.reset(new BugType(this, "Unlocalizable string",
+ "Localizability Issue (Apple)"));
+}
+
+namespace {
+class NonLocalizedStringBRVisitor final : public BugReporterVisitor {
+
+ const MemRegion *NonLocalizedString;
+ bool Satisfied;
+
+public:
+ NonLocalizedStringBRVisitor(const MemRegion *NonLocalizedString)
+ : NonLocalizedString(NonLocalizedString), Satisfied(false) {
+ assert(NonLocalizedString);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.Add(NonLocalizedString);
+ }
+};
+} // End anonymous namespace.
+
+#define NEW_RECEIVER(receiver) \
+ llvm::DenseMap<Selector, uint8_t> &receiver##M = \
+ UIMethods.insert({&Ctx.Idents.get(#receiver), \
+ llvm::DenseMap<Selector, uint8_t>()}) \
+ .first->second;
+#define ADD_NULLARY_METHOD(receiver, method, argument) \
+ receiver##M.insert( \
+ {Ctx.Selectors.getNullarySelector(&Ctx.Idents.get(#method)), argument});
+#define ADD_UNARY_METHOD(receiver, method, argument) \
+ receiver##M.insert( \
+ {Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(#method)), argument});
+#define ADD_METHOD(receiver, method_list, count, argument) \
+ receiver##M.insert({Ctx.Selectors.getSelector(count, method_list), argument});
+
+/// Initializes a list of methods that require a localized string
+/// Format: {"ClassName", {{"selectorName:", LocStringArg#}, ...}, ...}
+void NonLocalizedStringChecker::initUIMethods(ASTContext &Ctx) const {
+ if (!UIMethods.empty())
+ return;
+
+ // UI Methods
+ NEW_RECEIVER(UISearchDisplayController)
+ ADD_UNARY_METHOD(UISearchDisplayController, setSearchResultsTitle, 0)
+
+ NEW_RECEIVER(UITabBarItem)
+ IdentifierInfo *initWithTitleUITabBarItemTag[] = {
+ &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("image"),
+ &Ctx.Idents.get("tag")};
+ ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemTag, 3, 0)
+ IdentifierInfo *initWithTitleUITabBarItemImage[] = {
+ &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("image"),
+ &Ctx.Idents.get("selectedImage")};
+ ADD_METHOD(UITabBarItem, initWithTitleUITabBarItemImage, 3, 0)
+
+ NEW_RECEIVER(NSDockTile)
+ ADD_UNARY_METHOD(NSDockTile, setBadgeLabel, 0)
+
+ NEW_RECEIVER(NSStatusItem)
+ ADD_UNARY_METHOD(NSStatusItem, setTitle, 0)
+ ADD_UNARY_METHOD(NSStatusItem, setToolTip, 0)
+
+ NEW_RECEIVER(UITableViewRowAction)
+ IdentifierInfo *rowActionWithStyleUITableViewRowAction[] = {
+ &Ctx.Idents.get("rowActionWithStyle"), &Ctx.Idents.get("title"),
+ &Ctx.Idents.get("handler")};
+ ADD_METHOD(UITableViewRowAction, rowActionWithStyleUITableViewRowAction, 3, 1)
+ ADD_UNARY_METHOD(UITableViewRowAction, setTitle, 0)
+
+ NEW_RECEIVER(NSBox)
+ ADD_UNARY_METHOD(NSBox, setTitle, 0)
+
+ NEW_RECEIVER(NSButton)
+ ADD_UNARY_METHOD(NSButton, setTitle, 0)
+ ADD_UNARY_METHOD(NSButton, setAlternateTitle, 0)
+ IdentifierInfo *radioButtonWithTitleNSButton[] = {
+ &Ctx.Idents.get("radioButtonWithTitle"), &Ctx.Idents.get("target"),
+ &Ctx.Idents.get("action")};
+ ADD_METHOD(NSButton, radioButtonWithTitleNSButton, 3, 0)
+ IdentifierInfo *buttonWithTitleNSButtonImage[] = {
+ &Ctx.Idents.get("buttonWithTitle"), &Ctx.Idents.get("image"),
+ &Ctx.Idents.get("target"), &Ctx.Idents.get("action")};
+ ADD_METHOD(NSButton, buttonWithTitleNSButtonImage, 4, 0)
+ IdentifierInfo *checkboxWithTitleNSButton[] = {
+ &Ctx.Idents.get("checkboxWithTitle"), &Ctx.Idents.get("target"),
+ &Ctx.Idents.get("action")};
+ ADD_METHOD(NSButton, checkboxWithTitleNSButton, 3, 0)
+ IdentifierInfo *buttonWithTitleNSButtonTarget[] = {
+ &Ctx.Idents.get("buttonWithTitle"), &Ctx.Idents.get("target"),
+ &Ctx.Idents.get("action")};
+ ADD_METHOD(NSButton, buttonWithTitleNSButtonTarget, 3, 0)
+
+ NEW_RECEIVER(NSSavePanel)
+ ADD_UNARY_METHOD(NSSavePanel, setPrompt, 0)
+ ADD_UNARY_METHOD(NSSavePanel, setTitle, 0)
+ ADD_UNARY_METHOD(NSSavePanel, setNameFieldLabel, 0)
+ ADD_UNARY_METHOD(NSSavePanel, setNameFieldStringValue, 0)
+ ADD_UNARY_METHOD(NSSavePanel, setMessage, 0)
+
+ NEW_RECEIVER(UIPrintInfo)
+ ADD_UNARY_METHOD(UIPrintInfo, setJobName, 0)
+
+ NEW_RECEIVER(NSTabViewItem)
+ ADD_UNARY_METHOD(NSTabViewItem, setLabel, 0)
+ ADD_UNARY_METHOD(NSTabViewItem, setToolTip, 0)
+
+ NEW_RECEIVER(NSBrowser)
+ IdentifierInfo *setTitleNSBrowser[] = {&Ctx.Idents.get("setTitle"),
+ &Ctx.Idents.get("ofColumn")};
+ ADD_METHOD(NSBrowser, setTitleNSBrowser, 2, 0)
+
+ NEW_RECEIVER(UIAccessibilityElement)
+ ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityLabel, 0)
+ ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityHint, 0)
+ ADD_UNARY_METHOD(UIAccessibilityElement, setAccessibilityValue, 0)
+
+ NEW_RECEIVER(UIAlertAction)
+ IdentifierInfo *actionWithTitleUIAlertAction[] = {
+ &Ctx.Idents.get("actionWithTitle"), &Ctx.Idents.get("style"),
+ &Ctx.Idents.get("handler")};
+ ADD_METHOD(UIAlertAction, actionWithTitleUIAlertAction, 3, 0)
+
+ NEW_RECEIVER(NSPopUpButton)
+ ADD_UNARY_METHOD(NSPopUpButton, addItemWithTitle, 0)
+ IdentifierInfo *insertItemWithTitleNSPopUpButton[] = {
+ &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("atIndex")};
+ ADD_METHOD(NSPopUpButton, insertItemWithTitleNSPopUpButton, 2, 0)
+ ADD_UNARY_METHOD(NSPopUpButton, removeItemWithTitle, 0)
+ ADD_UNARY_METHOD(NSPopUpButton, selectItemWithTitle, 0)
+ ADD_UNARY_METHOD(NSPopUpButton, setTitle, 0)
+
+ NEW_RECEIVER(NSTableViewRowAction)
+ IdentifierInfo *rowActionWithStyleNSTableViewRowAction[] = {
+ &Ctx.Idents.get("rowActionWithStyle"), &Ctx.Idents.get("title"),
+ &Ctx.Idents.get("handler")};
+ ADD_METHOD(NSTableViewRowAction, rowActionWithStyleNSTableViewRowAction, 3, 1)
+ ADD_UNARY_METHOD(NSTableViewRowAction, setTitle, 0)
+
+ NEW_RECEIVER(NSImage)
+ ADD_UNARY_METHOD(NSImage, setAccessibilityDescription, 0)
+
+ NEW_RECEIVER(NSUserActivity)
+ ADD_UNARY_METHOD(NSUserActivity, setTitle, 0)
+
+ NEW_RECEIVER(NSPathControlItem)
+ ADD_UNARY_METHOD(NSPathControlItem, setTitle, 0)
+
+ NEW_RECEIVER(NSCell)
+ ADD_UNARY_METHOD(NSCell, initTextCell, 0)
+ ADD_UNARY_METHOD(NSCell, setTitle, 0)
+ ADD_UNARY_METHOD(NSCell, setStringValue, 0)
+
+ NEW_RECEIVER(NSPathControl)
+ ADD_UNARY_METHOD(NSPathControl, setPlaceholderString, 0)
+
+ NEW_RECEIVER(UIAccessibility)
+ ADD_UNARY_METHOD(UIAccessibility, setAccessibilityLabel, 0)
+ ADD_UNARY_METHOD(UIAccessibility, setAccessibilityHint, 0)
+ ADD_UNARY_METHOD(UIAccessibility, setAccessibilityValue, 0)
+
+ NEW_RECEIVER(NSTableColumn)
+ ADD_UNARY_METHOD(NSTableColumn, setTitle, 0)
+ ADD_UNARY_METHOD(NSTableColumn, setHeaderToolTip, 0)
+
+ NEW_RECEIVER(NSSegmentedControl)
+ IdentifierInfo *setLabelNSSegmentedControl[] = {
+ &Ctx.Idents.get("setLabel"), &Ctx.Idents.get("forSegment")};
+ ADD_METHOD(NSSegmentedControl, setLabelNSSegmentedControl, 2, 0)
+ IdentifierInfo *setToolTipNSSegmentedControl[] = {
+ &Ctx.Idents.get("setToolTip"), &Ctx.Idents.get("forSegment")};
+ ADD_METHOD(NSSegmentedControl, setToolTipNSSegmentedControl, 2, 0)
+
+ NEW_RECEIVER(NSButtonCell)
+ ADD_UNARY_METHOD(NSButtonCell, setTitle, 0)
+ ADD_UNARY_METHOD(NSButtonCell, setAlternateTitle, 0)
+
+ NEW_RECEIVER(NSDatePickerCell)
+ ADD_UNARY_METHOD(NSDatePickerCell, initTextCell, 0)
+
+ NEW_RECEIVER(NSSliderCell)
+ ADD_UNARY_METHOD(NSSliderCell, setTitle, 0)
+
+ NEW_RECEIVER(NSControl)
+ ADD_UNARY_METHOD(NSControl, setStringValue, 0)
+
+ NEW_RECEIVER(NSAccessibility)
+ ADD_UNARY_METHOD(NSAccessibility, setAccessibilityValueDescription, 0)
+ ADD_UNARY_METHOD(NSAccessibility, setAccessibilityLabel, 0)
+ ADD_UNARY_METHOD(NSAccessibility, setAccessibilityTitle, 0)
+ ADD_UNARY_METHOD(NSAccessibility, setAccessibilityPlaceholderValue, 0)
+ ADD_UNARY_METHOD(NSAccessibility, setAccessibilityHelp, 0)
+
+ NEW_RECEIVER(NSMatrix)
+ IdentifierInfo *setToolTipNSMatrix[] = {&Ctx.Idents.get("setToolTip"),
+ &Ctx.Idents.get("forCell")};
+ ADD_METHOD(NSMatrix, setToolTipNSMatrix, 2, 0)
+
+ NEW_RECEIVER(NSPrintPanel)
+ ADD_UNARY_METHOD(NSPrintPanel, setDefaultButtonTitle, 0)
+
+ NEW_RECEIVER(UILocalNotification)
+ ADD_UNARY_METHOD(UILocalNotification, setAlertBody, 0)
+ ADD_UNARY_METHOD(UILocalNotification, setAlertAction, 0)
+ ADD_UNARY_METHOD(UILocalNotification, setAlertTitle, 0)
+
+ NEW_RECEIVER(NSSlider)
+ ADD_UNARY_METHOD(NSSlider, setTitle, 0)
+
+ NEW_RECEIVER(UIMenuItem)
+ IdentifierInfo *initWithTitleUIMenuItem[] = {&Ctx.Idents.get("initWithTitle"),
+ &Ctx.Idents.get("action")};
+ ADD_METHOD(UIMenuItem, initWithTitleUIMenuItem, 2, 0)
+ ADD_UNARY_METHOD(UIMenuItem, setTitle, 0)
+
+ NEW_RECEIVER(UIAlertController)
+ IdentifierInfo *alertControllerWithTitleUIAlertController[] = {
+ &Ctx.Idents.get("alertControllerWithTitle"), &Ctx.Idents.get("message"),
+ &Ctx.Idents.get("preferredStyle")};
+ ADD_METHOD(UIAlertController, alertControllerWithTitleUIAlertController, 3, 1)
+ ADD_UNARY_METHOD(UIAlertController, setTitle, 0)
+ ADD_UNARY_METHOD(UIAlertController, setMessage, 0)
+
+ NEW_RECEIVER(UIApplicationShortcutItem)
+ IdentifierInfo *initWithTypeUIApplicationShortcutItemIcon[] = {
+ &Ctx.Idents.get("initWithType"), &Ctx.Idents.get("localizedTitle"),
+ &Ctx.Idents.get("localizedSubtitle"), &Ctx.Idents.get("icon"),
+ &Ctx.Idents.get("userInfo")};
+ ADD_METHOD(UIApplicationShortcutItem,
+ initWithTypeUIApplicationShortcutItemIcon, 5, 1)
+ IdentifierInfo *initWithTypeUIApplicationShortcutItem[] = {
+ &Ctx.Idents.get("initWithType"), &Ctx.Idents.get("localizedTitle")};
+ ADD_METHOD(UIApplicationShortcutItem, initWithTypeUIApplicationShortcutItem,
+ 2, 1)
+
+ NEW_RECEIVER(UIActionSheet)
+ IdentifierInfo *initWithTitleUIActionSheet[] = {
+ &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("delegate"),
+ &Ctx.Idents.get("cancelButtonTitle"),
+ &Ctx.Idents.get("destructiveButtonTitle"),
+ &Ctx.Idents.get("otherButtonTitles")};
+ ADD_METHOD(UIActionSheet, initWithTitleUIActionSheet, 5, 0)
+ ADD_UNARY_METHOD(UIActionSheet, addButtonWithTitle, 0)
+ ADD_UNARY_METHOD(UIActionSheet, setTitle, 0)
+
+ NEW_RECEIVER(UIAccessibilityCustomAction)
+ IdentifierInfo *initWithNameUIAccessibilityCustomAction[] = {
+ &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("target"),
+ &Ctx.Idents.get("selector")};
+ ADD_METHOD(UIAccessibilityCustomAction,
+ initWithNameUIAccessibilityCustomAction, 3, 0)
+ ADD_UNARY_METHOD(UIAccessibilityCustomAction, setName, 0)
+
+ NEW_RECEIVER(UISearchBar)
+ ADD_UNARY_METHOD(UISearchBar, setText, 0)
+ ADD_UNARY_METHOD(UISearchBar, setPrompt, 0)
+ ADD_UNARY_METHOD(UISearchBar, setPlaceholder, 0)
+
+ NEW_RECEIVER(UIBarItem)
+ ADD_UNARY_METHOD(UIBarItem, setTitle, 0)
+
+ NEW_RECEIVER(UITextView)
+ ADD_UNARY_METHOD(UITextView, setText, 0)
+
+ NEW_RECEIVER(NSView)
+ ADD_UNARY_METHOD(NSView, setToolTip, 0)
+
+ NEW_RECEIVER(NSTextField)
+ ADD_UNARY_METHOD(NSTextField, setPlaceholderString, 0)
+ ADD_UNARY_METHOD(NSTextField, textFieldWithString, 0)
+ ADD_UNARY_METHOD(NSTextField, wrappingLabelWithString, 0)
+ ADD_UNARY_METHOD(NSTextField, labelWithString, 0)
+
+ NEW_RECEIVER(NSAttributedString)
+ ADD_UNARY_METHOD(NSAttributedString, initWithString, 0)
+ IdentifierInfo *initWithStringNSAttributedString[] = {
+ &Ctx.Idents.get("initWithString"), &Ctx.Idents.get("attributes")};
+ ADD_METHOD(NSAttributedString, initWithStringNSAttributedString, 2, 0)
+
+ NEW_RECEIVER(NSText)
+ ADD_UNARY_METHOD(NSText, setString, 0)
+
+ NEW_RECEIVER(UIKeyCommand)
+ IdentifierInfo *keyCommandWithInputUIKeyCommand[] = {
+ &Ctx.Idents.get("keyCommandWithInput"), &Ctx.Idents.get("modifierFlags"),
+ &Ctx.Idents.get("action"), &Ctx.Idents.get("discoverabilityTitle")};
+ ADD_METHOD(UIKeyCommand, keyCommandWithInputUIKeyCommand, 4, 3)
+ ADD_UNARY_METHOD(UIKeyCommand, setDiscoverabilityTitle, 0)
+
+ NEW_RECEIVER(UILabel)
+ ADD_UNARY_METHOD(UILabel, setText, 0)
+
+ NEW_RECEIVER(NSAlert)
+ IdentifierInfo *alertWithMessageTextNSAlert[] = {
+ &Ctx.Idents.get("alertWithMessageText"), &Ctx.Idents.get("defaultButton"),
+ &Ctx.Idents.get("alternateButton"), &Ctx.Idents.get("otherButton"),
+ &Ctx.Idents.get("informativeTextWithFormat")};
+ ADD_METHOD(NSAlert, alertWithMessageTextNSAlert, 5, 0)
+ ADD_UNARY_METHOD(NSAlert, addButtonWithTitle, 0)
+ ADD_UNARY_METHOD(NSAlert, setMessageText, 0)
+ ADD_UNARY_METHOD(NSAlert, setInformativeText, 0)
+ ADD_UNARY_METHOD(NSAlert, setHelpAnchor, 0)
+
+ NEW_RECEIVER(UIMutableApplicationShortcutItem)
+ ADD_UNARY_METHOD(UIMutableApplicationShortcutItem, setLocalizedTitle, 0)
+ ADD_UNARY_METHOD(UIMutableApplicationShortcutItem, setLocalizedSubtitle, 0)
+
+ NEW_RECEIVER(UIButton)
+ IdentifierInfo *setTitleUIButton[] = {&Ctx.Idents.get("setTitle"),
+ &Ctx.Idents.get("forState")};
+ ADD_METHOD(UIButton, setTitleUIButton, 2, 0)
+
+ NEW_RECEIVER(NSWindow)
+ ADD_UNARY_METHOD(NSWindow, setTitle, 0)
+ IdentifierInfo *minFrameWidthWithTitleNSWindow[] = {
+ &Ctx.Idents.get("minFrameWidthWithTitle"), &Ctx.Idents.get("styleMask")};
+ ADD_METHOD(NSWindow, minFrameWidthWithTitleNSWindow, 2, 0)
+ ADD_UNARY_METHOD(NSWindow, setMiniwindowTitle, 0)
+
+ NEW_RECEIVER(NSPathCell)
+ ADD_UNARY_METHOD(NSPathCell, setPlaceholderString, 0)
+
+ NEW_RECEIVER(UIDocumentMenuViewController)
+ IdentifierInfo *addOptionWithTitleUIDocumentMenuViewController[] = {
+ &Ctx.Idents.get("addOptionWithTitle"), &Ctx.Idents.get("image"),
+ &Ctx.Idents.get("order"), &Ctx.Idents.get("handler")};
+ ADD_METHOD(UIDocumentMenuViewController,
+ addOptionWithTitleUIDocumentMenuViewController, 4, 0)
+
+ NEW_RECEIVER(UINavigationItem)
+ ADD_UNARY_METHOD(UINavigationItem, initWithTitle, 0)
+ ADD_UNARY_METHOD(UINavigationItem, setTitle, 0)
+ ADD_UNARY_METHOD(UINavigationItem, setPrompt, 0)
+
+ NEW_RECEIVER(UIAlertView)
+ IdentifierInfo *initWithTitleUIAlertView[] = {
+ &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("message"),
+ &Ctx.Idents.get("delegate"), &Ctx.Idents.get("cancelButtonTitle"),
+ &Ctx.Idents.get("otherButtonTitles")};
+ ADD_METHOD(UIAlertView, initWithTitleUIAlertView, 5, 0)
+ ADD_UNARY_METHOD(UIAlertView, addButtonWithTitle, 0)
+ ADD_UNARY_METHOD(UIAlertView, setTitle, 0)
+ ADD_UNARY_METHOD(UIAlertView, setMessage, 0)
+
+ NEW_RECEIVER(NSFormCell)
+ ADD_UNARY_METHOD(NSFormCell, initTextCell, 0)
+ ADD_UNARY_METHOD(NSFormCell, setTitle, 0)
+ ADD_UNARY_METHOD(NSFormCell, setPlaceholderString, 0)
+
+ NEW_RECEIVER(NSUserNotification)
+ ADD_UNARY_METHOD(NSUserNotification, setTitle, 0)
+ ADD_UNARY_METHOD(NSUserNotification, setSubtitle, 0)
+ ADD_UNARY_METHOD(NSUserNotification, setInformativeText, 0)
+ ADD_UNARY_METHOD(NSUserNotification, setActionButtonTitle, 0)
+ ADD_UNARY_METHOD(NSUserNotification, setOtherButtonTitle, 0)
+ ADD_UNARY_METHOD(NSUserNotification, setResponsePlaceholder, 0)
+
+ NEW_RECEIVER(NSToolbarItem)
+ ADD_UNARY_METHOD(NSToolbarItem, setLabel, 0)
+ ADD_UNARY_METHOD(NSToolbarItem, setPaletteLabel, 0)
+ ADD_UNARY_METHOD(NSToolbarItem, setToolTip, 0)
+
+ NEW_RECEIVER(NSProgress)
+ ADD_UNARY_METHOD(NSProgress, setLocalizedDescription, 0)
+ ADD_UNARY_METHOD(NSProgress, setLocalizedAdditionalDescription, 0)
+
+ NEW_RECEIVER(NSSegmentedCell)
+ IdentifierInfo *setLabelNSSegmentedCell[] = {&Ctx.Idents.get("setLabel"),
+ &Ctx.Idents.get("forSegment")};
+ ADD_METHOD(NSSegmentedCell, setLabelNSSegmentedCell, 2, 0)
+ IdentifierInfo *setToolTipNSSegmentedCell[] = {&Ctx.Idents.get("setToolTip"),
+ &Ctx.Idents.get("forSegment")};
+ ADD_METHOD(NSSegmentedCell, setToolTipNSSegmentedCell, 2, 0)
+
+ NEW_RECEIVER(NSUndoManager)
+ ADD_UNARY_METHOD(NSUndoManager, setActionName, 0)
+ ADD_UNARY_METHOD(NSUndoManager, undoMenuTitleForUndoActionName, 0)
+ ADD_UNARY_METHOD(NSUndoManager, redoMenuTitleForUndoActionName, 0)
+
+ NEW_RECEIVER(NSMenuItem)
+ IdentifierInfo *initWithTitleNSMenuItem[] = {
+ &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("action"),
+ &Ctx.Idents.get("keyEquivalent")};
+ ADD_METHOD(NSMenuItem, initWithTitleNSMenuItem, 3, 0)
+ ADD_UNARY_METHOD(NSMenuItem, setTitle, 0)
+ ADD_UNARY_METHOD(NSMenuItem, setToolTip, 0)
+
+ NEW_RECEIVER(NSPopUpButtonCell)
+ IdentifierInfo *initTextCellNSPopUpButtonCell[] = {
+ &Ctx.Idents.get("initTextCell"), &Ctx.Idents.get("pullsDown")};
+ ADD_METHOD(NSPopUpButtonCell, initTextCellNSPopUpButtonCell, 2, 0)
+ ADD_UNARY_METHOD(NSPopUpButtonCell, addItemWithTitle, 0)
+ IdentifierInfo *insertItemWithTitleNSPopUpButtonCell[] = {
+ &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("atIndex")};
+ ADD_METHOD(NSPopUpButtonCell, insertItemWithTitleNSPopUpButtonCell, 2, 0)
+ ADD_UNARY_METHOD(NSPopUpButtonCell, removeItemWithTitle, 0)
+ ADD_UNARY_METHOD(NSPopUpButtonCell, selectItemWithTitle, 0)
+ ADD_UNARY_METHOD(NSPopUpButtonCell, setTitle, 0)
+
+ NEW_RECEIVER(NSViewController)
+ ADD_UNARY_METHOD(NSViewController, setTitle, 0)
+
+ NEW_RECEIVER(NSMenu)
+ ADD_UNARY_METHOD(NSMenu, initWithTitle, 0)
+ IdentifierInfo *insertItemWithTitleNSMenu[] = {
+ &Ctx.Idents.get("insertItemWithTitle"), &Ctx.Idents.get("action"),
+ &Ctx.Idents.get("keyEquivalent"), &Ctx.Idents.get("atIndex")};
+ ADD_METHOD(NSMenu, insertItemWithTitleNSMenu, 4, 0)
+ IdentifierInfo *addItemWithTitleNSMenu[] = {
+ &Ctx.Idents.get("addItemWithTitle"), &Ctx.Idents.get("action"),
+ &Ctx.Idents.get("keyEquivalent")};
+ ADD_METHOD(NSMenu, addItemWithTitleNSMenu, 3, 0)
+ ADD_UNARY_METHOD(NSMenu, setTitle, 0)
+
+ NEW_RECEIVER(UIMutableUserNotificationAction)
+ ADD_UNARY_METHOD(UIMutableUserNotificationAction, setTitle, 0)
+
+ NEW_RECEIVER(NSForm)
+ ADD_UNARY_METHOD(NSForm, addEntry, 0)
+ IdentifierInfo *insertEntryNSForm[] = {&Ctx.Idents.get("insertEntry"),
+ &Ctx.Idents.get("atIndex")};
+ ADD_METHOD(NSForm, insertEntryNSForm, 2, 0)
+
+ NEW_RECEIVER(NSTextFieldCell)
+ ADD_UNARY_METHOD(NSTextFieldCell, setPlaceholderString, 0)
+
+ NEW_RECEIVER(NSUserNotificationAction)
+ IdentifierInfo *actionWithIdentifierNSUserNotificationAction[] = {
+ &Ctx.Idents.get("actionWithIdentifier"), &Ctx.Idents.get("title")};
+ ADD_METHOD(NSUserNotificationAction,
+ actionWithIdentifierNSUserNotificationAction, 2, 1)
+
+ NEW_RECEIVER(UITextField)
+ ADD_UNARY_METHOD(UITextField, setText, 0)
+ ADD_UNARY_METHOD(UITextField, setPlaceholder, 0)
+
+ NEW_RECEIVER(UIBarButtonItem)
+ IdentifierInfo *initWithTitleUIBarButtonItem[] = {
+ &Ctx.Idents.get("initWithTitle"), &Ctx.Idents.get("style"),
+ &Ctx.Idents.get("target"), &Ctx.Idents.get("action")};
+ ADD_METHOD(UIBarButtonItem, initWithTitleUIBarButtonItem, 4, 0)
+
+ NEW_RECEIVER(UIViewController)
+ ADD_UNARY_METHOD(UIViewController, setTitle, 0)
+
+ NEW_RECEIVER(UISegmentedControl)
+ IdentifierInfo *insertSegmentWithTitleUISegmentedControl[] = {
+ &Ctx.Idents.get("insertSegmentWithTitle"), &Ctx.Idents.get("atIndex"),
+ &Ctx.Idents.get("animated")};
+ ADD_METHOD(UISegmentedControl, insertSegmentWithTitleUISegmentedControl, 3, 0)
+ IdentifierInfo *setTitleUISegmentedControl[] = {
+ &Ctx.Idents.get("setTitle"), &Ctx.Idents.get("forSegmentAtIndex")};
+ ADD_METHOD(UISegmentedControl, setTitleUISegmentedControl, 2, 0)
+
+ NEW_RECEIVER(NSAccessibilityCustomRotorItemResult)
+ IdentifierInfo
+ *initWithItemLoadingTokenNSAccessibilityCustomRotorItemResult[] = {
+ &Ctx.Idents.get("initWithItemLoadingToken"),
+ &Ctx.Idents.get("customLabel")};
+ ADD_METHOD(NSAccessibilityCustomRotorItemResult,
+ initWithItemLoadingTokenNSAccessibilityCustomRotorItemResult, 2, 1)
+ ADD_UNARY_METHOD(NSAccessibilityCustomRotorItemResult, setCustomLabel, 0)
+
+ NEW_RECEIVER(UIContextualAction)
+ IdentifierInfo *contextualActionWithStyleUIContextualAction[] = {
+ &Ctx.Idents.get("contextualActionWithStyle"), &Ctx.Idents.get("title"),
+ &Ctx.Idents.get("handler")};
+ ADD_METHOD(UIContextualAction, contextualActionWithStyleUIContextualAction, 3,
+ 1)
+ ADD_UNARY_METHOD(UIContextualAction, setTitle, 0)
+
+ NEW_RECEIVER(NSAccessibilityCustomRotor)
+ IdentifierInfo *initWithLabelNSAccessibilityCustomRotor[] = {
+ &Ctx.Idents.get("initWithLabel"), &Ctx.Idents.get("itemSearchDelegate")};
+ ADD_METHOD(NSAccessibilityCustomRotor,
+ initWithLabelNSAccessibilityCustomRotor, 2, 0)
+ ADD_UNARY_METHOD(NSAccessibilityCustomRotor, setLabel, 0)
+
+ NEW_RECEIVER(NSWindowTab)
+ ADD_UNARY_METHOD(NSWindowTab, setTitle, 0)
+ ADD_UNARY_METHOD(NSWindowTab, setToolTip, 0)
+
+ NEW_RECEIVER(NSAccessibilityCustomAction)
+ IdentifierInfo *initWithNameNSAccessibilityCustomAction[] = {
+ &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("handler")};
+ ADD_METHOD(NSAccessibilityCustomAction,
+ initWithNameNSAccessibilityCustomAction, 2, 0)
+ IdentifierInfo *initWithNameTargetNSAccessibilityCustomAction[] = {
+ &Ctx.Idents.get("initWithName"), &Ctx.Idents.get("target"),
+ &Ctx.Idents.get("selector")};
+ ADD_METHOD(NSAccessibilityCustomAction,
+ initWithNameTargetNSAccessibilityCustomAction, 3, 0)
+ ADD_UNARY_METHOD(NSAccessibilityCustomAction, setName, 0)
+}
+
+#define LSF_INSERT(function_name) LSF.insert(&Ctx.Idents.get(function_name));
+#define LSM_INSERT_NULLARY(receiver, method_name) \
+ LSM.insert({&Ctx.Idents.get(receiver), Ctx.Selectors.getNullarySelector( \
+ &Ctx.Idents.get(method_name))});
+#define LSM_INSERT_UNARY(receiver, method_name) \
+ LSM.insert({&Ctx.Idents.get(receiver), \
+ Ctx.Selectors.getUnarySelector(&Ctx.Idents.get(method_name))});
+#define LSM_INSERT_SELECTOR(receiver, method_list, arguments) \
+ LSM.insert({&Ctx.Idents.get(receiver), \
+ Ctx.Selectors.getSelector(arguments, method_list)});
+
+/// Initializes a list of methods and C functions that return a localized string
+void NonLocalizedStringChecker::initLocStringsMethods(ASTContext &Ctx) const {
+ if (!LSM.empty())
+ return;
+
+ IdentifierInfo *LocalizedStringMacro[] = {
+ &Ctx.Idents.get("localizedStringForKey"), &Ctx.Idents.get("value"),
+ &Ctx.Idents.get("table")};
+ LSM_INSERT_SELECTOR("NSBundle", LocalizedStringMacro, 3)
+ LSM_INSERT_UNARY("NSDateFormatter", "stringFromDate")
+ IdentifierInfo *LocalizedStringFromDate[] = {
+ &Ctx.Idents.get("localizedStringFromDate"), &Ctx.Idents.get("dateStyle"),
+ &Ctx.Idents.get("timeStyle")};
+ LSM_INSERT_SELECTOR("NSDateFormatter", LocalizedStringFromDate, 3)
+ LSM_INSERT_UNARY("NSNumberFormatter", "stringFromNumber")
+ LSM_INSERT_NULLARY("UITextField", "text")
+ LSM_INSERT_NULLARY("UITextView", "text")
+ LSM_INSERT_NULLARY("UILabel", "text")
+
+ LSF_INSERT("CFDateFormatterCreateStringWithDate");
+ LSF_INSERT("CFDateFormatterCreateStringWithAbsoluteTime");
+ LSF_INSERT("CFNumberFormatterCreateStringWithNumber");
+}
+
+/// Checks to see if the method / function declaration includes
+/// __attribute__((annotate("returns_localized_nsstring")))
+bool NonLocalizedStringChecker::isAnnotatedAsReturningLocalized(
+ const Decl *D) const {
+ if (!D)
+ return false;
+ return std::any_of(
+ D->specific_attr_begin<AnnotateAttr>(),
+ D->specific_attr_end<AnnotateAttr>(), [](const AnnotateAttr *Ann) {
+ return Ann->getAnnotation() == "returns_localized_nsstring";
+ });
+}
+
+/// Checks to see if the method / function declaration includes
+/// __attribute__((annotate("takes_localized_nsstring")))
+bool NonLocalizedStringChecker::isAnnotatedAsTakingLocalized(
+ const Decl *D) const {
+ if (!D)
+ return false;
+ return std::any_of(
+ D->specific_attr_begin<AnnotateAttr>(),
+ D->specific_attr_end<AnnotateAttr>(), [](const AnnotateAttr *Ann) {
+ return Ann->getAnnotation() == "takes_localized_nsstring";
+ });
+}
+
+/// Returns true if the given SVal is marked as Localized in the program state
+bool NonLocalizedStringChecker::hasLocalizedState(SVal S,
+ CheckerContext &C) const {
+ const MemRegion *mt = S.getAsRegion();
+ if (mt) {
+ const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt);
+ if (LS && LS->isLocalized())
+ return true;
+ }
+ return false;
+}
+
+/// Returns true if the given SVal is marked as NonLocalized in the program
+/// state
+bool NonLocalizedStringChecker::hasNonLocalizedState(SVal S,
+ CheckerContext &C) const {
+ const MemRegion *mt = S.getAsRegion();
+ if (mt) {
+ const LocalizedState *LS = C.getState()->get<LocalizedMemMap>(mt);
+ if (LS && LS->isNonLocalized())
+ return true;
+ }
+ return false;
+}
+
+/// Marks the given SVal as Localized in the program state
+void NonLocalizedStringChecker::setLocalizedState(const SVal S,
+ CheckerContext &C) const {
+ const MemRegion *mt = S.getAsRegion();
+ if (mt) {
+ ProgramStateRef State =
+ C.getState()->set<LocalizedMemMap>(mt, LocalizedState::getLocalized());
+ C.addTransition(State);
+ }
+}
+
+/// Marks the given SVal as NonLocalized in the program state
+void NonLocalizedStringChecker::setNonLocalizedState(const SVal S,
+ CheckerContext &C) const {
+ const MemRegion *mt = S.getAsRegion();
+ if (mt) {
+ ProgramStateRef State = C.getState()->set<LocalizedMemMap>(
+ mt, LocalizedState::getNonLocalized());
+ C.addTransition(State);
+ }
+}
+
+
+static bool isDebuggingName(std::string name) {
+ return StringRef(name).lower().find("debug") != StringRef::npos;
+}
+
+/// Returns true when, heuristically, the analyzer may be analyzing debugging
+/// code. We use this to suppress localization diagnostics in un-localized user
+/// interfaces that are only used for debugging and are therefore not user
+/// facing.
+static bool isDebuggingContext(CheckerContext &C) {
+ const Decl *D = C.getCurrentAnalysisDeclContext()->getDecl();
+ if (!D)
+ return false;
+
+ if (auto *ND = dyn_cast<NamedDecl>(D)) {
+ if (isDebuggingName(ND->getNameAsString()))
+ return true;
+ }
+
+ const DeclContext *DC = D->getDeclContext();
+
+ if (auto *CD = dyn_cast<ObjCContainerDecl>(DC)) {
+ if (isDebuggingName(CD->getNameAsString()))
+ return true;
+ }
+
+ return false;
+}
+
+
+/// Reports a localization error for the passed in method call and SVal
+void NonLocalizedStringChecker::reportLocalizationError(
+ SVal S, const CallEvent &M, CheckerContext &C, int argumentNumber) const {
+
+ // Don't warn about localization errors in classes and methods that
+ // may be debug code.
+ if (isDebuggingContext(C))
+ return;
+
+ ExplodedNode *ErrNode = C.getPredecessor();
+ static CheckerProgramPointTag Tag("NonLocalizedStringChecker",
+ "UnlocalizedString");
+ ErrNode = C.addTransition(C.getState(), C.getPredecessor(), &Tag);
+
+ if (!ErrNode)
+ return;
+
+ // Generate the bug report.
+ std::unique_ptr<BugReport> R(new BugReport(
+ *BT, "User-facing text should use localized string macro", ErrNode));
+ if (argumentNumber) {
+ R->addRange(M.getArgExpr(argumentNumber - 1)->getSourceRange());
+ } else {
+ R->addRange(M.getSourceRange());
+ }
+ R->markInteresting(S);
+
+ const MemRegion *StringRegion = S.getAsRegion();
+ if (StringRegion)
+ R->addVisitor(llvm::make_unique<NonLocalizedStringBRVisitor>(StringRegion));
+
+ C.emitReport(std::move(R));
+}
+
+/// Returns the argument number requiring localized string if it exists
+/// otherwise, returns -1
+int NonLocalizedStringChecker::getLocalizedArgumentForSelector(
+ const IdentifierInfo *Receiver, Selector S) const {
+ auto method = UIMethods.find(Receiver);
+
+ if (method == UIMethods.end())
+ return -1;
+
+ auto argumentIterator = method->getSecond().find(S);
+
+ if (argumentIterator == method->getSecond().end())
+ return -1;
+
+ int argumentNumber = argumentIterator->getSecond();
+ return argumentNumber;
+}
+
+/// Check if the string being passed in has NonLocalized state
+void NonLocalizedStringChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
+ CheckerContext &C) const {
+ initUIMethods(C.getASTContext());
+
+ const ObjCInterfaceDecl *OD = msg.getReceiverInterface();
+ if (!OD)
+ return;
+ const IdentifierInfo *odInfo = OD->getIdentifier();
+
+ Selector S = msg.getSelector();
+
+ std::string SelectorString = S.getAsString();
+ StringRef SelectorName = SelectorString;
+ assert(!SelectorName.empty());
+
+ if (odInfo->isStr("NSString")) {
+ // Handle the case where the receiver is an NSString
+ // These special NSString methods draw to the screen
+
+ if (!(SelectorName.startswith("drawAtPoint") ||
+ SelectorName.startswith("drawInRect") ||
+ SelectorName.startswith("drawWithRect")))
+ return;
+
+ SVal svTitle = msg.getReceiverSVal();
+
+ bool isNonLocalized = hasNonLocalizedState(svTitle, C);
+
+ if (isNonLocalized) {
+ reportLocalizationError(svTitle, msg, C);
+ }
+ }
+
+ int argumentNumber = getLocalizedArgumentForSelector(odInfo, S);
+ // Go up each hierarchy of superclasses and their protocols
+ while (argumentNumber < 0 && OD->getSuperClass() != nullptr) {
+ for (const auto *P : OD->all_referenced_protocols()) {
+ argumentNumber = getLocalizedArgumentForSelector(P->getIdentifier(), S);
+ if (argumentNumber >= 0)
+ break;
+ }
+ if (argumentNumber < 0) {
+ OD = OD->getSuperClass();
+ argumentNumber = getLocalizedArgumentForSelector(OD->getIdentifier(), S);
+ }
+ }
+
+ if (argumentNumber < 0) { // There was no match in UIMethods
+ if (const Decl *D = msg.getDecl()) {
+ if (const ObjCMethodDecl *OMD = dyn_cast_or_null<ObjCMethodDecl>(D)) {
+ auto formals = OMD->parameters();
+ for (unsigned i = 0, ei = formals.size(); i != ei; ++i) {
+ if (isAnnotatedAsTakingLocalized(formals[i])) {
+ argumentNumber = i;
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ if (argumentNumber < 0) // Still no match
+ return;
+
+ SVal svTitle = msg.getArgSVal(argumentNumber);
+
+ if (const ObjCStringRegion *SR =
+ dyn_cast_or_null<ObjCStringRegion>(svTitle.getAsRegion())) {
+ StringRef stringValue =
+ SR->getObjCStringLiteral()->getString()->getString();
+ if ((stringValue.trim().size() == 0 && stringValue.size() > 0) ||
+ stringValue.empty())
+ return;
+ if (!IsAggressive && llvm::sys::unicode::columnWidthUTF8(stringValue) < 2)
+ return;
+ }
+
+ bool isNonLocalized = hasNonLocalizedState(svTitle, C);
+
+ if (isNonLocalized) {
+ reportLocalizationError(svTitle, msg, C, argumentNumber + 1);
+ }
+}
+
+void NonLocalizedStringChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const Decl *D = Call.getDecl();
+ if (D && isa<FunctionDecl>(D)) {
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(D);
+ auto formals = FD->parameters();
+ for (unsigned i = 0,
+ ei = std::min(unsigned(formals.size()), Call.getNumArgs());
+ i != ei; ++i) {
+ if (isAnnotatedAsTakingLocalized(formals[i])) {
+ auto actual = Call.getArgSVal(i);
+ if (hasNonLocalizedState(actual, C)) {
+ reportLocalizationError(actual, Call, C, i + 1);
+ }
+ }
+ }
+ }
+}
+
+static inline bool isNSStringType(QualType T, ASTContext &Ctx) {
+
+ const ObjCObjectPointerType *PT = T->getAs<ObjCObjectPointerType>();
+ if (!PT)
+ return false;
+
+ ObjCInterfaceDecl *Cls = PT->getObjectType()->getInterface();
+ if (!Cls)
+ return false;
+
+ IdentifierInfo *ClsName = Cls->getIdentifier();
+
+ // FIXME: Should we walk the chain of classes?
+ return ClsName == &Ctx.Idents.get("NSString") ||
+ ClsName == &Ctx.Idents.get("NSMutableString");
+}
+
+/// Marks a string being returned by any call as localized
+/// if it is in LocStringFunctions (LSF) or the function is annotated.
+/// Otherwise, we mark it as NonLocalized (Aggressive) or
+/// NonLocalized only if it is not backed by a SymRegion (Non-Aggressive),
+/// basically leaving only string literals as NonLocalized.
+void NonLocalizedStringChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ initLocStringsMethods(C.getASTContext());
+
+ if (!Call.getOriginExpr())
+ return;
+
+ // Anything that takes in a localized NSString as an argument
+ // and returns an NSString will be assumed to be returning a
+ // localized NSString. (Counter: Incorrectly combining two LocalizedStrings)
+ const QualType RT = Call.getResultType();
+ if (isNSStringType(RT, C.getASTContext())) {
+ for (unsigned i = 0; i < Call.getNumArgs(); ++i) {
+ SVal argValue = Call.getArgSVal(i);
+ if (hasLocalizedState(argValue, C)) {
+ SVal sv = Call.getReturnValue();
+ setLocalizedState(sv, C);
+ return;
+ }
+ }
+ }
+
+ const Decl *D = Call.getDecl();
+ if (!D)
+ return;
+
+ const IdentifierInfo *Identifier = Call.getCalleeIdentifier();
+
+ SVal sv = Call.getReturnValue();
+ if (isAnnotatedAsReturningLocalized(D) || LSF.count(Identifier) != 0) {
+ setLocalizedState(sv, C);
+ } else if (isNSStringType(RT, C.getASTContext()) &&
+ !hasLocalizedState(sv, C)) {
+ if (IsAggressive) {
+ setNonLocalizedState(sv, C);
+ } else {
+ const SymbolicRegion *SymReg =
+ dyn_cast_or_null<SymbolicRegion>(sv.getAsRegion());
+ if (!SymReg)
+ setNonLocalizedState(sv, C);
+ }
+ }
+}
+
+/// Marks a string being returned by an ObjC method as localized
+/// if it is in LocStringMethods or the method is annotated
+void NonLocalizedStringChecker::checkPostObjCMessage(const ObjCMethodCall &msg,
+ CheckerContext &C) const {
+ initLocStringsMethods(C.getASTContext());
+
+ if (!msg.isInstanceMessage())
+ return;
+
+ const ObjCInterfaceDecl *OD = msg.getReceiverInterface();
+ if (!OD)
+ return;
+ const IdentifierInfo *odInfo = OD->getIdentifier();
+
+ Selector S = msg.getSelector();
+ std::string SelectorName = S.getAsString();
+
+ std::pair<const IdentifierInfo *, Selector> MethodDescription = {odInfo, S};
+
+ if (LSM.count(MethodDescription) ||
+ isAnnotatedAsReturningLocalized(msg.getDecl())) {
+ SVal sv = msg.getReturnValue();
+ setLocalizedState(sv, C);
+ }
+}
+
+/// Marks all empty string literals as localized
+void NonLocalizedStringChecker::checkPostStmt(const ObjCStringLiteral *SL,
+ CheckerContext &C) const {
+ SVal sv = C.getSVal(SL);
+ setNonLocalizedState(sv, C);
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+NonLocalizedStringBRVisitor::VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC, BugReport &BR) {
+ if (Satisfied)
+ return nullptr;
+
+ Optional<StmtPoint> Point = Succ->getLocation().getAs<StmtPoint>();
+ if (!Point.hasValue())
+ return nullptr;
+
+ auto *LiteralExpr = dyn_cast<ObjCStringLiteral>(Point->getStmt());
+ if (!LiteralExpr)
+ return nullptr;
+
+ SVal LiteralSVal = Succ->getSVal(LiteralExpr);
+ if (LiteralSVal.getAsRegion() != NonLocalizedString)
+ return nullptr;
+
+ Satisfied = true;
+
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(*Point, BRC.getSourceManager());
+
+ if (!L.isValid() || !L.asLocation().isValid())
+ return nullptr;
+
+ auto Piece = std::make_shared<PathDiagnosticEventPiece>(
+ L, "Non-localized string literal here");
+ Piece->addRange(LiteralExpr->getSourceRange());
+
+ return std::move(Piece);
+}
+
+namespace {
+class EmptyLocalizationContextChecker
+ : public Checker<check::ASTDecl<ObjCImplementationDecl>> {
+
+ // A helper class, which walks the AST
+ class MethodCrawler : public ConstStmtVisitor<MethodCrawler> {
+ const ObjCMethodDecl *MD;
+ BugReporter &BR;
+ AnalysisManager &Mgr;
+ const CheckerBase *Checker;
+ LocationOrAnalysisDeclContext DCtx;
+
+ public:
+ MethodCrawler(const ObjCMethodDecl *InMD, BugReporter &InBR,
+ const CheckerBase *Checker, AnalysisManager &InMgr,
+ AnalysisDeclContext *InDCtx)
+ : MD(InMD), BR(InBR), Mgr(InMgr), Checker(Checker), DCtx(InDCtx) {}
+
+ void VisitStmt(const Stmt *S) { VisitChildren(S); }
+
+ void VisitObjCMessageExpr(const ObjCMessageExpr *ME);
+
+ void reportEmptyContextError(const ObjCMessageExpr *M) const;
+
+ void VisitChildren(const Stmt *S) {
+ for (const Stmt *Child : S->children()) {
+ if (Child)
+ this->Visit(Child);
+ }
+ }
+ };
+
+public:
+ void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const;
+};
+} // end anonymous namespace
+
+void EmptyLocalizationContextChecker::checkASTDecl(
+ const ObjCImplementationDecl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const {
+
+ for (const ObjCMethodDecl *M : D->methods()) {
+ AnalysisDeclContext *DCtx = Mgr.getAnalysisDeclContext(M);
+
+ const Stmt *Body = M->getBody();
+ assert(Body);
+
+ MethodCrawler MC(M->getCanonicalDecl(), BR, this, Mgr, DCtx);
+ MC.VisitStmt(Body);
+ }
+}
+
+/// This check attempts to match these macros, assuming they are defined as
+/// follows:
+///
+/// #define NSLocalizedString(key, comment) \
+/// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:nil]
+/// #define NSLocalizedStringFromTable(key, tbl, comment) \
+/// [[NSBundle mainBundle] localizedStringForKey:(key) value:@"" table:(tbl)]
+/// #define NSLocalizedStringFromTableInBundle(key, tbl, bundle, comment) \
+/// [bundle localizedStringForKey:(key) value:@"" table:(tbl)]
+/// #define NSLocalizedStringWithDefaultValue(key, tbl, bundle, val, comment)
+///
+/// We cannot use the path sensitive check because the macro argument we are
+/// checking for (comment) is not used and thus not present in the AST,
+/// so we use Lexer on the original macro call and retrieve the value of
+/// the comment. If it's empty or nil, we raise a warning.
+void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr(
+ const ObjCMessageExpr *ME) {
+
+ // FIXME: We may be able to use PPCallbacks to check for empty context
+ // comments as part of preprocessing and avoid this re-lexing hack.
+ const ObjCInterfaceDecl *OD = ME->getReceiverInterface();
+ if (!OD)
+ return;
+
+ const IdentifierInfo *odInfo = OD->getIdentifier();
+
+ if (!(odInfo->isStr("NSBundle") &&
+ ME->getSelector().getAsString() ==
+ "localizedStringForKey:value:table:")) {
+ return;
+ }
+
+ SourceRange R = ME->getSourceRange();
+ if (!R.getBegin().isMacroID())
+ return;
+
+ // getImmediateMacroCallerLoc gets the location of the immediate macro
+ // caller, one level up the stack toward the initial macro typed into the
+ // source, so SL should point to the NSLocalizedString macro.
+ SourceLocation SL =
+ Mgr.getSourceManager().getImmediateMacroCallerLoc(R.getBegin());
+ std::pair<FileID, unsigned> SLInfo =
+ Mgr.getSourceManager().getDecomposedLoc(SL);
+
+ SrcMgr::SLocEntry SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first);
+
+ // If NSLocalizedString macro is wrapped in another macro, we need to
+ // unwrap the expansion until we get to the NSLocalizedStringMacro.
+ while (SE.isExpansion()) {
+ SL = SE.getExpansion().getSpellingLoc();
+ SLInfo = Mgr.getSourceManager().getDecomposedLoc(SL);
+ SE = Mgr.getSourceManager().getSLocEntry(SLInfo.first);
+ }
+
+ bool Invalid = false;
+ llvm::MemoryBuffer *BF =
+ Mgr.getSourceManager().getBuffer(SLInfo.first, SL, &Invalid);
+ if (Invalid)
+ return;
+
+ Lexer TheLexer(SL, LangOptions(), BF->getBufferStart(),
+ BF->getBufferStart() + SLInfo.second, BF->getBufferEnd());
+
+ Token I;
+ Token Result; // This will hold the token just before the last ')'
+ int p_count = 0; // This is for parenthesis matching
+ while (!TheLexer.LexFromRawLexer(I)) {
+ if (I.getKind() == tok::l_paren)
+ ++p_count;
+ if (I.getKind() == tok::r_paren) {
+ if (p_count == 1)
+ break;
+ --p_count;
+ }
+ Result = I;
+ }
+
+ if (isAnyIdentifier(Result.getKind())) {
+ if (Result.getRawIdentifier().equals("nil")) {
+ reportEmptyContextError(ME);
+ return;
+ }
+ }
+
+ if (!isStringLiteral(Result.getKind()))
+ return;
+
+ StringRef Comment =
+ StringRef(Result.getLiteralData(), Result.getLength()).trim('"');
+
+ if ((Comment.trim().size() == 0 && Comment.size() > 0) || // Is Whitespace
+ Comment.empty()) {
+ reportEmptyContextError(ME);
+ }
+}
+
+void EmptyLocalizationContextChecker::MethodCrawler::reportEmptyContextError(
+ const ObjCMessageExpr *ME) const {
+ // Generate the bug report.
+ BR.EmitBasicReport(MD, Checker, "Context Missing",
+ "Localizability Issue (Apple)",
+ "Localized string macro should include a non-empty "
+ "comment for translators",
+ PathDiagnosticLocation(ME, BR.getSourceManager(), DCtx));
+}
+
+namespace {
+class PluralMisuseChecker : public Checker<check::ASTCodeBody> {
+
+ // A helper class, which walks the AST
+ class MethodCrawler : public RecursiveASTVisitor<MethodCrawler> {
+ BugReporter &BR;
+ const CheckerBase *Checker;
+ AnalysisDeclContext *AC;
+
+ // This functions like a stack. We push on any IfStmt or
+ // ConditionalOperator that matches the condition
+ // and pop it off when we leave that statement
+ llvm::SmallVector<const clang::Stmt *, 8> MatchingStatements;
+ // This is true when we are the direct-child of a
+ // matching statement
+ bool InMatchingStatement = false;
+
+ public:
+ explicit MethodCrawler(BugReporter &InBR, const CheckerBase *Checker,
+ AnalysisDeclContext *InAC)
+ : BR(InBR), Checker(Checker), AC(InAC) {}
+
+ bool VisitIfStmt(const IfStmt *I);
+ bool EndVisitIfStmt(IfStmt *I);
+ bool TraverseIfStmt(IfStmt *x);
+ bool VisitConditionalOperator(const ConditionalOperator *C);
+ bool TraverseConditionalOperator(ConditionalOperator *C);
+ bool VisitCallExpr(const CallExpr *CE);
+ bool VisitObjCMessageExpr(const ObjCMessageExpr *ME);
+
+ private:
+ void reportPluralMisuseError(const Stmt *S) const;
+ bool isCheckingPlurality(const Expr *E) const;
+ };
+
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const {
+ MethodCrawler Visitor(BR, this, Mgr.getAnalysisDeclContext(D));
+ Visitor.TraverseDecl(const_cast<Decl *>(D));
+ }
+};
+} // end anonymous namespace
+
+// Checks the condition of the IfStmt and returns true if one
+// of the following heuristics are met:
+// 1) The conidtion is a variable with "singular" or "plural" in the name
+// 2) The condition is a binary operator with 1 or 2 on the right-hand side
+bool PluralMisuseChecker::MethodCrawler::isCheckingPlurality(
+ const Expr *Condition) const {
+ const BinaryOperator *BO = nullptr;
+ // Accounts for when a VarDecl represents a BinaryOperator
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Condition)) {
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
+ const Expr *InitExpr = VD->getInit();
+ if (InitExpr) {
+ if (const BinaryOperator *B =
+ dyn_cast<BinaryOperator>(InitExpr->IgnoreParenImpCasts())) {
+ BO = B;
+ }
+ }
+ if (VD->getName().lower().find("plural") != StringRef::npos ||
+ VD->getName().lower().find("singular") != StringRef::npos) {
+ return true;
+ }
+ }
+ } else if (const BinaryOperator *B = dyn_cast<BinaryOperator>(Condition)) {
+ BO = B;
+ }
+
+ if (BO == nullptr)
+ return false;
+
+ if (IntegerLiteral *IL = dyn_cast_or_null<IntegerLiteral>(
+ BO->getRHS()->IgnoreParenImpCasts())) {
+ llvm::APInt Value = IL->getValue();
+ if (Value == 1 || Value == 2) {
+ return true;
+ }
+ }
+ return false;
+}
+
+// A CallExpr with "LOC" in its identifier that takes in a string literal
+// has been shown to almost always be a function that returns a localized
+// string. Raise a diagnostic when this is in a statement that matches
+// the condition.
+bool PluralMisuseChecker::MethodCrawler::VisitCallExpr(const CallExpr *CE) {
+ if (InMatchingStatement) {
+ if (const FunctionDecl *FD = CE->getDirectCallee()) {
+ std::string NormalizedName =
+ StringRef(FD->getNameInfo().getAsString()).lower();
+ if (NormalizedName.find("loc") != std::string::npos) {
+ for (const Expr *Arg : CE->arguments()) {
+ if (isa<ObjCStringLiteral>(Arg))
+ reportPluralMisuseError(CE);
+ }
+ }
+ }
+ }
+ return true;
+}
+
+// The other case is for NSLocalizedString which also returns
+// a localized string. It's a macro for the ObjCMessageExpr
+// [NSBundle localizedStringForKey:value:table:] Raise a
+// diagnostic when this is in a statement that matches
+// the condition.
+bool PluralMisuseChecker::MethodCrawler::VisitObjCMessageExpr(
+ const ObjCMessageExpr *ME) {
+ const ObjCInterfaceDecl *OD = ME->getReceiverInterface();
+ if (!OD)
+ return true;
+
+ const IdentifierInfo *odInfo = OD->getIdentifier();
+
+ if (odInfo->isStr("NSBundle") &&
+ ME->getSelector().getAsString() == "localizedStringForKey:value:table:") {
+ if (InMatchingStatement) {
+ reportPluralMisuseError(ME);
+ }
+ }
+ return true;
+}
+
+/// Override TraverseIfStmt so we know when we are done traversing an IfStmt
+bool PluralMisuseChecker::MethodCrawler::TraverseIfStmt(IfStmt *I) {
+ RecursiveASTVisitor<MethodCrawler>::TraverseIfStmt(I);
+ return EndVisitIfStmt(I);
+}
+
+// EndVisit callbacks are not provided by the RecursiveASTVisitor
+// so we override TraverseIfStmt and make a call to EndVisitIfStmt
+// after traversing the IfStmt
+bool PluralMisuseChecker::MethodCrawler::EndVisitIfStmt(IfStmt *I) {
+ MatchingStatements.pop_back();
+ if (!MatchingStatements.empty()) {
+ if (MatchingStatements.back() != nullptr) {
+ InMatchingStatement = true;
+ return true;
+ }
+ }
+ InMatchingStatement = false;
+ return true;
+}
+
+bool PluralMisuseChecker::MethodCrawler::VisitIfStmt(const IfStmt *I) {
+ const Expr *Condition = I->getCond()->IgnoreParenImpCasts();
+ if (isCheckingPlurality(Condition)) {
+ MatchingStatements.push_back(I);
+ InMatchingStatement = true;
+ } else {
+ MatchingStatements.push_back(nullptr);
+ InMatchingStatement = false;
+ }
+
+ return true;
+}
+
+// Preliminary support for conditional operators.
+bool PluralMisuseChecker::MethodCrawler::TraverseConditionalOperator(
+ ConditionalOperator *C) {
+ RecursiveASTVisitor<MethodCrawler>::TraverseConditionalOperator(C);
+ MatchingStatements.pop_back();
+ if (!MatchingStatements.empty()) {
+ if (MatchingStatements.back() != nullptr)
+ InMatchingStatement = true;
+ else
+ InMatchingStatement = false;
+ } else {
+ InMatchingStatement = false;
+ }
+ return true;
+}
+
+bool PluralMisuseChecker::MethodCrawler::VisitConditionalOperator(
+ const ConditionalOperator *C) {
+ const Expr *Condition = C->getCond()->IgnoreParenImpCasts();
+ if (isCheckingPlurality(Condition)) {
+ MatchingStatements.push_back(C);
+ InMatchingStatement = true;
+ } else {
+ MatchingStatements.push_back(nullptr);
+ InMatchingStatement = false;
+ }
+ return true;
+}
+
+void PluralMisuseChecker::MethodCrawler::reportPluralMisuseError(
+ const Stmt *S) const {
+ // Generate the bug report.
+ BR.EmitBasicReport(AC->getDecl(), Checker, "Plural Misuse",
+ "Localizability Issue (Apple)",
+ "Plural cases are not supported across all languages. "
+ "Use a .stringsdict file instead",
+ PathDiagnosticLocation(S, BR.getSourceManager(), AC));
+}
+
+//===----------------------------------------------------------------------===//
+// Checker registration.
+//===----------------------------------------------------------------------===//
+
+void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) {
+ NonLocalizedStringChecker *checker =
+ mgr.registerChecker<NonLocalizedStringChecker>();
+ checker->IsAggressive =
+ mgr.getAnalyzerOptions().getCheckerBooleanOption("AggressiveReport",
+ false, checker);
+}
+
+void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) {
+ mgr.registerChecker<EmptyLocalizationContextChecker>();
+}
+
+void ento::registerPluralMisuseChecker(CheckerManager &mgr) {
+ mgr.registerChecker<PluralMisuseChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
new file mode 100644
index 000000000000..fb9bccebe465
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
@@ -0,0 +1,116 @@
+//===-- MPIBugReporter.cpp - bug reporter -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines prefabricated reports which are emitted in
+/// case of MPI related bugs, detected by path-sensitive analysis.
+///
+//===----------------------------------------------------------------------===//
+
+#include "MPIBugReporter.h"
+#include "MPIChecker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+
+namespace clang {
+namespace ento {
+namespace mpi {
+
+void MPIBugReporter::reportDoubleNonblocking(
+ const CallEvent &MPICallEvent, const ento::mpi::Request &Req,
+ const MemRegion *const RequestRegion,
+ const ExplodedNode *const ExplNode,
+ BugReporter &BReporter) const {
+
+ std::string ErrorText;
+ ErrorText = "Double nonblocking on request " +
+ RequestRegion->getDescriptiveName() + ". ";
+
+ auto Report = llvm::make_unique<BugReport>(*DoubleNonblockingBugType,
+ ErrorText, ExplNode);
+
+ Report->addRange(MPICallEvent.getSourceRange());
+ SourceRange Range = RequestRegion->sourceRange();
+
+ if (Range.isValid())
+ Report->addRange(Range);
+
+ Report->addVisitor(llvm::make_unique<RequestNodeVisitor>(
+ RequestRegion, "Request is previously used by nonblocking call here. "));
+ Report->markInteresting(RequestRegion);
+
+ BReporter.emitReport(std::move(Report));
+}
+
+void MPIBugReporter::reportMissingWait(
+ const ento::mpi::Request &Req, const MemRegion *const RequestRegion,
+ const ExplodedNode *const ExplNode,
+ BugReporter &BReporter) const {
+ std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() +
+ " has no matching wait. "};
+
+ auto Report =
+ llvm::make_unique<BugReport>(*MissingWaitBugType, ErrorText, ExplNode);
+
+ SourceRange Range = RequestRegion->sourceRange();
+ if (Range.isValid())
+ Report->addRange(Range);
+ Report->addVisitor(llvm::make_unique<RequestNodeVisitor>(
+ RequestRegion, "Request is previously used by nonblocking call here. "));
+ Report->markInteresting(RequestRegion);
+
+ BReporter.emitReport(std::move(Report));
+}
+
+void MPIBugReporter::reportUnmatchedWait(
+ const CallEvent &CE, const clang::ento::MemRegion *const RequestRegion,
+ const ExplodedNode *const ExplNode,
+ BugReporter &BReporter) const {
+ std::string ErrorText{"Request " + RequestRegion->getDescriptiveName() +
+ " has no matching nonblocking call. "};
+
+ auto Report =
+ llvm::make_unique<BugReport>(*UnmatchedWaitBugType, ErrorText, ExplNode);
+
+ Report->addRange(CE.getSourceRange());
+ SourceRange Range = RequestRegion->sourceRange();
+ if (Range.isValid())
+ Report->addRange(Range);
+
+ BReporter.emitReport(std::move(Report));
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+MPIBugReporter::RequestNodeVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+
+ if (IsNodeFound)
+ return nullptr;
+
+ const Request *const Req = N->getState()->get<RequestMap>(RequestRegion);
+ const Request *const PrevReq =
+ N->getFirstPred()->getState()->get<RequestMap>(RequestRegion);
+
+ // Check if request was previously unused or in a different state.
+ if ((Req && !PrevReq) || (Req->CurrentState != PrevReq->CurrentState)) {
+ IsNodeFound = true;
+
+ ProgramPoint P = N->getFirstPred()->getLocation();
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(P, BRC.getSourceManager());
+
+ return std::make_shared<PathDiagnosticEventPiece>(L, ErrorText);
+ }
+
+ return nullptr;
+}
+
+} // end of namespace: mpi
+} // end of namespace: ento
+} // end of namespace: clang
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
new file mode 100644
index 000000000000..32fcb07e3371
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
@@ -0,0 +1,108 @@
+//===-- MPIBugReporter.h - bug reporter -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines prefabricated reports which are emitted in
+/// case of MPI related bugs, detected by path-sensitive analysis.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPIBUGREPORTER_H
+
+#include "MPITypes.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+
+namespace clang {
+namespace ento {
+namespace mpi {
+
+class MPIBugReporter {
+public:
+ MPIBugReporter(const CheckerBase &CB) {
+ UnmatchedWaitBugType.reset(new BugType(&CB, "Unmatched wait", MPIError));
+ DoubleNonblockingBugType.reset(
+ new BugType(&CB, "Double nonblocking", MPIError));
+ MissingWaitBugType.reset(new BugType(&CB, "Missing wait", MPIError));
+ }
+
+ /// Report duplicate request use by nonblocking calls without intermediate
+ /// wait.
+ ///
+ /// \param MPICallEvent MPI call that caused the double nonblocking
+ /// \param Req request that was used by two nonblocking calls in sequence
+ /// \param RequestRegion memory region of the request
+ /// \param ExplNode node in the graph the bug appeared at
+ /// \param BReporter bug reporter for current context
+ void reportDoubleNonblocking(const CallEvent &MPICallEvent,
+ const Request &Req,
+ const MemRegion *const RequestRegion,
+ const ExplodedNode *const ExplNode,
+ BugReporter &BReporter) const;
+
+ /// Report a missing wait for a nonblocking call.
+ ///
+ /// \param Req request that is not matched by a wait
+ /// \param RequestRegion memory region of the request
+ /// \param ExplNode node in the graph the bug appeared at
+ /// \param BReporter bug reporter for current context
+ void reportMissingWait(const Request &Req,
+ const MemRegion *const RequestRegion,
+ const ExplodedNode *const ExplNode,
+ BugReporter &BReporter) const;
+
+ /// Report a wait on a request that has not been used at all before.
+ ///
+ /// \param CE wait call that uses the request
+ /// \param RequestRegion memory region of the request
+ /// \param ExplNode node in the graph the bug appeared at
+ /// \param BReporter bug reporter for current context
+ void reportUnmatchedWait(const CallEvent &CE,
+ const MemRegion *const RequestRegion,
+ const ExplodedNode *const ExplNode,
+ BugReporter &BReporter) const;
+
+private:
+ const std::string MPIError = "MPI Error";
+
+ // path-sensitive bug types
+ std::unique_ptr<BugType> UnmatchedWaitBugType;
+ std::unique_ptr<BugType> MissingWaitBugType;
+ std::unique_ptr<BugType> DoubleNonblockingBugType;
+
+ /// Bug visitor class to find the node where the request region was previously
+ /// used in order to include it into the BugReport path.
+ class RequestNodeVisitor : public BugReporterVisitor {
+ public:
+ RequestNodeVisitor(const MemRegion *const MemoryRegion,
+ const std::string &ErrText)
+ : RequestRegion(MemoryRegion), ErrorText(ErrText) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int X = 0;
+ ID.AddPointer(&X);
+ ID.AddPointer(RequestRegion);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ private:
+ const MemRegion *const RequestRegion;
+ bool IsNodeFound = false;
+ std::string ErrorText;
+ };
+};
+
+} // end of namespace: mpi
+} // end of namespace: ento
+} // end of namespace: clang
+
+#endif
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp
new file mode 100644
index 000000000000..28c6898f7947
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp
@@ -0,0 +1,190 @@
+//===-- MPIChecker.cpp - Checker Entry Point Class --------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines the main class of MPI-Checker which serves as an entry
+/// point. It is created once for each translation unit analysed.
+/// The checker defines path-sensitive checks, to verify correct usage of the
+/// MPI API.
+///
+//===----------------------------------------------------------------------===//
+
+#include "MPIChecker.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+
+namespace clang {
+namespace ento {
+namespace mpi {
+
+void MPIChecker::checkDoubleNonblocking(const CallEvent &PreCallEvent,
+ CheckerContext &Ctx) const {
+ if (!FuncClassifier->isNonBlockingType(PreCallEvent.getCalleeIdentifier())) {
+ return;
+ }
+ const MemRegion *const MR =
+ PreCallEvent.getArgSVal(PreCallEvent.getNumArgs() - 1).getAsRegion();
+ if (!MR)
+ return;
+ const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
+
+ // The region must be typed, in order to reason about it.
+ if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
+ return;
+
+ ProgramStateRef State = Ctx.getState();
+ const Request *const Req = State->get<RequestMap>(MR);
+
+ // double nonblocking detected
+ if (Req && Req->CurrentState == Request::State::Nonblocking) {
+ ExplodedNode *ErrorNode = Ctx.generateNonFatalErrorNode();
+ BReporter.reportDoubleNonblocking(PreCallEvent, *Req, MR, ErrorNode,
+ Ctx.getBugReporter());
+ Ctx.addTransition(ErrorNode->getState(), ErrorNode);
+ }
+ // no error
+ else {
+ State = State->set<RequestMap>(MR, Request::State::Nonblocking);
+ Ctx.addTransition(State);
+ }
+}
+
+void MPIChecker::checkUnmatchedWaits(const CallEvent &PreCallEvent,
+ CheckerContext &Ctx) const {
+ if (!FuncClassifier->isWaitType(PreCallEvent.getCalleeIdentifier()))
+ return;
+ const MemRegion *const MR = topRegionUsedByWait(PreCallEvent);
+ if (!MR)
+ return;
+ const ElementRegion *const ER = dyn_cast<ElementRegion>(MR);
+
+ // The region must be typed, in order to reason about it.
+ if (!isa<TypedRegion>(MR) || (ER && !isa<TypedRegion>(ER->getSuperRegion())))
+ return;
+
+ llvm::SmallVector<const MemRegion *, 2> ReqRegions;
+ allRegionsUsedByWait(ReqRegions, MR, PreCallEvent, Ctx);
+ if (ReqRegions.empty())
+ return;
+
+ ProgramStateRef State = Ctx.getState();
+ static CheckerProgramPointTag Tag("MPI-Checker", "UnmatchedWait");
+ ExplodedNode *ErrorNode{nullptr};
+
+ // Check all request regions used by the wait function.
+ for (const auto &ReqRegion : ReqRegions) {
+ const Request *const Req = State->get<RequestMap>(ReqRegion);
+ State = State->set<RequestMap>(ReqRegion, Request::State::Wait);
+ if (!Req) {
+ if (!ErrorNode) {
+ ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
+ State = ErrorNode->getState();
+ }
+ // A wait has no matching nonblocking call.
+ BReporter.reportUnmatchedWait(PreCallEvent, ReqRegion, ErrorNode,
+ Ctx.getBugReporter());
+ }
+ }
+
+ if (!ErrorNode) {
+ Ctx.addTransition(State);
+ } else {
+ Ctx.addTransition(State, ErrorNode);
+ }
+}
+
+void MPIChecker::checkMissingWaits(SymbolReaper &SymReaper,
+ CheckerContext &Ctx) const {
+ ProgramStateRef State = Ctx.getState();
+ const auto &Requests = State->get<RequestMap>();
+ if (Requests.isEmpty())
+ return;
+
+ static CheckerProgramPointTag Tag("MPI-Checker", "MissingWait");
+ ExplodedNode *ErrorNode{nullptr};
+
+ auto ReqMap = State->get<RequestMap>();
+ for (const auto &Req : ReqMap) {
+ if (!SymReaper.isLiveRegion(Req.first)) {
+ if (Req.second.CurrentState == Request::State::Nonblocking) {
+
+ if (!ErrorNode) {
+ ErrorNode = Ctx.generateNonFatalErrorNode(State, &Tag);
+ State = ErrorNode->getState();
+ }
+ BReporter.reportMissingWait(Req.second, Req.first, ErrorNode,
+ Ctx.getBugReporter());
+ }
+ State = State->remove<RequestMap>(Req.first);
+ }
+ }
+
+ // Transition to update the state regarding removed requests.
+ if (!ErrorNode) {
+ Ctx.addTransition(State);
+ } else {
+ Ctx.addTransition(State, ErrorNode);
+ }
+}
+
+const MemRegion *MPIChecker::topRegionUsedByWait(const CallEvent &CE) const {
+
+ if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
+ return CE.getArgSVal(0).getAsRegion();
+ } else if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
+ return CE.getArgSVal(1).getAsRegion();
+ } else {
+ return (const MemRegion *)nullptr;
+ }
+}
+
+void MPIChecker::allRegionsUsedByWait(
+ llvm::SmallVector<const MemRegion *, 2> &ReqRegions,
+ const MemRegion *const MR, const CallEvent &CE, CheckerContext &Ctx) const {
+
+ MemRegionManager *const RegionManager = MR->getMemRegionManager();
+
+ if (FuncClassifier->isMPI_Waitall(CE.getCalleeIdentifier())) {
+ const SubRegion *SuperRegion{nullptr};
+ if (const ElementRegion *const ER = MR->getAs<ElementRegion>()) {
+ SuperRegion = cast<SubRegion>(ER->getSuperRegion());
+ }
+
+ // A single request is passed to MPI_Waitall.
+ if (!SuperRegion) {
+ ReqRegions.push_back(MR);
+ return;
+ }
+
+ const auto &Size = Ctx.getStoreManager().getSizeInElements(
+ Ctx.getState(), SuperRegion,
+ CE.getArgExpr(1)->getType()->getPointeeType());
+ const llvm::APSInt &ArrSize = Size.getAs<nonloc::ConcreteInt>()->getValue();
+
+ for (size_t i = 0; i < ArrSize; ++i) {
+ const NonLoc Idx = Ctx.getSValBuilder().makeArrayIndex(i);
+
+ const ElementRegion *const ER = RegionManager->getElementRegion(
+ CE.getArgExpr(1)->getType()->getPointeeType(), Idx, SuperRegion,
+ Ctx.getASTContext());
+
+ ReqRegions.push_back(ER->getAs<MemRegion>());
+ }
+ } else if (FuncClassifier->isMPI_Wait(CE.getCalleeIdentifier())) {
+ ReqRegions.push_back(MR);
+ }
+}
+
+} // end of namespace: mpi
+} // end of namespace: ento
+} // end of namespace: clang
+
+// Registers the checker for static analysis.
+void clang::ento::registerMPIChecker(CheckerManager &MGR) {
+ MGR.registerChecker<clang::ento::mpi::MPIChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h
new file mode 100644
index 000000000000..6b1c062ef3d5
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h
@@ -0,0 +1,105 @@
+//===-- MPIChecker.h - Verify MPI API usage- --------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines the main class of MPI-Checker which serves as an entry
+/// point. It is created once for each translation unit analysed.
+/// The checker defines path-sensitive checks, to verify correct usage of the
+/// MPI API.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKER_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPICHECKER_H
+
+#include "MPIBugReporter.h"
+#include "MPITypes.h"
+#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+namespace clang {
+namespace ento {
+namespace mpi {
+
+class MPIChecker : public Checker<check::PreCall, check::DeadSymbols> {
+public:
+ MPIChecker() : BReporter(*this) {}
+
+ // path-sensitive callbacks
+ void checkPreCall(const CallEvent &CE, CheckerContext &Ctx) const {
+ dynamicInit(Ctx);
+ checkUnmatchedWaits(CE, Ctx);
+ checkDoubleNonblocking(CE, Ctx);
+ }
+
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &Ctx) const {
+ dynamicInit(Ctx);
+ checkMissingWaits(SymReaper, Ctx);
+ }
+
+ void dynamicInit(CheckerContext &Ctx) const {
+ if (FuncClassifier)
+ return;
+ const_cast<std::unique_ptr<MPIFunctionClassifier> &>(FuncClassifier)
+ .reset(new MPIFunctionClassifier{Ctx.getASTContext()});
+ }
+
+ /// Checks if a request is used by nonblocking calls multiple times
+ /// in sequence without intermediate wait. The check contains a guard,
+ /// in order to only inspect nonblocking functions.
+ ///
+ /// \param PreCallEvent MPI call to verify
+ void checkDoubleNonblocking(const clang::ento::CallEvent &PreCallEvent,
+ clang::ento::CheckerContext &Ctx) const;
+
+ /// Checks if the request used by the wait function was not used at all
+ /// before. The check contains a guard, in order to only inspect wait
+ /// functions.
+ ///
+ /// \param PreCallEvent MPI call to verify
+ void checkUnmatchedWaits(const clang::ento::CallEvent &PreCallEvent,
+ clang::ento::CheckerContext &Ctx) const;
+
+ /// Check if a nonblocking call is not matched by a wait.
+ /// If a memory region is not alive and the last function using the
+ /// request was a nonblocking call, this is rated as a missing wait.
+ void checkMissingWaits(clang::ento::SymbolReaper &SymReaper,
+ clang::ento::CheckerContext &Ctx) const;
+
+private:
+ /// Collects all memory regions of a request(array) used by a wait
+ /// function. If the wait function uses a single request, this is a single
+ /// region. For wait functions using multiple requests, multiple regions
+ /// representing elements in the array are collected.
+ ///
+ /// \param ReqRegions vector the regions get pushed into
+ /// \param MR top most region to iterate
+ /// \param CE MPI wait call using the request(s)
+ void allRegionsUsedByWait(
+ llvm::SmallVector<const clang::ento::MemRegion *, 2> &ReqRegions,
+ const clang::ento::MemRegion *const MR, const clang::ento::CallEvent &CE,
+ clang::ento::CheckerContext &Ctx) const;
+
+ /// Returns the memory region used by a wait function.
+ /// Distinguishes between MPI_Wait and MPI_Waitall.
+ ///
+ /// \param CE MPI wait call
+ const clang::ento::MemRegion *
+ topRegionUsedByWait(const clang::ento::CallEvent &CE) const;
+
+ const std::unique_ptr<MPIFunctionClassifier> FuncClassifier;
+ MPIBugReporter BReporter;
+};
+
+} // end of namespace: mpi
+} // end of namespace: ento
+} // end of namespace: clang
+
+#endif
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp
new file mode 100644
index 000000000000..12760abaeeff
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp
@@ -0,0 +1,284 @@
+//===-- MPIFunctionClassifier.cpp - classifies MPI functions ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines functionality to identify and classify MPI functions.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h"
+#include "llvm/ADT/STLExtras.h"
+
+namespace clang {
+namespace ento {
+namespace mpi {
+
+void MPIFunctionClassifier::identifierInit(ASTContext &ASTCtx) {
+ // Initialize function identifiers.
+ initPointToPointIdentifiers(ASTCtx);
+ initCollectiveIdentifiers(ASTCtx);
+ initAdditionalIdentifiers(ASTCtx);
+}
+
+void MPIFunctionClassifier::initPointToPointIdentifiers(ASTContext &ASTCtx) {
+ // Copy identifiers into the correct classification containers.
+ IdentInfo_MPI_Send = &ASTCtx.Idents.get("MPI_Send");
+ MPIPointToPointTypes.push_back(IdentInfo_MPI_Send);
+ MPIType.push_back(IdentInfo_MPI_Send);
+ assert(IdentInfo_MPI_Send);
+
+ IdentInfo_MPI_Isend = &ASTCtx.Idents.get("MPI_Isend");
+ MPIPointToPointTypes.push_back(IdentInfo_MPI_Isend);
+ MPINonBlockingTypes.push_back(IdentInfo_MPI_Isend);
+ MPIType.push_back(IdentInfo_MPI_Isend);
+ assert(IdentInfo_MPI_Isend);
+
+ IdentInfo_MPI_Ssend = &ASTCtx.Idents.get("MPI_Ssend");
+ MPIPointToPointTypes.push_back(IdentInfo_MPI_Ssend);
+ MPIType.push_back(IdentInfo_MPI_Ssend);
+ assert(IdentInfo_MPI_Ssend);
+
+ IdentInfo_MPI_Issend = &ASTCtx.Idents.get("MPI_Issend");
+ MPIPointToPointTypes.push_back(IdentInfo_MPI_Issend);
+ MPINonBlockingTypes.push_back(IdentInfo_MPI_Issend);
+ MPIType.push_back(IdentInfo_MPI_Issend);
+ assert(IdentInfo_MPI_Issend);
+
+ IdentInfo_MPI_Bsend = &ASTCtx.Idents.get("MPI_Bsend");
+ MPIPointToPointTypes.push_back(IdentInfo_MPI_Bsend);
+ MPIType.push_back(IdentInfo_MPI_Bsend);
+ assert(IdentInfo_MPI_Bsend);
+
+ IdentInfo_MPI_Ibsend = &ASTCtx.Idents.get("MPI_Ibsend");
+ MPIPointToPointTypes.push_back(IdentInfo_MPI_Ibsend);
+ MPINonBlockingTypes.push_back(IdentInfo_MPI_Ibsend);
+ MPIType.push_back(IdentInfo_MPI_Ibsend);
+ assert(IdentInfo_MPI_Ibsend);
+
+ IdentInfo_MPI_Rsend = &ASTCtx.Idents.get("MPI_Rsend");
+ MPIPointToPointTypes.push_back(IdentInfo_MPI_Rsend);
+ MPIType.push_back(IdentInfo_MPI_Rsend);
+ assert(IdentInfo_MPI_Rsend);
+
+ IdentInfo_MPI_Irsend = &ASTCtx.Idents.get("MPI_Irsend");
+ MPIPointToPointTypes.push_back(IdentInfo_MPI_Irsend);
+ MPIType.push_back(IdentInfo_MPI_Irsend);
+ assert(IdentInfo_MPI_Irsend);
+
+ IdentInfo_MPI_Recv = &ASTCtx.Idents.get("MPI_Recv");
+ MPIPointToPointTypes.push_back(IdentInfo_MPI_Recv);
+ MPIType.push_back(IdentInfo_MPI_Recv);
+ assert(IdentInfo_MPI_Recv);
+
+ IdentInfo_MPI_Irecv = &ASTCtx.Idents.get("MPI_Irecv");
+ MPIPointToPointTypes.push_back(IdentInfo_MPI_Irecv);
+ MPINonBlockingTypes.push_back(IdentInfo_MPI_Irecv);
+ MPIType.push_back(IdentInfo_MPI_Irecv);
+ assert(IdentInfo_MPI_Irecv);
+}
+
+void MPIFunctionClassifier::initCollectiveIdentifiers(ASTContext &ASTCtx) {
+ // Copy identifiers into the correct classification containers.
+ IdentInfo_MPI_Scatter = &ASTCtx.Idents.get("MPI_Scatter");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Scatter);
+ MPIPointToCollTypes.push_back(IdentInfo_MPI_Scatter);
+ MPIType.push_back(IdentInfo_MPI_Scatter);
+ assert(IdentInfo_MPI_Scatter);
+
+ IdentInfo_MPI_Iscatter = &ASTCtx.Idents.get("MPI_Iscatter");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Iscatter);
+ MPIPointToCollTypes.push_back(IdentInfo_MPI_Iscatter);
+ MPINonBlockingTypes.push_back(IdentInfo_MPI_Iscatter);
+ MPIType.push_back(IdentInfo_MPI_Iscatter);
+ assert(IdentInfo_MPI_Iscatter);
+
+ IdentInfo_MPI_Gather = &ASTCtx.Idents.get("MPI_Gather");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Gather);
+ MPICollToPointTypes.push_back(IdentInfo_MPI_Gather);
+ MPIType.push_back(IdentInfo_MPI_Gather);
+ assert(IdentInfo_MPI_Gather);
+
+ IdentInfo_MPI_Igather = &ASTCtx.Idents.get("MPI_Igather");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Igather);
+ MPICollToPointTypes.push_back(IdentInfo_MPI_Igather);
+ MPINonBlockingTypes.push_back(IdentInfo_MPI_Igather);
+ MPIType.push_back(IdentInfo_MPI_Igather);
+ assert(IdentInfo_MPI_Igather);
+
+ IdentInfo_MPI_Allgather = &ASTCtx.Idents.get("MPI_Allgather");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Allgather);
+ MPICollToCollTypes.push_back(IdentInfo_MPI_Allgather);
+ MPIType.push_back(IdentInfo_MPI_Allgather);
+ assert(IdentInfo_MPI_Allgather);
+
+ IdentInfo_MPI_Iallgather = &ASTCtx.Idents.get("MPI_Iallgather");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Iallgather);
+ MPICollToCollTypes.push_back(IdentInfo_MPI_Iallgather);
+ MPINonBlockingTypes.push_back(IdentInfo_MPI_Iallgather);
+ MPIType.push_back(IdentInfo_MPI_Iallgather);
+ assert(IdentInfo_MPI_Iallgather);
+
+ IdentInfo_MPI_Bcast = &ASTCtx.Idents.get("MPI_Bcast");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Bcast);
+ MPIPointToCollTypes.push_back(IdentInfo_MPI_Bcast);
+ MPIType.push_back(IdentInfo_MPI_Bcast);
+ assert(IdentInfo_MPI_Bcast);
+
+ IdentInfo_MPI_Ibcast = &ASTCtx.Idents.get("MPI_Ibcast");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Ibcast);
+ MPIPointToCollTypes.push_back(IdentInfo_MPI_Ibcast);
+ MPINonBlockingTypes.push_back(IdentInfo_MPI_Ibcast);
+ MPIType.push_back(IdentInfo_MPI_Ibcast);
+ assert(IdentInfo_MPI_Ibcast);
+
+ IdentInfo_MPI_Reduce = &ASTCtx.Idents.get("MPI_Reduce");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Reduce);
+ MPICollToPointTypes.push_back(IdentInfo_MPI_Reduce);
+ MPIType.push_back(IdentInfo_MPI_Reduce);
+ assert(IdentInfo_MPI_Reduce);
+
+ IdentInfo_MPI_Ireduce = &ASTCtx.Idents.get("MPI_Ireduce");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Ireduce);
+ MPICollToPointTypes.push_back(IdentInfo_MPI_Ireduce);
+ MPINonBlockingTypes.push_back(IdentInfo_MPI_Ireduce);
+ MPIType.push_back(IdentInfo_MPI_Ireduce);
+ assert(IdentInfo_MPI_Ireduce);
+
+ IdentInfo_MPI_Allreduce = &ASTCtx.Idents.get("MPI_Allreduce");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Allreduce);
+ MPICollToCollTypes.push_back(IdentInfo_MPI_Allreduce);
+ MPIType.push_back(IdentInfo_MPI_Allreduce);
+ assert(IdentInfo_MPI_Allreduce);
+
+ IdentInfo_MPI_Iallreduce = &ASTCtx.Idents.get("MPI_Iallreduce");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Iallreduce);
+ MPICollToCollTypes.push_back(IdentInfo_MPI_Iallreduce);
+ MPINonBlockingTypes.push_back(IdentInfo_MPI_Iallreduce);
+ MPIType.push_back(IdentInfo_MPI_Iallreduce);
+ assert(IdentInfo_MPI_Iallreduce);
+
+ IdentInfo_MPI_Alltoall = &ASTCtx.Idents.get("MPI_Alltoall");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Alltoall);
+ MPICollToCollTypes.push_back(IdentInfo_MPI_Alltoall);
+ MPIType.push_back(IdentInfo_MPI_Alltoall);
+ assert(IdentInfo_MPI_Alltoall);
+
+ IdentInfo_MPI_Ialltoall = &ASTCtx.Idents.get("MPI_Ialltoall");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Ialltoall);
+ MPICollToCollTypes.push_back(IdentInfo_MPI_Ialltoall);
+ MPINonBlockingTypes.push_back(IdentInfo_MPI_Ialltoall);
+ MPIType.push_back(IdentInfo_MPI_Ialltoall);
+ assert(IdentInfo_MPI_Ialltoall);
+}
+
+void MPIFunctionClassifier::initAdditionalIdentifiers(ASTContext &ASTCtx) {
+ IdentInfo_MPI_Comm_rank = &ASTCtx.Idents.get("MPI_Comm_rank");
+ MPIType.push_back(IdentInfo_MPI_Comm_rank);
+ assert(IdentInfo_MPI_Comm_rank);
+
+ IdentInfo_MPI_Comm_size = &ASTCtx.Idents.get("MPI_Comm_size");
+ MPIType.push_back(IdentInfo_MPI_Comm_size);
+ assert(IdentInfo_MPI_Comm_size);
+
+ IdentInfo_MPI_Wait = &ASTCtx.Idents.get("MPI_Wait");
+ MPIType.push_back(IdentInfo_MPI_Wait);
+ assert(IdentInfo_MPI_Wait);
+
+ IdentInfo_MPI_Waitall = &ASTCtx.Idents.get("MPI_Waitall");
+ MPIType.push_back(IdentInfo_MPI_Waitall);
+ assert(IdentInfo_MPI_Waitall);
+
+ IdentInfo_MPI_Barrier = &ASTCtx.Idents.get("MPI_Barrier");
+ MPICollectiveTypes.push_back(IdentInfo_MPI_Barrier);
+ MPIType.push_back(IdentInfo_MPI_Barrier);
+ assert(IdentInfo_MPI_Barrier);
+}
+
+// general identifiers
+bool MPIFunctionClassifier::isMPIType(const IdentifierInfo *IdentInfo) const {
+ return llvm::is_contained(MPIType, IdentInfo);
+}
+
+bool MPIFunctionClassifier::isNonBlockingType(
+ const IdentifierInfo *IdentInfo) const {
+ return llvm::is_contained(MPINonBlockingTypes, IdentInfo);
+}
+
+// point-to-point identifiers
+bool MPIFunctionClassifier::isPointToPointType(
+ const IdentifierInfo *IdentInfo) const {
+ return llvm::is_contained(MPIPointToPointTypes, IdentInfo);
+}
+
+// collective identifiers
+bool MPIFunctionClassifier::isCollectiveType(
+ const IdentifierInfo *IdentInfo) const {
+ return llvm::is_contained(MPICollectiveTypes, IdentInfo);
+}
+
+bool MPIFunctionClassifier::isCollToColl(
+ const IdentifierInfo *IdentInfo) const {
+ return llvm::is_contained(MPICollToCollTypes, IdentInfo);
+}
+
+bool MPIFunctionClassifier::isScatterType(
+ const IdentifierInfo *IdentInfo) const {
+ return IdentInfo == IdentInfo_MPI_Scatter ||
+ IdentInfo == IdentInfo_MPI_Iscatter;
+}
+
+bool MPIFunctionClassifier::isGatherType(
+ const IdentifierInfo *IdentInfo) const {
+ return IdentInfo == IdentInfo_MPI_Gather ||
+ IdentInfo == IdentInfo_MPI_Igather ||
+ IdentInfo == IdentInfo_MPI_Allgather ||
+ IdentInfo == IdentInfo_MPI_Iallgather;
+}
+
+bool MPIFunctionClassifier::isAllgatherType(
+ const IdentifierInfo *IdentInfo) const {
+ return IdentInfo == IdentInfo_MPI_Allgather ||
+ IdentInfo == IdentInfo_MPI_Iallgather;
+}
+
+bool MPIFunctionClassifier::isAlltoallType(
+ const IdentifierInfo *IdentInfo) const {
+ return IdentInfo == IdentInfo_MPI_Alltoall ||
+ IdentInfo == IdentInfo_MPI_Ialltoall;
+}
+
+bool MPIFunctionClassifier::isBcastType(const IdentifierInfo *IdentInfo) const {
+ return IdentInfo == IdentInfo_MPI_Bcast || IdentInfo == IdentInfo_MPI_Ibcast;
+}
+
+bool MPIFunctionClassifier::isReduceType(
+ const IdentifierInfo *IdentInfo) const {
+ return IdentInfo == IdentInfo_MPI_Reduce ||
+ IdentInfo == IdentInfo_MPI_Ireduce ||
+ IdentInfo == IdentInfo_MPI_Allreduce ||
+ IdentInfo == IdentInfo_MPI_Iallreduce;
+}
+
+// additional identifiers
+bool MPIFunctionClassifier::isMPI_Wait(const IdentifierInfo *IdentInfo) const {
+ return IdentInfo == IdentInfo_MPI_Wait;
+}
+
+bool MPIFunctionClassifier::isMPI_Waitall(
+ const IdentifierInfo *IdentInfo) const {
+ return IdentInfo == IdentInfo_MPI_Waitall;
+}
+
+bool MPIFunctionClassifier::isWaitType(const IdentifierInfo *IdentInfo) const {
+ return IdentInfo == IdentInfo_MPI_Wait || IdentInfo == IdentInfo_MPI_Waitall;
+}
+
+} // end of namespace: mpi
+} // end of namespace: ento
+} // end of namespace: clang
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h
new file mode 100644
index 000000000000..2e7140cd771e
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h
@@ -0,0 +1,67 @@
+//===-- MPITypes.h - Functionality to model MPI concepts --------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file provides definitions to model concepts of MPI. The mpi::Request
+/// class defines a wrapper class, in order to make MPI requests trackable for
+/// path-sensitive analysis.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPITYPES_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MPICHECKER_MPITYPES_H
+
+#include "clang/StaticAnalyzer/Checkers/MPIFunctionClassifier.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "llvm/ADT/SmallSet.h"
+
+namespace clang {
+namespace ento {
+namespace mpi {
+
+class Request {
+public:
+ enum State : unsigned char { Nonblocking, Wait };
+
+ Request(State S) : CurrentState{S} {}
+
+ void Profile(llvm::FoldingSetNodeID &Id) const {
+ Id.AddInteger(CurrentState);
+ }
+
+ bool operator==(const Request &ToCompare) const {
+ return CurrentState == ToCompare.CurrentState;
+ }
+
+ const State CurrentState;
+};
+
+// The RequestMap stores MPI requests which are identified by their memory
+// region. Requests are used in MPI to complete nonblocking operations with wait
+// operations. A custom map implementation is used, in order to make it
+// available in an arbitrary amount of translation units.
+struct RequestMap {};
+typedef llvm::ImmutableMap<const clang::ento::MemRegion *,
+ clang::ento::mpi::Request>
+ RequestMapImpl;
+
+} // end of namespace: mpi
+
+template <>
+struct ProgramStateTrait<mpi::RequestMap>
+ : public ProgramStatePartialTrait<mpi::RequestMapImpl> {
+ static void *GDMIndex() {
+ static int index = 0;
+ return &index;
+ }
+};
+
+} // end of namespace: ento
+} // end of namespace: clang
+#endif
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
new file mode 100644
index 000000000000..06e27fc5718d
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -0,0 +1,664 @@
+//==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This checker flags misuses of KeyChainAPI. In particular, the password data
+// allocated/returned by SecKeychainItemCopyContent,
+// SecKeychainFindGenericPassword, SecKeychainFindInternetPassword functions has
+// to be freed using a call to SecKeychainItemFreeContent.
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class MacOSKeychainAPIChecker : public Checker<check::PreStmt<CallExpr>,
+ check::PostStmt<CallExpr>,
+ check::DeadSymbols,
+ check::PointerEscape,
+ eval::Assume> {
+ mutable std::unique_ptr<BugType> BT;
+
+public:
+ /// AllocationState is a part of the checker specific state together with the
+ /// MemRegion corresponding to the allocated data.
+ struct AllocationState {
+ /// The index of the allocator function.
+ unsigned int AllocatorIdx;
+ SymbolRef Region;
+
+ AllocationState(const Expr *E, unsigned int Idx, SymbolRef R) :
+ AllocatorIdx(Idx),
+ Region(R) {}
+
+ bool operator==(const AllocationState &X) const {
+ return (AllocatorIdx == X.AllocatorIdx &&
+ Region == X.Region);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(AllocatorIdx);
+ ID.AddPointer(Region);
+ }
+ };
+
+ void checkPreStmt(const CallExpr *S, CheckerContext &C) const;
+ void checkPostStmt(const CallExpr *S, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+ ProgramStateRef checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
+ ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
+ bool Assumption) const;
+ void printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const;
+
+private:
+ typedef std::pair<SymbolRef, const AllocationState*> AllocationPair;
+ typedef SmallVector<AllocationPair, 2> AllocationPairVec;
+
+ enum APIKind {
+ /// Denotes functions tracked by this checker.
+ ValidAPI = 0,
+ /// The functions commonly/mistakenly used in place of the given API.
+ ErrorAPI = 1,
+ /// The functions which may allocate the data. These are tracked to reduce
+ /// the false alarm rate.
+ PossibleAPI = 2
+ };
+ /// Stores the information about the allocator and deallocator functions -
+ /// these are the functions the checker is tracking.
+ struct ADFunctionInfo {
+ const char* Name;
+ unsigned int Param;
+ unsigned int DeallocatorIdx;
+ APIKind Kind;
+ };
+ static const unsigned InvalidIdx = 100000;
+ static const unsigned FunctionsToTrackSize = 8;
+ static const ADFunctionInfo FunctionsToTrack[FunctionsToTrackSize];
+ /// The value, which represents no error return value for allocator functions.
+ static const unsigned NoErr = 0;
+
+ /// Given the function name, returns the index of the allocator/deallocator
+ /// function.
+ static unsigned getTrackedFunctionIndex(StringRef Name, bool IsAllocator);
+
+ inline void initBugType() const {
+ if (!BT)
+ BT.reset(new BugType(this, "Improper use of SecKeychain API",
+ "API Misuse (Apple)"));
+ }
+
+ void generateDeallocatorMismatchReport(const AllocationPair &AP,
+ const Expr *ArgExpr,
+ CheckerContext &C) const;
+
+ /// Find the allocation site for Sym on the path leading to the node N.
+ const ExplodedNode *getAllocationNode(const ExplodedNode *N, SymbolRef Sym,
+ CheckerContext &C) const;
+
+ std::unique_ptr<BugReport> generateAllocatedDataNotReleasedReport(
+ const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const;
+
+ /// Mark an AllocationPair interesting for diagnostic reporting.
+ void markInteresting(BugReport *R, const AllocationPair &AP) const {
+ R->markInteresting(AP.first);
+ R->markInteresting(AP.second->Region);
+ }
+
+ /// The bug visitor which allows us to print extra diagnostics along the
+ /// BugReport path. For example, showing the allocation site of the leaked
+ /// region.
+ class SecKeychainBugVisitor : public BugReporterVisitor {
+ protected:
+ // The allocated region symbol tracked by the main analysis.
+ SymbolRef Sym;
+
+ public:
+ SecKeychainBugVisitor(SymbolRef S) : Sym(S) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int X = 0;
+ ID.AddPointer(&X);
+ ID.AddPointer(Sym);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+ };
+};
+}
+
+/// ProgramState traits to store the currently allocated (and not yet freed)
+/// symbols. This is a map from the allocated content symbol to the
+/// corresponding AllocationState.
+REGISTER_MAP_WITH_PROGRAMSTATE(AllocatedData,
+ SymbolRef,
+ MacOSKeychainAPIChecker::AllocationState)
+
+static bool isEnclosingFunctionParam(const Expr *E) {
+ E = E->IgnoreParenCasts();
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
+ const ValueDecl *VD = DRE->getDecl();
+ if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
+ return true;
+ }
+ return false;
+}
+
+const MacOSKeychainAPIChecker::ADFunctionInfo
+ MacOSKeychainAPIChecker::FunctionsToTrack[FunctionsToTrackSize] = {
+ {"SecKeychainItemCopyContent", 4, 3, ValidAPI}, // 0
+ {"SecKeychainFindGenericPassword", 6, 3, ValidAPI}, // 1
+ {"SecKeychainFindInternetPassword", 13, 3, ValidAPI}, // 2
+ {"SecKeychainItemFreeContent", 1, InvalidIdx, ValidAPI}, // 3
+ {"SecKeychainItemCopyAttributesAndData", 5, 5, ValidAPI}, // 4
+ {"SecKeychainItemFreeAttributesAndData", 1, InvalidIdx, ValidAPI}, // 5
+ {"free", 0, InvalidIdx, ErrorAPI}, // 6
+ {"CFStringCreateWithBytesNoCopy", 1, InvalidIdx, PossibleAPI}, // 7
+};
+
+unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
+ bool IsAllocator) {
+ for (unsigned I = 0; I < FunctionsToTrackSize; ++I) {
+ ADFunctionInfo FI = FunctionsToTrack[I];
+ if (FI.Name != Name)
+ continue;
+ // Make sure the function is of the right type (allocator vs deallocator).
+ if (IsAllocator && (FI.DeallocatorIdx == InvalidIdx))
+ return InvalidIdx;
+ if (!IsAllocator && (FI.DeallocatorIdx != InvalidIdx))
+ return InvalidIdx;
+
+ return I;
+ }
+ // The function is not tracked.
+ return InvalidIdx;
+}
+
+static bool isBadDeallocationArgument(const MemRegion *Arg) {
+ if (!Arg)
+ return false;
+ return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) ||
+ isa<TypedRegion>(Arg);
+}
+
+/// Given the address expression, retrieve the value it's pointing to. Assume
+/// that value is itself an address, and return the corresponding symbol.
+static SymbolRef getAsPointeeSymbol(const Expr *Expr,
+ CheckerContext &C) {
+ ProgramStateRef State = C.getState();
+ SVal ArgV = C.getSVal(Expr);
+
+ if (Optional<loc::MemRegionVal> X = ArgV.getAs<loc::MemRegionVal>()) {
+ StoreManager& SM = C.getStoreManager();
+ SymbolRef sym = SM.getBinding(State->getStore(), *X).getAsLocSymbol();
+ if (sym)
+ return sym;
+ }
+ return nullptr;
+}
+
+// Report deallocator mismatch. Remove the region from tracking - reporting a
+// missing free error after this one is redundant.
+void MacOSKeychainAPIChecker::
+ generateDeallocatorMismatchReport(const AllocationPair &AP,
+ const Expr *ArgExpr,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ State = State->remove<AllocatedData>(AP.first);
+ ExplodedNode *N = C.generateNonFatalErrorNode(State);
+
+ if (!N)
+ return;
+ initBugType();
+ SmallString<80> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ unsigned int PDeallocIdx =
+ FunctionsToTrack[AP.second->AllocatorIdx].DeallocatorIdx;
+
+ os << "Deallocator doesn't match the allocator: '"
+ << FunctionsToTrack[PDeallocIdx].Name << "' should be used.";
+ auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
+ Report->addRange(ArgExpr->getSourceRange());
+ markInteresting(Report.get(), AP);
+ C.emitReport(std::move(Report));
+}
+
+void MacOSKeychainAPIChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ unsigned idx = InvalidIdx;
+ ProgramStateRef State = C.getState();
+
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD || FD->getKind() != Decl::Function)
+ return;
+
+ StringRef funName = C.getCalleeName(FD);
+ if (funName.empty())
+ return;
+
+ // If it is a call to an allocator function, it could be a double allocation.
+ idx = getTrackedFunctionIndex(funName, true);
+ if (idx != InvalidIdx) {
+ unsigned paramIdx = FunctionsToTrack[idx].Param;
+ if (CE->getNumArgs() <= paramIdx)
+ return;
+
+ const Expr *ArgExpr = CE->getArg(paramIdx);
+ if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C))
+ if (const AllocationState *AS = State->get<AllocatedData>(V)) {
+ // Remove the value from the state. The new symbol will be added for
+ // tracking when the second allocator is processed in checkPostStmt().
+ State = State->remove<AllocatedData>(V);
+ ExplodedNode *N = C.generateNonFatalErrorNode(State);
+ if (!N)
+ return;
+ initBugType();
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ unsigned int DIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
+ os << "Allocated data should be released before another call to "
+ << "the allocator: missing a call to '"
+ << FunctionsToTrack[DIdx].Name
+ << "'.";
+ auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(V));
+ Report->addRange(ArgExpr->getSourceRange());
+ Report->markInteresting(AS->Region);
+ C.emitReport(std::move(Report));
+ }
+ return;
+ }
+
+ // Is it a call to one of deallocator functions?
+ idx = getTrackedFunctionIndex(funName, false);
+ if (idx == InvalidIdx)
+ return;
+
+ unsigned paramIdx = FunctionsToTrack[idx].Param;
+ if (CE->getNumArgs() <= paramIdx)
+ return;
+
+ // Check the argument to the deallocator.
+ const Expr *ArgExpr = CE->getArg(paramIdx);
+ SVal ArgSVal = C.getSVal(ArgExpr);
+
+ // Undef is reported by another checker.
+ if (ArgSVal.isUndef())
+ return;
+
+ SymbolRef ArgSM = ArgSVal.getAsLocSymbol();
+
+ // If the argument is coming from the heap, globals, or unknown, do not
+ // report it.
+ bool RegionArgIsBad = false;
+ if (!ArgSM) {
+ if (!isBadDeallocationArgument(ArgSVal.getAsRegion()))
+ return;
+ RegionArgIsBad = true;
+ }
+
+ // Is the argument to the call being tracked?
+ const AllocationState *AS = State->get<AllocatedData>(ArgSM);
+ if (!AS)
+ return;
+
+ // TODO: We might want to report double free here.
+ // (that would involve tracking all the freed symbols in the checker state).
+ if (RegionArgIsBad) {
+ // It is possible that this is a false positive - the argument might
+ // have entered as an enclosing function parameter.
+ if (isEnclosingFunctionParam(ArgExpr))
+ return;
+
+ ExplodedNode *N = C.generateNonFatalErrorNode(State);
+ if (!N)
+ return;
+ initBugType();
+ auto Report = llvm::make_unique<BugReport>(
+ *BT, "Trying to free data which has not been allocated.", N);
+ Report->addRange(ArgExpr->getSourceRange());
+ if (AS)
+ Report->markInteresting(AS->Region);
+ C.emitReport(std::move(Report));
+ return;
+ }
+
+ // Process functions which might deallocate.
+ if (FunctionsToTrack[idx].Kind == PossibleAPI) {
+
+ if (funName == "CFStringCreateWithBytesNoCopy") {
+ const Expr *DeallocatorExpr = CE->getArg(5)->IgnoreParenCasts();
+ // NULL ~ default deallocator, so warn.
+ if (DeallocatorExpr->isNullPointerConstant(C.getASTContext(),
+ Expr::NPC_ValueDependentIsNotNull)) {
+ const AllocationPair AP = std::make_pair(ArgSM, AS);
+ generateDeallocatorMismatchReport(AP, ArgExpr, C);
+ return;
+ }
+ // One of the default allocators, so warn.
+ if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(DeallocatorExpr)) {
+ StringRef DeallocatorName = DE->getFoundDecl()->getName();
+ if (DeallocatorName == "kCFAllocatorDefault" ||
+ DeallocatorName == "kCFAllocatorSystemDefault" ||
+ DeallocatorName == "kCFAllocatorMalloc") {
+ const AllocationPair AP = std::make_pair(ArgSM, AS);
+ generateDeallocatorMismatchReport(AP, ArgExpr, C);
+ return;
+ }
+ // If kCFAllocatorNull, which does not deallocate, we still have to
+ // find the deallocator.
+ if (DE->getFoundDecl()->getName() == "kCFAllocatorNull")
+ return;
+ }
+ // In all other cases, assume the user supplied a correct deallocator
+ // that will free memory so stop tracking.
+ State = State->remove<AllocatedData>(ArgSM);
+ C.addTransition(State);
+ return;
+ }
+
+ llvm_unreachable("We know of no other possible APIs.");
+ }
+
+ // The call is deallocating a value we previously allocated, so remove it
+ // from the next state.
+ State = State->remove<AllocatedData>(ArgSM);
+
+ // Check if the proper deallocator is used.
+ unsigned int PDeallocIdx = FunctionsToTrack[AS->AllocatorIdx].DeallocatorIdx;
+ if (PDeallocIdx != idx || (FunctionsToTrack[idx].Kind == ErrorAPI)) {
+ const AllocationPair AP = std::make_pair(ArgSM, AS);
+ generateDeallocatorMismatchReport(AP, ArgExpr, C);
+ return;
+ }
+
+ C.addTransition(State);
+}
+
+void MacOSKeychainAPIChecker::checkPostStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD || FD->getKind() != Decl::Function)
+ return;
+
+ StringRef funName = C.getCalleeName(FD);
+
+ // If a value has been allocated, add it to the set for tracking.
+ unsigned idx = getTrackedFunctionIndex(funName, true);
+ if (idx == InvalidIdx)
+ return;
+
+ const Expr *ArgExpr = CE->getArg(FunctionsToTrack[idx].Param);
+ // If the argument entered as an enclosing function parameter, skip it to
+ // avoid false positives.
+ if (isEnclosingFunctionParam(ArgExpr) &&
+ C.getLocationContext()->getParent() == nullptr)
+ return;
+
+ if (SymbolRef V = getAsPointeeSymbol(ArgExpr, C)) {
+ // If the argument points to something that's not a symbolic region, it
+ // can be:
+ // - unknown (cannot reason about it)
+ // - undefined (already reported by other checker)
+ // - constant (null - should not be tracked,
+ // other constant will generate a compiler warning)
+ // - goto (should be reported by other checker)
+
+ // The call return value symbol should stay alive for as long as the
+ // allocated value symbol, since our diagnostics depend on the value
+ // returned by the call. Ex: Data should only be freed if noErr was
+ // returned during allocation.)
+ SymbolRef RetStatusSymbol = C.getSVal(CE).getAsSymbol();
+ C.getSymbolManager().addSymbolDependency(V, RetStatusSymbol);
+
+ // Track the allocated value in the checker state.
+ State = State->set<AllocatedData>(V, AllocationState(ArgExpr, idx,
+ RetStatusSymbol));
+ assert(State);
+ C.addTransition(State);
+ }
+}
+
+// TODO: This logic is the same as in Malloc checker.
+const ExplodedNode *
+MacOSKeychainAPIChecker::getAllocationNode(const ExplodedNode *N,
+ SymbolRef Sym,
+ CheckerContext &C) const {
+ const LocationContext *LeakContext = N->getLocationContext();
+ // Walk the ExplodedGraph backwards and find the first node that referred to
+ // the tracked symbol.
+ const ExplodedNode *AllocNode = N;
+
+ while (N) {
+ if (!N->getState()->get<AllocatedData>(Sym))
+ break;
+ // Allocation node, is the last node in the current or parent context in
+ // which the symbol was tracked.
+ const LocationContext *NContext = N->getLocationContext();
+ if (NContext == LeakContext ||
+ NContext->isParentOf(LeakContext))
+ AllocNode = N;
+ N = N->pred_empty() ? nullptr : *(N->pred_begin());
+ }
+
+ return AllocNode;
+}
+
+std::unique_ptr<BugReport>
+MacOSKeychainAPIChecker::generateAllocatedDataNotReleasedReport(
+ const AllocationPair &AP, ExplodedNode *N, CheckerContext &C) const {
+ const ADFunctionInfo &FI = FunctionsToTrack[AP.second->AllocatorIdx];
+ initBugType();
+ SmallString<70> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ os << "Allocated data is not released: missing a call to '"
+ << FunctionsToTrack[FI.DeallocatorIdx].Name << "'.";
+
+ // Most bug reports are cached at the location where they occurred.
+ // With leaks, we want to unique them by the location where they were
+ // allocated, and only report a single path.
+ PathDiagnosticLocation LocUsedForUniqueing;
+ const ExplodedNode *AllocNode = getAllocationNode(N, AP.first, C);
+ const Stmt *AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
+
+ if (AllocStmt)
+ LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocStmt,
+ C.getSourceManager(),
+ AllocNode->getLocationContext());
+
+ auto Report =
+ llvm::make_unique<BugReport>(*BT, os.str(), N, LocUsedForUniqueing,
+ AllocNode->getLocationContext()->getDecl());
+
+ Report->addVisitor(llvm::make_unique<SecKeychainBugVisitor>(AP.first));
+ markInteresting(Report.get(), AP);
+ return Report;
+}
+
+/// If the return symbol is assumed to be error, remove the allocated info
+/// from consideration.
+ProgramStateRef MacOSKeychainAPIChecker::evalAssume(ProgramStateRef State,
+ SVal Cond,
+ bool Assumption) const {
+ AllocatedDataTy AMap = State->get<AllocatedData>();
+ if (AMap.isEmpty())
+ return State;
+
+ auto *CondBSE = dyn_cast_or_null<BinarySymExpr>(Cond.getAsSymExpr());
+ if (!CondBSE)
+ return State;
+ BinaryOperator::Opcode OpCode = CondBSE->getOpcode();
+ if (OpCode != BO_EQ && OpCode != BO_NE)
+ return State;
+
+ // Match for a restricted set of patterns for cmparison of error codes.
+ // Note, the comparisons of type '0 == st' are transformed into SymIntExpr.
+ SymbolRef ReturnSymbol = nullptr;
+ if (auto *SIE = dyn_cast<SymIntExpr>(CondBSE)) {
+ const llvm::APInt &RHS = SIE->getRHS();
+ bool ErrorIsReturned = (OpCode == BO_EQ && RHS != NoErr) ||
+ (OpCode == BO_NE && RHS == NoErr);
+ if (!Assumption)
+ ErrorIsReturned = !ErrorIsReturned;
+ if (ErrorIsReturned)
+ ReturnSymbol = SIE->getLHS();
+ }
+
+ if (ReturnSymbol)
+ for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
+ if (ReturnSymbol == I->second.Region)
+ State = State->remove<AllocatedData>(I->first);
+ }
+
+ return State;
+}
+
+void MacOSKeychainAPIChecker::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ AllocatedDataTy AMap = State->get<AllocatedData>();
+ if (AMap.isEmpty())
+ return;
+
+ bool Changed = false;
+ AllocationPairVec Errors;
+ for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
+ if (!SR.isDead(I->first))
+ continue;
+
+ Changed = true;
+ State = State->remove<AllocatedData>(I->first);
+ // If the allocated symbol is null do not report.
+ ConstraintManager &CMgr = State->getConstraintManager();
+ ConditionTruthVal AllocFailed = CMgr.isNull(State, I.getKey());
+ if (AllocFailed.isConstrainedTrue())
+ continue;
+ Errors.push_back(std::make_pair(I->first, &I->second));
+ }
+ if (!Changed) {
+ // Generate the new, cleaned up state.
+ C.addTransition(State);
+ return;
+ }
+
+ static CheckerProgramPointTag Tag(this, "DeadSymbolsLeak");
+ ExplodedNode *N = C.generateNonFatalErrorNode(C.getState(), &Tag);
+ if (!N)
+ return;
+
+ // Generate the error reports.
+ for (const auto &P : Errors)
+ C.emitReport(generateAllocatedDataNotReleasedReport(P, N, C));
+
+ // Generate the new, cleaned up state.
+ C.addTransition(State, N);
+}
+
+ProgramStateRef MacOSKeychainAPIChecker::checkPointerEscape(
+ ProgramStateRef State, const InvalidatedSymbols &Escaped,
+ const CallEvent *Call, PointerEscapeKind Kind) const {
+ // FIXME: This branch doesn't make any sense at all, but it is an overfitted
+ // replacement for a previous overfitted code that was making even less sense.
+ if (!Call || Call->getDecl())
+ return State;
+
+ for (auto I : State->get<AllocatedData>()) {
+ SymbolRef Sym = I.first;
+ if (Escaped.count(Sym))
+ State = State->remove<AllocatedData>(Sym);
+
+ // This checker is special. Most checkers in fact only track symbols of
+ // SymbolConjured type, eg. symbols returned from functions such as
+ // malloc(). This checker tracks symbols returned as out-parameters.
+ //
+ // When a function is evaluated conservatively, the out-parameter's pointee
+ // base region gets invalidated with a SymbolConjured. If the base region is
+ // larger than the region we're interested in, the value we're interested in
+ // would be SymbolDerived based on that SymbolConjured. However, such
+ // SymbolDerived will never be listed in the Escaped set when the base
+ // region is invalidated because ExprEngine doesn't know which symbols
+ // were derived from a given symbol, while there can be infinitely many
+ // valid symbols derived from any given symbol.
+ //
+ // Hence the extra boilerplate: remove the derived symbol when its parent
+ // symbol escapes.
+ //
+ if (const auto *SD = dyn_cast<SymbolDerived>(Sym)) {
+ SymbolRef ParentSym = SD->getParentSymbol();
+ if (Escaped.count(ParentSym))
+ State = State->remove<AllocatedData>(Sym);
+ }
+ }
+ return State;
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+MacOSKeychainAPIChecker::SecKeychainBugVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) {
+ const AllocationState *AS = N->getState()->get<AllocatedData>(Sym);
+ if (!AS)
+ return nullptr;
+ const AllocationState *ASPrev =
+ N->getFirstPred()->getState()->get<AllocatedData>(Sym);
+ if (ASPrev)
+ return nullptr;
+
+ // (!ASPrev && AS) ~ We started tracking symbol in node N, it must be the
+ // allocation site.
+ const CallExpr *CE =
+ cast<CallExpr>(N->getLocation().castAs<StmtPoint>().getStmt());
+ const FunctionDecl *funDecl = CE->getDirectCallee();
+ assert(funDecl && "We do not support indirect function calls as of now.");
+ StringRef funName = funDecl->getName();
+
+ // Get the expression of the corresponding argument.
+ unsigned Idx = getTrackedFunctionIndex(funName, true);
+ assert(Idx != InvalidIdx && "This should be a call to an allocator.");
+ const Expr *ArgExpr = CE->getArg(FunctionsToTrack[Idx].Param);
+ PathDiagnosticLocation Pos(ArgExpr, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos,
+ "Data is allocated here.");
+}
+
+void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
+ ProgramStateRef State,
+ const char *NL,
+ const char *Sep) const {
+
+ AllocatedDataTy AMap = State->get<AllocatedData>();
+
+ if (!AMap.isEmpty()) {
+ Out << Sep << "KeychainAPIChecker :" << NL;
+ for (auto I = AMap.begin(), E = AMap.end(); I != E; ++I) {
+ I.getKey()->dumpToStream(Out);
+ }
+ }
+}
+
+
+void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
+ mgr.registerChecker<MacOSKeychainAPIChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
new file mode 100644
index 000000000000..f5976d7da4c1
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
@@ -0,0 +1,176 @@
+// MacOSXAPIChecker.h - Checks proper use of various MacOS X APIs --*- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines MacOSXAPIChecker, which is an assortment of checks on calls
+// to various, widely used Apple APIs.
+//
+// FIXME: What's currently in BasicObjCFoundationChecks.cpp should be migrated
+// to here, using the new Checker interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class MacOSXAPIChecker : public Checker< check::PreStmt<CallExpr> > {
+ mutable std::unique_ptr<BugType> BT_dispatchOnce;
+
+ static const ObjCIvarRegion *getParentIvarRegion(const MemRegion *R);
+
+public:
+ void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+
+ void CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
+ StringRef FName) const;
+
+ typedef void (MacOSXAPIChecker::*SubChecker)(CheckerContext &,
+ const CallExpr *,
+ StringRef FName) const;
+};
+} //end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// dispatch_once and dispatch_once_f
+//===----------------------------------------------------------------------===//
+
+const ObjCIvarRegion *
+MacOSXAPIChecker::getParentIvarRegion(const MemRegion *R) {
+ const SubRegion *SR = dyn_cast<SubRegion>(R);
+ while (SR) {
+ if (const ObjCIvarRegion *IR = dyn_cast<ObjCIvarRegion>(SR))
+ return IR;
+ SR = dyn_cast<SubRegion>(SR->getSuperRegion());
+ }
+ return nullptr;
+}
+
+void MacOSXAPIChecker::CheckDispatchOnce(CheckerContext &C, const CallExpr *CE,
+ StringRef FName) const {
+ if (CE->getNumArgs() < 1)
+ return;
+
+ // Check if the first argument is improperly allocated. If so, issue a
+ // warning because that's likely to be bad news.
+ const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
+ if (!R)
+ return;
+
+ // Global variables are fine.
+ const MemRegion *RB = R->getBaseRegion();
+ const MemSpaceRegion *RS = RB->getMemorySpace();
+ if (isa<GlobalsSpaceRegion>(RS))
+ return;
+
+ // Handle _dispatch_once. In some versions of the OS X SDK we have the case
+ // that dispatch_once is a macro that wraps a call to _dispatch_once.
+ // _dispatch_once is then a function which then calls the real dispatch_once.
+ // Users do not care; they just want the warning at the top-level call.
+ if (CE->getBeginLoc().isMacroID()) {
+ StringRef TrimmedFName = FName.ltrim('_');
+ if (TrimmedFName != FName)
+ FName = TrimmedFName;
+ }
+
+ SmallString<256> S;
+ llvm::raw_svector_ostream os(S);
+ bool SuggestStatic = false;
+ os << "Call to '" << FName << "' uses";
+ if (const VarRegion *VR = dyn_cast<VarRegion>(RB)) {
+ const VarDecl *VD = VR->getDecl();
+ // FIXME: These should have correct memory space and thus should be filtered
+ // out earlier. This branch only fires when we're looking from a block,
+ // which we analyze as a top-level declaration, onto a static local
+ // in a function that contains the block.
+ if (VD->isStaticLocal())
+ return;
+ // We filtered out globals earlier, so it must be a local variable
+ // or a block variable which is under UnknownSpaceRegion.
+ if (VR != R)
+ os << " memory within";
+ if (VD->hasAttr<BlocksAttr>())
+ os << " the block variable '";
+ else
+ os << " the local variable '";
+ os << VR->getDecl()->getName() << '\'';
+ SuggestStatic = true;
+ } else if (const ObjCIvarRegion *IVR = getParentIvarRegion(R)) {
+ if (IVR != R)
+ os << " memory within";
+ os << " the instance variable '" << IVR->getDecl()->getName() << '\'';
+ } else if (isa<HeapSpaceRegion>(RS)) {
+ os << " heap-allocated memory";
+ } else if (isa<UnknownSpaceRegion>(RS)) {
+ // Presence of an IVar superregion has priority over this branch, because
+ // ObjC objects are on the heap even if the core doesn't realize this.
+ // Presence of a block variable base region has priority over this branch,
+ // because block variables are known to be either on stack or on heap
+ // (might actually move between the two, hence UnknownSpace).
+ return;
+ } else {
+ os << " stack allocated memory";
+ }
+ os << " for the predicate value. Using such transient memory for "
+ "the predicate is potentially dangerous.";
+ if (SuggestStatic)
+ os << " Perhaps you intended to declare the variable as 'static'?";
+
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+
+ if (!BT_dispatchOnce)
+ BT_dispatchOnce.reset(new BugType(this, "Improper use of 'dispatch_once'",
+ "API Misuse (Apple)"));
+
+ auto report = llvm::make_unique<BugReport>(*BT_dispatchOnce, os.str(), N);
+ report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(std::move(report));
+}
+
+//===----------------------------------------------------------------------===//
+// Central dispatch function.
+//===----------------------------------------------------------------------===//
+
+void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ StringRef Name = C.getCalleeName(CE);
+ if (Name.empty())
+ return;
+
+ SubChecker SC =
+ llvm::StringSwitch<SubChecker>(Name)
+ .Cases("dispatch_once",
+ "_dispatch_once",
+ "dispatch_once_f",
+ &MacOSXAPIChecker::CheckDispatchOnce)
+ .Default(nullptr);
+
+ if (SC)
+ (this->*SC)(C, CE, Name);
+}
+
+//===----------------------------------------------------------------------===//
+// Registration.
+//===----------------------------------------------------------------------===//
+
+void ento::registerMacOSXAPIChecker(CheckerManager &mgr) {
+ mgr.registerChecker<MacOSXAPIChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
new file mode 100644
index 000000000000..ae1b1fc837be
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -0,0 +1,3133 @@
+//=== MallocChecker.cpp - A malloc/free checker -------------------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines malloc/free checker, which checks for potential memory
+// leaks, double free, and use-after-free problems.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "InterCheckerAPI.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "AllocationState.h"
+#include <climits>
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+// Used to check correspondence between allocators and deallocators.
+enum AllocationFamily {
+ AF_None,
+ AF_Malloc,
+ AF_CXXNew,
+ AF_CXXNewArray,
+ AF_IfNameIndex,
+ AF_Alloca,
+ AF_InnerBuffer
+};
+
+class RefState {
+ enum Kind { // Reference to allocated memory.
+ Allocated,
+ // Reference to zero-allocated memory.
+ AllocatedOfSizeZero,
+ // Reference to released/freed memory.
+ Released,
+ // The responsibility for freeing resources has transferred from
+ // this reference. A relinquished symbol should not be freed.
+ Relinquished,
+ // We are no longer guaranteed to have observed all manipulations
+ // of this pointer/memory. For example, it could have been
+ // passed as a parameter to an opaque function.
+ Escaped
+ };
+
+ const Stmt *S;
+ unsigned K : 3; // Kind enum, but stored as a bitfield.
+ unsigned Family : 29; // Rest of 32-bit word, currently just an allocation
+ // family.
+
+ RefState(Kind k, const Stmt *s, unsigned family)
+ : S(s), K(k), Family(family) {
+ assert(family != AF_None);
+ }
+public:
+ bool isAllocated() const { return K == Allocated; }
+ bool isAllocatedOfSizeZero() const { return K == AllocatedOfSizeZero; }
+ bool isReleased() const { return K == Released; }
+ bool isRelinquished() const { return K == Relinquished; }
+ bool isEscaped() const { return K == Escaped; }
+ AllocationFamily getAllocationFamily() const {
+ return (AllocationFamily)Family;
+ }
+ const Stmt *getStmt() const { return S; }
+
+ bool operator==(const RefState &X) const {
+ return K == X.K && S == X.S && Family == X.Family;
+ }
+
+ static RefState getAllocated(unsigned family, const Stmt *s) {
+ return RefState(Allocated, s, family);
+ }
+ static RefState getAllocatedOfSizeZero(const RefState *RS) {
+ return RefState(AllocatedOfSizeZero, RS->getStmt(),
+ RS->getAllocationFamily());
+ }
+ static RefState getReleased(unsigned family, const Stmt *s) {
+ return RefState(Released, s, family);
+ }
+ static RefState getRelinquished(unsigned family, const Stmt *s) {
+ return RefState(Relinquished, s, family);
+ }
+ static RefState getEscaped(const RefState *RS) {
+ return RefState(Escaped, RS->getStmt(), RS->getAllocationFamily());
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(K);
+ ID.AddPointer(S);
+ ID.AddInteger(Family);
+ }
+
+ void dump(raw_ostream &OS) const {
+ switch (static_cast<Kind>(K)) {
+#define CASE(ID) case ID: OS << #ID; break;
+ CASE(Allocated)
+ CASE(AllocatedOfSizeZero)
+ CASE(Released)
+ CASE(Relinquished)
+ CASE(Escaped)
+ }
+ }
+
+ LLVM_DUMP_METHOD void dump() const { dump(llvm::errs()); }
+};
+
+enum ReallocPairKind {
+ RPToBeFreedAfterFailure,
+ // The symbol has been freed when reallocation failed.
+ RPIsFreeOnFailure,
+ // The symbol does not need to be freed after reallocation fails.
+ RPDoNotTrackAfterFailure
+};
+
+/// \class ReallocPair
+/// Stores information about the symbol being reallocated by a call to
+/// 'realloc' to allow modeling failed reallocation later in the path.
+struct ReallocPair {
+ // The symbol which realloc reallocated.
+ SymbolRef ReallocatedSym;
+ ReallocPairKind Kind;
+
+ ReallocPair(SymbolRef S, ReallocPairKind K) :
+ ReallocatedSym(S), Kind(K) {}
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(Kind);
+ ID.AddPointer(ReallocatedSym);
+ }
+ bool operator==(const ReallocPair &X) const {
+ return ReallocatedSym == X.ReallocatedSym &&
+ Kind == X.Kind;
+ }
+};
+
+typedef std::pair<const ExplodedNode*, const MemRegion*> LeakInfo;
+
+class MallocChecker : public Checker<check::DeadSymbols,
+ check::PointerEscape,
+ check::ConstPointerEscape,
+ check::PreStmt<ReturnStmt>,
+ check::EndFunction,
+ check::PreCall,
+ check::PostStmt<CallExpr>,
+ check::PostStmt<CXXNewExpr>,
+ check::NewAllocator,
+ check::PreStmt<CXXDeleteExpr>,
+ check::PostStmt<BlockExpr>,
+ check::PostObjCMessage,
+ check::Location,
+ eval::Assume>
+{
+public:
+ MallocChecker()
+ : II_alloca(nullptr), II_win_alloca(nullptr), II_malloc(nullptr),
+ II_free(nullptr), II_realloc(nullptr), II_calloc(nullptr),
+ II_valloc(nullptr), II_reallocf(nullptr), II_strndup(nullptr),
+ II_strdup(nullptr), II_win_strdup(nullptr), II_kmalloc(nullptr),
+ II_if_nameindex(nullptr), II_if_freenameindex(nullptr),
+ II_wcsdup(nullptr), II_win_wcsdup(nullptr), II_g_malloc(nullptr),
+ II_g_malloc0(nullptr), II_g_realloc(nullptr), II_g_try_malloc(nullptr),
+ II_g_try_malloc0(nullptr), II_g_try_realloc(nullptr),
+ II_g_free(nullptr), II_g_memdup(nullptr), II_g_malloc_n(nullptr),
+ II_g_malloc0_n(nullptr), II_g_realloc_n(nullptr),
+ II_g_try_malloc_n(nullptr), II_g_try_malloc0_n(nullptr),
+ II_g_try_realloc_n(nullptr) {}
+
+ /// In pessimistic mode, the checker assumes that it does not know which
+ /// functions might free the memory.
+ enum CheckKind {
+ CK_MallocChecker,
+ CK_NewDeleteChecker,
+ CK_NewDeleteLeaksChecker,
+ CK_MismatchedDeallocatorChecker,
+ CK_InnerPointerChecker,
+ CK_NumCheckKinds
+ };
+
+ enum class MemoryOperationKind {
+ MOK_Allocate,
+ MOK_Free,
+ MOK_Any
+ };
+
+ DefaultBool IsOptimistic;
+
+ DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ CheckName CheckNames[CK_NumCheckKinds];
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
+ void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
+ void checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+ CheckerContext &C) const;
+ void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &Call, CheckerContext &C) const;
+ void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *S, CheckerContext &C) const;
+ ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
+ bool Assumption) const;
+ void checkLocation(SVal l, bool isLoad, const Stmt *S,
+ CheckerContext &C) const;
+
+ ProgramStateRef checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
+ ProgramStateRef checkConstPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
+
+ void printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const override;
+
+private:
+ mutable std::unique_ptr<BugType> BT_DoubleFree[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_DoubleDelete;
+ mutable std::unique_ptr<BugType> BT_Leak[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_UseFree[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_BadFree[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_FreeAlloca[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_MismatchedDealloc;
+ mutable std::unique_ptr<BugType> BT_OffsetFree[CK_NumCheckKinds];
+ mutable std::unique_ptr<BugType> BT_UseZerroAllocated[CK_NumCheckKinds];
+ mutable IdentifierInfo *II_alloca, *II_win_alloca, *II_malloc, *II_free,
+ *II_realloc, *II_calloc, *II_valloc, *II_reallocf,
+ *II_strndup, *II_strdup, *II_win_strdup, *II_kmalloc,
+ *II_if_nameindex, *II_if_freenameindex, *II_wcsdup,
+ *II_win_wcsdup, *II_g_malloc, *II_g_malloc0,
+ *II_g_realloc, *II_g_try_malloc, *II_g_try_malloc0,
+ *II_g_try_realloc, *II_g_free, *II_g_memdup,
+ *II_g_malloc_n, *II_g_malloc0_n, *II_g_realloc_n,
+ *II_g_try_malloc_n, *II_g_try_malloc0_n,
+ *II_g_try_realloc_n;
+ mutable Optional<uint64_t> KernelZeroFlagVal;
+
+ void initIdentifierInfo(ASTContext &C) const;
+
+ /// Determine family of a deallocation expression.
+ AllocationFamily getAllocationFamily(CheckerContext &C, const Stmt *S) const;
+
+ /// Print names of allocators and deallocators.
+ ///
+ /// \returns true on success.
+ bool printAllocDeallocName(raw_ostream &os, CheckerContext &C,
+ const Expr *E) const;
+
+ /// Print expected name of an allocator based on the deallocator's
+ /// family derived from the DeallocExpr.
+ void printExpectedAllocName(raw_ostream &os, CheckerContext &C,
+ const Expr *DeallocExpr) const;
+ /// Print expected name of a deallocator based on the allocator's
+ /// family.
+ void printExpectedDeallocName(raw_ostream &os, AllocationFamily Family) const;
+
+ ///@{
+ /// Check if this is one of the functions which can allocate/reallocate memory
+ /// pointed to by one of its arguments.
+ bool isMemFunction(const FunctionDecl *FD, ASTContext &C) const;
+ bool isCMemFunction(const FunctionDecl *FD,
+ ASTContext &C,
+ AllocationFamily Family,
+ MemoryOperationKind MemKind) const;
+ bool isStandardNewDelete(const FunctionDecl *FD, ASTContext &C) const;
+ ///@}
+
+ /// Process C++ operator new()'s allocation, which is the part of C++
+ /// new-expression that goes before the constructor.
+ void processNewAllocation(const CXXNewExpr *NE, CheckerContext &C,
+ SVal Target) const;
+
+ /// Perform a zero-allocation check.
+ /// The optional \p RetVal parameter specifies the newly allocated pointer
+ /// value; if unspecified, the value of expression \p E is used.
+ ProgramStateRef ProcessZeroAllocation(CheckerContext &C, const Expr *E,
+ const unsigned AllocationSizeArg,
+ ProgramStateRef State,
+ Optional<SVal> RetVal = None) const;
+
+ ProgramStateRef MallocMemReturnsAttr(CheckerContext &C,
+ const CallExpr *CE,
+ const OwnershipAttr* Att,
+ ProgramStateRef State) const;
+ static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
+ const Expr *SizeEx, SVal Init,
+ ProgramStateRef State,
+ AllocationFamily Family = AF_Malloc);
+ static ProgramStateRef MallocMemAux(CheckerContext &C, const CallExpr *CE,
+ SVal SizeEx, SVal Init,
+ ProgramStateRef State,
+ AllocationFamily Family = AF_Malloc);
+
+ static ProgramStateRef addExtentSize(CheckerContext &C, const CXXNewExpr *NE,
+ ProgramStateRef State, SVal Target);
+
+ // Check if this malloc() for special flags. At present that means M_ZERO or
+ // __GFP_ZERO (in which case, treat it like calloc).
+ llvm::Optional<ProgramStateRef>
+ performKernelMalloc(const CallExpr *CE, CheckerContext &C,
+ const ProgramStateRef &State) const;
+
+ /// Update the RefState to reflect the new memory allocation.
+ /// The optional \p RetVal parameter specifies the newly allocated pointer
+ /// value; if unspecified, the value of expression \p E is used.
+ static ProgramStateRef
+ MallocUpdateRefState(CheckerContext &C, const Expr *E, ProgramStateRef State,
+ AllocationFamily Family = AF_Malloc,
+ Optional<SVal> RetVal = None);
+
+ ProgramStateRef FreeMemAttr(CheckerContext &C, const CallExpr *CE,
+ const OwnershipAttr* Att,
+ ProgramStateRef State) const;
+ ProgramStateRef FreeMemAux(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef state, unsigned Num,
+ bool Hold,
+ bool &ReleasedAllocated,
+ bool ReturnsNullOnFailure = false) const;
+ ProgramStateRef FreeMemAux(CheckerContext &C, const Expr *Arg,
+ const Expr *ParentExpr,
+ ProgramStateRef State,
+ bool Hold,
+ bool &ReleasedAllocated,
+ bool ReturnsNullOnFailure = false) const;
+
+ ProgramStateRef ReallocMemAux(CheckerContext &C, const CallExpr *CE,
+ bool FreesMemOnFailure,
+ ProgramStateRef State,
+ bool SuffixWithN = false) const;
+ static SVal evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
+ const Expr *BlockBytes);
+ static ProgramStateRef CallocMem(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State);
+
+ /// Check if the memory associated with this symbol was released.
+ bool isReleased(SymbolRef Sym, CheckerContext &C) const;
+
+ bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
+
+ void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
+ const Stmt *S) const;
+
+ bool checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const;
+
+ /// Check if the function is known free memory, or if it is
+ /// "interesting" and should be modeled explicitly.
+ ///
+ /// \param [out] EscapingSymbol A function might not free memory in general,
+ /// but could be known to free a particular symbol. In this case, false is
+ /// returned and the single escaping symbol is returned through the out
+ /// parameter.
+ ///
+ /// We assume that pointers do not escape through calls to system functions
+ /// not handled by this checker.
+ bool mayFreeAnyEscapedMemoryOrIsModeledExplicitly(const CallEvent *Call,
+ ProgramStateRef State,
+ SymbolRef &EscapingSymbol) const;
+
+ // Implementation of the checkPointerEscape callbacks.
+ ProgramStateRef checkPointerEscapeAux(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind,
+ bool(*CheckRefState)(const RefState*)) const;
+
+ // Implementation of the checkPreStmt and checkEndFunction callbacks.
+ void checkEscapeOnReturn(const ReturnStmt *S, CheckerContext &C) const;
+
+ ///@{
+ /// Tells if a given family/call/symbol is tracked by the current checker.
+ /// Sets CheckKind to the kind of the checker responsible for this
+ /// family/call/symbol.
+ Optional<CheckKind> getCheckIfTracked(AllocationFamily Family,
+ bool IsALeakCheck = false) const;
+ Optional<CheckKind> getCheckIfTracked(CheckerContext &C,
+ const Stmt *AllocDeallocStmt,
+ bool IsALeakCheck = false) const;
+ Optional<CheckKind> getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
+ bool IsALeakCheck = false) const;
+ ///@}
+ static bool SummarizeValue(raw_ostream &os, SVal V);
+ static bool SummarizeRegion(raw_ostream &os, const MemRegion *MR);
+ void ReportBadFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
+ const Expr *DeallocExpr) const;
+ void ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
+ SourceRange Range) const;
+ void ReportMismatchedDealloc(CheckerContext &C, SourceRange Range,
+ const Expr *DeallocExpr, const RefState *RS,
+ SymbolRef Sym, bool OwnershipTransferred) const;
+ void ReportOffsetFree(CheckerContext &C, SVal ArgVal, SourceRange Range,
+ const Expr *DeallocExpr,
+ const Expr *AllocExpr = nullptr) const;
+ void ReportUseAfterFree(CheckerContext &C, SourceRange Range,
+ SymbolRef Sym) const;
+ void ReportDoubleFree(CheckerContext &C, SourceRange Range, bool Released,
+ SymbolRef Sym, SymbolRef PrevSym) const;
+
+ void ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const;
+
+ void ReportUseZeroAllocated(CheckerContext &C, SourceRange Range,
+ SymbolRef Sym) const;
+
+ void ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal,
+ SourceRange Range, const Expr *FreeExpr) const;
+
+ /// Find the location of the allocation for Sym on the path leading to the
+ /// exploded node N.
+ LeakInfo getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
+ CheckerContext &C) const;
+
+ void reportLeak(SymbolRef Sym, ExplodedNode *N, CheckerContext &C) const;
+
+ /// The bug visitor which allows us to print extra diagnostics along the
+ /// BugReport path. For example, showing the allocation site of the leaked
+ /// region.
+ class MallocBugVisitor final : public BugReporterVisitor {
+ protected:
+ enum NotificationMode {
+ Normal,
+ ReallocationFailed
+ };
+
+ // The allocated region symbol tracked by the main analysis.
+ SymbolRef Sym;
+
+ // The mode we are in, i.e. what kind of diagnostics will be emitted.
+ NotificationMode Mode;
+
+ // A symbol from when the primary region should have been reallocated.
+ SymbolRef FailedReallocSymbol;
+
+ // A C++ destructor stack frame in which memory was released. Used for
+ // miscellaneous false positive suppression.
+ const StackFrameContext *ReleaseDestructorLC;
+
+ bool IsLeak;
+
+ public:
+ MallocBugVisitor(SymbolRef S, bool isLeak = false)
+ : Sym(S), Mode(Normal), FailedReallocSymbol(nullptr),
+ ReleaseDestructorLC(nullptr), IsLeak(isLeak) {}
+
+ static void *getTag() {
+ static int Tag = 0;
+ return &Tag;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddPointer(getTag());
+ ID.AddPointer(Sym);
+ }
+
+ inline bool isAllocated(const RefState *S, const RefState *SPrev,
+ const Stmt *Stmt) {
+ // Did not track -> allocated. Other state (released) -> allocated.
+ return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
+ (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) &&
+ (!SPrev || !(SPrev->isAllocated() ||
+ SPrev->isAllocatedOfSizeZero())));
+ }
+
+ inline bool isReleased(const RefState *S, const RefState *SPrev,
+ const Stmt *Stmt) {
+ // Did not track -> released. Other state (allocated) -> released.
+ // The statement associated with the release might be missing.
+ bool IsReleased = (S && S->isReleased()) &&
+ (!SPrev || !SPrev->isReleased());
+ assert(!IsReleased ||
+ (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt))) ||
+ (!Stmt && S->getAllocationFamily() == AF_InnerBuffer));
+ return IsReleased;
+ }
+
+ inline bool isRelinquished(const RefState *S, const RefState *SPrev,
+ const Stmt *Stmt) {
+ // Did not track -> relinquished. Other state (allocated) -> relinquished.
+ return (Stmt && (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) ||
+ isa<ObjCPropertyRefExpr>(Stmt)) &&
+ (S && S->isRelinquished()) &&
+ (!SPrev || !SPrev->isRelinquished()));
+ }
+
+ inline bool isReallocFailedCheck(const RefState *S, const RefState *SPrev,
+ const Stmt *Stmt) {
+ // If the expression is not a call, and the state change is
+ // released -> allocated, it must be the realloc return value
+ // check. If we have to handle more cases here, it might be cleaner just
+ // to track this extra bit in the state itself.
+ return ((!Stmt || !isa<CallExpr>(Stmt)) &&
+ (S && (S->isAllocated() || S->isAllocatedOfSizeZero())) &&
+ (SPrev && !(SPrev->isAllocated() ||
+ SPrev->isAllocatedOfSizeZero())));
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ std::shared_ptr<PathDiagnosticPiece>
+ getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
+ BugReport &BR) override {
+ if (!IsLeak)
+ return nullptr;
+
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::createEndOfPath(EndPathNode,
+ BRC.getSourceManager());
+ // Do not add the statement itself as a range in case of leak.
+ return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(),
+ false);
+ }
+
+ private:
+ class StackHintGeneratorForReallocationFailed
+ : public StackHintGeneratorForSymbol {
+ public:
+ StackHintGeneratorForReallocationFailed(SymbolRef S, StringRef M)
+ : StackHintGeneratorForSymbol(S, M) {}
+
+ std::string getMessageForArg(const Expr *ArgE,
+ unsigned ArgIndex) override {
+ // Printed parameters start at 1, not 0.
+ ++ArgIndex;
+
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << "Reallocation of " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex)
+ << " parameter failed";
+
+ return os.str();
+ }
+
+ std::string getMessageForReturn(const CallExpr *CallExpr) override {
+ return "Reallocation of returned value failed";
+ }
+ };
+ };
+};
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, SymbolRef, RefState)
+REGISTER_MAP_WITH_PROGRAMSTATE(ReallocPairs, SymbolRef, ReallocPair)
+REGISTER_SET_WITH_PROGRAMSTATE(ReallocSizeZeroSymbols, SymbolRef)
+
+// A map from the freed symbol to the symbol representing the return value of
+// the free function.
+REGISTER_MAP_WITH_PROGRAMSTATE(FreeReturnValue, SymbolRef, SymbolRef)
+
+namespace {
+class StopTrackingCallback final : public SymbolVisitor {
+ ProgramStateRef state;
+public:
+ StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {}
+ ProgramStateRef getState() const { return state; }
+
+ bool VisitSymbol(SymbolRef sym) override {
+ state = state->remove<RegionState>(sym);
+ return true;
+ }
+};
+} // end anonymous namespace
+
+void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
+ if (II_malloc)
+ return;
+ II_alloca = &Ctx.Idents.get("alloca");
+ II_malloc = &Ctx.Idents.get("malloc");
+ II_free = &Ctx.Idents.get("free");
+ II_realloc = &Ctx.Idents.get("realloc");
+ II_reallocf = &Ctx.Idents.get("reallocf");
+ II_calloc = &Ctx.Idents.get("calloc");
+ II_valloc = &Ctx.Idents.get("valloc");
+ II_strdup = &Ctx.Idents.get("strdup");
+ II_strndup = &Ctx.Idents.get("strndup");
+ II_wcsdup = &Ctx.Idents.get("wcsdup");
+ II_kmalloc = &Ctx.Idents.get("kmalloc");
+ II_if_nameindex = &Ctx.Idents.get("if_nameindex");
+ II_if_freenameindex = &Ctx.Idents.get("if_freenameindex");
+
+ //MSVC uses `_`-prefixed instead, so we check for them too.
+ II_win_strdup = &Ctx.Idents.get("_strdup");
+ II_win_wcsdup = &Ctx.Idents.get("_wcsdup");
+ II_win_alloca = &Ctx.Idents.get("_alloca");
+
+ // Glib
+ II_g_malloc = &Ctx.Idents.get("g_malloc");
+ II_g_malloc0 = &Ctx.Idents.get("g_malloc0");
+ II_g_realloc = &Ctx.Idents.get("g_realloc");
+ II_g_try_malloc = &Ctx.Idents.get("g_try_malloc");
+ II_g_try_malloc0 = &Ctx.Idents.get("g_try_malloc0");
+ II_g_try_realloc = &Ctx.Idents.get("g_try_realloc");
+ II_g_free = &Ctx.Idents.get("g_free");
+ II_g_memdup = &Ctx.Idents.get("g_memdup");
+ II_g_malloc_n = &Ctx.Idents.get("g_malloc_n");
+ II_g_malloc0_n = &Ctx.Idents.get("g_malloc0_n");
+ II_g_realloc_n = &Ctx.Idents.get("g_realloc_n");
+ II_g_try_malloc_n = &Ctx.Idents.get("g_try_malloc_n");
+ II_g_try_malloc0_n = &Ctx.Idents.get("g_try_malloc0_n");
+ II_g_try_realloc_n = &Ctx.Idents.get("g_try_realloc_n");
+}
+
+bool MallocChecker::isMemFunction(const FunctionDecl *FD, ASTContext &C) const {
+ if (isCMemFunction(FD, C, AF_Malloc, MemoryOperationKind::MOK_Any))
+ return true;
+
+ if (isCMemFunction(FD, C, AF_IfNameIndex, MemoryOperationKind::MOK_Any))
+ return true;
+
+ if (isCMemFunction(FD, C, AF_Alloca, MemoryOperationKind::MOK_Any))
+ return true;
+
+ if (isStandardNewDelete(FD, C))
+ return true;
+
+ return false;
+}
+
+bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
+ ASTContext &C,
+ AllocationFamily Family,
+ MemoryOperationKind MemKind) const {
+ if (!FD)
+ return false;
+
+ bool CheckFree = (MemKind == MemoryOperationKind::MOK_Any ||
+ MemKind == MemoryOperationKind::MOK_Free);
+ bool CheckAlloc = (MemKind == MemoryOperationKind::MOK_Any ||
+ MemKind == MemoryOperationKind::MOK_Allocate);
+
+ if (FD->getKind() == Decl::Function) {
+ const IdentifierInfo *FunI = FD->getIdentifier();
+ initIdentifierInfo(C);
+
+ if (Family == AF_Malloc && CheckFree) {
+ if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf ||
+ FunI == II_g_free)
+ return true;
+ }
+
+ if (Family == AF_Malloc && CheckAlloc) {
+ if (FunI == II_malloc || FunI == II_realloc || FunI == II_reallocf ||
+ FunI == II_calloc || FunI == II_valloc || FunI == II_strdup ||
+ FunI == II_win_strdup || FunI == II_strndup || FunI == II_wcsdup ||
+ FunI == II_win_wcsdup || FunI == II_kmalloc ||
+ FunI == II_g_malloc || FunI == II_g_malloc0 ||
+ FunI == II_g_realloc || FunI == II_g_try_malloc ||
+ FunI == II_g_try_malloc0 || FunI == II_g_try_realloc ||
+ FunI == II_g_memdup || FunI == II_g_malloc_n ||
+ FunI == II_g_malloc0_n || FunI == II_g_realloc_n ||
+ FunI == II_g_try_malloc_n || FunI == II_g_try_malloc0_n ||
+ FunI == II_g_try_realloc_n)
+ return true;
+ }
+
+ if (Family == AF_IfNameIndex && CheckFree) {
+ if (FunI == II_if_freenameindex)
+ return true;
+ }
+
+ if (Family == AF_IfNameIndex && CheckAlloc) {
+ if (FunI == II_if_nameindex)
+ return true;
+ }
+
+ if (Family == AF_Alloca && CheckAlloc) {
+ if (FunI == II_alloca || FunI == II_win_alloca)
+ return true;
+ }
+ }
+
+ if (Family != AF_Malloc)
+ return false;
+
+ if (IsOptimistic && FD->hasAttrs()) {
+ for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
+ OwnershipAttr::OwnershipKind OwnKind = I->getOwnKind();
+ if(OwnKind == OwnershipAttr::Takes || OwnKind == OwnershipAttr::Holds) {
+ if (CheckFree)
+ return true;
+ } else if (OwnKind == OwnershipAttr::Returns) {
+ if (CheckAlloc)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
+// Tells if the callee is one of the builtin new/delete operators, including
+// placement operators and other standard overloads.
+bool MallocChecker::isStandardNewDelete(const FunctionDecl *FD,
+ ASTContext &C) const {
+ if (!FD)
+ return false;
+
+ OverloadedOperatorKind Kind = FD->getOverloadedOperator();
+ if (Kind != OO_New && Kind != OO_Array_New &&
+ Kind != OO_Delete && Kind != OO_Array_Delete)
+ return false;
+
+ // This is standard if and only if it's not defined in a user file.
+ SourceLocation L = FD->getLocation();
+ // If the header for operator delete is not included, it's still defined
+ // in an invalid source location. Check to make sure we don't crash.
+ return !L.isValid() || C.getSourceManager().isInSystemHeader(L);
+}
+
+llvm::Optional<ProgramStateRef> MallocChecker::performKernelMalloc(
+ const CallExpr *CE, CheckerContext &C, const ProgramStateRef &State) const {
+ // 3-argument malloc(), as commonly used in {Free,Net,Open}BSD Kernels:
+ //
+ // void *malloc(unsigned long size, struct malloc_type *mtp, int flags);
+ //
+ // One of the possible flags is M_ZERO, which means 'give me back an
+ // allocation which is already zeroed', like calloc.
+
+ // 2-argument kmalloc(), as used in the Linux kernel:
+ //
+ // void *kmalloc(size_t size, gfp_t flags);
+ //
+ // Has the similar flag value __GFP_ZERO.
+
+ // This logic is largely cloned from O_CREAT in UnixAPIChecker, maybe some
+ // code could be shared.
+
+ ASTContext &Ctx = C.getASTContext();
+ llvm::Triple::OSType OS = Ctx.getTargetInfo().getTriple().getOS();
+
+ if (!KernelZeroFlagVal.hasValue()) {
+ if (OS == llvm::Triple::FreeBSD)
+ KernelZeroFlagVal = 0x0100;
+ else if (OS == llvm::Triple::NetBSD)
+ KernelZeroFlagVal = 0x0002;
+ else if (OS == llvm::Triple::OpenBSD)
+ KernelZeroFlagVal = 0x0008;
+ else if (OS == llvm::Triple::Linux)
+ // __GFP_ZERO
+ KernelZeroFlagVal = 0x8000;
+ else
+ // FIXME: We need a more general way of getting the M_ZERO value.
+ // See also: O_CREAT in UnixAPIChecker.cpp.
+
+ // Fall back to normal malloc behavior on platforms where we don't
+ // know M_ZERO.
+ return None;
+ }
+
+ // We treat the last argument as the flags argument, and callers fall-back to
+ // normal malloc on a None return. This works for the FreeBSD kernel malloc
+ // as well as Linux kmalloc.
+ if (CE->getNumArgs() < 2)
+ return None;
+
+ const Expr *FlagsEx = CE->getArg(CE->getNumArgs() - 1);
+ const SVal V = C.getSVal(FlagsEx);
+ if (!V.getAs<NonLoc>()) {
+ // The case where 'V' can be a location can only be due to a bad header,
+ // so in this case bail out.
+ return None;
+ }
+
+ NonLoc Flags = V.castAs<NonLoc>();
+ NonLoc ZeroFlag = C.getSValBuilder()
+ .makeIntVal(KernelZeroFlagVal.getValue(), FlagsEx->getType())
+ .castAs<NonLoc>();
+ SVal MaskedFlagsUC = C.getSValBuilder().evalBinOpNN(State, BO_And,
+ Flags, ZeroFlag,
+ FlagsEx->getType());
+ if (MaskedFlagsUC.isUnknownOrUndef())
+ return None;
+ DefinedSVal MaskedFlags = MaskedFlagsUC.castAs<DefinedSVal>();
+
+ // Check if maskedFlags is non-zero.
+ ProgramStateRef TrueState, FalseState;
+ std::tie(TrueState, FalseState) = State->assume(MaskedFlags);
+
+ // If M_ZERO is set, treat this like calloc (initialized).
+ if (TrueState && !FalseState) {
+ SVal ZeroVal = C.getSValBuilder().makeZeroVal(Ctx.CharTy);
+ return MallocMemAux(C, CE, CE->getArg(0), ZeroVal, TrueState);
+ }
+
+ return None;
+}
+
+SVal MallocChecker::evalMulForBufferSize(CheckerContext &C, const Expr *Blocks,
+ const Expr *BlockBytes) {
+ SValBuilder &SB = C.getSValBuilder();
+ SVal BlocksVal = C.getSVal(Blocks);
+ SVal BlockBytesVal = C.getSVal(BlockBytes);
+ ProgramStateRef State = C.getState();
+ SVal TotalSize = SB.evalBinOp(State, BO_Mul, BlocksVal, BlockBytesVal,
+ SB.getContext().getSizeType());
+ return TotalSize;
+}
+
+void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
+ if (C.wasInlined)
+ return;
+
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD)
+ return;
+
+ ProgramStateRef State = C.getState();
+ bool ReleasedAllocatedMemory = false;
+
+ if (FD->getKind() == Decl::Function) {
+ initIdentifierInfo(C.getASTContext());
+ IdentifierInfo *FunI = FD->getIdentifier();
+
+ if (FunI == II_malloc || FunI == II_g_malloc || FunI == II_g_try_malloc) {
+ if (CE->getNumArgs() < 1)
+ return;
+ if (CE->getNumArgs() < 3) {
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ if (CE->getNumArgs() == 1)
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ } else if (CE->getNumArgs() == 3) {
+ llvm::Optional<ProgramStateRef> MaybeState =
+ performKernelMalloc(CE, C, State);
+ if (MaybeState.hasValue())
+ State = MaybeState.getValue();
+ else
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ }
+ } else if (FunI == II_kmalloc) {
+ if (CE->getNumArgs() < 1)
+ return;
+ llvm::Optional<ProgramStateRef> MaybeState =
+ performKernelMalloc(CE, C, State);
+ if (MaybeState.hasValue())
+ State = MaybeState.getValue();
+ else
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ } else if (FunI == II_valloc) {
+ if (CE->getNumArgs() < 1)
+ return;
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ } else if (FunI == II_realloc || FunI == II_g_realloc ||
+ FunI == II_g_try_realloc) {
+ State = ReallocMemAux(C, CE, false, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
+ } else if (FunI == II_reallocf) {
+ State = ReallocMemAux(C, CE, true, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
+ } else if (FunI == II_calloc) {
+ State = CallocMem(C, CE, State);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
+ } else if (FunI == II_free || FunI == II_g_free) {
+ State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
+ } else if (FunI == II_strdup || FunI == II_win_strdup ||
+ FunI == II_wcsdup || FunI == II_win_wcsdup) {
+ State = MallocUpdateRefState(C, CE, State);
+ } else if (FunI == II_strndup) {
+ State = MallocUpdateRefState(C, CE, State);
+ } else if (FunI == II_alloca || FunI == II_win_alloca) {
+ if (CE->getNumArgs() < 1)
+ return;
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+ AF_Alloca);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ } else if (isStandardNewDelete(FD, C.getASTContext())) {
+ // Process direct calls to operator new/new[]/delete/delete[] functions
+ // as distinct from new/new[]/delete/delete[] expressions that are
+ // processed by the checkPostStmt callbacks for CXXNewExpr and
+ // CXXDeleteExpr.
+ OverloadedOperatorKind K = FD->getOverloadedOperator();
+ if (K == OO_New) {
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+ AF_CXXNew);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ }
+ else if (K == OO_Array_New) {
+ State = MallocMemAux(C, CE, CE->getArg(0), UndefinedVal(), State,
+ AF_CXXNewArray);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ }
+ else if (K == OO_Delete || K == OO_Array_Delete)
+ State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
+ else
+ llvm_unreachable("not a new/delete operator");
+ } else if (FunI == II_if_nameindex) {
+ // Should we model this differently? We can allocate a fixed number of
+ // elements with zeros in the last one.
+ State = MallocMemAux(C, CE, UnknownVal(), UnknownVal(), State,
+ AF_IfNameIndex);
+ } else if (FunI == II_if_freenameindex) {
+ State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
+ } else if (FunI == II_g_malloc0 || FunI == II_g_try_malloc0) {
+ if (CE->getNumArgs() < 1)
+ return;
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
+ State = MallocMemAux(C, CE, CE->getArg(0), zeroVal, State);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ } else if (FunI == II_g_memdup) {
+ if (CE->getNumArgs() < 2)
+ return;
+ State = MallocMemAux(C, CE, CE->getArg(1), UndefinedVal(), State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
+ } else if (FunI == II_g_malloc_n || FunI == II_g_try_malloc_n ||
+ FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) {
+ if (CE->getNumArgs() < 2)
+ return;
+ SVal Init = UndefinedVal();
+ if (FunI == II_g_malloc0_n || FunI == II_g_try_malloc0_n) {
+ SValBuilder &SB = C.getSValBuilder();
+ Init = SB.makeZeroVal(SB.getContext().CharTy);
+ }
+ SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
+ State = MallocMemAux(C, CE, TotalSize, Init, State);
+ State = ProcessZeroAllocation(C, CE, 0, State);
+ State = ProcessZeroAllocation(C, CE, 1, State);
+ } else if (FunI == II_g_realloc_n || FunI == II_g_try_realloc_n) {
+ if (CE->getNumArgs() < 3)
+ return;
+ State = ReallocMemAux(C, CE, false, State, true);
+ State = ProcessZeroAllocation(C, CE, 1, State);
+ State = ProcessZeroAllocation(C, CE, 2, State);
+ }
+ }
+
+ if (IsOptimistic || ChecksEnabled[CK_MismatchedDeallocatorChecker]) {
+ // Check all the attributes, if there are any.
+ // There can be multiple of these attributes.
+ if (FD->hasAttrs())
+ for (const auto *I : FD->specific_attrs<OwnershipAttr>()) {
+ switch (I->getOwnKind()) {
+ case OwnershipAttr::Returns:
+ State = MallocMemReturnsAttr(C, CE, I, State);
+ break;
+ case OwnershipAttr::Takes:
+ case OwnershipAttr::Holds:
+ State = FreeMemAttr(C, CE, I, State);
+ break;
+ }
+ }
+ }
+ C.addTransition(State);
+}
+
+// Performs a 0-sized allocations check.
+ProgramStateRef MallocChecker::ProcessZeroAllocation(
+ CheckerContext &C, const Expr *E, const unsigned AllocationSizeArg,
+ ProgramStateRef State, Optional<SVal> RetVal) const {
+ if (!State)
+ return nullptr;
+
+ if (!RetVal)
+ RetVal = C.getSVal(E);
+
+ const Expr *Arg = nullptr;
+
+ if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+ Arg = CE->getArg(AllocationSizeArg);
+ }
+ else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
+ if (NE->isArray())
+ Arg = NE->getArraySize();
+ else
+ return State;
+ }
+ else
+ llvm_unreachable("not a CallExpr or CXXNewExpr");
+
+ assert(Arg);
+
+ Optional<DefinedSVal> DefArgVal = C.getSVal(Arg).getAs<DefinedSVal>();
+
+ if (!DefArgVal)
+ return State;
+
+ // Check if the allocation size is 0.
+ ProgramStateRef TrueState, FalseState;
+ SValBuilder &SvalBuilder = C.getSValBuilder();
+ DefinedSVal Zero =
+ SvalBuilder.makeZeroVal(Arg->getType()).castAs<DefinedSVal>();
+
+ std::tie(TrueState, FalseState) =
+ State->assume(SvalBuilder.evalEQ(State, *DefArgVal, Zero));
+
+ if (TrueState && !FalseState) {
+ SymbolRef Sym = RetVal->getAsLocSymbol();
+ if (!Sym)
+ return State;
+
+ const RefState *RS = State->get<RegionState>(Sym);
+ if (RS) {
+ if (RS->isAllocated())
+ return TrueState->set<RegionState>(Sym,
+ RefState::getAllocatedOfSizeZero(RS));
+ else
+ return State;
+ } else {
+ // Case of zero-size realloc. Historically 'realloc(ptr, 0)' is treated as
+ // 'free(ptr)' and the returned value from 'realloc(ptr, 0)' is not
+ // tracked. Add zero-reallocated Sym to the state to catch references
+ // to zero-allocated memory.
+ return TrueState->add<ReallocSizeZeroSymbols>(Sym);
+ }
+ }
+
+ // Assume the value is non-zero going forward.
+ assert(FalseState);
+ return FalseState;
+}
+
+static QualType getDeepPointeeType(QualType T) {
+ QualType Result = T, PointeeType = T->getPointeeType();
+ while (!PointeeType.isNull()) {
+ Result = PointeeType;
+ PointeeType = PointeeType->getPointeeType();
+ }
+ return Result;
+}
+
+static bool treatUnusedNewEscaped(const CXXNewExpr *NE) {
+
+ const CXXConstructExpr *ConstructE = NE->getConstructExpr();
+ if (!ConstructE)
+ return false;
+
+ if (!NE->getAllocatedType()->getAsCXXRecordDecl())
+ return false;
+
+ const CXXConstructorDecl *CtorD = ConstructE->getConstructor();
+
+ // Iterate over the constructor parameters.
+ for (const auto *CtorParam : CtorD->parameters()) {
+
+ QualType CtorParamPointeeT = CtorParam->getType()->getPointeeType();
+ if (CtorParamPointeeT.isNull())
+ continue;
+
+ CtorParamPointeeT = getDeepPointeeType(CtorParamPointeeT);
+
+ if (CtorParamPointeeT->getAsCXXRecordDecl())
+ return true;
+ }
+
+ return false;
+}
+
+void MallocChecker::processNewAllocation(const CXXNewExpr *NE,
+ CheckerContext &C,
+ SVal Target) const {
+ if (!isStandardNewDelete(NE->getOperatorNew(), C.getASTContext()))
+ return;
+
+ ParentMap &PM = C.getLocationContext()->getParentMap();
+ if (!PM.isConsumedExpr(NE) && treatUnusedNewEscaped(NE))
+ return;
+
+ ProgramStateRef State = C.getState();
+ // The return value from operator new is bound to a specified initialization
+ // value (if any) and we don't want to loose this value. So we call
+ // MallocUpdateRefState() instead of MallocMemAux() which breaks the
+ // existing binding.
+ State = MallocUpdateRefState(C, NE, State, NE->isArray() ? AF_CXXNewArray
+ : AF_CXXNew, Target);
+ State = addExtentSize(C, NE, State, Target);
+ State = ProcessZeroAllocation(C, NE, 0, State, Target);
+ C.addTransition(State);
+}
+
+void MallocChecker::checkPostStmt(const CXXNewExpr *NE,
+ CheckerContext &C) const {
+ if (!C.getAnalysisManager().getAnalyzerOptions().MayInlineCXXAllocator)
+ processNewAllocation(NE, C, C.getSVal(NE));
+}
+
+void MallocChecker::checkNewAllocator(const CXXNewExpr *NE, SVal Target,
+ CheckerContext &C) const {
+ if (!C.wasInlined)
+ processNewAllocation(NE, C, Target);
+}
+
+// Sets the extent value of the MemRegion allocated by
+// new expression NE to its size in Bytes.
+//
+ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C,
+ const CXXNewExpr *NE,
+ ProgramStateRef State,
+ SVal Target) {
+ if (!State)
+ return nullptr;
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ SVal ElementCount;
+ const SubRegion *Region;
+ if (NE->isArray()) {
+ const Expr *SizeExpr = NE->getArraySize();
+ ElementCount = C.getSVal(SizeExpr);
+ // Store the extent size for the (symbolic)region
+ // containing the elements.
+ Region = Target.getAsRegion()
+ ->getAs<SubRegion>()
+ ->StripCasts()
+ ->getAs<SubRegion>();
+ } else {
+ ElementCount = svalBuilder.makeIntVal(1, true);
+ Region = Target.getAsRegion()->getAs<SubRegion>();
+ }
+ assert(Region);
+
+ // Set the region's extent equal to the Size in Bytes.
+ QualType ElementType = NE->getAllocatedType();
+ ASTContext &AstContext = C.getASTContext();
+ CharUnits TypeSize = AstContext.getTypeSizeInChars(ElementType);
+
+ if (ElementCount.getAs<NonLoc>()) {
+ DefinedOrUnknownSVal Extent = Region->getExtent(svalBuilder);
+ // size in Bytes = ElementCount*TypeSize
+ SVal SizeInBytes = svalBuilder.evalBinOpNN(
+ State, BO_Mul, ElementCount.castAs<NonLoc>(),
+ svalBuilder.makeArrayIndex(TypeSize.getQuantity()),
+ svalBuilder.getArrayIndexType());
+ DefinedOrUnknownSVal extentMatchesSize = svalBuilder.evalEQ(
+ State, Extent, SizeInBytes.castAs<DefinedOrUnknownSVal>());
+ State = State->assume(extentMatchesSize, true);
+ }
+ return State;
+}
+
+void MallocChecker::checkPreStmt(const CXXDeleteExpr *DE,
+ CheckerContext &C) const {
+
+ if (!ChecksEnabled[CK_NewDeleteChecker])
+ if (SymbolRef Sym = C.getSVal(DE->getArgument()).getAsSymbol())
+ checkUseAfterFree(Sym, C, DE->getArgument());
+
+ if (!isStandardNewDelete(DE->getOperatorDelete(), C.getASTContext()))
+ return;
+
+ ProgramStateRef State = C.getState();
+ bool ReleasedAllocated;
+ State = FreeMemAux(C, DE->getArgument(), DE, State,
+ /*Hold*/false, ReleasedAllocated);
+
+ C.addTransition(State);
+}
+
+static bool isKnownDeallocObjCMethodName(const ObjCMethodCall &Call) {
+ // If the first selector piece is one of the names below, assume that the
+ // object takes ownership of the memory, promising to eventually deallocate it
+ // with free().
+ // Ex: [NSData dataWithBytesNoCopy:bytes length:10];
+ // (...unless a 'freeWhenDone' parameter is false, but that's checked later.)
+ StringRef FirstSlot = Call.getSelector().getNameForSlot(0);
+ return FirstSlot == "dataWithBytesNoCopy" ||
+ FirstSlot == "initWithBytesNoCopy" ||
+ FirstSlot == "initWithCharactersNoCopy";
+}
+
+static Optional<bool> getFreeWhenDoneArg(const ObjCMethodCall &Call) {
+ Selector S = Call.getSelector();
+
+ // FIXME: We should not rely on fully-constrained symbols being folded.
+ for (unsigned i = 1; i < S.getNumArgs(); ++i)
+ if (S.getNameForSlot(i).equals("freeWhenDone"))
+ return !Call.getArgSVal(i).isZeroConstant();
+
+ return None;
+}
+
+void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
+ CheckerContext &C) const {
+ if (C.wasInlined)
+ return;
+
+ if (!isKnownDeallocObjCMethodName(Call))
+ return;
+
+ if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(Call))
+ if (!*FreeWhenDone)
+ return;
+
+ bool ReleasedAllocatedMemory;
+ ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0),
+ Call.getOriginExpr(), C.getState(),
+ /*Hold=*/true, ReleasedAllocatedMemory,
+ /*RetNullOnFailure=*/true);
+
+ C.addTransition(State);
+}
+
+ProgramStateRef
+MallocChecker::MallocMemReturnsAttr(CheckerContext &C, const CallExpr *CE,
+ const OwnershipAttr *Att,
+ ProgramStateRef State) const {
+ if (!State)
+ return nullptr;
+
+ if (Att->getModule() != II_malloc)
+ return nullptr;
+
+ OwnershipAttr::args_iterator I = Att->args_begin(), E = Att->args_end();
+ if (I != E) {
+ return MallocMemAux(C, CE, CE->getArg(I->getASTIndex()), UndefinedVal(),
+ State);
+ }
+ return MallocMemAux(C, CE, UnknownVal(), UndefinedVal(), State);
+}
+
+ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
+ const CallExpr *CE,
+ const Expr *SizeEx, SVal Init,
+ ProgramStateRef State,
+ AllocationFamily Family) {
+ if (!State)
+ return nullptr;
+
+ return MallocMemAux(C, CE, C.getSVal(SizeEx), Init, State, Family);
+}
+
+ProgramStateRef MallocChecker::MallocMemAux(CheckerContext &C,
+ const CallExpr *CE,
+ SVal Size, SVal Init,
+ ProgramStateRef State,
+ AllocationFamily Family) {
+ if (!State)
+ return nullptr;
+
+ // We expect the malloc functions to return a pointer.
+ if (!Loc::isLocType(CE->getType()))
+ return nullptr;
+
+ // Bind the return value to the symbolic value from the heap region.
+ // TODO: We could rewrite post visit to eval call; 'malloc' does not have
+ // side effects other than what we model here.
+ unsigned Count = C.blockCount();
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
+ DefinedSVal RetVal = svalBuilder.getConjuredHeapSymbolVal(CE, LCtx, Count)
+ .castAs<DefinedSVal>();
+ State = State->BindExpr(CE, C.getLocationContext(), RetVal);
+
+ // Fill the region with the initialization value.
+ State = State->bindDefaultInitial(RetVal, Init, LCtx);
+
+ // Set the region's extent equal to the Size parameter.
+ const SymbolicRegion *R =
+ dyn_cast_or_null<SymbolicRegion>(RetVal.getAsRegion());
+ if (!R)
+ return nullptr;
+ if (Optional<DefinedOrUnknownSVal> DefinedSize =
+ Size.getAs<DefinedOrUnknownSVal>()) {
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder);
+ DefinedOrUnknownSVal extentMatchesSize =
+ svalBuilder.evalEQ(State, Extent, *DefinedSize);
+
+ State = State->assume(extentMatchesSize, true);
+ assert(State);
+ }
+
+ return MallocUpdateRefState(C, CE, State, Family);
+}
+
+ProgramStateRef MallocChecker::MallocUpdateRefState(CheckerContext &C,
+ const Expr *E,
+ ProgramStateRef State,
+ AllocationFamily Family,
+ Optional<SVal> RetVal) {
+ if (!State)
+ return nullptr;
+
+ // Get the return value.
+ if (!RetVal)
+ RetVal = C.getSVal(E);
+
+ // We expect the malloc functions to return a pointer.
+ if (!RetVal->getAs<Loc>())
+ return nullptr;
+
+ SymbolRef Sym = RetVal->getAsLocSymbol();
+ // This is a return value of a function that was not inlined, such as malloc()
+ // or new(). We've checked that in the caller. Therefore, it must be a symbol.
+ assert(Sym);
+
+ // Set the symbol's state to Allocated.
+ return State->set<RegionState>(Sym, RefState::getAllocated(Family, E));
+}
+
+ProgramStateRef MallocChecker::FreeMemAttr(CheckerContext &C,
+ const CallExpr *CE,
+ const OwnershipAttr *Att,
+ ProgramStateRef State) const {
+ if (!State)
+ return nullptr;
+
+ if (Att->getModule() != II_malloc)
+ return nullptr;
+
+ bool ReleasedAllocated = false;
+
+ for (const auto &Arg : Att->args()) {
+ ProgramStateRef StateI = FreeMemAux(
+ C, CE, State, Arg.getASTIndex(),
+ Att->getOwnKind() == OwnershipAttr::Holds, ReleasedAllocated);
+ if (StateI)
+ State = StateI;
+ }
+ return State;
+}
+
+ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
+ const CallExpr *CE,
+ ProgramStateRef State,
+ unsigned Num,
+ bool Hold,
+ bool &ReleasedAllocated,
+ bool ReturnsNullOnFailure) const {
+ if (!State)
+ return nullptr;
+
+ if (CE->getNumArgs() < (Num + 1))
+ return nullptr;
+
+ return FreeMemAux(C, CE->getArg(Num), CE, State, Hold,
+ ReleasedAllocated, ReturnsNullOnFailure);
+}
+
+/// Checks if the previous call to free on the given symbol failed - if free
+/// failed, returns true. Also, returns the corresponding return value symbol.
+static bool didPreviousFreeFail(ProgramStateRef State,
+ SymbolRef Sym, SymbolRef &RetStatusSymbol) {
+ const SymbolRef *Ret = State->get<FreeReturnValue>(Sym);
+ if (Ret) {
+ assert(*Ret && "We should not store the null return symbol");
+ ConstraintManager &CMgr = State->getConstraintManager();
+ ConditionTruthVal FreeFailed = CMgr.isNull(State, *Ret);
+ RetStatusSymbol = *Ret;
+ return FreeFailed.isConstrainedTrue();
+ }
+ return false;
+}
+
+AllocationFamily MallocChecker::getAllocationFamily(CheckerContext &C,
+ const Stmt *S) const {
+ if (!S)
+ return AF_None;
+
+ if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+
+ if (!FD)
+ FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
+
+ ASTContext &Ctx = C.getASTContext();
+
+ if (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Any))
+ return AF_Malloc;
+
+ if (isStandardNewDelete(FD, Ctx)) {
+ OverloadedOperatorKind Kind = FD->getOverloadedOperator();
+ if (Kind == OO_New || Kind == OO_Delete)
+ return AF_CXXNew;
+ else if (Kind == OO_Array_New || Kind == OO_Array_Delete)
+ return AF_CXXNewArray;
+ }
+
+ if (isCMemFunction(FD, Ctx, AF_IfNameIndex, MemoryOperationKind::MOK_Any))
+ return AF_IfNameIndex;
+
+ if (isCMemFunction(FD, Ctx, AF_Alloca, MemoryOperationKind::MOK_Any))
+ return AF_Alloca;
+
+ return AF_None;
+ }
+
+ if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(S))
+ return NE->isArray() ? AF_CXXNewArray : AF_CXXNew;
+
+ if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(S))
+ return DE->isArrayForm() ? AF_CXXNewArray : AF_CXXNew;
+
+ if (isa<ObjCMessageExpr>(S))
+ return AF_Malloc;
+
+ return AF_None;
+}
+
+bool MallocChecker::printAllocDeallocName(raw_ostream &os, CheckerContext &C,
+ const Expr *E) const {
+ if (const CallExpr *CE = dyn_cast<CallExpr>(E)) {
+ // FIXME: This doesn't handle indirect calls.
+ const FunctionDecl *FD = CE->getDirectCallee();
+ if (!FD)
+ return false;
+
+ os << *FD;
+ if (!FD->isOverloadedOperator())
+ os << "()";
+ return true;
+ }
+
+ if (const ObjCMessageExpr *Msg = dyn_cast<ObjCMessageExpr>(E)) {
+ if (Msg->isInstanceMessage())
+ os << "-";
+ else
+ os << "+";
+ Msg->getSelector().print(os);
+ return true;
+ }
+
+ if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
+ os << "'"
+ << getOperatorSpelling(NE->getOperatorNew()->getOverloadedOperator())
+ << "'";
+ return true;
+ }
+
+ if (const CXXDeleteExpr *DE = dyn_cast<CXXDeleteExpr>(E)) {
+ os << "'"
+ << getOperatorSpelling(DE->getOperatorDelete()->getOverloadedOperator())
+ << "'";
+ return true;
+ }
+
+ return false;
+}
+
+void MallocChecker::printExpectedAllocName(raw_ostream &os, CheckerContext &C,
+ const Expr *E) const {
+ AllocationFamily Family = getAllocationFamily(C, E);
+
+ switch(Family) {
+ case AF_Malloc: os << "malloc()"; return;
+ case AF_CXXNew: os << "'new'"; return;
+ case AF_CXXNewArray: os << "'new[]'"; return;
+ case AF_IfNameIndex: os << "'if_nameindex()'"; return;
+ case AF_InnerBuffer: os << "container-specific allocator"; return;
+ case AF_Alloca:
+ case AF_None: llvm_unreachable("not a deallocation expression");
+ }
+}
+
+void MallocChecker::printExpectedDeallocName(raw_ostream &os,
+ AllocationFamily Family) const {
+ switch(Family) {
+ case AF_Malloc: os << "free()"; return;
+ case AF_CXXNew: os << "'delete'"; return;
+ case AF_CXXNewArray: os << "'delete[]'"; return;
+ case AF_IfNameIndex: os << "'if_freenameindex()'"; return;
+ case AF_InnerBuffer: os << "container-specific deallocator"; return;
+ case AF_Alloca:
+ case AF_None: llvm_unreachable("suspicious argument");
+ }
+}
+
+ProgramStateRef MallocChecker::FreeMemAux(CheckerContext &C,
+ const Expr *ArgExpr,
+ const Expr *ParentExpr,
+ ProgramStateRef State,
+ bool Hold,
+ bool &ReleasedAllocated,
+ bool ReturnsNullOnFailure) const {
+
+ if (!State)
+ return nullptr;
+
+ SVal ArgVal = C.getSVal(ArgExpr);
+ if (!ArgVal.getAs<DefinedOrUnknownSVal>())
+ return nullptr;
+ DefinedOrUnknownSVal location = ArgVal.castAs<DefinedOrUnknownSVal>();
+
+ // Check for null dereferences.
+ if (!location.getAs<Loc>())
+ return nullptr;
+
+ // The explicit NULL case, no operation is performed.
+ ProgramStateRef notNullState, nullState;
+ std::tie(notNullState, nullState) = State->assume(location);
+ if (nullState && !notNullState)
+ return nullptr;
+
+ // Unknown values could easily be okay
+ // Undefined values are handled elsewhere
+ if (ArgVal.isUnknownOrUndef())
+ return nullptr;
+
+ const MemRegion *R = ArgVal.getAsRegion();
+
+ // Nonlocs can't be freed, of course.
+ // Non-region locations (labels and fixed addresses) also shouldn't be freed.
+ if (!R) {
+ ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+ return nullptr;
+ }
+
+ R = R->StripCasts();
+
+ // Blocks might show up as heap data, but should not be free()d
+ if (isa<BlockDataRegion>(R)) {
+ ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+ return nullptr;
+ }
+
+ const MemSpaceRegion *MS = R->getMemorySpace();
+
+ // Parameters, locals, statics, globals, and memory returned by
+ // __builtin_alloca() shouldn't be freed.
+ if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) {
+ // FIXME: at the time this code was written, malloc() regions were
+ // represented by conjured symbols, which are all in UnknownSpaceRegion.
+ // This means that there isn't actually anything from HeapSpaceRegion
+ // that should be freed, even though we allow it here.
+ // Of course, free() can work on memory allocated outside the current
+ // function, so UnknownSpaceRegion is always a possibility.
+ // False negatives are better than false positives.
+
+ if (isa<AllocaRegion>(R))
+ ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
+ else
+ ReportBadFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+
+ return nullptr;
+ }
+
+ const SymbolicRegion *SrBase = dyn_cast<SymbolicRegion>(R->getBaseRegion());
+ // Various cases could lead to non-symbol values here.
+ // For now, ignore them.
+ if (!SrBase)
+ return nullptr;
+
+ SymbolRef SymBase = SrBase->getSymbol();
+ const RefState *RsBase = State->get<RegionState>(SymBase);
+ SymbolRef PreviousRetStatusSymbol = nullptr;
+
+ if (RsBase) {
+
+ // Memory returned by alloca() shouldn't be freed.
+ if (RsBase->getAllocationFamily() == AF_Alloca) {
+ ReportFreeAlloca(C, ArgVal, ArgExpr->getSourceRange());
+ return nullptr;
+ }
+
+ // Check for double free first.
+ if ((RsBase->isReleased() || RsBase->isRelinquished()) &&
+ !didPreviousFreeFail(State, SymBase, PreviousRetStatusSymbol)) {
+ ReportDoubleFree(C, ParentExpr->getSourceRange(), RsBase->isReleased(),
+ SymBase, PreviousRetStatusSymbol);
+ return nullptr;
+
+ // If the pointer is allocated or escaped, but we are now trying to free it,
+ // check that the call to free is proper.
+ } else if (RsBase->isAllocated() || RsBase->isAllocatedOfSizeZero() ||
+ RsBase->isEscaped()) {
+
+ // Check if an expected deallocation function matches the real one.
+ bool DeallocMatchesAlloc =
+ RsBase->getAllocationFamily() == getAllocationFamily(C, ParentExpr);
+ if (!DeallocMatchesAlloc) {
+ ReportMismatchedDealloc(C, ArgExpr->getSourceRange(),
+ ParentExpr, RsBase, SymBase, Hold);
+ return nullptr;
+ }
+
+ // Check if the memory location being freed is the actual location
+ // allocated, or an offset.
+ RegionOffset Offset = R->getAsOffset();
+ if (Offset.isValid() &&
+ !Offset.hasSymbolicOffset() &&
+ Offset.getOffset() != 0) {
+ const Expr *AllocExpr = cast<Expr>(RsBase->getStmt());
+ ReportOffsetFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr,
+ AllocExpr);
+ return nullptr;
+ }
+ }
+ }
+
+ if (SymBase->getType()->isFunctionPointerType()) {
+ ReportFunctionPointerFree(C, ArgVal, ArgExpr->getSourceRange(), ParentExpr);
+ return nullptr;
+ }
+
+ ReleasedAllocated = (RsBase != nullptr) && (RsBase->isAllocated() ||
+ RsBase->isAllocatedOfSizeZero());
+
+ // Clean out the info on previous call to free return info.
+ State = State->remove<FreeReturnValue>(SymBase);
+
+ // Keep track of the return value. If it is NULL, we will know that free
+ // failed.
+ if (ReturnsNullOnFailure) {
+ SVal RetVal = C.getSVal(ParentExpr);
+ SymbolRef RetStatusSymbol = RetVal.getAsSymbol();
+ if (RetStatusSymbol) {
+ C.getSymbolManager().addSymbolDependency(SymBase, RetStatusSymbol);
+ State = State->set<FreeReturnValue>(SymBase, RetStatusSymbol);
+ }
+ }
+
+ AllocationFamily Family = RsBase ? RsBase->getAllocationFamily()
+ : getAllocationFamily(C, ParentExpr);
+ // Normal free.
+ if (Hold)
+ return State->set<RegionState>(SymBase,
+ RefState::getRelinquished(Family,
+ ParentExpr));
+
+ return State->set<RegionState>(SymBase,
+ RefState::getReleased(Family, ParentExpr));
+}
+
+Optional<MallocChecker::CheckKind>
+MallocChecker::getCheckIfTracked(AllocationFamily Family,
+ bool IsALeakCheck) const {
+ switch (Family) {
+ case AF_Malloc:
+ case AF_Alloca:
+ case AF_IfNameIndex: {
+ if (ChecksEnabled[CK_MallocChecker])
+ return CK_MallocChecker;
+ return None;
+ }
+ case AF_CXXNew:
+ case AF_CXXNewArray: {
+ if (IsALeakCheck) {
+ if (ChecksEnabled[CK_NewDeleteLeaksChecker])
+ return CK_NewDeleteLeaksChecker;
+ }
+ else {
+ if (ChecksEnabled[CK_NewDeleteChecker])
+ return CK_NewDeleteChecker;
+ }
+ return None;
+ }
+ case AF_InnerBuffer: {
+ if (ChecksEnabled[CK_InnerPointerChecker])
+ return CK_InnerPointerChecker;
+ return None;
+ }
+ case AF_None: {
+ llvm_unreachable("no family");
+ }
+ }
+ llvm_unreachable("unhandled family");
+}
+
+Optional<MallocChecker::CheckKind>
+MallocChecker::getCheckIfTracked(CheckerContext &C,
+ const Stmt *AllocDeallocStmt,
+ bool IsALeakCheck) const {
+ return getCheckIfTracked(getAllocationFamily(C, AllocDeallocStmt),
+ IsALeakCheck);
+}
+
+Optional<MallocChecker::CheckKind>
+MallocChecker::getCheckIfTracked(CheckerContext &C, SymbolRef Sym,
+ bool IsALeakCheck) const {
+ if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym))
+ return CK_MallocChecker;
+
+ const RefState *RS = C.getState()->get<RegionState>(Sym);
+ assert(RS);
+ return getCheckIfTracked(RS->getAllocationFamily(), IsALeakCheck);
+}
+
+bool MallocChecker::SummarizeValue(raw_ostream &os, SVal V) {
+ if (Optional<nonloc::ConcreteInt> IntVal = V.getAs<nonloc::ConcreteInt>())
+ os << "an integer (" << IntVal->getValue() << ")";
+ else if (Optional<loc::ConcreteInt> ConstAddr = V.getAs<loc::ConcreteInt>())
+ os << "a constant address (" << ConstAddr->getValue() << ")";
+ else if (Optional<loc::GotoLabel> Label = V.getAs<loc::GotoLabel>())
+ os << "the address of the label '" << Label->getLabel()->getName() << "'";
+ else
+ return false;
+
+ return true;
+}
+
+bool MallocChecker::SummarizeRegion(raw_ostream &os,
+ const MemRegion *MR) {
+ switch (MR->getKind()) {
+ case MemRegion::FunctionCodeRegionKind: {
+ const NamedDecl *FD = cast<FunctionCodeRegion>(MR)->getDecl();
+ if (FD)
+ os << "the address of the function '" << *FD << '\'';
+ else
+ os << "the address of a function";
+ return true;
+ }
+ case MemRegion::BlockCodeRegionKind:
+ os << "block text";
+ return true;
+ case MemRegion::BlockDataRegionKind:
+ // FIXME: where the block came from?
+ os << "a block";
+ return true;
+ default: {
+ const MemSpaceRegion *MS = MR->getMemorySpace();
+
+ if (isa<StackLocalsSpaceRegion>(MS)) {
+ const VarRegion *VR = dyn_cast<VarRegion>(MR);
+ const VarDecl *VD;
+ if (VR)
+ VD = VR->getDecl();
+ else
+ VD = nullptr;
+
+ if (VD)
+ os << "the address of the local variable '" << VD->getName() << "'";
+ else
+ os << "the address of a local stack variable";
+ return true;
+ }
+
+ if (isa<StackArgumentsSpaceRegion>(MS)) {
+ const VarRegion *VR = dyn_cast<VarRegion>(MR);
+ const VarDecl *VD;
+ if (VR)
+ VD = VR->getDecl();
+ else
+ VD = nullptr;
+
+ if (VD)
+ os << "the address of the parameter '" << VD->getName() << "'";
+ else
+ os << "the address of a parameter";
+ return true;
+ }
+
+ if (isa<GlobalsSpaceRegion>(MS)) {
+ const VarRegion *VR = dyn_cast<VarRegion>(MR);
+ const VarDecl *VD;
+ if (VR)
+ VD = VR->getDecl();
+ else
+ VD = nullptr;
+
+ if (VD) {
+ if (VD->isStaticLocal())
+ os << "the address of the static variable '" << VD->getName() << "'";
+ else
+ os << "the address of the global variable '" << VD->getName() << "'";
+ } else
+ os << "the address of a global variable";
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
+
+void MallocChecker::ReportBadFree(CheckerContext &C, SVal ArgVal,
+ SourceRange Range,
+ const Expr *DeallocExpr) const {
+
+ if (!ChecksEnabled[CK_MallocChecker] &&
+ !ChecksEnabled[CK_NewDeleteChecker])
+ return;
+
+ Optional<MallocChecker::CheckKind> CheckKind =
+ getCheckIfTracked(C, DeallocExpr);
+ if (!CheckKind.hasValue())
+ return;
+
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT_BadFree[*CheckKind])
+ BT_BadFree[*CheckKind].reset(new BugType(
+ CheckNames[*CheckKind], "Bad free", categories::MemoryError));
+
+ SmallString<100> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ const MemRegion *MR = ArgVal.getAsRegion();
+ while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR))
+ MR = ER->getSuperRegion();
+
+ os << "Argument to ";
+ if (!printAllocDeallocName(os, C, DeallocExpr))
+ os << "deallocator";
+
+ os << " is ";
+ bool Summarized = MR ? SummarizeRegion(os, MR)
+ : SummarizeValue(os, ArgVal);
+ if (Summarized)
+ os << ", which is not memory allocated by ";
+ else
+ os << "not memory allocated by ";
+
+ printExpectedAllocName(os, C, DeallocExpr);
+
+ auto R = llvm::make_unique<BugReport>(*BT_BadFree[*CheckKind], os.str(), N);
+ R->markInteresting(MR);
+ R->addRange(Range);
+ C.emitReport(std::move(R));
+ }
+}
+
+void MallocChecker::ReportFreeAlloca(CheckerContext &C, SVal ArgVal,
+ SourceRange Range) const {
+
+ Optional<MallocChecker::CheckKind> CheckKind;
+
+ if (ChecksEnabled[CK_MallocChecker])
+ CheckKind = CK_MallocChecker;
+ else if (ChecksEnabled[CK_MismatchedDeallocatorChecker])
+ CheckKind = CK_MismatchedDeallocatorChecker;
+ else
+ return;
+
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT_FreeAlloca[*CheckKind])
+ BT_FreeAlloca[*CheckKind].reset(new BugType(
+ CheckNames[*CheckKind], "Free alloca()", categories::MemoryError));
+
+ auto R = llvm::make_unique<BugReport>(
+ *BT_FreeAlloca[*CheckKind],
+ "Memory allocated by alloca() should not be deallocated", N);
+ R->markInteresting(ArgVal.getAsRegion());
+ R->addRange(Range);
+ C.emitReport(std::move(R));
+ }
+}
+
+void MallocChecker::ReportMismatchedDealloc(CheckerContext &C,
+ SourceRange Range,
+ const Expr *DeallocExpr,
+ const RefState *RS,
+ SymbolRef Sym,
+ bool OwnershipTransferred) const {
+
+ if (!ChecksEnabled[CK_MismatchedDeallocatorChecker])
+ return;
+
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT_MismatchedDealloc)
+ BT_MismatchedDealloc.reset(
+ new BugType(CheckNames[CK_MismatchedDeallocatorChecker],
+ "Bad deallocator", categories::MemoryError));
+
+ SmallString<100> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ const Expr *AllocExpr = cast<Expr>(RS->getStmt());
+ SmallString<20> AllocBuf;
+ llvm::raw_svector_ostream AllocOs(AllocBuf);
+ SmallString<20> DeallocBuf;
+ llvm::raw_svector_ostream DeallocOs(DeallocBuf);
+
+ if (OwnershipTransferred) {
+ if (printAllocDeallocName(DeallocOs, C, DeallocExpr))
+ os << DeallocOs.str() << " cannot";
+ else
+ os << "Cannot";
+
+ os << " take ownership of memory";
+
+ if (printAllocDeallocName(AllocOs, C, AllocExpr))
+ os << " allocated by " << AllocOs.str();
+ } else {
+ os << "Memory";
+ if (printAllocDeallocName(AllocOs, C, AllocExpr))
+ os << " allocated by " << AllocOs.str();
+
+ os << " should be deallocated by ";
+ printExpectedDeallocName(os, RS->getAllocationFamily());
+
+ if (printAllocDeallocName(DeallocOs, C, DeallocExpr))
+ os << ", not " << DeallocOs.str();
+ }
+
+ auto R = llvm::make_unique<BugReport>(*BT_MismatchedDealloc, os.str(), N);
+ R->markInteresting(Sym);
+ R->addRange(Range);
+ R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+ C.emitReport(std::move(R));
+ }
+}
+
+void MallocChecker::ReportOffsetFree(CheckerContext &C, SVal ArgVal,
+ SourceRange Range, const Expr *DeallocExpr,
+ const Expr *AllocExpr) const {
+
+
+ if (!ChecksEnabled[CK_MallocChecker] &&
+ !ChecksEnabled[CK_NewDeleteChecker])
+ return;
+
+ Optional<MallocChecker::CheckKind> CheckKind =
+ getCheckIfTracked(C, AllocExpr);
+ if (!CheckKind.hasValue())
+ return;
+
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+
+ if (!BT_OffsetFree[*CheckKind])
+ BT_OffsetFree[*CheckKind].reset(new BugType(
+ CheckNames[*CheckKind], "Offset free", categories::MemoryError));
+
+ SmallString<100> buf;
+ llvm::raw_svector_ostream os(buf);
+ SmallString<20> AllocNameBuf;
+ llvm::raw_svector_ostream AllocNameOs(AllocNameBuf);
+
+ const MemRegion *MR = ArgVal.getAsRegion();
+ assert(MR && "Only MemRegion based symbols can have offset free errors");
+
+ RegionOffset Offset = MR->getAsOffset();
+ assert((Offset.isValid() &&
+ !Offset.hasSymbolicOffset() &&
+ Offset.getOffset() != 0) &&
+ "Only symbols with a valid offset can have offset free errors");
+
+ int offsetBytes = Offset.getOffset() / C.getASTContext().getCharWidth();
+
+ os << "Argument to ";
+ if (!printAllocDeallocName(os, C, DeallocExpr))
+ os << "deallocator";
+ os << " is offset by "
+ << offsetBytes
+ << " "
+ << ((abs(offsetBytes) > 1) ? "bytes" : "byte")
+ << " from the start of ";
+ if (AllocExpr && printAllocDeallocName(AllocNameOs, C, AllocExpr))
+ os << "memory allocated by " << AllocNameOs.str();
+ else
+ os << "allocated memory";
+
+ auto R = llvm::make_unique<BugReport>(*BT_OffsetFree[*CheckKind], os.str(), N);
+ R->markInteresting(MR->getBaseRegion());
+ R->addRange(Range);
+ C.emitReport(std::move(R));
+}
+
+void MallocChecker::ReportUseAfterFree(CheckerContext &C, SourceRange Range,
+ SymbolRef Sym) const {
+
+ if (!ChecksEnabled[CK_MallocChecker] &&
+ !ChecksEnabled[CK_NewDeleteChecker] &&
+ !ChecksEnabled[CK_InnerPointerChecker])
+ return;
+
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ if (!CheckKind.hasValue())
+ return;
+
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT_UseFree[*CheckKind])
+ BT_UseFree[*CheckKind].reset(new BugType(
+ CheckNames[*CheckKind], "Use-after-free", categories::MemoryError));
+
+ AllocationFamily AF =
+ C.getState()->get<RegionState>(Sym)->getAllocationFamily();
+
+ auto R = llvm::make_unique<BugReport>(*BT_UseFree[*CheckKind],
+ AF == AF_InnerBuffer
+ ? "Inner pointer of container used after re/deallocation"
+ : "Use of memory after it is freed",
+ N);
+
+ R->markInteresting(Sym);
+ R->addRange(Range);
+ R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+
+ if (AF == AF_InnerBuffer)
+ R->addVisitor(allocation_state::getInnerPointerBRVisitor(Sym));
+
+ C.emitReport(std::move(R));
+ }
+}
+
+void MallocChecker::ReportDoubleFree(CheckerContext &C, SourceRange Range,
+ bool Released, SymbolRef Sym,
+ SymbolRef PrevSym) const {
+
+ if (!ChecksEnabled[CK_MallocChecker] &&
+ !ChecksEnabled[CK_NewDeleteChecker])
+ return;
+
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ if (!CheckKind.hasValue())
+ return;
+
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT_DoubleFree[*CheckKind])
+ BT_DoubleFree[*CheckKind].reset(new BugType(
+ CheckNames[*CheckKind], "Double free", categories::MemoryError));
+
+ auto R = llvm::make_unique<BugReport>(
+ *BT_DoubleFree[*CheckKind],
+ (Released ? "Attempt to free released memory"
+ : "Attempt to free non-owned memory"),
+ N);
+ R->addRange(Range);
+ R->markInteresting(Sym);
+ if (PrevSym)
+ R->markInteresting(PrevSym);
+ R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+ C.emitReport(std::move(R));
+ }
+}
+
+void MallocChecker::ReportDoubleDelete(CheckerContext &C, SymbolRef Sym) const {
+
+ if (!ChecksEnabled[CK_NewDeleteChecker])
+ return;
+
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+ if (!CheckKind.hasValue())
+ return;
+
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT_DoubleDelete)
+ BT_DoubleDelete.reset(new BugType(CheckNames[CK_NewDeleteChecker],
+ "Double delete",
+ categories::MemoryError));
+
+ auto R = llvm::make_unique<BugReport>(
+ *BT_DoubleDelete, "Attempt to delete released memory", N);
+
+ R->markInteresting(Sym);
+ R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+ C.emitReport(std::move(R));
+ }
+}
+
+void MallocChecker::ReportUseZeroAllocated(CheckerContext &C,
+ SourceRange Range,
+ SymbolRef Sym) const {
+
+ if (!ChecksEnabled[CK_MallocChecker] &&
+ !ChecksEnabled[CK_NewDeleteChecker])
+ return;
+
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, Sym);
+
+ if (!CheckKind.hasValue())
+ return;
+
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT_UseZerroAllocated[*CheckKind])
+ BT_UseZerroAllocated[*CheckKind].reset(
+ new BugType(CheckNames[*CheckKind], "Use of zero allocated",
+ categories::MemoryError));
+
+ auto R = llvm::make_unique<BugReport>(*BT_UseZerroAllocated[*CheckKind],
+ "Use of zero-allocated memory", N);
+
+ R->addRange(Range);
+ if (Sym) {
+ R->markInteresting(Sym);
+ R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym));
+ }
+ C.emitReport(std::move(R));
+ }
+}
+
+void MallocChecker::ReportFunctionPointerFree(CheckerContext &C, SVal ArgVal,
+ SourceRange Range,
+ const Expr *FreeExpr) const {
+ if (!ChecksEnabled[CK_MallocChecker])
+ return;
+
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(C, FreeExpr);
+ if (!CheckKind.hasValue())
+ return;
+
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT_BadFree[*CheckKind])
+ BT_BadFree[*CheckKind].reset(new BugType(
+ CheckNames[*CheckKind], "Bad free", categories::MemoryError));
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+
+ const MemRegion *MR = ArgVal.getAsRegion();
+ while (const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(MR))
+ MR = ER->getSuperRegion();
+
+ Os << "Argument to ";
+ if (!printAllocDeallocName(Os, C, FreeExpr))
+ Os << "deallocator";
+
+ Os << " is a function pointer";
+
+ auto R = llvm::make_unique<BugReport>(*BT_BadFree[*CheckKind], Os.str(), N);
+ R->markInteresting(MR);
+ R->addRange(Range);
+ C.emitReport(std::move(R));
+ }
+}
+
+ProgramStateRef MallocChecker::ReallocMemAux(CheckerContext &C,
+ const CallExpr *CE,
+ bool FreesOnFail,
+ ProgramStateRef State,
+ bool SuffixWithN) const {
+ if (!State)
+ return nullptr;
+
+ if (SuffixWithN && CE->getNumArgs() < 3)
+ return nullptr;
+ else if (CE->getNumArgs() < 2)
+ return nullptr;
+
+ const Expr *arg0Expr = CE->getArg(0);
+ SVal Arg0Val = C.getSVal(arg0Expr);
+ if (!Arg0Val.getAs<DefinedOrUnknownSVal>())
+ return nullptr;
+ DefinedOrUnknownSVal arg0Val = Arg0Val.castAs<DefinedOrUnknownSVal>();
+
+ SValBuilder &svalBuilder = C.getSValBuilder();
+
+ DefinedOrUnknownSVal PtrEQ =
+ svalBuilder.evalEQ(State, arg0Val, svalBuilder.makeNull());
+
+ // Get the size argument.
+ const Expr *Arg1 = CE->getArg(1);
+
+ // Get the value of the size argument.
+ SVal TotalSize = C.getSVal(Arg1);
+ if (SuffixWithN)
+ TotalSize = evalMulForBufferSize(C, Arg1, CE->getArg(2));
+ if (!TotalSize.getAs<DefinedOrUnknownSVal>())
+ return nullptr;
+
+ // Compare the size argument to 0.
+ DefinedOrUnknownSVal SizeZero =
+ svalBuilder.evalEQ(State, TotalSize.castAs<DefinedOrUnknownSVal>(),
+ svalBuilder.makeIntValWithPtrWidth(0, false));
+
+ ProgramStateRef StatePtrIsNull, StatePtrNotNull;
+ std::tie(StatePtrIsNull, StatePtrNotNull) = State->assume(PtrEQ);
+ ProgramStateRef StateSizeIsZero, StateSizeNotZero;
+ std::tie(StateSizeIsZero, StateSizeNotZero) = State->assume(SizeZero);
+ // We only assume exceptional states if they are definitely true; if the
+ // state is under-constrained, assume regular realloc behavior.
+ bool PrtIsNull = StatePtrIsNull && !StatePtrNotNull;
+ bool SizeIsZero = StateSizeIsZero && !StateSizeNotZero;
+
+ // If the ptr is NULL and the size is not 0, the call is equivalent to
+ // malloc(size).
+ if (PrtIsNull && !SizeIsZero) {
+ ProgramStateRef stateMalloc = MallocMemAux(C, CE, TotalSize,
+ UndefinedVal(), StatePtrIsNull);
+ return stateMalloc;
+ }
+
+ if (PrtIsNull && SizeIsZero)
+ return State;
+
+ // Get the from and to pointer symbols as in toPtr = realloc(fromPtr, size).
+ assert(!PrtIsNull);
+ SymbolRef FromPtr = arg0Val.getAsSymbol();
+ SVal RetVal = C.getSVal(CE);
+ SymbolRef ToPtr = RetVal.getAsSymbol();
+ if (!FromPtr || !ToPtr)
+ return nullptr;
+
+ bool ReleasedAllocated = false;
+
+ // If the size is 0, free the memory.
+ if (SizeIsZero)
+ if (ProgramStateRef stateFree = FreeMemAux(C, CE, StateSizeIsZero, 0,
+ false, ReleasedAllocated)){
+ // The semantics of the return value are:
+ // If size was equal to 0, either NULL or a pointer suitable to be passed
+ // to free() is returned. We just free the input pointer and do not add
+ // any constrains on the output pointer.
+ return stateFree;
+ }
+
+ // Default behavior.
+ if (ProgramStateRef stateFree =
+ FreeMemAux(C, CE, State, 0, false, ReleasedAllocated)) {
+
+ ProgramStateRef stateRealloc = MallocMemAux(C, CE, TotalSize,
+ UnknownVal(), stateFree);
+ if (!stateRealloc)
+ return nullptr;
+
+ ReallocPairKind Kind = RPToBeFreedAfterFailure;
+ if (FreesOnFail)
+ Kind = RPIsFreeOnFailure;
+ else if (!ReleasedAllocated)
+ Kind = RPDoNotTrackAfterFailure;
+
+ // Record the info about the reallocated symbol so that we could properly
+ // process failed reallocation.
+ stateRealloc = stateRealloc->set<ReallocPairs>(ToPtr,
+ ReallocPair(FromPtr, Kind));
+ // The reallocated symbol should stay alive for as long as the new symbol.
+ C.getSymbolManager().addSymbolDependency(ToPtr, FromPtr);
+ return stateRealloc;
+ }
+ return nullptr;
+}
+
+ProgramStateRef MallocChecker::CallocMem(CheckerContext &C, const CallExpr *CE,
+ ProgramStateRef State) {
+ if (!State)
+ return nullptr;
+
+ if (CE->getNumArgs() < 2)
+ return nullptr;
+
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ SVal zeroVal = svalBuilder.makeZeroVal(svalBuilder.getContext().CharTy);
+ SVal TotalSize = evalMulForBufferSize(C, CE->getArg(0), CE->getArg(1));
+
+ return MallocMemAux(C, CE, TotalSize, zeroVal, State);
+}
+
+LeakInfo
+MallocChecker::getAllocationSite(const ExplodedNode *N, SymbolRef Sym,
+ CheckerContext &C) const {
+ const LocationContext *LeakContext = N->getLocationContext();
+ // Walk the ExplodedGraph backwards and find the first node that referred to
+ // the tracked symbol.
+ const ExplodedNode *AllocNode = N;
+ const MemRegion *ReferenceRegion = nullptr;
+
+ while (N) {
+ ProgramStateRef State = N->getState();
+ if (!State->get<RegionState>(Sym))
+ break;
+
+ // Find the most recent expression bound to the symbol in the current
+ // context.
+ if (!ReferenceRegion) {
+ if (const MemRegion *MR = C.getLocationRegionIfPostStore(N)) {
+ SVal Val = State->getSVal(MR);
+ if (Val.getAsLocSymbol() == Sym) {
+ const VarRegion* VR = MR->getBaseRegion()->getAs<VarRegion>();
+ // Do not show local variables belonging to a function other than
+ // where the error is reported.
+ if (!VR ||
+ (VR->getStackFrame() == LeakContext->getStackFrame()))
+ ReferenceRegion = MR;
+ }
+ }
+ }
+
+ // Allocation node, is the last node in the current or parent context in
+ // which the symbol was tracked.
+ const LocationContext *NContext = N->getLocationContext();
+ if (NContext == LeakContext ||
+ NContext->isParentOf(LeakContext))
+ AllocNode = N;
+ N = N->pred_empty() ? nullptr : *(N->pred_begin());
+ }
+
+ return LeakInfo(AllocNode, ReferenceRegion);
+}
+
+void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
+ CheckerContext &C) const {
+
+ if (!ChecksEnabled[CK_MallocChecker] &&
+ !ChecksEnabled[CK_NewDeleteLeaksChecker])
+ return;
+
+ const RefState *RS = C.getState()->get<RegionState>(Sym);
+ assert(RS && "cannot leak an untracked symbol");
+ AllocationFamily Family = RS->getAllocationFamily();
+
+ if (Family == AF_Alloca)
+ return;
+
+ Optional<MallocChecker::CheckKind>
+ CheckKind = getCheckIfTracked(Family, true);
+
+ if (!CheckKind.hasValue())
+ return;
+
+ assert(N);
+ if (!BT_Leak[*CheckKind]) {
+ BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak",
+ categories::MemoryError));
+ // Leaks should not be reported if they are post-dominated by a sink:
+ // (1) Sinks are higher importance bugs.
+ // (2) NoReturnFunctionChecker uses sink nodes to represent paths ending
+ // with __noreturn functions such as assert() or exit(). We choose not
+ // to report leaks on such paths.
+ BT_Leak[*CheckKind]->setSuppressOnSink(true);
+ }
+
+ // Most bug reports are cached at the location where they occurred.
+ // With leaks, we want to unique them by the location where they were
+ // allocated, and only report a single path.
+ PathDiagnosticLocation LocUsedForUniqueing;
+ const ExplodedNode *AllocNode = nullptr;
+ const MemRegion *Region = nullptr;
+ std::tie(AllocNode, Region) = getAllocationSite(N, Sym, C);
+
+ const Stmt *AllocationStmt = PathDiagnosticLocation::getStmt(AllocNode);
+ if (AllocationStmt)
+ LocUsedForUniqueing = PathDiagnosticLocation::createBegin(AllocationStmt,
+ C.getSourceManager(),
+ AllocNode->getLocationContext());
+
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
+ if (Region && Region->canPrintPretty()) {
+ os << "Potential leak of memory pointed to by ";
+ Region->printPretty(os);
+ } else {
+ os << "Potential memory leak";
+ }
+
+ auto R = llvm::make_unique<BugReport>(
+ *BT_Leak[*CheckKind], os.str(), N, LocUsedForUniqueing,
+ AllocNode->getLocationContext()->getDecl());
+ R->markInteresting(Sym);
+ R->addVisitor(llvm::make_unique<MallocBugVisitor>(Sym, true));
+ C.emitReport(std::move(R));
+}
+
+void MallocChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const
+{
+ ProgramStateRef state = C.getState();
+ RegionStateTy OldRS = state->get<RegionState>();
+ RegionStateTy::Factory &F = state->get_context<RegionState>();
+
+ RegionStateTy RS = OldRS;
+ SmallVector<SymbolRef, 2> Errors;
+ for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
+ if (SymReaper.isDead(I->first)) {
+ if (I->second.isAllocated() || I->second.isAllocatedOfSizeZero())
+ Errors.push_back(I->first);
+ // Remove the dead symbol from the map.
+ RS = F.remove(RS, I->first);
+ }
+ }
+
+ if (RS == OldRS) {
+ // We shouldn't have touched other maps yet.
+ assert(state->get<ReallocPairs>() ==
+ C.getState()->get<ReallocPairs>());
+ assert(state->get<FreeReturnValue>() ==
+ C.getState()->get<FreeReturnValue>());
+ return;
+ }
+
+ // Cleanup the Realloc Pairs Map.
+ ReallocPairsTy RP = state->get<ReallocPairs>();
+ for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) {
+ if (SymReaper.isDead(I->first) ||
+ SymReaper.isDead(I->second.ReallocatedSym)) {
+ state = state->remove<ReallocPairs>(I->first);
+ }
+ }
+
+ // Cleanup the FreeReturnValue Map.
+ FreeReturnValueTy FR = state->get<FreeReturnValue>();
+ for (FreeReturnValueTy::iterator I = FR.begin(), E = FR.end(); I != E; ++I) {
+ if (SymReaper.isDead(I->first) ||
+ SymReaper.isDead(I->second)) {
+ state = state->remove<FreeReturnValue>(I->first);
+ }
+ }
+
+ // Generate leak node.
+ ExplodedNode *N = C.getPredecessor();
+ if (!Errors.empty()) {
+ static CheckerProgramPointTag Tag("MallocChecker", "DeadSymbolsLeak");
+ N = C.generateNonFatalErrorNode(C.getState(), &Tag);
+ if (N) {
+ for (SmallVectorImpl<SymbolRef>::iterator
+ I = Errors.begin(), E = Errors.end(); I != E; ++I) {
+ reportLeak(*I, N, C);
+ }
+ }
+ }
+
+ C.addTransition(state->set<RegionState>(RS), N);
+}
+
+void MallocChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+
+ if (const CXXDestructorCall *DC = dyn_cast<CXXDestructorCall>(&Call)) {
+ SymbolRef Sym = DC->getCXXThisVal().getAsSymbol();
+ if (!Sym || checkDoubleDelete(Sym, C))
+ return;
+ }
+
+ // We will check for double free in the post visit.
+ if (const AnyFunctionCall *FC = dyn_cast<AnyFunctionCall>(&Call)) {
+ const FunctionDecl *FD = FC->getDecl();
+ if (!FD)
+ return;
+
+ ASTContext &Ctx = C.getASTContext();
+ if (ChecksEnabled[CK_MallocChecker] &&
+ (isCMemFunction(FD, Ctx, AF_Malloc, MemoryOperationKind::MOK_Free) ||
+ isCMemFunction(FD, Ctx, AF_IfNameIndex,
+ MemoryOperationKind::MOK_Free)))
+ return;
+ }
+
+ // Check if the callee of a method is deleted.
+ if (const CXXInstanceCall *CC = dyn_cast<CXXInstanceCall>(&Call)) {
+ SymbolRef Sym = CC->getCXXThisVal().getAsSymbol();
+ if (!Sym || checkUseAfterFree(Sym, C, CC->getCXXThisExpr()))
+ return;
+ }
+
+ // Check arguments for being used after free.
+ for (unsigned I = 0, E = Call.getNumArgs(); I != E; ++I) {
+ SVal ArgSVal = Call.getArgSVal(I);
+ if (ArgSVal.getAs<Loc>()) {
+ SymbolRef Sym = ArgSVal.getAsSymbol();
+ if (!Sym)
+ continue;
+ if (checkUseAfterFree(Sym, C, Call.getArgExpr(I)))
+ return;
+ }
+ }
+}
+
+void MallocChecker::checkPreStmt(const ReturnStmt *S,
+ CheckerContext &C) const {
+ checkEscapeOnReturn(S, C);
+}
+
+// In the CFG, automatic destructors come after the return statement.
+// This callback checks for returning memory that is freed by automatic
+// destructors, as those cannot be reached in checkPreStmt().
+void MallocChecker::checkEndFunction(const ReturnStmt *S,
+ CheckerContext &C) const {
+ checkEscapeOnReturn(S, C);
+}
+
+void MallocChecker::checkEscapeOnReturn(const ReturnStmt *S,
+ CheckerContext &C) const {
+ if (!S)
+ return;
+
+ const Expr *E = S->getRetValue();
+ if (!E)
+ return;
+
+ // Check if we are returning a symbol.
+ ProgramStateRef State = C.getState();
+ SVal RetVal = C.getSVal(E);
+ SymbolRef Sym = RetVal.getAsSymbol();
+ if (!Sym)
+ // If we are returning a field of the allocated struct or an array element,
+ // the callee could still free the memory.
+ // TODO: This logic should be a part of generic symbol escape callback.
+ if (const MemRegion *MR = RetVal.getAsRegion())
+ if (isa<FieldRegion>(MR) || isa<ElementRegion>(MR))
+ if (const SymbolicRegion *BMR =
+ dyn_cast<SymbolicRegion>(MR->getBaseRegion()))
+ Sym = BMR->getSymbol();
+
+ // Check if we are returning freed memory.
+ if (Sym)
+ checkUseAfterFree(Sym, C, E);
+}
+
+// TODO: Blocks should be either inlined or should call invalidate regions
+// upon invocation. After that's in place, special casing here will not be
+// needed.
+void MallocChecker::checkPostStmt(const BlockExpr *BE,
+ CheckerContext &C) const {
+
+ // Scan the BlockDecRefExprs for any object the retain count checker
+ // may be tracking.
+ if (!BE->getBlockDecl()->hasCaptures())
+ return;
+
+ ProgramStateRef state = C.getState();
+ const BlockDataRegion *R =
+ cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
+
+ BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
+ E = R->referenced_vars_end();
+
+ if (I == E)
+ return;
+
+ SmallVector<const MemRegion*, 10> Regions;
+ const LocationContext *LC = C.getLocationContext();
+ MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
+
+ for ( ; I != E; ++I) {
+ const VarRegion *VR = I.getCapturedRegion();
+ if (VR->getSuperRegion() == R) {
+ VR = MemMgr.getVarRegion(VR->getDecl(), LC);
+ }
+ Regions.push_back(VR);
+ }
+
+ state =
+ state->scanReachableSymbols<StopTrackingCallback>(Regions).getState();
+ C.addTransition(state);
+}
+
+bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const {
+ assert(Sym);
+ const RefState *RS = C.getState()->get<RegionState>(Sym);
+ return (RS && RS->isReleased());
+}
+
+bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
+ const Stmt *S) const {
+
+ if (isReleased(Sym, C)) {
+ ReportUseAfterFree(C, S->getSourceRange(), Sym);
+ return true;
+ }
+
+ return false;
+}
+
+void MallocChecker::checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
+ const Stmt *S) const {
+ assert(Sym);
+
+ if (const RefState *RS = C.getState()->get<RegionState>(Sym)) {
+ if (RS->isAllocatedOfSizeZero())
+ ReportUseZeroAllocated(C, RS->getStmt()->getSourceRange(), Sym);
+ }
+ else if (C.getState()->contains<ReallocSizeZeroSymbols>(Sym)) {
+ ReportUseZeroAllocated(C, S->getSourceRange(), Sym);
+ }
+}
+
+bool MallocChecker::checkDoubleDelete(SymbolRef Sym, CheckerContext &C) const {
+
+ if (isReleased(Sym, C)) {
+ ReportDoubleDelete(C, Sym);
+ return true;
+ }
+ return false;
+}
+
+// Check if the location is a freed symbolic region.
+void MallocChecker::checkLocation(SVal l, bool isLoad, const Stmt *S,
+ CheckerContext &C) const {
+ SymbolRef Sym = l.getLocSymbolInBase();
+ if (Sym) {
+ checkUseAfterFree(Sym, C, S);
+ checkUseZeroAllocated(Sym, C, S);
+ }
+}
+
+// If a symbolic region is assumed to NULL (or another constant), stop tracking
+// it - assuming that allocation failed on this path.
+ProgramStateRef MallocChecker::evalAssume(ProgramStateRef state,
+ SVal Cond,
+ bool Assumption) const {
+ RegionStateTy RS = state->get<RegionState>();
+ for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
+ // If the symbol is assumed to be NULL, remove it from consideration.
+ ConstraintManager &CMgr = state->getConstraintManager();
+ ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey());
+ if (AllocFailed.isConstrainedTrue())
+ state = state->remove<RegionState>(I.getKey());
+ }
+
+ // Realloc returns 0 when reallocation fails, which means that we should
+ // restore the state of the pointer being reallocated.
+ ReallocPairsTy RP = state->get<ReallocPairs>();
+ for (ReallocPairsTy::iterator I = RP.begin(), E = RP.end(); I != E; ++I) {
+ // If the symbol is assumed to be NULL, remove it from consideration.
+ ConstraintManager &CMgr = state->getConstraintManager();
+ ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey());
+ if (!AllocFailed.isConstrainedTrue())
+ continue;
+
+ SymbolRef ReallocSym = I.getData().ReallocatedSym;
+ if (const RefState *RS = state->get<RegionState>(ReallocSym)) {
+ if (RS->isReleased()) {
+ if (I.getData().Kind == RPToBeFreedAfterFailure)
+ state = state->set<RegionState>(ReallocSym,
+ RefState::getAllocated(RS->getAllocationFamily(), RS->getStmt()));
+ else if (I.getData().Kind == RPDoNotTrackAfterFailure)
+ state = state->remove<RegionState>(ReallocSym);
+ else
+ assert(I.getData().Kind == RPIsFreeOnFailure);
+ }
+ }
+ state = state->remove<ReallocPairs>(I.getKey());
+ }
+
+ return state;
+}
+
+bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
+ const CallEvent *Call,
+ ProgramStateRef State,
+ SymbolRef &EscapingSymbol) const {
+ assert(Call);
+ EscapingSymbol = nullptr;
+
+ // For now, assume that any C++ or block call can free memory.
+ // TODO: If we want to be more optimistic here, we'll need to make sure that
+ // regions escape to C++ containers. They seem to do that even now, but for
+ // mysterious reasons.
+ if (!(isa<SimpleFunctionCall>(Call) || isa<ObjCMethodCall>(Call)))
+ return true;
+
+ // Check Objective-C messages by selector name.
+ if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
+ // If it's not a framework call, or if it takes a callback, assume it
+ // can free memory.
+ if (!Call->isInSystemHeader() || Call->argumentsMayEscape())
+ return true;
+
+ // If it's a method we know about, handle it explicitly post-call.
+ // This should happen before the "freeWhenDone" check below.
+ if (isKnownDeallocObjCMethodName(*Msg))
+ return false;
+
+ // If there's a "freeWhenDone" parameter, but the method isn't one we know
+ // about, we can't be sure that the object will use free() to deallocate the
+ // memory, so we can't model it explicitly. The best we can do is use it to
+ // decide whether the pointer escapes.
+ if (Optional<bool> FreeWhenDone = getFreeWhenDoneArg(*Msg))
+ return *FreeWhenDone;
+
+ // If the first selector piece ends with "NoCopy", and there is no
+ // "freeWhenDone" parameter set to zero, we know ownership is being
+ // transferred. Again, though, we can't be sure that the object will use
+ // free() to deallocate the memory, so we can't model it explicitly.
+ StringRef FirstSlot = Msg->getSelector().getNameForSlot(0);
+ if (FirstSlot.endswith("NoCopy"))
+ return true;
+
+ // If the first selector starts with addPointer, insertPointer,
+ // or replacePointer, assume we are dealing with NSPointerArray or similar.
+ // This is similar to C++ containers (vector); we still might want to check
+ // that the pointers get freed by following the container itself.
+ if (FirstSlot.startswith("addPointer") ||
+ FirstSlot.startswith("insertPointer") ||
+ FirstSlot.startswith("replacePointer") ||
+ FirstSlot.equals("valueWithPointer")) {
+ return true;
+ }
+
+ // We should escape receiver on call to 'init'. This is especially relevant
+ // to the receiver, as the corresponding symbol is usually not referenced
+ // after the call.
+ if (Msg->getMethodFamily() == OMF_init) {
+ EscapingSymbol = Msg->getReceiverSVal().getAsSymbol();
+ return true;
+ }
+
+ // Otherwise, assume that the method does not free memory.
+ // Most framework methods do not free memory.
+ return false;
+ }
+
+ // At this point the only thing left to handle is straight function calls.
+ const FunctionDecl *FD = cast<SimpleFunctionCall>(Call)->getDecl();
+ if (!FD)
+ return true;
+
+ ASTContext &ASTC = State->getStateManager().getContext();
+
+ // If it's one of the allocation functions we can reason about, we model
+ // its behavior explicitly.
+ if (isMemFunction(FD, ASTC))
+ return false;
+
+ // If it's not a system call, assume it frees memory.
+ if (!Call->isInSystemHeader())
+ return true;
+
+ // White list the system functions whose arguments escape.
+ const IdentifierInfo *II = FD->getIdentifier();
+ if (!II)
+ return true;
+ StringRef FName = II->getName();
+
+ // White list the 'XXXNoCopy' CoreFoundation functions.
+ // We specifically check these before
+ if (FName.endswith("NoCopy")) {
+ // Look for the deallocator argument. We know that the memory ownership
+ // is not transferred only if the deallocator argument is
+ // 'kCFAllocatorNull'.
+ for (unsigned i = 1; i < Call->getNumArgs(); ++i) {
+ const Expr *ArgE = Call->getArgExpr(i)->IgnoreParenCasts();
+ if (const DeclRefExpr *DE = dyn_cast<DeclRefExpr>(ArgE)) {
+ StringRef DeallocatorName = DE->getFoundDecl()->getName();
+ if (DeallocatorName == "kCFAllocatorNull")
+ return false;
+ }
+ }
+ return true;
+ }
+
+ // Associating streams with malloced buffers. The pointer can escape if
+ // 'closefn' is specified (and if that function does free memory),
+ // but it will not if closefn is not specified.
+ // Currently, we do not inspect the 'closefn' function (PR12101).
+ if (FName == "funopen")
+ if (Call->getNumArgs() >= 4 && Call->getArgSVal(4).isConstant(0))
+ return false;
+
+ // Do not warn on pointers passed to 'setbuf' when used with std streams,
+ // these leaks might be intentional when setting the buffer for stdio.
+ // http://stackoverflow.com/questions/2671151/who-frees-setvbuf-buffer
+ if (FName == "setbuf" || FName =="setbuffer" ||
+ FName == "setlinebuf" || FName == "setvbuf") {
+ if (Call->getNumArgs() >= 1) {
+ const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts();
+ if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE))
+ if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl()))
+ if (D->getCanonicalDecl()->getName().find("std") != StringRef::npos)
+ return true;
+ }
+ }
+
+ // A bunch of other functions which either take ownership of a pointer or
+ // wrap the result up in a struct or object, meaning it can be freed later.
+ // (See RetainCountChecker.) Not all the parameters here are invalidated,
+ // but the Malloc checker cannot differentiate between them. The right way
+ // of doing this would be to implement a pointer escapes callback.
+ if (FName == "CGBitmapContextCreate" ||
+ FName == "CGBitmapContextCreateWithData" ||
+ FName == "CVPixelBufferCreateWithBytes" ||
+ FName == "CVPixelBufferCreateWithPlanarBytes" ||
+ FName == "OSAtomicEnqueue") {
+ return true;
+ }
+
+ if (FName == "postEvent" &&
+ FD->getQualifiedNameAsString() == "QCoreApplication::postEvent") {
+ return true;
+ }
+
+ if (FName == "postEvent" &&
+ FD->getQualifiedNameAsString() == "QCoreApplication::postEvent") {
+ return true;
+ }
+
+ if (FName == "connectImpl" &&
+ FD->getQualifiedNameAsString() == "QObject::connectImpl") {
+ return true;
+ }
+
+ // Handle cases where we know a buffer's /address/ can escape.
+ // Note that the above checks handle some special cases where we know that
+ // even though the address escapes, it's still our responsibility to free the
+ // buffer.
+ if (Call->argumentsMayEscape())
+ return true;
+
+ // Otherwise, assume that the function does not free memory.
+ // Most system calls do not free the memory.
+ return false;
+}
+
+static bool retTrue(const RefState *RS) {
+ return true;
+}
+
+static bool checkIfNewOrNewArrayFamily(const RefState *RS) {
+ return (RS->getAllocationFamily() == AF_CXXNewArray ||
+ RS->getAllocationFamily() == AF_CXXNew);
+}
+
+ProgramStateRef MallocChecker::checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const {
+ return checkPointerEscapeAux(State, Escaped, Call, Kind, &retTrue);
+}
+
+ProgramStateRef MallocChecker::checkConstPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const {
+ return checkPointerEscapeAux(State, Escaped, Call, Kind,
+ &checkIfNewOrNewArrayFamily);
+}
+
+ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind,
+ bool(*CheckRefState)(const RefState*)) const {
+ // If we know that the call does not free memory, or we want to process the
+ // call later, keep tracking the top level arguments.
+ SymbolRef EscapingSymbol = nullptr;
+ if (Kind == PSK_DirectEscapeOnCall &&
+ !mayFreeAnyEscapedMemoryOrIsModeledExplicitly(Call, State,
+ EscapingSymbol) &&
+ !EscapingSymbol) {
+ return State;
+ }
+
+ for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
+ E = Escaped.end();
+ I != E; ++I) {
+ SymbolRef sym = *I;
+
+ if (EscapingSymbol && EscapingSymbol != sym)
+ continue;
+
+ if (const RefState *RS = State->get<RegionState>(sym)) {
+ if ((RS->isAllocated() || RS->isAllocatedOfSizeZero()) &&
+ CheckRefState(RS)) {
+ State = State->remove<RegionState>(sym);
+ State = State->set<RegionState>(sym, RefState::getEscaped(RS));
+ }
+ }
+ }
+ return State;
+}
+
+static SymbolRef findFailedReallocSymbol(ProgramStateRef currState,
+ ProgramStateRef prevState) {
+ ReallocPairsTy currMap = currState->get<ReallocPairs>();
+ ReallocPairsTy prevMap = prevState->get<ReallocPairs>();
+
+ for (ReallocPairsTy::iterator I = prevMap.begin(), E = prevMap.end();
+ I != E; ++I) {
+ SymbolRef sym = I.getKey();
+ if (!currMap.lookup(sym))
+ return sym;
+ }
+
+ return nullptr;
+}
+
+static bool isReferenceCountingPointerDestructor(const CXXDestructorDecl *DD) {
+ if (const IdentifierInfo *II = DD->getParent()->getIdentifier()) {
+ StringRef N = II->getName();
+ if (N.contains_lower("ptr") || N.contains_lower("pointer")) {
+ if (N.contains_lower("ref") || N.contains_lower("cnt") ||
+ N.contains_lower("intrusive") || N.contains_lower("shared")) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+std::shared_ptr<PathDiagnosticPiece> MallocChecker::MallocBugVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) {
+
+ ProgramStateRef state = N->getState();
+ ProgramStateRef statePrev = N->getFirstPred()->getState();
+
+ const RefState *RS = state->get<RegionState>(Sym);
+ const RefState *RSPrev = statePrev->get<RegionState>(Sym);
+
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ // When dealing with containers, we sometimes want to give a note
+ // even if the statement is missing.
+ if (!S && (!RS || RS->getAllocationFamily() != AF_InnerBuffer))
+ return nullptr;
+
+ const LocationContext *CurrentLC = N->getLocationContext();
+
+ // If we find an atomic fetch_add or fetch_sub within the destructor in which
+ // the pointer was released (before the release), this is likely a destructor
+ // of a shared pointer.
+ // Because we don't model atomics, and also because we don't know that the
+ // original reference count is positive, we should not report use-after-frees
+ // on objects deleted in such destructors. This can probably be improved
+ // through better shared pointer modeling.
+ if (ReleaseDestructorLC) {
+ if (const auto *AE = dyn_cast<AtomicExpr>(S)) {
+ AtomicExpr::AtomicOp Op = AE->getOp();
+ if (Op == AtomicExpr::AO__c11_atomic_fetch_add ||
+ Op == AtomicExpr::AO__c11_atomic_fetch_sub) {
+ if (ReleaseDestructorLC == CurrentLC ||
+ ReleaseDestructorLC->isParentOf(CurrentLC)) {
+ BR.markInvalid(getTag(), S);
+ }
+ }
+ }
+ }
+
+ // FIXME: We will eventually need to handle non-statement-based events
+ // (__attribute__((cleanup))).
+
+ // Find out if this is an interesting point and what is the kind.
+ StringRef Msg;
+ StackHintGeneratorForSymbol *StackHint = nullptr;
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+
+ if (Mode == Normal) {
+ if (isAllocated(RS, RSPrev, S)) {
+ Msg = "Memory is allocated";
+ StackHint = new StackHintGeneratorForSymbol(Sym,
+ "Returned allocated memory");
+ } else if (isReleased(RS, RSPrev, S)) {
+ const auto Family = RS->getAllocationFamily();
+ switch (Family) {
+ case AF_Alloca:
+ case AF_Malloc:
+ case AF_CXXNew:
+ case AF_CXXNewArray:
+ case AF_IfNameIndex:
+ Msg = "Memory is released";
+ StackHint = new StackHintGeneratorForSymbol(Sym,
+ "Returning; memory was released");
+ break;
+ case AF_InnerBuffer: {
+ const MemRegion *ObjRegion =
+ allocation_state::getContainerObjRegion(statePrev, Sym);
+ const auto *TypedRegion = cast<TypedValueRegion>(ObjRegion);
+ QualType ObjTy = TypedRegion->getValueType();
+ OS << "Inner buffer of '" << ObjTy.getAsString() << "' ";
+
+ if (N->getLocation().getKind() == ProgramPoint::PostImplicitCallKind) {
+ OS << "deallocated by call to destructor";
+ StackHint = new StackHintGeneratorForSymbol(Sym,
+ "Returning; inner buffer was deallocated");
+ } else {
+ OS << "reallocated by call to '";
+ const Stmt *S = RS->getStmt();
+ if (const auto *MemCallE = dyn_cast<CXXMemberCallExpr>(S)) {
+ OS << MemCallE->getMethodDecl()->getNameAsString();
+ } else if (const auto *OpCallE = dyn_cast<CXXOperatorCallExpr>(S)) {
+ OS << OpCallE->getDirectCallee()->getNameAsString();
+ } else if (const auto *CallE = dyn_cast<CallExpr>(S)) {
+ auto &CEMgr = BRC.getStateManager().getCallEventManager();
+ CallEventRef<> Call = CEMgr.getSimpleCall(CallE, state, CurrentLC);
+ const auto *D = dyn_cast_or_null<NamedDecl>(Call->getDecl());
+ OS << (D ? D->getNameAsString() : "unknown");
+ }
+ OS << "'";
+ StackHint = new StackHintGeneratorForSymbol(Sym,
+ "Returning; inner buffer was reallocated");
+ }
+ Msg = OS.str();
+ break;
+ }
+ case AF_None:
+ llvm_unreachable("Unhandled allocation family!");
+ }
+
+ // See if we're releasing memory while inlining a destructor
+ // (or one of its callees). This turns on various common
+ // false positive suppressions.
+ bool FoundAnyDestructor = false;
+ for (const LocationContext *LC = CurrentLC; LC; LC = LC->getParent()) {
+ if (const auto *DD = dyn_cast<CXXDestructorDecl>(LC->getDecl())) {
+ if (isReferenceCountingPointerDestructor(DD)) {
+ // This immediately looks like a reference-counting destructor.
+ // We're bad at guessing the original reference count of the object,
+ // so suppress the report for now.
+ BR.markInvalid(getTag(), DD);
+ } else if (!FoundAnyDestructor) {
+ assert(!ReleaseDestructorLC &&
+ "There can be only one release point!");
+ // Suspect that it's a reference counting pointer destructor.
+ // On one of the next nodes might find out that it has atomic
+ // reference counting operations within it (see the code above),
+ // and if so, we'd conclude that it likely is a reference counting
+ // pointer destructor.
+ ReleaseDestructorLC = LC->getStackFrame();
+ // It is unlikely that releasing memory is delegated to a destructor
+ // inside a destructor of a shared pointer, because it's fairly hard
+ // to pass the information that the pointer indeed needs to be
+ // released into it. So we're only interested in the innermost
+ // destructor.
+ FoundAnyDestructor = true;
+ }
+ }
+ }
+ } else if (isRelinquished(RS, RSPrev, S)) {
+ Msg = "Memory ownership is transferred";
+ StackHint = new StackHintGeneratorForSymbol(Sym, "");
+ } else if (isReallocFailedCheck(RS, RSPrev, S)) {
+ Mode = ReallocationFailed;
+ Msg = "Reallocation failed";
+ StackHint = new StackHintGeneratorForReallocationFailed(Sym,
+ "Reallocation failed");
+
+ if (SymbolRef sym = findFailedReallocSymbol(state, statePrev)) {
+ // Is it possible to fail two reallocs WITHOUT testing in between?
+ assert((!FailedReallocSymbol || FailedReallocSymbol == sym) &&
+ "We only support one failed realloc at a time.");
+ BR.markInteresting(sym);
+ FailedReallocSymbol = sym;
+ }
+ }
+
+ // We are in a special mode if a reallocation failed later in the path.
+ } else if (Mode == ReallocationFailed) {
+ assert(FailedReallocSymbol && "No symbol to look for.");
+
+ // Is this is the first appearance of the reallocated symbol?
+ if (!statePrev->get<RegionState>(FailedReallocSymbol)) {
+ // We're at the reallocation point.
+ Msg = "Attempt to reallocate memory";
+ StackHint = new StackHintGeneratorForSymbol(Sym,
+ "Returned reallocated memory");
+ FailedReallocSymbol = nullptr;
+ Mode = Normal;
+ }
+ }
+
+ if (Msg.empty())
+ return nullptr;
+ assert(StackHint);
+
+ // Generate the extra diagnostic.
+ PathDiagnosticLocation Pos;
+ if (!S) {
+ assert(RS->getAllocationFamily() == AF_InnerBuffer);
+ auto PostImplCall = N->getLocation().getAs<PostImplicitCall>();
+ if (!PostImplCall)
+ return nullptr;
+ Pos = PathDiagnosticLocation(PostImplCall->getLocation(),
+ BRC.getSourceManager());
+ } else {
+ Pos = PathDiagnosticLocation(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ }
+
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true, StackHint);
+}
+
+void MallocChecker::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+
+ RegionStateTy RS = State->get<RegionState>();
+
+ if (!RS.isEmpty()) {
+ Out << Sep << "MallocChecker :" << NL;
+ for (RegionStateTy::iterator I = RS.begin(), E = RS.end(); I != E; ++I) {
+ const RefState *RefS = State->get<RegionState>(I.getKey());
+ AllocationFamily Family = RefS->getAllocationFamily();
+ Optional<MallocChecker::CheckKind> CheckKind = getCheckIfTracked(Family);
+ if (!CheckKind.hasValue())
+ CheckKind = getCheckIfTracked(Family, true);
+
+ I.getKey()->dumpToStream(Out);
+ Out << " : ";
+ I.getData().dump(Out);
+ if (CheckKind.hasValue())
+ Out << " (" << CheckNames[*CheckKind].getName() << ")";
+ Out << NL;
+ }
+ }
+}
+
+namespace clang {
+namespace ento {
+namespace allocation_state {
+
+ProgramStateRef
+markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
+ AllocationFamily Family = AF_InnerBuffer;
+ return State->set<RegionState>(Sym, RefState::getReleased(Family, Origin));
+}
+
+} // end namespace allocation_state
+} // end namespace ento
+} // end namespace clang
+
+void ento::registerNewDeleteLeaksChecker(CheckerManager &mgr) {
+ registerCStringCheckerBasic(mgr);
+ MallocChecker *checker = mgr.registerChecker<MallocChecker>();
+ checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ "Optimistic", false, checker);
+ checker->ChecksEnabled[MallocChecker::CK_NewDeleteLeaksChecker] = true;
+ checker->CheckNames[MallocChecker::CK_NewDeleteLeaksChecker] =
+ mgr.getCurrentCheckName();
+ // We currently treat NewDeleteLeaks checker as a subchecker of NewDelete
+ // checker.
+ if (!checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker]) {
+ checker->ChecksEnabled[MallocChecker::CK_NewDeleteChecker] = true;
+ // FIXME: This does not set the correct name, but without this workaround
+ // no name will be set at all.
+ checker->CheckNames[MallocChecker::CK_NewDeleteChecker] =
+ mgr.getCurrentCheckName();
+ }
+}
+
+// Intended to be used in InnerPointerChecker to register the part of
+// MallocChecker connected to it.
+void ento::registerInnerPointerCheckerAux(CheckerManager &mgr) {
+ registerCStringCheckerBasic(mgr);
+ MallocChecker *checker = mgr.registerChecker<MallocChecker>();
+ checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ "Optimistic", false, checker);
+ checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true;
+ checker->CheckNames[MallocChecker::CK_InnerPointerChecker] =
+ mgr.getCurrentCheckName();
+}
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &mgr) { \
+ registerCStringCheckerBasic(mgr); \
+ MallocChecker *checker = mgr.registerChecker<MallocChecker>(); \
+ checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption( \
+ "Optimistic", false, checker); \
+ checker->ChecksEnabled[MallocChecker::CK_##name] = true; \
+ checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \
+ }
+
+REGISTER_CHECKER(MallocChecker)
+REGISTER_CHECKER(NewDeleteChecker)
+REGISTER_CHECKER(MismatchedDeallocatorChecker)
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
new file mode 100644
index 000000000000..d02ed48bceaa
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
@@ -0,0 +1,340 @@
+// MallocOverflowSecurityChecker.cpp - Check for malloc overflows -*- C++ -*-=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker detects a common memory allocation security flaw.
+// Suppose 'unsigned int n' comes from an untrusted source. If the
+// code looks like 'malloc (n * 4)', and an attacker can make 'n' be
+// say MAX_UINT/4+2, then instead of allocating the correct 'n' 4-byte
+// elements, this will actually allocate only two because of overflow.
+// Then when the rest of the program attempts to store values past the
+// second element, these values will actually overwrite other items in
+// the heap, probably allowing the attacker to execute arbitrary code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/EvaluatedExprVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/SmallVector.h"
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+using llvm::APSInt;
+
+namespace {
+struct MallocOverflowCheck {
+ const BinaryOperator *mulop;
+ const Expr *variable;
+ APSInt maxVal;
+
+ MallocOverflowCheck(const BinaryOperator *m, const Expr *v, APSInt val)
+ : mulop(m), variable(v), maxVal(std::move(val)) {}
+};
+
+class MallocOverflowSecurityChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager &mgr,
+ BugReporter &BR) const;
+
+ void CheckMallocArgument(
+ SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const Expr *TheArgument, ASTContext &Context) const;
+
+ void OutputPossibleOverflows(
+ SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const Decl *D, BugReporter &BR, AnalysisManager &mgr) const;
+
+};
+} // end anonymous namespace
+
+// Return true for computations which evaluate to zero: e.g., mult by 0.
+static inline bool EvaluatesToZero(APSInt &Val, BinaryOperatorKind op) {
+ return (op == BO_Mul) && (Val == 0);
+}
+
+void MallocOverflowSecurityChecker::CheckMallocArgument(
+ SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const Expr *TheArgument,
+ ASTContext &Context) const {
+
+ /* Look for a linear combination with a single variable, and at least
+ one multiplication.
+ Reject anything that applies to the variable: an explicit cast,
+ conditional expression, an operation that could reduce the range
+ of the result, or anything too complicated :-). */
+ const Expr *e = TheArgument;
+ const BinaryOperator * mulop = nullptr;
+ APSInt maxVal;
+
+ for (;;) {
+ maxVal = 0;
+ e = e->IgnoreParenImpCasts();
+ if (const BinaryOperator *binop = dyn_cast<BinaryOperator>(e)) {
+ BinaryOperatorKind opc = binop->getOpcode();
+ // TODO: ignore multiplications by 1, reject if multiplied by 0.
+ if (mulop == nullptr && opc == BO_Mul)
+ mulop = binop;
+ if (opc != BO_Mul && opc != BO_Add && opc != BO_Sub && opc != BO_Shl)
+ return;
+
+ const Expr *lhs = binop->getLHS();
+ const Expr *rhs = binop->getRHS();
+ if (rhs->isEvaluatable(Context)) {
+ e = lhs;
+ maxVal = rhs->EvaluateKnownConstInt(Context);
+ if (EvaluatesToZero(maxVal, opc))
+ return;
+ } else if ((opc == BO_Add || opc == BO_Mul) &&
+ lhs->isEvaluatable(Context)) {
+ maxVal = lhs->EvaluateKnownConstInt(Context);
+ if (EvaluatesToZero(maxVal, opc))
+ return;
+ e = rhs;
+ } else
+ return;
+ }
+ else if (isa<DeclRefExpr>(e) || isa<MemberExpr>(e))
+ break;
+ else
+ return;
+ }
+
+ if (mulop == nullptr)
+ return;
+
+ // We've found the right structure of malloc argument, now save
+ // the data so when the body of the function is completely available
+ // we can check for comparisons.
+
+ // TODO: Could push this into the innermost scope where 'e' is
+ // defined, rather than the whole function.
+ PossibleMallocOverflows.push_back(MallocOverflowCheck(mulop, e, maxVal));
+}
+
+namespace {
+// A worker class for OutputPossibleOverflows.
+class CheckOverflowOps :
+ public EvaluatedExprVisitor<CheckOverflowOps> {
+public:
+ typedef SmallVectorImpl<MallocOverflowCheck> theVecType;
+
+private:
+ theVecType &toScanFor;
+ ASTContext &Context;
+
+ bool isIntZeroExpr(const Expr *E) const {
+ if (!E->getType()->isIntegralOrEnumerationType())
+ return false;
+ Expr::EvalResult Result;
+ if (E->EvaluateAsInt(Result, Context))
+ return Result.Val.getInt() == 0;
+ return false;
+ }
+
+ static const Decl *getDecl(const DeclRefExpr *DR) { return DR->getDecl(); }
+ static const Decl *getDecl(const MemberExpr *ME) {
+ return ME->getMemberDecl();
+ }
+
+ template <typename T1>
+ void Erase(const T1 *DR,
+ llvm::function_ref<bool(const MallocOverflowCheck &)> Pred) {
+ auto P = [DR, Pred](const MallocOverflowCheck &Check) {
+ if (const auto *CheckDR = dyn_cast<T1>(Check.variable))
+ return getDecl(CheckDR) == getDecl(DR) && Pred(Check);
+ return false;
+ };
+ toScanFor.erase(std::remove_if(toScanFor.begin(), toScanFor.end(), P),
+ toScanFor.end());
+ }
+
+ void CheckExpr(const Expr *E_p) {
+ auto PredTrue = [](const MallocOverflowCheck &) { return true; };
+ const Expr *E = E_p->IgnoreParenImpCasts();
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
+ Erase<DeclRefExpr>(DR, PredTrue);
+ else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
+ Erase<MemberExpr>(ME, PredTrue);
+ }
+ }
+
+ // Check if the argument to malloc is assigned a value
+ // which cannot cause an overflow.
+ // e.g., malloc (mul * x) and,
+ // case 1: mul = <constant value>
+ // case 2: mul = a/b, where b > x
+ void CheckAssignmentExpr(BinaryOperator *AssignEx) {
+ bool assignKnown = false;
+ bool numeratorKnown = false, denomKnown = false;
+ APSInt denomVal;
+ denomVal = 0;
+
+ // Erase if the multiplicand was assigned a constant value.
+ const Expr *rhs = AssignEx->getRHS();
+ if (rhs->isEvaluatable(Context))
+ assignKnown = true;
+
+ // Discard the report if the multiplicand was assigned a value,
+ // that can never overflow after multiplication. e.g., the assignment
+ // is a division operator and the denominator is > other multiplicand.
+ const Expr *rhse = rhs->IgnoreParenImpCasts();
+ if (const BinaryOperator *BOp = dyn_cast<BinaryOperator>(rhse)) {
+ if (BOp->getOpcode() == BO_Div) {
+ const Expr *denom = BOp->getRHS()->IgnoreParenImpCasts();
+ Expr::EvalResult Result;
+ if (denom->EvaluateAsInt(Result, Context)) {
+ denomVal = Result.Val.getInt();
+ denomKnown = true;
+ }
+ const Expr *numerator = BOp->getLHS()->IgnoreParenImpCasts();
+ if (numerator->isEvaluatable(Context))
+ numeratorKnown = true;
+ }
+ }
+ if (!assignKnown && !denomKnown)
+ return;
+ auto denomExtVal = denomVal.getExtValue();
+
+ // Ignore negative denominator.
+ if (denomExtVal < 0)
+ return;
+
+ const Expr *lhs = AssignEx->getLHS();
+ const Expr *E = lhs->IgnoreParenImpCasts();
+
+ auto pred = [assignKnown, numeratorKnown,
+ denomExtVal](const MallocOverflowCheck &Check) {
+ return assignKnown ||
+ (numeratorKnown && (denomExtVal >= Check.maxVal.getExtValue()));
+ };
+
+ if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
+ Erase<DeclRefExpr>(DR, pred);
+ else if (const auto *ME = dyn_cast<MemberExpr>(E))
+ Erase<MemberExpr>(ME, pred);
+ }
+
+ public:
+ void VisitBinaryOperator(BinaryOperator *E) {
+ if (E->isComparisonOp()) {
+ const Expr * lhs = E->getLHS();
+ const Expr * rhs = E->getRHS();
+ // Ignore comparisons against zero, since they generally don't
+ // protect against an overflow.
+ if (!isIntZeroExpr(lhs) && !isIntZeroExpr(rhs)) {
+ CheckExpr(lhs);
+ CheckExpr(rhs);
+ }
+ }
+ if (E->isAssignmentOp())
+ CheckAssignmentExpr(E);
+ EvaluatedExprVisitor<CheckOverflowOps>::VisitBinaryOperator(E);
+ }
+
+ /* We specifically ignore loop conditions, because they're typically
+ not error checks. */
+ void VisitWhileStmt(WhileStmt *S) {
+ return this->Visit(S->getBody());
+ }
+ void VisitForStmt(ForStmt *S) {
+ return this->Visit(S->getBody());
+ }
+ void VisitDoStmt(DoStmt *S) {
+ return this->Visit(S->getBody());
+ }
+
+ CheckOverflowOps(theVecType &v, ASTContext &ctx)
+ : EvaluatedExprVisitor<CheckOverflowOps>(ctx),
+ toScanFor(v), Context(ctx)
+ { }
+ };
+}
+
+// OutputPossibleOverflows - We've found a possible overflow earlier,
+// now check whether Body might contain a comparison which might be
+// preventing the overflow.
+// This doesn't do flow analysis, range analysis, or points-to analysis; it's
+// just a dumb "is there a comparison" scan. The aim here is to
+// detect the most blatent cases of overflow and educate the
+// programmer.
+void MallocOverflowSecurityChecker::OutputPossibleOverflows(
+ SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const Decl *D, BugReporter &BR, AnalysisManager &mgr) const {
+ // By far the most common case: nothing to check.
+ if (PossibleMallocOverflows.empty())
+ return;
+
+ // Delete any possible overflows which have a comparison.
+ CheckOverflowOps c(PossibleMallocOverflows, BR.getContext());
+ c.Visit(mgr.getAnalysisDeclContext(D)->getBody());
+
+ // Output warnings for all overflows that are left.
+ for (CheckOverflowOps::theVecType::iterator
+ i = PossibleMallocOverflows.begin(),
+ e = PossibleMallocOverflows.end();
+ i != e;
+ ++i) {
+ BR.EmitBasicReport(
+ D, this, "malloc() size overflow", categories::UnixAPI,
+ "the computation of the size of the memory allocation may overflow",
+ PathDiagnosticLocation::createOperatorLoc(i->mulop,
+ BR.getSourceManager()),
+ i->mulop->getSourceRange());
+ }
+}
+
+void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D,
+ AnalysisManager &mgr,
+ BugReporter &BR) const {
+
+ CFG *cfg = mgr.getCFG(D);
+ if (!cfg)
+ return;
+
+ // A list of variables referenced in possibly overflowing malloc operands.
+ SmallVector<MallocOverflowCheck, 2> PossibleMallocOverflows;
+
+ for (CFG::iterator it = cfg->begin(), ei = cfg->end(); it != ei; ++it) {
+ CFGBlock *block = *it;
+ for (CFGBlock::iterator bi = block->begin(), be = block->end();
+ bi != be; ++bi) {
+ if (Optional<CFGStmt> CS = bi->getAs<CFGStmt>()) {
+ if (const CallExpr *TheCall = dyn_cast<CallExpr>(CS->getStmt())) {
+ // Get the callee.
+ const FunctionDecl *FD = TheCall->getDirectCallee();
+
+ if (!FD)
+ continue;
+
+ // Get the name of the callee. If it's a builtin, strip off the prefix.
+ IdentifierInfo *FnInfo = FD->getIdentifier();
+ if (!FnInfo)
+ continue;
+
+ if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) {
+ if (TheCall->getNumArgs() == 1)
+ CheckMallocArgument(PossibleMallocOverflows, TheCall->getArg(0),
+ mgr.getASTContext());
+ }
+ }
+ }
+ }
+ }
+
+ OutputPossibleOverflows(PossibleMallocOverflows, D, BR, mgr);
+}
+
+void
+ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) {
+ mgr.registerChecker<MallocOverflowSecurityChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
new file mode 100644
index 000000000000..bb245d82bc2b
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
@@ -0,0 +1,252 @@
+// MallocSizeofChecker.cpp - Check for dubious malloc arguments ---*- C++ -*-=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Reports inconsistencies between the casted type of the return value of a
+// malloc/calloc/realloc call and the operand of any sizeof expressions
+// contained within its argument(s).
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/AST/TypeLoc.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+typedef std::pair<const TypeSourceInfo *, const CallExpr *> TypeCallPair;
+typedef llvm::PointerUnion<const Stmt *, const VarDecl *> ExprParent;
+
+class CastedAllocFinder
+ : public ConstStmtVisitor<CastedAllocFinder, TypeCallPair> {
+ IdentifierInfo *II_malloc, *II_calloc, *II_realloc;
+
+public:
+ struct CallRecord {
+ ExprParent CastedExprParent;
+ const Expr *CastedExpr;
+ const TypeSourceInfo *ExplicitCastType;
+ const CallExpr *AllocCall;
+
+ CallRecord(ExprParent CastedExprParent, const Expr *CastedExpr,
+ const TypeSourceInfo *ExplicitCastType,
+ const CallExpr *AllocCall)
+ : CastedExprParent(CastedExprParent), CastedExpr(CastedExpr),
+ ExplicitCastType(ExplicitCastType), AllocCall(AllocCall) {}
+ };
+
+ typedef std::vector<CallRecord> CallVec;
+ CallVec Calls;
+
+ CastedAllocFinder(ASTContext *Ctx) :
+ II_malloc(&Ctx->Idents.get("malloc")),
+ II_calloc(&Ctx->Idents.get("calloc")),
+ II_realloc(&Ctx->Idents.get("realloc")) {}
+
+ void VisitChild(ExprParent Parent, const Stmt *S) {
+ TypeCallPair AllocCall = Visit(S);
+ if (AllocCall.second && AllocCall.second != S)
+ Calls.push_back(CallRecord(Parent, cast<Expr>(S), AllocCall.first,
+ AllocCall.second));
+ }
+
+ void VisitChildren(const Stmt *S) {
+ for (const Stmt *Child : S->children())
+ if (Child)
+ VisitChild(S, Child);
+ }
+
+ TypeCallPair VisitCastExpr(const CastExpr *E) {
+ return Visit(E->getSubExpr());
+ }
+
+ TypeCallPair VisitExplicitCastExpr(const ExplicitCastExpr *E) {
+ return TypeCallPair(E->getTypeInfoAsWritten(),
+ Visit(E->getSubExpr()).second);
+ }
+
+ TypeCallPair VisitParenExpr(const ParenExpr *E) {
+ return Visit(E->getSubExpr());
+ }
+
+ TypeCallPair VisitStmt(const Stmt *S) {
+ VisitChildren(S);
+ return TypeCallPair();
+ }
+
+ TypeCallPair VisitCallExpr(const CallExpr *E) {
+ VisitChildren(E);
+ const FunctionDecl *FD = E->getDirectCallee();
+ if (FD) {
+ IdentifierInfo *II = FD->getIdentifier();
+ if (II == II_malloc || II == II_calloc || II == II_realloc)
+ return TypeCallPair((const TypeSourceInfo *)nullptr, E);
+ }
+ return TypeCallPair();
+ }
+
+ TypeCallPair VisitDeclStmt(const DeclStmt *S) {
+ for (const auto *I : S->decls())
+ if (const VarDecl *VD = dyn_cast<VarDecl>(I))
+ if (const Expr *Init = VD->getInit())
+ VisitChild(VD, Init);
+ return TypeCallPair();
+ }
+};
+
+class SizeofFinder : public ConstStmtVisitor<SizeofFinder> {
+public:
+ std::vector<const UnaryExprOrTypeTraitExpr *> Sizeofs;
+
+ void VisitBinMul(const BinaryOperator *E) {
+ Visit(E->getLHS());
+ Visit(E->getRHS());
+ }
+
+ void VisitImplicitCastExpr(const ImplicitCastExpr *E) {
+ return Visit(E->getSubExpr());
+ }
+
+ void VisitParenExpr(const ParenExpr *E) {
+ return Visit(E->getSubExpr());
+ }
+
+ void VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *E) {
+ if (E->getKind() != UETT_SizeOf)
+ return;
+
+ Sizeofs.push_back(E);
+ }
+};
+
+// Determine if the pointee and sizeof types are compatible. Here
+// we ignore constness of pointer types.
+static bool typesCompatible(ASTContext &C, QualType A, QualType B) {
+ // sizeof(void*) is compatible with any other pointer.
+ if (B->isVoidPointerType() && A->getAs<PointerType>())
+ return true;
+
+ while (true) {
+ A = A.getCanonicalType();
+ B = B.getCanonicalType();
+
+ if (A.getTypePtr() == B.getTypePtr())
+ return true;
+
+ if (const PointerType *ptrA = A->getAs<PointerType>())
+ if (const PointerType *ptrB = B->getAs<PointerType>()) {
+ A = ptrA->getPointeeType();
+ B = ptrB->getPointeeType();
+ continue;
+ }
+
+ break;
+ }
+
+ return false;
+}
+
+static bool compatibleWithArrayType(ASTContext &C, QualType PT, QualType T) {
+ // Ex: 'int a[10][2]' is compatible with 'int', 'int[2]', 'int[10][2]'.
+ while (const ArrayType *AT = T->getAsArrayTypeUnsafe()) {
+ QualType ElemType = AT->getElementType();
+ if (typesCompatible(C, PT, AT->getElementType()))
+ return true;
+ T = ElemType;
+ }
+
+ return false;
+}
+
+class MallocSizeofChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ AnalysisDeclContext *ADC = mgr.getAnalysisDeclContext(D);
+ CastedAllocFinder Finder(&BR.getContext());
+ Finder.Visit(D->getBody());
+ for (CastedAllocFinder::CallVec::iterator i = Finder.Calls.begin(),
+ e = Finder.Calls.end(); i != e; ++i) {
+ QualType CastedType = i->CastedExpr->getType();
+ if (!CastedType->isPointerType())
+ continue;
+ QualType PointeeType = CastedType->getAs<PointerType>()->getPointeeType();
+ if (PointeeType->isVoidType())
+ continue;
+
+ for (CallExpr::const_arg_iterator ai = i->AllocCall->arg_begin(),
+ ae = i->AllocCall->arg_end(); ai != ae; ++ai) {
+ if (!(*ai)->getType()->isIntegralOrUnscopedEnumerationType())
+ continue;
+
+ SizeofFinder SFinder;
+ SFinder.Visit(*ai);
+ if (SFinder.Sizeofs.size() != 1)
+ continue;
+
+ QualType SizeofType = SFinder.Sizeofs[0]->getTypeOfArgument();
+
+ if (typesCompatible(BR.getContext(), PointeeType, SizeofType))
+ continue;
+
+ // If the argument to sizeof is an array, the result could be a
+ // pointer to any array element.
+ if (compatibleWithArrayType(BR.getContext(), PointeeType, SizeofType))
+ continue;
+
+ const TypeSourceInfo *TSI = nullptr;
+ if (i->CastedExprParent.is<const VarDecl *>()) {
+ TSI =
+ i->CastedExprParent.get<const VarDecl *>()->getTypeSourceInfo();
+ } else {
+ TSI = i->ExplicitCastType;
+ }
+
+ SmallString<64> buf;
+ llvm::raw_svector_ostream OS(buf);
+
+ OS << "Result of ";
+ const FunctionDecl *Callee = i->AllocCall->getDirectCallee();
+ if (Callee && Callee->getIdentifier())
+ OS << '\'' << Callee->getIdentifier()->getName() << '\'';
+ else
+ OS << "call";
+ OS << " is converted to a pointer of type '"
+ << PointeeType.getAsString() << "', which is incompatible with "
+ << "sizeof operand type '" << SizeofType.getAsString() << "'";
+ SmallVector<SourceRange, 4> Ranges;
+ Ranges.push_back(i->AllocCall->getCallee()->getSourceRange());
+ Ranges.push_back(SFinder.Sizeofs[0]->getSourceRange());
+ if (TSI)
+ Ranges.push_back(TSI->getTypeLoc().getSourceRange());
+
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::createBegin(i->AllocCall->getCallee(),
+ BR.getSourceManager(), ADC);
+
+ BR.EmitBasicReport(D, this, "Allocator sizeof operand mismatch",
+ categories::UnixAPI, OS.str(), L, Ranges);
+ }
+ }
+ }
+};
+
+}
+
+void ento::registerMallocSizeofChecker(CheckerManager &mgr) {
+ mgr.registerChecker<MallocSizeofChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
new file mode 100644
index 000000000000..e3b24f20b0f0
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
@@ -0,0 +1,90 @@
+// MmapWriteExecChecker.cpp - Check for the prot argument -----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker tests the 3rd argument of mmap's calls to check if
+// it is writable and executable in the same time. It's somehow
+// an optional checker since for example in JIT libraries it is pretty common.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+using llvm::APSInt;
+
+namespace {
+class MmapWriteExecChecker : public Checker<check::PreCall> {
+ CallDescription MmapFn;
+ CallDescription MprotectFn;
+ static int ProtWrite;
+ static int ProtExec;
+ static int ProtRead;
+ mutable std::unique_ptr<BugType> BT;
+public:
+ MmapWriteExecChecker() : MmapFn("mmap", 6), MprotectFn("mprotect", 3) {}
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ int ProtExecOv;
+ int ProtReadOv;
+};
+}
+
+int MmapWriteExecChecker::ProtWrite = 0x02;
+int MmapWriteExecChecker::ProtExec = 0x04;
+int MmapWriteExecChecker::ProtRead = 0x01;
+
+void MmapWriteExecChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (Call.isCalled(MmapFn) || Call.isCalled(MprotectFn)) {
+ SVal ProtVal = Call.getArgSVal(2);
+ Optional<nonloc::ConcreteInt> ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>();
+ int64_t Prot = ProtLoc->getValue().getSExtValue();
+ if (ProtExecOv != ProtExec)
+ ProtExec = ProtExecOv;
+ if (ProtReadOv != ProtRead)
+ ProtRead = ProtReadOv;
+
+ // Wrong settings
+ if (ProtRead == ProtExec)
+ return;
+
+ if ((Prot & (ProtWrite | ProtExec)) == (ProtWrite | ProtExec)) {
+ if (!BT)
+ BT.reset(new BugType(this, "W^X check fails, Write Exec prot flags set", "Security"));
+
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (!N)
+ return;
+
+ auto Report = llvm::make_unique<BugReport>(
+ *BT, "Both PROT_WRITE and PROT_EXEC flags are set. This can "
+ "lead to exploitable memory regions, which could be overwritten "
+ "with malicious code", N);
+ Report->addRange(Call.getArgSourceRange(2));
+ C.emitReport(std::move(Report));
+ }
+ }
+}
+
+void ento::registerMmapWriteExecChecker(CheckerManager &mgr) {
+ MmapWriteExecChecker *Mwec =
+ mgr.registerChecker<MmapWriteExecChecker>();
+ Mwec->ProtExecOv =
+ mgr.getAnalyzerOptions()
+ .getCheckerIntegerOption("MmapProtExec", 0x04, Mwec);
+ Mwec->ProtReadOv =
+ mgr.getAnalyzerOptions()
+ .getCheckerIntegerOption("MmapProtRead", 0x01, Mwec);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
new file mode 100644
index 000000000000..6efa2dfbe5b4
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
@@ -0,0 +1,740 @@
+// MoveChecker.cpp - Check use of moved-from objects. - C++ ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines checker which checks for potential misuses of a moved-from
+// object. That means method calls on the object or copying it in moved-from
+// state.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ExprCXX.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/StringSet.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+struct RegionState {
+private:
+ enum Kind { Moved, Reported } K;
+ RegionState(Kind InK) : K(InK) {}
+
+public:
+ bool isReported() const { return K == Reported; }
+ bool isMoved() const { return K == Moved; }
+
+ static RegionState getReported() { return RegionState(Reported); }
+ static RegionState getMoved() { return RegionState(Moved); }
+
+ bool operator==(const RegionState &X) const { return K == X.K; }
+ void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddInteger(K); }
+};
+} // end of anonymous namespace
+
+namespace {
+class MoveChecker
+ : public Checker<check::PreCall, check::PostCall,
+ check::DeadSymbols, check::RegionChanges> {
+public:
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &MC, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &MC, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+ ProgramStateRef
+ checkRegionChanges(ProgramStateRef State,
+ const InvalidatedSymbols *Invalidated,
+ ArrayRef<const MemRegion *> RequestedRegions,
+ ArrayRef<const MemRegion *> InvalidatedRegions,
+ const LocationContext *LCtx, const CallEvent *Call) const;
+ void printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const override;
+
+private:
+ enum MisuseKind { MK_FunCall, MK_Copy, MK_Move, MK_Dereference };
+ enum StdObjectKind { SK_NonStd, SK_Unsafe, SK_Safe, SK_SmartPtr };
+
+ enum AggressivenessKind { // In any case, don't warn after a reset.
+ AK_Invalid = -1,
+ AK_KnownsOnly = 0, // Warn only about known move-unsafe classes.
+ AK_KnownsAndLocals = 1, // Also warn about all local objects.
+ AK_All = 2, // Warn on any use-after-move.
+ AK_NumKinds = AK_All
+ };
+
+ static bool misuseCausesCrash(MisuseKind MK) {
+ return MK == MK_Dereference;
+ }
+
+ struct ObjectKind {
+ // Is this a local variable or a local rvalue reference?
+ bool IsLocal;
+ // Is this an STL object? If so, of what kind?
+ StdObjectKind StdKind;
+ };
+
+ // STL smart pointers are automatically re-initialized to null when moved
+ // from. So we can't warn on many methods, but we can warn when it is
+ // dereferenced, which is UB even if the resulting lvalue never gets read.
+ const llvm::StringSet<> StdSmartPtrClasses = {
+ "shared_ptr",
+ "unique_ptr",
+ "weak_ptr",
+ };
+
+ // Not all of these are entirely move-safe, but they do provide *some*
+ // guarantees, and it means that somebody is using them after move
+ // in a valid manner.
+ // TODO: We can still try to identify *unsafe* use after move,
+ // like we did with smart pointers.
+ const llvm::StringSet<> StdSafeClasses = {
+ "basic_filebuf",
+ "basic_ios",
+ "future",
+ "optional",
+ "packaged_task"
+ "promise",
+ "shared_future",
+ "shared_lock",
+ "thread",
+ "unique_lock",
+ };
+
+ // Should we bother tracking the state of the object?
+ bool shouldBeTracked(ObjectKind OK) const {
+ // In non-aggressive mode, only warn on use-after-move of local variables
+ // (or local rvalue references) and of STL objects. The former is possible
+ // because local variables (or local rvalue references) are not tempting
+ // their user to re-use the storage. The latter is possible because STL
+ // objects are known to end up in a valid but unspecified state after the
+ // move and their state-reset methods are also known, which allows us to
+ // predict precisely when use-after-move is invalid.
+ // Some STL objects are known to conform to additional contracts after move,
+ // so they are not tracked. However, smart pointers specifically are tracked
+ // because we can perform extra checking over them.
+ // In aggressive mode, warn on any use-after-move because the user has
+ // intentionally asked us to completely eliminate use-after-move
+ // in his code.
+ return (Aggressiveness == AK_All) ||
+ (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
+ OK.StdKind == SK_Unsafe || OK.StdKind == SK_SmartPtr;
+ }
+
+ // Some objects only suffer from some kinds of misuses, but we need to track
+ // them anyway because we cannot know in advance what misuse will we find.
+ bool shouldWarnAbout(ObjectKind OK, MisuseKind MK) const {
+ // Additionally, only warn on smart pointers when they are dereferenced (or
+ // local or we are aggressive).
+ return shouldBeTracked(OK) &&
+ ((Aggressiveness == AK_All) ||
+ (Aggressiveness >= AK_KnownsAndLocals && OK.IsLocal) ||
+ OK.StdKind != SK_SmartPtr || MK == MK_Dereference);
+ }
+
+ // Obtains ObjectKind of an object. Because class declaration cannot always
+ // be easily obtained from the memory region, it is supplied separately.
+ ObjectKind classifyObject(const MemRegion *MR, const CXXRecordDecl *RD) const;
+
+ // Classifies the object and dumps a user-friendly description string to
+ // the stream.
+ void explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
+ const CXXRecordDecl *RD, MisuseKind MK) const;
+
+ bool belongsTo(const CXXRecordDecl *RD, const llvm::StringSet<> &Set) const;
+
+ class MovedBugVisitor : public BugReporterVisitor {
+ public:
+ MovedBugVisitor(const MoveChecker &Chk, const MemRegion *R,
+ const CXXRecordDecl *RD, MisuseKind MK)
+ : Chk(Chk), Region(R), RD(RD), MK(MK), Found(false) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int X = 0;
+ ID.AddPointer(&X);
+ ID.AddPointer(Region);
+ // Don't add RD because it's, in theory, uniquely determined by
+ // the region. In practice though, it's not always possible to obtain
+ // the declaration directly from the region, that's why we store it
+ // in the first place.
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ private:
+ const MoveChecker &Chk;
+ // The tracked region.
+ const MemRegion *Region;
+ // The class of the tracked object.
+ const CXXRecordDecl *RD;
+ // How exactly the object was misused.
+ const MisuseKind MK;
+ bool Found;
+ };
+
+ AggressivenessKind Aggressiveness;
+
+public:
+ void setAggressiveness(StringRef Str) {
+ Aggressiveness =
+ llvm::StringSwitch<AggressivenessKind>(Str)
+ .Case("KnownsOnly", AK_KnownsOnly)
+ .Case("KnownsAndLocals", AK_KnownsAndLocals)
+ .Case("All", AK_All)
+ .Default(AK_KnownsAndLocals); // A sane default.
+ };
+
+private:
+ mutable std::unique_ptr<BugType> BT;
+
+ // Check if the given form of potential misuse of a given object
+ // should be reported. If so, get it reported. The callback from which
+ // this function was called should immediately return after the call
+ // because this function adds one or two transitions.
+ void modelUse(ProgramStateRef State, const MemRegion *Region,
+ const CXXRecordDecl *RD, MisuseKind MK,
+ CheckerContext &C) const;
+
+ // Returns the exploded node against which the report was emitted.
+ // The caller *must* add any further transitions against this node.
+ ExplodedNode *reportBug(const MemRegion *Region, const CXXRecordDecl *RD,
+ CheckerContext &C, MisuseKind MK) const;
+
+ bool isInMoveSafeContext(const LocationContext *LC) const;
+ bool isStateResetMethod(const CXXMethodDecl *MethodDec) const;
+ bool isMoveSafeMethod(const CXXMethodDecl *MethodDec) const;
+ const ExplodedNode *getMoveLocation(const ExplodedNode *N,
+ const MemRegion *Region,
+ CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState)
+
+// If a region is removed all of the subregions needs to be removed too.
+static ProgramStateRef removeFromState(ProgramStateRef State,
+ const MemRegion *Region) {
+ if (!Region)
+ return State;
+ for (auto &E : State->get<TrackedRegionMap>()) {
+ if (E.first->isSubRegionOf(Region))
+ State = State->remove<TrackedRegionMap>(E.first);
+ }
+ return State;
+}
+
+static bool isAnyBaseRegionReported(ProgramStateRef State,
+ const MemRegion *Region) {
+ for (auto &E : State->get<TrackedRegionMap>()) {
+ if (Region->isSubRegionOf(E.first) && E.second.isReported())
+ return true;
+ }
+ return false;
+}
+
+static const MemRegion *unwrapRValueReferenceIndirection(const MemRegion *MR) {
+ if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(MR)) {
+ SymbolRef Sym = SR->getSymbol();
+ if (Sym->getType()->isRValueReferenceType())
+ if (const MemRegion *OriginMR = Sym->getOriginRegion())
+ return OriginMR;
+ }
+ return MR;
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+MoveChecker::MovedBugVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC, BugReport &BR) {
+ // We need only the last move of the reported object's region.
+ // The visitor walks the ExplodedGraph backwards.
+ if (Found)
+ return nullptr;
+ ProgramStateRef State = N->getState();
+ ProgramStateRef StatePrev = N->getFirstPred()->getState();
+ const RegionState *TrackedObject = State->get<TrackedRegionMap>(Region);
+ const RegionState *TrackedObjectPrev =
+ StatePrev->get<TrackedRegionMap>(Region);
+ if (!TrackedObject)
+ return nullptr;
+ if (TrackedObjectPrev && TrackedObject)
+ return nullptr;
+
+ // Retrieve the associated statement.
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ if (!S)
+ return nullptr;
+ Found = true;
+
+ SmallString<128> Str;
+ llvm::raw_svector_ostream OS(Str);
+
+ ObjectKind OK = Chk.classifyObject(Region, RD);
+ switch (OK.StdKind) {
+ case SK_SmartPtr:
+ if (MK == MK_Dereference) {
+ OS << "Smart pointer";
+ Chk.explainObject(OS, Region, RD, MK);
+ OS << " is reset to null when moved from";
+ break;
+ }
+
+ // If it's not a dereference, we don't care if it was reset to null
+ // or that it is even a smart pointer.
+ LLVM_FALLTHROUGH;
+ case SK_NonStd:
+ case SK_Safe:
+ OS << "Object";
+ Chk.explainObject(OS, Region, RD, MK);
+ OS << " is moved";
+ break;
+ case SK_Unsafe:
+ OS << "Object";
+ Chk.explainObject(OS, Region, RD, MK);
+ OS << " is left in a valid but unspecified state after move";
+ break;
+ }
+
+ // Generate the extra diagnostic.
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, OS.str(), true);
+}
+
+const ExplodedNode *MoveChecker::getMoveLocation(const ExplodedNode *N,
+ const MemRegion *Region,
+ CheckerContext &C) const {
+ // Walk the ExplodedGraph backwards and find the first node that referred to
+ // the tracked region.
+ const ExplodedNode *MoveNode = N;
+
+ while (N) {
+ ProgramStateRef State = N->getState();
+ if (!State->get<TrackedRegionMap>(Region))
+ break;
+ MoveNode = N;
+ N = N->pred_empty() ? nullptr : *(N->pred_begin());
+ }
+ return MoveNode;
+}
+
+void MoveChecker::modelUse(ProgramStateRef State, const MemRegion *Region,
+ const CXXRecordDecl *RD, MisuseKind MK,
+ CheckerContext &C) const {
+ assert(!C.isDifferent() && "No transitions should have been made by now");
+ const RegionState *RS = State->get<TrackedRegionMap>(Region);
+ ObjectKind OK = classifyObject(Region, RD);
+
+ // Just in case: if it's not a smart pointer but it does have operator *,
+ // we shouldn't call the bug a dereference.
+ if (MK == MK_Dereference && OK.StdKind != SK_SmartPtr)
+ MK = MK_FunCall;
+
+ if (!RS || !shouldWarnAbout(OK, MK)
+ || isInMoveSafeContext(C.getLocationContext())) {
+ // Finalize changes made by the caller.
+ C.addTransition(State);
+ return;
+ }
+
+ // Don't report it in case if any base region is already reported.
+ // But still generate a sink in case of UB.
+ // And still finalize changes made by the caller.
+ if (isAnyBaseRegionReported(State, Region)) {
+ if (misuseCausesCrash(MK)) {
+ C.generateSink(State, C.getPredecessor());
+ } else {
+ C.addTransition(State);
+ }
+ return;
+ }
+
+ ExplodedNode *N = reportBug(Region, RD, C, MK);
+
+ // If the program has already crashed on this path, don't bother.
+ if (N->isSink())
+ return;
+
+ State = State->set<TrackedRegionMap>(Region, RegionState::getReported());
+ C.addTransition(State, N);
+}
+
+ExplodedNode *MoveChecker::reportBug(const MemRegion *Region,
+ const CXXRecordDecl *RD, CheckerContext &C,
+ MisuseKind MK) const {
+ if (ExplodedNode *N = misuseCausesCrash(MK) ? C.generateErrorNode()
+ : C.generateNonFatalErrorNode()) {
+
+ if (!BT)
+ BT.reset(new BugType(this, "Use-after-move",
+ "C++ move semantics"));
+
+ // Uniqueing report to the same object.
+ PathDiagnosticLocation LocUsedForUniqueing;
+ const ExplodedNode *MoveNode = getMoveLocation(N, Region, C);
+
+ if (const Stmt *MoveStmt = PathDiagnosticLocation::getStmt(MoveNode))
+ LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
+ MoveStmt, C.getSourceManager(), MoveNode->getLocationContext());
+
+ // Creating the error message.
+ llvm::SmallString<128> Str;
+ llvm::raw_svector_ostream OS(Str);
+ switch(MK) {
+ case MK_FunCall:
+ OS << "Method called on moved-from object";
+ explainObject(OS, Region, RD, MK);
+ break;
+ case MK_Copy:
+ OS << "Moved-from object";
+ explainObject(OS, Region, RD, MK);
+ OS << " is copied";
+ break;
+ case MK_Move:
+ OS << "Moved-from object";
+ explainObject(OS, Region, RD, MK);
+ OS << " is moved";
+ break;
+ case MK_Dereference:
+ OS << "Dereference of null smart pointer";
+ explainObject(OS, Region, RD, MK);
+ break;
+ }
+
+ auto R =
+ llvm::make_unique<BugReport>(*BT, OS.str(), N, LocUsedForUniqueing,
+ MoveNode->getLocationContext()->getDecl());
+ R->addVisitor(llvm::make_unique<MovedBugVisitor>(*this, Region, RD, MK));
+ C.emitReport(std::move(R));
+ return N;
+ }
+ return nullptr;
+}
+
+void MoveChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *AFC = dyn_cast<AnyFunctionCall>(&Call);
+ if (!AFC)
+ return;
+
+ ProgramStateRef State = C.getState();
+ const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(AFC->getDecl());
+ if (!MethodDecl)
+ return;
+
+ // Check if an object became moved-from.
+ // Object can become moved from after a call to move assignment operator or
+ // move constructor .
+ const auto *ConstructorDecl = dyn_cast<CXXConstructorDecl>(MethodDecl);
+ if (ConstructorDecl && !ConstructorDecl->isMoveConstructor())
+ return;
+
+ if (!ConstructorDecl && !MethodDecl->isMoveAssignmentOperator())
+ return;
+
+ const auto ArgRegion = AFC->getArgSVal(0).getAsRegion();
+ if (!ArgRegion)
+ return;
+
+ // Skip moving the object to itself.
+ const auto *CC = dyn_cast_or_null<CXXConstructorCall>(&Call);
+ if (CC && CC->getCXXThisVal().getAsRegion() == ArgRegion)
+ return;
+
+ if (const auto *IC = dyn_cast<CXXInstanceCall>(AFC))
+ if (IC->getCXXThisVal().getAsRegion() == ArgRegion)
+ return;
+
+ const MemRegion *BaseRegion = ArgRegion->getBaseRegion();
+ // Skip temp objects because of their short lifetime.
+ if (BaseRegion->getAs<CXXTempObjectRegion>() ||
+ AFC->getArgExpr(0)->isRValue())
+ return;
+ // If it has already been reported do not need to modify the state.
+
+ if (State->get<TrackedRegionMap>(ArgRegion))
+ return;
+
+ const CXXRecordDecl *RD = MethodDecl->getParent();
+ ObjectKind OK = classifyObject(ArgRegion, RD);
+ if (shouldBeTracked(OK)) {
+ // Mark object as moved-from.
+ State = State->set<TrackedRegionMap>(ArgRegion, RegionState::getMoved());
+ C.addTransition(State);
+ return;
+ }
+ assert(!C.isDifferent() && "Should not have made transitions on this path!");
+}
+
+bool MoveChecker::isMoveSafeMethod(const CXXMethodDecl *MethodDec) const {
+ // We abandon the cases where bool/void/void* conversion happens.
+ if (const auto *ConversionDec =
+ dyn_cast_or_null<CXXConversionDecl>(MethodDec)) {
+ const Type *Tp = ConversionDec->getConversionType().getTypePtrOrNull();
+ if (!Tp)
+ return false;
+ if (Tp->isBooleanType() || Tp->isVoidType() || Tp->isVoidPointerType())
+ return true;
+ }
+ // Function call `empty` can be skipped.
+ return (MethodDec && MethodDec->getDeclName().isIdentifier() &&
+ (MethodDec->getName().lower() == "empty" ||
+ MethodDec->getName().lower() == "isempty"));
+}
+
+bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const {
+ if (!MethodDec)
+ return false;
+ if (MethodDec->hasAttr<ReinitializesAttr>())
+ return true;
+ if (MethodDec->getDeclName().isIdentifier()) {
+ std::string MethodName = MethodDec->getName().lower();
+ // TODO: Some of these methods (eg., resize) are not always resetting
+ // the state, so we should consider looking at the arguments.
+ if (MethodName == "reset" || MethodName == "clear" ||
+ MethodName == "destroy" || MethodName == "resize" ||
+ MethodName == "shrink")
+ return true;
+ }
+ return false;
+}
+
+// Don't report an error inside a move related operation.
+// We assume that the programmer knows what she does.
+bool MoveChecker::isInMoveSafeContext(const LocationContext *LC) const {
+ do {
+ const auto *CtxDec = LC->getDecl();
+ auto *CtorDec = dyn_cast_or_null<CXXConstructorDecl>(CtxDec);
+ auto *DtorDec = dyn_cast_or_null<CXXDestructorDecl>(CtxDec);
+ auto *MethodDec = dyn_cast_or_null<CXXMethodDecl>(CtxDec);
+ if (DtorDec || (CtorDec && CtorDec->isCopyOrMoveConstructor()) ||
+ (MethodDec && MethodDec->isOverloadedOperator() &&
+ MethodDec->getOverloadedOperator() == OO_Equal) ||
+ isStateResetMethod(MethodDec) || isMoveSafeMethod(MethodDec))
+ return true;
+ } while ((LC = LC->getParent()));
+ return false;
+}
+
+bool MoveChecker::belongsTo(const CXXRecordDecl *RD,
+ const llvm::StringSet<> &Set) const {
+ const IdentifierInfo *II = RD->getIdentifier();
+ return II && Set.count(II->getName());
+}
+
+MoveChecker::ObjectKind
+MoveChecker::classifyObject(const MemRegion *MR,
+ const CXXRecordDecl *RD) const {
+ // Local variables and local rvalue references are classified as "Local".
+ // For the purposes of this checker, we classify move-safe STL types
+ // as not-"STL" types, because that's how the checker treats them.
+ MR = unwrapRValueReferenceIndirection(MR);
+ bool IsLocal =
+ MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace());
+
+ if (!RD || !RD->getDeclContext()->isStdNamespace())
+ return { IsLocal, SK_NonStd };
+
+ if (belongsTo(RD, StdSmartPtrClasses))
+ return { IsLocal, SK_SmartPtr };
+
+ if (belongsTo(RD, StdSafeClasses))
+ return { IsLocal, SK_Safe };
+
+ return { IsLocal, SK_Unsafe };
+}
+
+void MoveChecker::explainObject(llvm::raw_ostream &OS, const MemRegion *MR,
+ const CXXRecordDecl *RD, MisuseKind MK) const {
+ // We may need a leading space every time we actually explain anything,
+ // and we never know if we are to explain anything until we try.
+ if (const auto DR =
+ dyn_cast_or_null<DeclRegion>(unwrapRValueReferenceIndirection(MR))) {
+ const auto *RegionDecl = cast<NamedDecl>(DR->getDecl());
+ OS << " '" << RegionDecl->getNameAsString() << "'";
+ }
+
+ ObjectKind OK = classifyObject(MR, RD);
+ switch (OK.StdKind) {
+ case SK_NonStd:
+ case SK_Safe:
+ break;
+ case SK_SmartPtr:
+ if (MK != MK_Dereference)
+ break;
+
+ // We only care about the type if it's a dereference.
+ LLVM_FALLTHROUGH;
+ case SK_Unsafe:
+ OS << " of type '" << RD->getQualifiedNameAsString() << "'";
+ break;
+ };
+}
+
+void MoveChecker::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // Remove the MemRegions from the map on which a ctor/dtor call or assignment
+ // happened.
+
+ // Checking constructor calls.
+ if (const auto *CC = dyn_cast<CXXConstructorCall>(&Call)) {
+ State = removeFromState(State, CC->getCXXThisVal().getAsRegion());
+ auto CtorDec = CC->getDecl();
+ // Check for copying a moved-from object and report the bug.
+ if (CtorDec && CtorDec->isCopyOrMoveConstructor()) {
+ const MemRegion *ArgRegion = CC->getArgSVal(0).getAsRegion();
+ const CXXRecordDecl *RD = CtorDec->getParent();
+ MisuseKind MK = CtorDec->isMoveConstructor() ? MK_Move : MK_Copy;
+ modelUse(State, ArgRegion, RD, MK, C);
+ return;
+ }
+ }
+
+ const auto IC = dyn_cast<CXXInstanceCall>(&Call);
+ if (!IC)
+ return;
+
+ // Calling a destructor on a moved object is fine.
+ if (isa<CXXDestructorCall>(IC))
+ return;
+
+ const MemRegion *ThisRegion = IC->getCXXThisVal().getAsRegion();
+ if (!ThisRegion)
+ return;
+
+ // The remaining part is check only for method call on a moved-from object.
+ const auto MethodDecl = dyn_cast_or_null<CXXMethodDecl>(IC->getDecl());
+ if (!MethodDecl)
+ return;
+
+ // We want to investigate the whole object, not only sub-object of a parent
+ // class in which the encountered method defined.
+ ThisRegion = ThisRegion->getMostDerivedObjectRegion();
+
+ if (isStateResetMethod(MethodDecl)) {
+ State = removeFromState(State, ThisRegion);
+ C.addTransition(State);
+ return;
+ }
+
+ if (isMoveSafeMethod(MethodDecl))
+ return;
+
+ // Store class declaration as well, for bug reporting purposes.
+ const CXXRecordDecl *RD = MethodDecl->getParent();
+
+ if (MethodDecl->isOverloadedOperator()) {
+ OverloadedOperatorKind OOK = MethodDecl->getOverloadedOperator();
+
+ if (OOK == OO_Equal) {
+ // Remove the tracked object for every assignment operator, but report bug
+ // only for move or copy assignment's argument.
+ State = removeFromState(State, ThisRegion);
+
+ if (MethodDecl->isCopyAssignmentOperator() ||
+ MethodDecl->isMoveAssignmentOperator()) {
+ const MemRegion *ArgRegion = IC->getArgSVal(0).getAsRegion();
+ MisuseKind MK =
+ MethodDecl->isMoveAssignmentOperator() ? MK_Move : MK_Copy;
+ modelUse(State, ArgRegion, RD, MK, C);
+ return;
+ }
+ C.addTransition(State);
+ return;
+ }
+
+ if (OOK == OO_Star || OOK == OO_Arrow) {
+ modelUse(State, ThisRegion, RD, MK_Dereference, C);
+ return;
+ }
+ }
+
+ modelUse(State, ThisRegion, RD, MK_FunCall, C);
+}
+
+void MoveChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ TrackedRegionMapTy TrackedRegions = State->get<TrackedRegionMap>();
+ for (TrackedRegionMapTy::value_type E : TrackedRegions) {
+ const MemRegion *Region = E.first;
+ bool IsRegDead = !SymReaper.isLiveRegion(Region);
+
+ // Remove the dead regions from the region map.
+ if (IsRegDead) {
+ State = State->remove<TrackedRegionMap>(Region);
+ }
+ }
+ C.addTransition(State);
+}
+
+ProgramStateRef MoveChecker::checkRegionChanges(
+ ProgramStateRef State, const InvalidatedSymbols *Invalidated,
+ ArrayRef<const MemRegion *> RequestedRegions,
+ ArrayRef<const MemRegion *> InvalidatedRegions,
+ const LocationContext *LCtx, const CallEvent *Call) const {
+ if (Call) {
+ // Relax invalidation upon function calls: only invalidate parameters
+ // that are passed directly via non-const pointers or non-const references
+ // or rvalue references.
+ // In case of an InstanceCall don't invalidate the this-region since
+ // it is fully handled in checkPreCall and checkPostCall.
+ const MemRegion *ThisRegion = nullptr;
+ if (const auto *IC = dyn_cast<CXXInstanceCall>(Call))
+ ThisRegion = IC->getCXXThisVal().getAsRegion();
+
+ // Requested ("explicit") regions are the regions passed into the call
+ // directly, but not all of them end up being invalidated.
+ // But when they do, they appear in the InvalidatedRegions array as well.
+ for (const auto *Region : RequestedRegions) {
+ if (ThisRegion != Region) {
+ if (llvm::find(InvalidatedRegions, Region) !=
+ std::end(InvalidatedRegions)) {
+ State = removeFromState(State, Region);
+ }
+ }
+ }
+ } else {
+ // For invalidations that aren't caused by calls, assume nothing. In
+ // particular, direct write into an object's field invalidates the status.
+ for (const auto *Region : InvalidatedRegions)
+ State = removeFromState(State, Region->getBaseRegion());
+ }
+
+ return State;
+}
+
+void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+
+ TrackedRegionMapTy RS = State->get<TrackedRegionMap>();
+
+ if (!RS.isEmpty()) {
+ Out << Sep << "Moved-from objects :" << NL;
+ for (auto I: RS) {
+ I.first->dumpToStream(Out);
+ if (I.second.isMoved())
+ Out << ": moved";
+ else
+ Out << ": moved and reported";
+ Out << NL;
+ }
+ }
+}
+void ento::registerMoveChecker(CheckerManager &mgr) {
+ MoveChecker *chk = mgr.registerChecker<MoveChecker>();
+ chk->setAggressiveness(
+ mgr.getAnalyzerOptions().getCheckerStringOption("WarnOn", "", chk));
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
new file mode 100644
index 000000000000..4ed1b25cb09e
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
@@ -0,0 +1,81 @@
+//=- NSAutoreleasePoolChecker.cpp --------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a NSAutoreleasePoolChecker, a small checker that warns
+// about subpar uses of NSAutoreleasePool. Note that while the check itself
+// (in its current form) could be written as a flow-insensitive check, in
+// can be potentially enhanced in the future with flow-sensitive information.
+// It is also a good example of the CheckerVisitor interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class NSAutoreleasePoolChecker
+ : public Checker<check::PreObjCMessage> {
+ mutable std::unique_ptr<BugType> BT;
+ mutable Selector releaseS;
+
+public:
+ void checkPreObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
+};
+
+} // end anonymous namespace
+
+void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
+ CheckerContext &C) const {
+ if (!msg.isInstanceMessage())
+ return;
+
+ const ObjCInterfaceDecl *OD = msg.getReceiverInterface();
+ if (!OD)
+ return;
+ if (!OD->getIdentifier()->isStr("NSAutoreleasePool"))
+ return;
+
+ if (releaseS.isNull())
+ releaseS = GetNullarySelector("release", C.getASTContext());
+ // Sending 'release' message?
+ if (msg.getSelector() != releaseS)
+ return;
+
+ if (!BT)
+ BT.reset(new BugType(this, "Use -drain instead of -release",
+ "API Upgrade (Apple)"));
+
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (!N) {
+ assert(0);
+ return;
+ }
+
+ auto Report = llvm::make_unique<BugReport>(
+ *BT, "Use -drain instead of -release when using NSAutoreleasePool and "
+ "garbage collection", N);
+ Report->addRange(msg.getSourceRange());
+ C.emitReport(std::move(Report));
+}
+
+void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) {
+ if (mgr.getLangOpts().getGC() != LangOptions::NonGC)
+ mgr.registerChecker<NSAutoreleasePoolChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
new file mode 100644
index 000000000000..06c43c6b9470
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -0,0 +1,323 @@
+//=- NSErrorChecker.cpp - Coding conventions for uses of NSError -*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a CheckNSError, a flow-insenstive check
+// that determines if an Objective-C class interface correctly returns
+// a non-void return type.
+//
+// File under feature request PR 2600.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+static bool IsNSError(QualType T, IdentifierInfo *II);
+static bool IsCFError(QualType T, IdentifierInfo *II);
+
+//===----------------------------------------------------------------------===//
+// NSErrorMethodChecker
+//===----------------------------------------------------------------------===//
+
+namespace {
+class NSErrorMethodChecker
+ : public Checker< check::ASTDecl<ObjCMethodDecl> > {
+ mutable IdentifierInfo *II;
+
+public:
+ NSErrorMethodChecker() : II(nullptr) {}
+
+ void checkASTDecl(const ObjCMethodDecl *D,
+ AnalysisManager &mgr, BugReporter &BR) const;
+};
+}
+
+void NSErrorMethodChecker::checkASTDecl(const ObjCMethodDecl *D,
+ AnalysisManager &mgr,
+ BugReporter &BR) const {
+ if (!D->isThisDeclarationADefinition())
+ return;
+ if (!D->getReturnType()->isVoidType())
+ return;
+
+ if (!II)
+ II = &D->getASTContext().Idents.get("NSError");
+
+ bool hasNSError = false;
+ for (const auto *I : D->parameters()) {
+ if (IsNSError(I->getType(), II)) {
+ hasNSError = true;
+ break;
+ }
+ }
+
+ if (hasNSError) {
+ const char *err = "Method accepting NSError** "
+ "should have a non-void return value to indicate whether or not an "
+ "error occurred";
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(D, BR.getSourceManager());
+ BR.EmitBasicReport(D, this, "Bad return type when passing NSError**",
+ "Coding conventions (Apple)", err, L);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// CFErrorFunctionChecker
+//===----------------------------------------------------------------------===//
+
+namespace {
+class CFErrorFunctionChecker
+ : public Checker< check::ASTDecl<FunctionDecl> > {
+ mutable IdentifierInfo *II;
+
+public:
+ CFErrorFunctionChecker() : II(nullptr) {}
+
+ void checkASTDecl(const FunctionDecl *D,
+ AnalysisManager &mgr, BugReporter &BR) const;
+};
+}
+
+void CFErrorFunctionChecker::checkASTDecl(const FunctionDecl *D,
+ AnalysisManager &mgr,
+ BugReporter &BR) const {
+ if (!D->doesThisDeclarationHaveABody())
+ return;
+ if (!D->getReturnType()->isVoidType())
+ return;
+
+ if (!II)
+ II = &D->getASTContext().Idents.get("CFErrorRef");
+
+ bool hasCFError = false;
+ for (auto I : D->parameters()) {
+ if (IsCFError(I->getType(), II)) {
+ hasCFError = true;
+ break;
+ }
+ }
+
+ if (hasCFError) {
+ const char *err = "Function accepting CFErrorRef* "
+ "should have a non-void return value to indicate whether or not an "
+ "error occurred";
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(D, BR.getSourceManager());
+ BR.EmitBasicReport(D, this, "Bad return type when passing CFErrorRef*",
+ "Coding conventions (Apple)", err, L);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// NSOrCFErrorDerefChecker
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class NSErrorDerefBug : public BugType {
+public:
+ NSErrorDerefBug(const CheckerBase *Checker)
+ : BugType(Checker, "NSError** null dereference",
+ "Coding conventions (Apple)") {}
+};
+
+class CFErrorDerefBug : public BugType {
+public:
+ CFErrorDerefBug(const CheckerBase *Checker)
+ : BugType(Checker, "CFErrorRef* null dereference",
+ "Coding conventions (Apple)") {}
+};
+
+}
+
+namespace {
+class NSOrCFErrorDerefChecker
+ : public Checker< check::Location,
+ check::Event<ImplicitNullDerefEvent> > {
+ mutable IdentifierInfo *NSErrorII, *CFErrorII;
+ mutable std::unique_ptr<NSErrorDerefBug> NSBT;
+ mutable std::unique_ptr<CFErrorDerefBug> CFBT;
+public:
+ bool ShouldCheckNSError, ShouldCheckCFError;
+ NSOrCFErrorDerefChecker() : NSErrorII(nullptr), CFErrorII(nullptr),
+ ShouldCheckNSError(0), ShouldCheckCFError(0) { }
+
+ void checkLocation(SVal loc, bool isLoad, const Stmt *S,
+ CheckerContext &C) const;
+ void checkEvent(ImplicitNullDerefEvent event) const;
+};
+}
+
+typedef llvm::ImmutableMap<SymbolRef, unsigned> ErrorOutFlag;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(NSErrorOut, ErrorOutFlag)
+REGISTER_TRAIT_WITH_PROGRAMSTATE(CFErrorOut, ErrorOutFlag)
+
+template <typename T>
+static bool hasFlag(SVal val, ProgramStateRef state) {
+ if (SymbolRef sym = val.getAsSymbol())
+ if (const unsigned *attachedFlags = state->get<T>(sym))
+ return *attachedFlags;
+ return false;
+}
+
+template <typename T>
+static void setFlag(ProgramStateRef state, SVal val, CheckerContext &C) {
+ // We tag the symbol that the SVal wraps.
+ if (SymbolRef sym = val.getAsSymbol())
+ C.addTransition(state->set<T>(sym, true));
+}
+
+static QualType parameterTypeFromSVal(SVal val, CheckerContext &C) {
+ const StackFrameContext * SFC = C.getStackFrame();
+ if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>()) {
+ const MemRegion* R = X->getRegion();
+ if (const VarRegion *VR = R->getAs<VarRegion>())
+ if (const StackArgumentsSpaceRegion *
+ stackReg = dyn_cast<StackArgumentsSpaceRegion>(VR->getMemorySpace()))
+ if (stackReg->getStackFrame() == SFC)
+ return VR->getValueType();
+ }
+
+ return QualType();
+}
+
+void NSOrCFErrorDerefChecker::checkLocation(SVal loc, bool isLoad,
+ const Stmt *S,
+ CheckerContext &C) const {
+ if (!isLoad)
+ return;
+ if (loc.isUndef() || !loc.getAs<Loc>())
+ return;
+
+ ASTContext &Ctx = C.getASTContext();
+ ProgramStateRef state = C.getState();
+
+ // If we are loading from NSError**/CFErrorRef* parameter, mark the resulting
+ // SVal so that we can later check it when handling the
+ // ImplicitNullDerefEvent event.
+ // FIXME: Cumbersome! Maybe add hook at construction of SVals at start of
+ // function ?
+
+ QualType parmT = parameterTypeFromSVal(loc, C);
+ if (parmT.isNull())
+ return;
+
+ if (!NSErrorII)
+ NSErrorII = &Ctx.Idents.get("NSError");
+ if (!CFErrorII)
+ CFErrorII = &Ctx.Idents.get("CFErrorRef");
+
+ if (ShouldCheckNSError && IsNSError(parmT, NSErrorII)) {
+ setFlag<NSErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
+ return;
+ }
+
+ if (ShouldCheckCFError && IsCFError(parmT, CFErrorII)) {
+ setFlag<CFErrorOut>(state, state->getSVal(loc.castAs<Loc>()), C);
+ return;
+ }
+}
+
+void NSOrCFErrorDerefChecker::checkEvent(ImplicitNullDerefEvent event) const {
+ if (event.IsLoad)
+ return;
+
+ SVal loc = event.Location;
+ ProgramStateRef state = event.SinkNode->getState();
+ BugReporter &BR = *event.BR;
+
+ bool isNSError = hasFlag<NSErrorOut>(loc, state);
+ bool isCFError = false;
+ if (!isNSError)
+ isCFError = hasFlag<CFErrorOut>(loc, state);
+
+ if (!(isNSError || isCFError))
+ return;
+
+ // Storing to possible null NSError/CFErrorRef out parameter.
+ SmallString<128> Buf;
+ llvm::raw_svector_ostream os(Buf);
+
+ os << "Potential null dereference. According to coding standards ";
+ os << (isNSError
+ ? "in 'Creating and Returning NSError Objects' the parameter"
+ : "documented in CoreFoundation/CFError.h the parameter");
+
+ os << " may be null";
+
+ BugType *bug = nullptr;
+ if (isNSError) {
+ if (!NSBT)
+ NSBT.reset(new NSErrorDerefBug(this));
+ bug = NSBT.get();
+ }
+ else {
+ if (!CFBT)
+ CFBT.reset(new CFErrorDerefBug(this));
+ bug = CFBT.get();
+ }
+ BR.emitReport(llvm::make_unique<BugReport>(*bug, os.str(), event.SinkNode));
+}
+
+static bool IsNSError(QualType T, IdentifierInfo *II) {
+
+ const PointerType* PPT = T->getAs<PointerType>();
+ if (!PPT)
+ return false;
+
+ const ObjCObjectPointerType* PT =
+ PPT->getPointeeType()->getAs<ObjCObjectPointerType>();
+
+ if (!PT)
+ return false;
+
+ const ObjCInterfaceDecl *ID = PT->getInterfaceDecl();
+
+ // FIXME: Can ID ever be NULL?
+ if (ID)
+ return II == ID->getIdentifier();
+
+ return false;
+}
+
+static bool IsCFError(QualType T, IdentifierInfo *II) {
+ const PointerType* PPT = T->getAs<PointerType>();
+ if (!PPT) return false;
+
+ const TypedefType* TT = PPT->getPointeeType()->getAs<TypedefType>();
+ if (!TT) return false;
+
+ return TT->getDecl()->getIdentifier() == II;
+}
+
+void ento::registerNSErrorChecker(CheckerManager &mgr) {
+ mgr.registerChecker<NSErrorMethodChecker>();
+ NSOrCFErrorDerefChecker *checker =
+ mgr.registerChecker<NSOrCFErrorDerefChecker>();
+ checker->ShouldCheckNSError = true;
+}
+
+void ento::registerCFErrorChecker(CheckerManager &mgr) {
+ mgr.registerChecker<CFErrorFunctionChecker>();
+ NSOrCFErrorDerefChecker *checker =
+ mgr.registerChecker<NSOrCFErrorDerefChecker>();
+ checker->ShouldCheckCFError = true;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
new file mode 100644
index 000000000000..83d4b5b0758b
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
@@ -0,0 +1,145 @@
+//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines NoReturnFunctionChecker, which evaluates functions that do not
+// return to the caller.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Attr.h"
+#include "clang/Analysis/SelectorExtras.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/StringSwitch.h"
+#include <cstdarg>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class NoReturnFunctionChecker : public Checker< check::PostCall,
+ check::PostObjCMessage > {
+ mutable Selector HandleFailureInFunctionSel;
+ mutable Selector HandleFailureInMethodSel;
+public:
+ void checkPostCall(const CallEvent &CE, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &msg, CheckerContext &C) const;
+};
+
+}
+
+void NoReturnFunctionChecker::checkPostCall(const CallEvent &CE,
+ CheckerContext &C) const {
+ bool BuildSinks = false;
+
+ if (const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE.getDecl()))
+ BuildSinks = FD->hasAttr<AnalyzerNoReturnAttr>() || FD->isNoReturn();
+
+ const Expr *Callee = CE.getOriginExpr();
+ if (!BuildSinks && Callee)
+ BuildSinks = getFunctionExtInfo(Callee->getType()).getNoReturn();
+
+ if (!BuildSinks && CE.isGlobalCFunction()) {
+ if (const IdentifierInfo *II = CE.getCalleeIdentifier()) {
+ // HACK: Some functions are not marked noreturn, and don't return.
+ // Here are a few hardwired ones. If this takes too long, we can
+ // potentially cache these results.
+ BuildSinks
+ = llvm::StringSwitch<bool>(StringRef(II->getName()))
+ .Case("exit", true)
+ .Case("panic", true)
+ .Case("error", true)
+ .Case("Assert", true)
+ // FIXME: This is just a wrapper around throwing an exception.
+ // Eventually inter-procedural analysis should handle this easily.
+ .Case("ziperr", true)
+ .Case("assfail", true)
+ .Case("db_error", true)
+ .Case("__assert", true)
+ .Case("__assert2", true)
+ // For the purpose of static analysis, we do not care that
+ // this MSVC function will return if the user decides to continue.
+ .Case("_wassert", true)
+ .Case("__assert_rtn", true)
+ .Case("__assert_fail", true)
+ .Case("dtrace_assfail", true)
+ .Case("yy_fatal_error", true)
+ .Case("_XCAssertionFailureHandler", true)
+ .Case("_DTAssertionFailureHandler", true)
+ .Case("_TSAssertionFailureHandler", true)
+ .Default(false);
+ }
+ }
+
+ if (BuildSinks)
+ C.generateSink(C.getState(), C.getPredecessor());
+}
+
+void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
+ CheckerContext &C) const {
+ // Check if the method is annotated with analyzer_noreturn.
+ if (const ObjCMethodDecl *MD = Msg.getDecl()) {
+ MD = MD->getCanonicalDecl();
+ if (MD->hasAttr<AnalyzerNoReturnAttr>()) {
+ C.generateSink(C.getState(), C.getPredecessor());
+ return;
+ }
+ }
+
+ // HACK: This entire check is to handle two messages in the Cocoa frameworks:
+ // -[NSAssertionHandler
+ // handleFailureInMethod:object:file:lineNumber:description:]
+ // -[NSAssertionHandler
+ // handleFailureInFunction:file:lineNumber:description:]
+ // Eventually these should be annotated with __attribute__((noreturn)).
+ // Because ObjC messages use dynamic dispatch, it is not generally safe to
+ // assume certain methods can't return. In cases where it is definitely valid,
+ // see if you can mark the methods noreturn or analyzer_noreturn instead of
+ // adding more explicit checks to this method.
+
+ if (!Msg.isInstanceMessage())
+ return;
+
+ const ObjCInterfaceDecl *Receiver = Msg.getReceiverInterface();
+ if (!Receiver)
+ return;
+ if (!Receiver->getIdentifier()->isStr("NSAssertionHandler"))
+ return;
+
+ Selector Sel = Msg.getSelector();
+ switch (Sel.getNumArgs()) {
+ default:
+ return;
+ case 4:
+ lazyInitKeywordSelector(HandleFailureInFunctionSel, C.getASTContext(),
+ "handleFailureInFunction", "file", "lineNumber",
+ "description");
+ if (Sel != HandleFailureInFunctionSel)
+ return;
+ break;
+ case 5:
+ lazyInitKeywordSelector(HandleFailureInMethodSel, C.getASTContext(),
+ "handleFailureInMethod", "object", "file",
+ "lineNumber", "description");
+ if (Sel != HandleFailureInMethodSel)
+ return;
+ break;
+ }
+
+ // If we got here, it's one of the messages we care about.
+ C.generateSink(C.getState(), C.getPredecessor());
+}
+
+void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) {
+ mgr.registerChecker<NoReturnFunctionChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
new file mode 100644
index 000000000000..3c4363b6850e
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -0,0 +1,219 @@
+//===--- NonNullParamChecker.cpp - Undefined arguments checker -*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines NonNullParamChecker, which checks for arguments expected not to
+// be null due to:
+// - the corresponding parameters being declared to have nonnull attribute
+// - the corresponding parameters being references; since the call would form
+// a reference to a null pointer
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Attr.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class NonNullParamChecker
+ : public Checker< check::PreCall, EventDispatcher<ImplicitNullDerefEvent> > {
+ mutable std::unique_ptr<BugType> BTAttrNonNull;
+ mutable std::unique_ptr<BugType> BTNullRefArg;
+
+public:
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+ std::unique_ptr<BugReport>
+ genReportNullAttrNonNull(const ExplodedNode *ErrorN, const Expr *ArgE) const;
+ std::unique_ptr<BugReport>
+ genReportReferenceToNullPointer(const ExplodedNode *ErrorN,
+ const Expr *ArgE) const;
+};
+} // end anonymous namespace
+
+/// \return Bitvector marking non-null attributes.
+static llvm::SmallBitVector getNonNullAttrs(const CallEvent &Call) {
+ const Decl *FD = Call.getDecl();
+ unsigned NumArgs = Call.getNumArgs();
+ llvm::SmallBitVector AttrNonNull(NumArgs);
+ for (const auto *NonNull : FD->specific_attrs<NonNullAttr>()) {
+ if (!NonNull->args_size()) {
+ AttrNonNull.set(0, NumArgs);
+ break;
+ }
+ for (const ParamIdx &Idx : NonNull->args()) {
+ unsigned IdxAST = Idx.getASTIndex();
+ if (IdxAST >= NumArgs)
+ continue;
+ AttrNonNull.set(IdxAST);
+ }
+ }
+ return AttrNonNull;
+}
+
+void NonNullParamChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!Call.getDecl())
+ return;
+
+ llvm::SmallBitVector AttrNonNull = getNonNullAttrs(Call);
+ unsigned NumArgs = Call.getNumArgs();
+
+ ProgramStateRef state = C.getState();
+ ArrayRef<ParmVarDecl*> parms = Call.parameters();
+
+ for (unsigned idx = 0; idx < NumArgs; ++idx) {
+ // For vararg functions, a corresponding parameter decl may not exist.
+ bool HasParam = idx < parms.size();
+
+ // Check if the parameter is a reference. We want to report when reference
+ // to a null pointer is passed as a parameter.
+ bool haveRefTypeParam =
+ HasParam ? parms[idx]->getType()->isReferenceType() : false;
+ bool haveAttrNonNull = AttrNonNull[idx];
+
+ // Check if the parameter is also marked 'nonnull'.
+ if (!haveAttrNonNull && HasParam)
+ haveAttrNonNull = parms[idx]->hasAttr<NonNullAttr>();
+
+ if (!haveAttrNonNull && !haveRefTypeParam)
+ continue;
+
+ // If the value is unknown or undefined, we can't perform this check.
+ const Expr *ArgE = Call.getArgExpr(idx);
+ SVal V = Call.getArgSVal(idx);
+ auto DV = V.getAs<DefinedSVal>();
+ if (!DV)
+ continue;
+
+ assert(!haveRefTypeParam || DV->getAs<Loc>());
+
+ // Process the case when the argument is not a location.
+ if (haveAttrNonNull && !DV->getAs<Loc>()) {
+ // If the argument is a union type, we want to handle a potential
+ // transparent_union GCC extension.
+ if (!ArgE)
+ continue;
+
+ QualType T = ArgE->getType();
+ const RecordType *UT = T->getAsUnionType();
+ if (!UT || !UT->getDecl()->hasAttr<TransparentUnionAttr>())
+ continue;
+
+ auto CSV = DV->getAs<nonloc::CompoundVal>();
+
+ // FIXME: Handle LazyCompoundVals?
+ if (!CSV)
+ continue;
+
+ V = *(CSV->begin());
+ DV = V.getAs<DefinedSVal>();
+ assert(++CSV->begin() == CSV->end());
+ // FIXME: Handle (some_union){ some_other_union_val }, which turns into
+ // a LazyCompoundVal inside a CompoundVal.
+ if (!V.getAs<Loc>())
+ continue;
+
+ // Retrieve the corresponding expression.
+ if (const auto *CE = dyn_cast<CompoundLiteralExpr>(ArgE))
+ if (const auto *IE = dyn_cast<InitListExpr>(CE->getInitializer()))
+ ArgE = dyn_cast<Expr>(*(IE->begin()));
+ }
+
+ ConstraintManager &CM = C.getConstraintManager();
+ ProgramStateRef stateNotNull, stateNull;
+ std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
+
+ // Generate an error node. Check for a null node in case
+ // we cache out.
+ if (stateNull && !stateNotNull) {
+ if (ExplodedNode *errorNode = C.generateErrorNode(stateNull)) {
+
+ std::unique_ptr<BugReport> R;
+ if (haveAttrNonNull)
+ R = genReportNullAttrNonNull(errorNode, ArgE);
+ else if (haveRefTypeParam)
+ R = genReportReferenceToNullPointer(errorNode, ArgE);
+
+ // Highlight the range of the argument that was null.
+ R->addRange(Call.getArgSourceRange(idx));
+
+ // Emit the bug report.
+ C.emitReport(std::move(R));
+ }
+
+ // Always return. Either we cached out or we just emitted an error.
+ return;
+ }
+
+ if (stateNull) {
+ if (ExplodedNode *N = C.generateSink(stateNull, C.getPredecessor())) {
+ ImplicitNullDerefEvent event = {
+ V, false, N, &C.getBugReporter(),
+ /*IsDirectDereference=*/haveRefTypeParam};
+ dispatchEvent(event);
+ }
+ }
+
+ // If a pointer value passed the check we should assume that it is
+ // indeed not null from this point forward.
+ state = stateNotNull;
+ }
+
+ // If we reach here all of the arguments passed the nonnull check.
+ // If 'state' has been updated generated a new node.
+ C.addTransition(state);
+}
+
+std::unique_ptr<BugReport>
+NonNullParamChecker::genReportNullAttrNonNull(const ExplodedNode *ErrorNode,
+ const Expr *ArgE) const {
+ // Lazily allocate the BugType object if it hasn't already been
+ // created. Ownership is transferred to the BugReporter object once
+ // the BugReport is passed to 'EmitWarning'.
+ if (!BTAttrNonNull)
+ BTAttrNonNull.reset(new BugType(
+ this, "Argument with 'nonnull' attribute passed null", "API"));
+
+ auto R = llvm::make_unique<BugReport>(
+ *BTAttrNonNull,
+ "Null pointer passed as an argument to a 'nonnull' parameter", ErrorNode);
+ if (ArgE)
+ bugreporter::trackExpressionValue(ErrorNode, ArgE, *R);
+
+ return R;
+}
+
+std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer(
+ const ExplodedNode *ErrorNode, const Expr *ArgE) const {
+ if (!BTNullRefArg)
+ BTNullRefArg.reset(new BuiltinBug(this, "Dereference of null pointer"));
+
+ auto R = llvm::make_unique<BugReport>(
+ *BTNullRefArg, "Forming reference to null pointer", ErrorNode);
+ if (ArgE) {
+ const Expr *ArgEDeref = bugreporter::getDerefExpr(ArgE);
+ if (!ArgEDeref)
+ ArgEDeref = ArgE;
+ bugreporter::trackExpressionValue(ErrorNode, ArgEDeref, *R);
+ }
+ return R;
+
+}
+
+void ento::registerNonNullParamChecker(CheckerManager &mgr) {
+ mgr.registerChecker<NonNullParamChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
new file mode 100644
index 000000000000..ce9e950aa9ba
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
@@ -0,0 +1,140 @@
+//==- NonnullGlobalConstantsChecker.cpp ---------------------------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker adds an assumption that constant globals of certain types* are
+// non-null, as otherwise they generally do not convey any useful information.
+// The assumption is useful, as many framework use e. g. global const strings,
+// and the analyzer might not be able to infer the global value if the
+// definition is in a separate translation unit.
+// The following types (and their typedef aliases) are considered to be
+// non-null:
+// - `char* const`
+// - `const CFStringRef` from CoreFoundation
+// - `NSString* const` from Foundation
+// - `CFBooleanRef` from Foundation
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class NonnullGlobalConstantsChecker : public Checker<check::Location> {
+ mutable IdentifierInfo *NSStringII = nullptr;
+ mutable IdentifierInfo *CFStringRefII = nullptr;
+ mutable IdentifierInfo *CFBooleanRefII = nullptr;
+
+public:
+ NonnullGlobalConstantsChecker() {}
+
+ void checkLocation(SVal l, bool isLoad, const Stmt *S,
+ CheckerContext &C) const;
+
+private:
+ void initIdentifierInfo(ASTContext &Ctx) const;
+
+ bool isGlobalConstString(SVal V) const;
+
+ bool isNonnullType(QualType Ty) const;
+};
+
+} // namespace
+
+/// Lazily initialize cache for required identifier information.
+void NonnullGlobalConstantsChecker::initIdentifierInfo(ASTContext &Ctx) const {
+ if (NSStringII)
+ return;
+
+ NSStringII = &Ctx.Idents.get("NSString");
+ CFStringRefII = &Ctx.Idents.get("CFStringRef");
+ CFBooleanRefII = &Ctx.Idents.get("CFBooleanRef");
+}
+
+/// Add an assumption that const string-like globals are non-null.
+void NonnullGlobalConstantsChecker::checkLocation(SVal location, bool isLoad,
+ const Stmt *S,
+ CheckerContext &C) const {
+ initIdentifierInfo(C.getASTContext());
+ if (!isLoad || !location.isValid())
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ if (isGlobalConstString(location)) {
+ SVal V = State->getSVal(location.castAs<Loc>());
+ Optional<DefinedOrUnknownSVal> Constr = V.getAs<DefinedOrUnknownSVal>();
+
+ if (Constr) {
+
+ // Assume that the variable is non-null.
+ ProgramStateRef OutputState = State->assume(*Constr, true);
+ C.addTransition(OutputState);
+ }
+ }
+}
+
+/// \param V loaded lvalue.
+/// \return whether {@code val} is a string-like const global.
+bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const {
+ Optional<loc::MemRegionVal> RegionVal = V.getAs<loc::MemRegionVal>();
+ if (!RegionVal)
+ return false;
+ auto *Region = dyn_cast<VarRegion>(RegionVal->getAsRegion());
+ if (!Region)
+ return false;
+ const VarDecl *Decl = Region->getDecl();
+
+ if (!Decl->hasGlobalStorage())
+ return false;
+
+ QualType Ty = Decl->getType();
+ bool HasConst = Ty.isConstQualified();
+ if (isNonnullType(Ty) && HasConst)
+ return true;
+
+ // Look through the typedefs.
+ while (auto *T = dyn_cast<TypedefType>(Ty)) {
+ Ty = T->getDecl()->getUnderlyingType();
+
+ // It is sufficient for any intermediate typedef
+ // to be classified const.
+ HasConst = HasConst || Ty.isConstQualified();
+ if (isNonnullType(Ty) && HasConst)
+ return true;
+ }
+ return false;
+}
+
+/// \return whether {@code type} is extremely unlikely to be null
+bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const {
+
+ if (Ty->isPointerType() && Ty->getPointeeType()->isCharType())
+ return true;
+
+ if (auto *T = dyn_cast<ObjCObjectPointerType>(Ty)) {
+ return T->getInterfaceDecl() &&
+ T->getInterfaceDecl()->getIdentifier() == NSStringII;
+ } else if (auto *T = dyn_cast<TypedefType>(Ty)) {
+ IdentifierInfo* II = T->getDecl()->getIdentifier();
+ return II == CFStringRefII || II == CFBooleanRefII;
+ }
+ return false;
+}
+
+void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<NonnullGlobalConstantsChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
new file mode 100644
index 000000000000..e535d1ae27ac
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -0,0 +1,1216 @@
+//== Nullabilityhecker.cpp - Nullability checker ----------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker tries to find nullability violations. There are several kinds of
+// possible violations:
+// * Null pointer is passed to a pointer which has a _Nonnull type.
+// * Null pointer is returned from a function which has a _Nonnull return type.
+// * Nullable pointer is passed to a pointer which has a _Nonnull type.
+// * Nullable pointer is returned from a function which has a _Nonnull return
+// type.
+// * Nullable pointer is dereferenced.
+//
+// This checker propagates the nullability information of the pointers and looks
+// for the patterns that are described above. Explicit casts are trusted and are
+// considered a way to suppress false positives for this checker. The other way
+// to suppress warnings would be to add asserts or guarding if statements to the
+// code. In addition to the nullability propagation this checker also uses some
+// heuristics to suppress potential false positives.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Path.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+/// Returns the most nullable nullability. This is used for message expressions
+/// like [receiver method], where the nullability of this expression is either
+/// the nullability of the receiver or the nullability of the return type of the
+/// method, depending on which is more nullable. Contradicted is considered to
+/// be the most nullable, to avoid false positive results.
+Nullability getMostNullable(Nullability Lhs, Nullability Rhs) {
+ return static_cast<Nullability>(
+ std::min(static_cast<char>(Lhs), static_cast<char>(Rhs)));
+}
+
+const char *getNullabilityString(Nullability Nullab) {
+ switch (Nullab) {
+ case Nullability::Contradicted:
+ return "contradicted";
+ case Nullability::Nullable:
+ return "nullable";
+ case Nullability::Unspecified:
+ return "unspecified";
+ case Nullability::Nonnull:
+ return "nonnull";
+ }
+ llvm_unreachable("Unexpected enumeration.");
+ return "";
+}
+
+// These enums are used as an index to ErrorMessages array.
+enum class ErrorKind : int {
+ NilAssignedToNonnull,
+ NilPassedToNonnull,
+ NilReturnedToNonnull,
+ NullableAssignedToNonnull,
+ NullableReturnedToNonnull,
+ NullableDereferenced,
+ NullablePassedToNonnull
+};
+
+class NullabilityChecker
+ : public Checker<check::Bind, check::PreCall, check::PreStmt<ReturnStmt>,
+ check::PostCall, check::PostStmt<ExplicitCastExpr>,
+ check::PostObjCMessage, check::DeadSymbols,
+ check::Event<ImplicitNullDerefEvent>> {
+ mutable std::unique_ptr<BugType> BT;
+
+public:
+ // If true, the checker will not diagnose nullabilility issues for calls
+ // to system headers. This option is motivated by the observation that large
+ // projects may have many nullability warnings. These projects may
+ // find warnings about nullability annotations that they have explicitly
+ // added themselves higher priority to fix than warnings on calls to system
+ // libraries.
+ DefaultBool NoDiagnoseCallsToSystemHeaders;
+
+ void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
+ void checkPostStmt(const ExplicitCastExpr *CE, CheckerContext &C) const;
+ void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
+ void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+ void checkEvent(ImplicitNullDerefEvent Event) const;
+
+ void printState(raw_ostream &Out, ProgramStateRef State, const char *NL,
+ const char *Sep) const override;
+
+ struct NullabilityChecksFilter {
+ DefaultBool CheckNullPassedToNonnull;
+ DefaultBool CheckNullReturnedFromNonnull;
+ DefaultBool CheckNullableDereferenced;
+ DefaultBool CheckNullablePassedToNonnull;
+ DefaultBool CheckNullableReturnedFromNonnull;
+
+ CheckName CheckNameNullPassedToNonnull;
+ CheckName CheckNameNullReturnedFromNonnull;
+ CheckName CheckNameNullableDereferenced;
+ CheckName CheckNameNullablePassedToNonnull;
+ CheckName CheckNameNullableReturnedFromNonnull;
+ };
+
+ NullabilityChecksFilter Filter;
+ // When set to false no nullability information will be tracked in
+ // NullabilityMap. It is possible to catch errors like passing a null pointer
+ // to a callee that expects nonnull argument without the information that is
+ // stroed in the NullabilityMap. This is an optimization.
+ DefaultBool NeedTracking;
+
+private:
+ class NullabilityBugVisitor : public BugReporterVisitor {
+ public:
+ NullabilityBugVisitor(const MemRegion *M) : Region(M) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int X = 0;
+ ID.AddPointer(&X);
+ ID.AddPointer(Region);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ private:
+ // The tracked region.
+ const MemRegion *Region;
+ };
+
+ /// When any of the nonnull arguments of the analyzed function is null, do not
+ /// report anything and turn off the check.
+ ///
+ /// When \p SuppressPath is set to true, no more bugs will be reported on this
+ /// path by this checker.
+ void reportBugIfInvariantHolds(StringRef Msg, ErrorKind Error,
+ ExplodedNode *N, const MemRegion *Region,
+ CheckerContext &C,
+ const Stmt *ValueExpr = nullptr,
+ bool SuppressPath = false) const;
+
+ void reportBug(StringRef Msg, ErrorKind Error, ExplodedNode *N,
+ const MemRegion *Region, BugReporter &BR,
+ const Stmt *ValueExpr = nullptr) const {
+ if (!BT)
+ BT.reset(new BugType(this, "Nullability", categories::MemoryError));
+
+ auto R = llvm::make_unique<BugReport>(*BT, Msg, N);
+ if (Region) {
+ R->markInteresting(Region);
+ R->addVisitor(llvm::make_unique<NullabilityBugVisitor>(Region));
+ }
+ if (ValueExpr) {
+ R->addRange(ValueExpr->getSourceRange());
+ if (Error == ErrorKind::NilAssignedToNonnull ||
+ Error == ErrorKind::NilPassedToNonnull ||
+ Error == ErrorKind::NilReturnedToNonnull)
+ if (const auto *Ex = dyn_cast<Expr>(ValueExpr))
+ bugreporter::trackExpressionValue(N, Ex, *R);
+ }
+ BR.emitReport(std::move(R));
+ }
+
+ /// If an SVal wraps a region that should be tracked, it will return a pointer
+ /// to the wrapped region. Otherwise it will return a nullptr.
+ const SymbolicRegion *getTrackRegion(SVal Val,
+ bool CheckSuperRegion = false) const;
+
+ /// Returns true if the call is diagnosable in the current analyzer
+ /// configuration.
+ bool isDiagnosableCall(const CallEvent &Call) const {
+ if (NoDiagnoseCallsToSystemHeaders && Call.isInSystemHeader())
+ return false;
+
+ return true;
+ }
+};
+
+class NullabilityState {
+public:
+ NullabilityState(Nullability Nullab, const Stmt *Source = nullptr)
+ : Nullab(Nullab), Source(Source) {}
+
+ const Stmt *getNullabilitySource() const { return Source; }
+
+ Nullability getValue() const { return Nullab; }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(static_cast<char>(Nullab));
+ ID.AddPointer(Source);
+ }
+
+ void print(raw_ostream &Out) const {
+ Out << getNullabilityString(Nullab) << "\n";
+ }
+
+private:
+ Nullability Nullab;
+ // Source is the expression which determined the nullability. For example in a
+ // message like [nullable nonnull_returning] has nullable nullability, because
+ // the receiver is nullable. Here the receiver will be the source of the
+ // nullability. This is useful information when the diagnostics are generated.
+ const Stmt *Source;
+};
+
+bool operator==(NullabilityState Lhs, NullabilityState Rhs) {
+ return Lhs.getValue() == Rhs.getValue() &&
+ Lhs.getNullabilitySource() == Rhs.getNullabilitySource();
+}
+
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(NullabilityMap, const MemRegion *,
+ NullabilityState)
+
+// We say "the nullability type invariant is violated" when a location with a
+// non-null type contains NULL or a function with a non-null return type returns
+// NULL. Violations of the nullability type invariant can be detected either
+// directly (for example, when NULL is passed as an argument to a nonnull
+// parameter) or indirectly (for example, when, inside a function, the
+// programmer defensively checks whether a nonnull parameter contains NULL and
+// finds that it does).
+//
+// As a matter of policy, the nullability checker typically warns on direct
+// violations of the nullability invariant (although it uses various
+// heuristics to suppress warnings in some cases) but will not warn if the
+// invariant has already been violated along the path (either directly or
+// indirectly). As a practical matter, this prevents the analyzer from
+// (1) warning on defensive code paths where a nullability precondition is
+// determined to have been violated, (2) warning additional times after an
+// initial direct violation has been discovered, and (3) warning after a direct
+// violation that has been implicitly or explicitly suppressed (for
+// example, with a cast of NULL to _Nonnull). In essence, once an invariant
+// violation is detected on a path, this checker will be essentially turned off
+// for the rest of the analysis
+//
+// The analyzer takes this approach (rather than generating a sink node) to
+// ensure coverage of defensive paths, which may be important for backwards
+// compatibility in codebases that were developed without nullability in mind.
+REGISTER_TRAIT_WITH_PROGRAMSTATE(InvariantViolated, bool)
+
+enum class NullConstraint { IsNull, IsNotNull, Unknown };
+
+static NullConstraint getNullConstraint(DefinedOrUnknownSVal Val,
+ ProgramStateRef State) {
+ ConditionTruthVal Nullness = State->isNull(Val);
+ if (Nullness.isConstrainedFalse())
+ return NullConstraint::IsNotNull;
+ if (Nullness.isConstrainedTrue())
+ return NullConstraint::IsNull;
+ return NullConstraint::Unknown;
+}
+
+const SymbolicRegion *
+NullabilityChecker::getTrackRegion(SVal Val, bool CheckSuperRegion) const {
+ if (!NeedTracking)
+ return nullptr;
+
+ auto RegionSVal = Val.getAs<loc::MemRegionVal>();
+ if (!RegionSVal)
+ return nullptr;
+
+ const MemRegion *Region = RegionSVal->getRegion();
+
+ if (CheckSuperRegion) {
+ if (auto FieldReg = Region->getAs<FieldRegion>())
+ return dyn_cast<SymbolicRegion>(FieldReg->getSuperRegion());
+ if (auto ElementReg = Region->getAs<ElementRegion>())
+ return dyn_cast<SymbolicRegion>(ElementReg->getSuperRegion());
+ }
+
+ return dyn_cast<SymbolicRegion>(Region);
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+NullabilityChecker::NullabilityBugVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+ ProgramStateRef State = N->getState();
+ ProgramStateRef StatePrev = N->getFirstPred()->getState();
+
+ const NullabilityState *TrackedNullab = State->get<NullabilityMap>(Region);
+ const NullabilityState *TrackedNullabPrev =
+ StatePrev->get<NullabilityMap>(Region);
+ if (!TrackedNullab)
+ return nullptr;
+
+ if (TrackedNullabPrev &&
+ TrackedNullabPrev->getValue() == TrackedNullab->getValue())
+ return nullptr;
+
+ // Retrieve the associated statement.
+ const Stmt *S = TrackedNullab->getNullabilitySource();
+ if (!S || S->getBeginLoc().isInvalid()) {
+ S = PathDiagnosticLocation::getStmt(N);
+ }
+
+ if (!S)
+ return nullptr;
+
+ std::string InfoText =
+ (llvm::Twine("Nullability '") +
+ getNullabilityString(TrackedNullab->getValue()) + "' is inferred")
+ .str();
+
+ // Generate the extra diagnostic.
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true,
+ nullptr);
+}
+
+/// Returns true when the value stored at the given location has been
+/// constrained to null after being passed through an object of nonnnull type.
+static bool checkValueAtLValForInvariantViolation(ProgramStateRef State,
+ SVal LV, QualType T) {
+ if (getNullabilityAnnotation(T) != Nullability::Nonnull)
+ return false;
+
+ auto RegionVal = LV.getAs<loc::MemRegionVal>();
+ if (!RegionVal)
+ return false;
+
+ // If the value was constrained to null *after* it was passed through that
+ // location, it could not have been a concrete pointer *when* it was passed.
+ // In that case we would have handled the situation when the value was
+ // bound to that location, by emitting (or not emitting) a report.
+ // Therefore we are only interested in symbolic regions that can be either
+ // null or non-null depending on the value of their respective symbol.
+ auto StoredVal = State->getSVal(*RegionVal).getAs<loc::MemRegionVal>();
+ if (!StoredVal || !isa<SymbolicRegion>(StoredVal->getRegion()))
+ return false;
+
+ if (getNullConstraint(*StoredVal, State) == NullConstraint::IsNull)
+ return true;
+
+ return false;
+}
+
+static bool
+checkParamsForPreconditionViolation(ArrayRef<ParmVarDecl *> Params,
+ ProgramStateRef State,
+ const LocationContext *LocCtxt) {
+ for (const auto *ParamDecl : Params) {
+ if (ParamDecl->isParameterPack())
+ break;
+
+ SVal LV = State->getLValue(ParamDecl, LocCtxt);
+ if (checkValueAtLValForInvariantViolation(State, LV,
+ ParamDecl->getType())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool
+checkSelfIvarsForInvariantViolation(ProgramStateRef State,
+ const LocationContext *LocCtxt) {
+ auto *MD = dyn_cast<ObjCMethodDecl>(LocCtxt->getDecl());
+ if (!MD || !MD->isInstanceMethod())
+ return false;
+
+ const ImplicitParamDecl *SelfDecl = LocCtxt->getSelfDecl();
+ if (!SelfDecl)
+ return false;
+
+ SVal SelfVal = State->getSVal(State->getRegion(SelfDecl, LocCtxt));
+
+ const ObjCObjectPointerType *SelfType =
+ dyn_cast<ObjCObjectPointerType>(SelfDecl->getType());
+ if (!SelfType)
+ return false;
+
+ const ObjCInterfaceDecl *ID = SelfType->getInterfaceDecl();
+ if (!ID)
+ return false;
+
+ for (const auto *IvarDecl : ID->ivars()) {
+ SVal LV = State->getLValue(IvarDecl, SelfVal);
+ if (checkValueAtLValForInvariantViolation(State, LV, IvarDecl->getType())) {
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool checkInvariantViolation(ProgramStateRef State, ExplodedNode *N,
+ CheckerContext &C) {
+ if (State->get<InvariantViolated>())
+ return true;
+
+ const LocationContext *LocCtxt = C.getLocationContext();
+ const Decl *D = LocCtxt->getDecl();
+ if (!D)
+ return false;
+
+ ArrayRef<ParmVarDecl*> Params;
+ if (const auto *BD = dyn_cast<BlockDecl>(D))
+ Params = BD->parameters();
+ else if (const auto *FD = dyn_cast<FunctionDecl>(D))
+ Params = FD->parameters();
+ else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
+ Params = MD->parameters();
+ else
+ return false;
+
+ if (checkParamsForPreconditionViolation(Params, State, LocCtxt) ||
+ checkSelfIvarsForInvariantViolation(State, LocCtxt)) {
+ if (!N->isSink())
+ C.addTransition(State->set<InvariantViolated>(true), N);
+ return true;
+ }
+ return false;
+}
+
+void NullabilityChecker::reportBugIfInvariantHolds(StringRef Msg,
+ ErrorKind Error, ExplodedNode *N, const MemRegion *Region,
+ CheckerContext &C, const Stmt *ValueExpr, bool SuppressPath) const {
+ ProgramStateRef OriginalState = N->getState();
+
+ if (checkInvariantViolation(OriginalState, N, C))
+ return;
+ if (SuppressPath) {
+ OriginalState = OriginalState->set<InvariantViolated>(true);
+ N = C.addTransition(OriginalState, N);
+ }
+
+ reportBug(Msg, Error, N, Region, C.getBugReporter(), ValueExpr);
+}
+
+/// Cleaning up the program state.
+void NullabilityChecker::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ NullabilityMapTy Nullabilities = State->get<NullabilityMap>();
+ for (NullabilityMapTy::iterator I = Nullabilities.begin(),
+ E = Nullabilities.end();
+ I != E; ++I) {
+ const auto *Region = I->first->getAs<SymbolicRegion>();
+ assert(Region && "Non-symbolic region is tracked.");
+ if (SR.isDead(Region->getSymbol())) {
+ State = State->remove<NullabilityMap>(I->first);
+ }
+ }
+ // When one of the nonnull arguments are constrained to be null, nullability
+ // preconditions are violated. It is not enough to check this only when we
+ // actually report an error, because at that time interesting symbols might be
+ // reaped.
+ if (checkInvariantViolation(State, C.getPredecessor(), C))
+ return;
+ C.addTransition(State);
+}
+
+/// This callback triggers when a pointer is dereferenced and the analyzer does
+/// not know anything about the value of that pointer. When that pointer is
+/// nullable, this code emits a warning.
+void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
+ if (Event.SinkNode->getState()->get<InvariantViolated>())
+ return;
+
+ const MemRegion *Region =
+ getTrackRegion(Event.Location, /*CheckSuperregion=*/true);
+ if (!Region)
+ return;
+
+ ProgramStateRef State = Event.SinkNode->getState();
+ const NullabilityState *TrackedNullability =
+ State->get<NullabilityMap>(Region);
+
+ if (!TrackedNullability)
+ return;
+
+ if (Filter.CheckNullableDereferenced &&
+ TrackedNullability->getValue() == Nullability::Nullable) {
+ BugReporter &BR = *Event.BR;
+ // Do not suppress errors on defensive code paths, because dereferencing
+ // a nullable pointer is always an error.
+ if (Event.IsDirectDereference)
+ reportBug("Nullable pointer is dereferenced",
+ ErrorKind::NullableDereferenced, Event.SinkNode, Region, BR);
+ else {
+ reportBug("Nullable pointer is passed to a callee that requires a "
+ "non-null", ErrorKind::NullablePassedToNonnull,
+ Event.SinkNode, Region, BR);
+ }
+ }
+}
+
+/// Find the outermost subexpression of E that is not an implicit cast.
+/// This looks through the implicit casts to _Nonnull that ARC adds to
+/// return expressions of ObjC types when the return type of the function or
+/// method is non-null but the express is not.
+static const Expr *lookThroughImplicitCasts(const Expr *E) {
+ assert(E);
+
+ while (auto *ICE = dyn_cast<ImplicitCastExpr>(E)) {
+ E = ICE->getSubExpr();
+ }
+
+ return E;
+}
+
+/// This method check when nullable pointer or null value is returned from a
+/// function that has nonnull return type.
+void NullabilityChecker::checkPreStmt(const ReturnStmt *S,
+ CheckerContext &C) const {
+ auto RetExpr = S->getRetValue();
+ if (!RetExpr)
+ return;
+
+ if (!RetExpr->getType()->isAnyPointerType())
+ return;
+
+ ProgramStateRef State = C.getState();
+ if (State->get<InvariantViolated>())
+ return;
+
+ auto RetSVal = C.getSVal(S).getAs<DefinedOrUnknownSVal>();
+ if (!RetSVal)
+ return;
+
+ bool InSuppressedMethodFamily = false;
+
+ QualType RequiredRetType;
+ AnalysisDeclContext *DeclCtxt =
+ C.getLocationContext()->getAnalysisDeclContext();
+ const Decl *D = DeclCtxt->getDecl();
+ if (auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ // HACK: This is a big hammer to avoid warning when there are defensive
+ // nil checks in -init and -copy methods. We should add more sophisticated
+ // logic here to suppress on common defensive idioms but still
+ // warn when there is a likely problem.
+ ObjCMethodFamily Family = MD->getMethodFamily();
+ if (OMF_init == Family || OMF_copy == Family || OMF_mutableCopy == Family)
+ InSuppressedMethodFamily = true;
+
+ RequiredRetType = MD->getReturnType();
+ } else if (auto *FD = dyn_cast<FunctionDecl>(D)) {
+ RequiredRetType = FD->getReturnType();
+ } else {
+ return;
+ }
+
+ NullConstraint Nullness = getNullConstraint(*RetSVal, State);
+
+ Nullability RequiredNullability = getNullabilityAnnotation(RequiredRetType);
+
+ // If the returned value is null but the type of the expression
+ // generating it is nonnull then we will suppress the diagnostic.
+ // This enables explicit suppression when returning a nil literal in a
+ // function with a _Nonnull return type:
+ // return (NSString * _Nonnull)0;
+ Nullability RetExprTypeLevelNullability =
+ getNullabilityAnnotation(lookThroughImplicitCasts(RetExpr)->getType());
+
+ bool NullReturnedFromNonNull = (RequiredNullability == Nullability::Nonnull &&
+ Nullness == NullConstraint::IsNull);
+ if (Filter.CheckNullReturnedFromNonnull &&
+ NullReturnedFromNonNull &&
+ RetExprTypeLevelNullability != Nullability::Nonnull &&
+ !InSuppressedMethodFamily &&
+ C.getLocationContext()->inTopFrame()) {
+ static CheckerProgramPointTag Tag(this, "NullReturnedFromNonnull");
+ ExplodedNode *N = C.generateErrorNode(State, &Tag);
+ if (!N)
+ return;
+
+ SmallString<256> SBuf;
+ llvm::raw_svector_ostream OS(SBuf);
+ OS << (RetExpr->getType()->isObjCObjectPointerType() ? "nil" : "Null");
+ OS << " returned from a " << C.getDeclDescription(D) <<
+ " that is expected to return a non-null value";
+ reportBugIfInvariantHolds(OS.str(),
+ ErrorKind::NilReturnedToNonnull, N, nullptr, C,
+ RetExpr);
+ return;
+ }
+
+ // If null was returned from a non-null function, mark the nullability
+ // invariant as violated even if the diagnostic was suppressed.
+ if (NullReturnedFromNonNull) {
+ State = State->set<InvariantViolated>(true);
+ C.addTransition(State);
+ return;
+ }
+
+ const MemRegion *Region = getTrackRegion(*RetSVal);
+ if (!Region)
+ return;
+
+ const NullabilityState *TrackedNullability =
+ State->get<NullabilityMap>(Region);
+ if (TrackedNullability) {
+ Nullability TrackedNullabValue = TrackedNullability->getValue();
+ if (Filter.CheckNullableReturnedFromNonnull &&
+ Nullness != NullConstraint::IsNotNull &&
+ TrackedNullabValue == Nullability::Nullable &&
+ RequiredNullability == Nullability::Nonnull) {
+ static CheckerProgramPointTag Tag(this, "NullableReturnedFromNonnull");
+ ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
+
+ SmallString<256> SBuf;
+ llvm::raw_svector_ostream OS(SBuf);
+ OS << "Nullable pointer is returned from a " << C.getDeclDescription(D) <<
+ " that is expected to return a non-null value";
+
+ reportBugIfInvariantHolds(OS.str(),
+ ErrorKind::NullableReturnedToNonnull, N,
+ Region, C);
+ }
+ return;
+ }
+ if (RequiredNullability == Nullability::Nullable) {
+ State = State->set<NullabilityMap>(Region,
+ NullabilityState(RequiredNullability,
+ S));
+ C.addTransition(State);
+ }
+}
+
+/// This callback warns when a nullable pointer or a null value is passed to a
+/// function that expects its argument to be nonnull.
+void NullabilityChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!Call.getDecl())
+ return;
+
+ ProgramStateRef State = C.getState();
+ if (State->get<InvariantViolated>())
+ return;
+
+ ProgramStateRef OrigState = State;
+
+ unsigned Idx = 0;
+ for (const ParmVarDecl *Param : Call.parameters()) {
+ if (Param->isParameterPack())
+ break;
+
+ if (Idx >= Call.getNumArgs())
+ break;
+
+ const Expr *ArgExpr = Call.getArgExpr(Idx);
+ auto ArgSVal = Call.getArgSVal(Idx++).getAs<DefinedOrUnknownSVal>();
+ if (!ArgSVal)
+ continue;
+
+ if (!Param->getType()->isAnyPointerType() &&
+ !Param->getType()->isReferenceType())
+ continue;
+
+ NullConstraint Nullness = getNullConstraint(*ArgSVal, State);
+
+ Nullability RequiredNullability =
+ getNullabilityAnnotation(Param->getType());
+ Nullability ArgExprTypeLevelNullability =
+ getNullabilityAnnotation(ArgExpr->getType());
+
+ unsigned ParamIdx = Param->getFunctionScopeIndex() + 1;
+
+ if (Filter.CheckNullPassedToNonnull && Nullness == NullConstraint::IsNull &&
+ ArgExprTypeLevelNullability != Nullability::Nonnull &&
+ RequiredNullability == Nullability::Nonnull &&
+ isDiagnosableCall(Call)) {
+ ExplodedNode *N = C.generateErrorNode(State);
+ if (!N)
+ return;
+
+ SmallString<256> SBuf;
+ llvm::raw_svector_ostream OS(SBuf);
+ OS << (Param->getType()->isObjCObjectPointerType() ? "nil" : "Null");
+ OS << " passed to a callee that requires a non-null " << ParamIdx
+ << llvm::getOrdinalSuffix(ParamIdx) << " parameter";
+ reportBugIfInvariantHolds(OS.str(), ErrorKind::NilPassedToNonnull, N,
+ nullptr, C,
+ ArgExpr, /*SuppressPath=*/false);
+ return;
+ }
+
+ const MemRegion *Region = getTrackRegion(*ArgSVal);
+ if (!Region)
+ continue;
+
+ const NullabilityState *TrackedNullability =
+ State->get<NullabilityMap>(Region);
+
+ if (TrackedNullability) {
+ if (Nullness == NullConstraint::IsNotNull ||
+ TrackedNullability->getValue() != Nullability::Nullable)
+ continue;
+
+ if (Filter.CheckNullablePassedToNonnull &&
+ RequiredNullability == Nullability::Nonnull &&
+ isDiagnosableCall(Call)) {
+ ExplodedNode *N = C.addTransition(State);
+ SmallString<256> SBuf;
+ llvm::raw_svector_ostream OS(SBuf);
+ OS << "Nullable pointer is passed to a callee that requires a non-null "
+ << ParamIdx << llvm::getOrdinalSuffix(ParamIdx) << " parameter";
+ reportBugIfInvariantHolds(OS.str(),
+ ErrorKind::NullablePassedToNonnull, N,
+ Region, C, ArgExpr, /*SuppressPath=*/true);
+ return;
+ }
+ if (Filter.CheckNullableDereferenced &&
+ Param->getType()->isReferenceType()) {
+ ExplodedNode *N = C.addTransition(State);
+ reportBugIfInvariantHolds("Nullable pointer is dereferenced",
+ ErrorKind::NullableDereferenced, N, Region,
+ C, ArgExpr, /*SuppressPath=*/true);
+ return;
+ }
+ continue;
+ }
+ // No tracked nullability yet.
+ if (ArgExprTypeLevelNullability != Nullability::Nullable)
+ continue;
+ State = State->set<NullabilityMap>(
+ Region, NullabilityState(ArgExprTypeLevelNullability, ArgExpr));
+ }
+ if (State != OrigState)
+ C.addTransition(State);
+}
+
+/// Suppress the nullability warnings for some functions.
+void NullabilityChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ auto Decl = Call.getDecl();
+ if (!Decl)
+ return;
+ // ObjC Messages handles in a different callback.
+ if (Call.getKind() == CE_ObjCMessage)
+ return;
+ const FunctionType *FuncType = Decl->getFunctionType();
+ if (!FuncType)
+ return;
+ QualType ReturnType = FuncType->getReturnType();
+ if (!ReturnType->isAnyPointerType())
+ return;
+ ProgramStateRef State = C.getState();
+ if (State->get<InvariantViolated>())
+ return;
+
+ const MemRegion *Region = getTrackRegion(Call.getReturnValue());
+ if (!Region)
+ return;
+
+ // CG headers are misannotated. Do not warn for symbols that are the results
+ // of CG calls.
+ const SourceManager &SM = C.getSourceManager();
+ StringRef FilePath = SM.getFilename(SM.getSpellingLoc(Decl->getBeginLoc()));
+ if (llvm::sys::path::filename(FilePath).startswith("CG")) {
+ State = State->set<NullabilityMap>(Region, Nullability::Contradicted);
+ C.addTransition(State);
+ return;
+ }
+
+ const NullabilityState *TrackedNullability =
+ State->get<NullabilityMap>(Region);
+
+ if (!TrackedNullability &&
+ getNullabilityAnnotation(ReturnType) == Nullability::Nullable) {
+ State = State->set<NullabilityMap>(Region, Nullability::Nullable);
+ C.addTransition(State);
+ }
+}
+
+static Nullability getReceiverNullability(const ObjCMethodCall &M,
+ ProgramStateRef State) {
+ if (M.isReceiverSelfOrSuper()) {
+ // For super and super class receivers we assume that the receiver is
+ // nonnull.
+ return Nullability::Nonnull;
+ }
+ // Otherwise look up nullability in the state.
+ SVal Receiver = M.getReceiverSVal();
+ if (auto DefOrUnknown = Receiver.getAs<DefinedOrUnknownSVal>()) {
+ // If the receiver is constrained to be nonnull, assume that it is nonnull
+ // regardless of its type.
+ NullConstraint Nullness = getNullConstraint(*DefOrUnknown, State);
+ if (Nullness == NullConstraint::IsNotNull)
+ return Nullability::Nonnull;
+ }
+ auto ValueRegionSVal = Receiver.getAs<loc::MemRegionVal>();
+ if (ValueRegionSVal) {
+ const MemRegion *SelfRegion = ValueRegionSVal->getRegion();
+ assert(SelfRegion);
+
+ const NullabilityState *TrackedSelfNullability =
+ State->get<NullabilityMap>(SelfRegion);
+ if (TrackedSelfNullability)
+ return TrackedSelfNullability->getValue();
+ }
+ return Nullability::Unspecified;
+}
+
+/// Calculate the nullability of the result of a message expr based on the
+/// nullability of the receiver, the nullability of the return value, and the
+/// constraints.
+void NullabilityChecker::checkPostObjCMessage(const ObjCMethodCall &M,
+ CheckerContext &C) const {
+ auto Decl = M.getDecl();
+ if (!Decl)
+ return;
+ QualType RetType = Decl->getReturnType();
+ if (!RetType->isAnyPointerType())
+ return;
+
+ ProgramStateRef State = C.getState();
+ if (State->get<InvariantViolated>())
+ return;
+
+ const MemRegion *ReturnRegion = getTrackRegion(M.getReturnValue());
+ if (!ReturnRegion)
+ return;
+
+ auto Interface = Decl->getClassInterface();
+ auto Name = Interface ? Interface->getName() : "";
+ // In order to reduce the noise in the diagnostics generated by this checker,
+ // some framework and programming style based heuristics are used. These
+ // heuristics are for Cocoa APIs which have NS prefix.
+ if (Name.startswith("NS")) {
+ // Developers rely on dynamic invariants such as an item should be available
+ // in a collection, or a collection is not empty often. Those invariants can
+ // not be inferred by any static analysis tool. To not to bother the users
+ // with too many false positives, every item retrieval function should be
+ // ignored for collections. The instance methods of dictionaries in Cocoa
+ // are either item retrieval related or not interesting nullability wise.
+ // Using this fact, to keep the code easier to read just ignore the return
+ // value of every instance method of dictionaries.
+ if (M.isInstanceMessage() && Name.contains("Dictionary")) {
+ State =
+ State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted);
+ C.addTransition(State);
+ return;
+ }
+ // For similar reasons ignore some methods of Cocoa arrays.
+ StringRef FirstSelectorSlot = M.getSelector().getNameForSlot(0);
+ if (Name.contains("Array") &&
+ (FirstSelectorSlot == "firstObject" ||
+ FirstSelectorSlot == "lastObject")) {
+ State =
+ State->set<NullabilityMap>(ReturnRegion, Nullability::Contradicted);
+ C.addTransition(State);
+ return;
+ }
+
+ // Encoding related methods of string should not fail when lossless
+ // encodings are used. Using lossless encodings is so frequent that ignoring
+ // this class of methods reduced the emitted diagnostics by about 30% on
+ // some projects (and all of that was false positives).
+ if (Name.contains("String")) {
+ for (auto Param : M.parameters()) {
+ if (Param->getName() == "encoding") {
+ State = State->set<NullabilityMap>(ReturnRegion,
+ Nullability::Contradicted);
+ C.addTransition(State);
+ return;
+ }
+ }
+ }
+ }
+
+ const ObjCMessageExpr *Message = M.getOriginExpr();
+ Nullability SelfNullability = getReceiverNullability(M, State);
+
+ const NullabilityState *NullabilityOfReturn =
+ State->get<NullabilityMap>(ReturnRegion);
+
+ if (NullabilityOfReturn) {
+ // When we have a nullability tracked for the return value, the nullability
+ // of the expression will be the most nullable of the receiver and the
+ // return value.
+ Nullability RetValTracked = NullabilityOfReturn->getValue();
+ Nullability ComputedNullab =
+ getMostNullable(RetValTracked, SelfNullability);
+ if (ComputedNullab != RetValTracked &&
+ ComputedNullab != Nullability::Unspecified) {
+ const Stmt *NullabilitySource =
+ ComputedNullab == RetValTracked
+ ? NullabilityOfReturn->getNullabilitySource()
+ : Message->getInstanceReceiver();
+ State = State->set<NullabilityMap>(
+ ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
+ C.addTransition(State);
+ }
+ return;
+ }
+
+ // No tracked information. Use static type information for return value.
+ Nullability RetNullability = getNullabilityAnnotation(RetType);
+
+ // Properties might be computed. For this reason the static analyzer creates a
+ // new symbol each time an unknown property is read. To avoid false pozitives
+ // do not treat unknown properties as nullable, even when they explicitly
+ // marked nullable.
+ if (M.getMessageKind() == OCM_PropertyAccess && !C.wasInlined)
+ RetNullability = Nullability::Nonnull;
+
+ Nullability ComputedNullab = getMostNullable(RetNullability, SelfNullability);
+ if (ComputedNullab == Nullability::Nullable) {
+ const Stmt *NullabilitySource = ComputedNullab == RetNullability
+ ? Message
+ : Message->getInstanceReceiver();
+ State = State->set<NullabilityMap>(
+ ReturnRegion, NullabilityState(ComputedNullab, NullabilitySource));
+ C.addTransition(State);
+ }
+}
+
+/// Explicit casts are trusted. If there is a disagreement in the nullability
+/// annotations in the destination and the source or '0' is casted to nonnull
+/// track the value as having contraditory nullability. This will allow users to
+/// suppress warnings.
+void NullabilityChecker::checkPostStmt(const ExplicitCastExpr *CE,
+ CheckerContext &C) const {
+ QualType OriginType = CE->getSubExpr()->getType();
+ QualType DestType = CE->getType();
+ if (!OriginType->isAnyPointerType())
+ return;
+ if (!DestType->isAnyPointerType())
+ return;
+
+ ProgramStateRef State = C.getState();
+ if (State->get<InvariantViolated>())
+ return;
+
+ Nullability DestNullability = getNullabilityAnnotation(DestType);
+
+ // No explicit nullability in the destination type, so this cast does not
+ // change the nullability.
+ if (DestNullability == Nullability::Unspecified)
+ return;
+
+ auto RegionSVal = C.getSVal(CE).getAs<DefinedOrUnknownSVal>();
+ const MemRegion *Region = getTrackRegion(*RegionSVal);
+ if (!Region)
+ return;
+
+ // When 0 is converted to nonnull mark it as contradicted.
+ if (DestNullability == Nullability::Nonnull) {
+ NullConstraint Nullness = getNullConstraint(*RegionSVal, State);
+ if (Nullness == NullConstraint::IsNull) {
+ State = State->set<NullabilityMap>(Region, Nullability::Contradicted);
+ C.addTransition(State);
+ return;
+ }
+ }
+
+ const NullabilityState *TrackedNullability =
+ State->get<NullabilityMap>(Region);
+
+ if (!TrackedNullability) {
+ if (DestNullability != Nullability::Nullable)
+ return;
+ State = State->set<NullabilityMap>(Region,
+ NullabilityState(DestNullability, CE));
+ C.addTransition(State);
+ return;
+ }
+
+ if (TrackedNullability->getValue() != DestNullability &&
+ TrackedNullability->getValue() != Nullability::Contradicted) {
+ State = State->set<NullabilityMap>(Region, Nullability::Contradicted);
+ C.addTransition(State);
+ }
+}
+
+/// For a given statement performing a bind, attempt to syntactically
+/// match the expression resulting in the bound value.
+static const Expr * matchValueExprForBind(const Stmt *S) {
+ // For `x = e` the value expression is the right-hand side.
+ if (auto *BinOp = dyn_cast<BinaryOperator>(S)) {
+ if (BinOp->getOpcode() == BO_Assign)
+ return BinOp->getRHS();
+ }
+
+ // For `int x = e` the value expression is the initializer.
+ if (auto *DS = dyn_cast<DeclStmt>(S)) {
+ if (DS->isSingleDecl()) {
+ auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
+ if (!VD)
+ return nullptr;
+
+ if (const Expr *Init = VD->getInit())
+ return Init;
+ }
+ }
+
+ return nullptr;
+}
+
+/// Returns true if \param S is a DeclStmt for a local variable that
+/// ObjC automated reference counting initialized with zero.
+static bool isARCNilInitializedLocal(CheckerContext &C, const Stmt *S) {
+ // We suppress diagnostics for ARC zero-initialized _Nonnull locals. This
+ // prevents false positives when a _Nonnull local variable cannot be
+ // initialized with an initialization expression:
+ // NSString * _Nonnull s; // no-warning
+ // @autoreleasepool {
+ // s = ...
+ // }
+ //
+ // FIXME: We should treat implicitly zero-initialized _Nonnull locals as
+ // uninitialized in Sema's UninitializedValues analysis to warn when a use of
+ // the zero-initialized definition will unexpectedly yield nil.
+
+ // Locals are only zero-initialized when automated reference counting
+ // is turned on.
+ if (!C.getASTContext().getLangOpts().ObjCAutoRefCount)
+ return false;
+
+ auto *DS = dyn_cast<DeclStmt>(S);
+ if (!DS || !DS->isSingleDecl())
+ return false;
+
+ auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
+ if (!VD)
+ return false;
+
+ // Sema only zero-initializes locals with ObjCLifetimes.
+ if(!VD->getType().getQualifiers().hasObjCLifetime())
+ return false;
+
+ const Expr *Init = VD->getInit();
+ assert(Init && "ObjC local under ARC without initializer");
+
+ // Return false if the local is explicitly initialized (e.g., with '= nil').
+ if (!isa<ImplicitValueInitExpr>(Init))
+ return false;
+
+ return true;
+}
+
+/// Propagate the nullability information through binds and warn when nullable
+/// pointer or null symbol is assigned to a pointer with a nonnull type.
+void NullabilityChecker::checkBind(SVal L, SVal V, const Stmt *S,
+ CheckerContext &C) const {
+ const TypedValueRegion *TVR =
+ dyn_cast_or_null<TypedValueRegion>(L.getAsRegion());
+ if (!TVR)
+ return;
+
+ QualType LocType = TVR->getValueType();
+ if (!LocType->isAnyPointerType())
+ return;
+
+ ProgramStateRef State = C.getState();
+ if (State->get<InvariantViolated>())
+ return;
+
+ auto ValDefOrUnknown = V.getAs<DefinedOrUnknownSVal>();
+ if (!ValDefOrUnknown)
+ return;
+
+ NullConstraint RhsNullness = getNullConstraint(*ValDefOrUnknown, State);
+
+ Nullability ValNullability = Nullability::Unspecified;
+ if (SymbolRef Sym = ValDefOrUnknown->getAsSymbol())
+ ValNullability = getNullabilityAnnotation(Sym->getType());
+
+ Nullability LocNullability = getNullabilityAnnotation(LocType);
+
+ // If the type of the RHS expression is nonnull, don't warn. This
+ // enables explicit suppression with a cast to nonnull.
+ Nullability ValueExprTypeLevelNullability = Nullability::Unspecified;
+ const Expr *ValueExpr = matchValueExprForBind(S);
+ if (ValueExpr) {
+ ValueExprTypeLevelNullability =
+ getNullabilityAnnotation(lookThroughImplicitCasts(ValueExpr)->getType());
+ }
+
+ bool NullAssignedToNonNull = (LocNullability == Nullability::Nonnull &&
+ RhsNullness == NullConstraint::IsNull);
+ if (Filter.CheckNullPassedToNonnull &&
+ NullAssignedToNonNull &&
+ ValNullability != Nullability::Nonnull &&
+ ValueExprTypeLevelNullability != Nullability::Nonnull &&
+ !isARCNilInitializedLocal(C, S)) {
+ static CheckerProgramPointTag Tag(this, "NullPassedToNonnull");
+ ExplodedNode *N = C.generateErrorNode(State, &Tag);
+ if (!N)
+ return;
+
+
+ const Stmt *ValueStmt = S;
+ if (ValueExpr)
+ ValueStmt = ValueExpr;
+
+ SmallString<256> SBuf;
+ llvm::raw_svector_ostream OS(SBuf);
+ OS << (LocType->isObjCObjectPointerType() ? "nil" : "Null");
+ OS << " assigned to a pointer which is expected to have non-null value";
+ reportBugIfInvariantHolds(OS.str(),
+ ErrorKind::NilAssignedToNonnull, N, nullptr, C,
+ ValueStmt);
+ return;
+ }
+
+ // If null was returned from a non-null function, mark the nullability
+ // invariant as violated even if the diagnostic was suppressed.
+ if (NullAssignedToNonNull) {
+ State = State->set<InvariantViolated>(true);
+ C.addTransition(State);
+ return;
+ }
+
+ // Intentionally missing case: '0' is bound to a reference. It is handled by
+ // the DereferenceChecker.
+
+ const MemRegion *ValueRegion = getTrackRegion(*ValDefOrUnknown);
+ if (!ValueRegion)
+ return;
+
+ const NullabilityState *TrackedNullability =
+ State->get<NullabilityMap>(ValueRegion);
+
+ if (TrackedNullability) {
+ if (RhsNullness == NullConstraint::IsNotNull ||
+ TrackedNullability->getValue() != Nullability::Nullable)
+ return;
+ if (Filter.CheckNullablePassedToNonnull &&
+ LocNullability == Nullability::Nonnull) {
+ static CheckerProgramPointTag Tag(this, "NullablePassedToNonnull");
+ ExplodedNode *N = C.addTransition(State, C.getPredecessor(), &Tag);
+ reportBugIfInvariantHolds("Nullable pointer is assigned to a pointer "
+ "which is expected to have non-null value",
+ ErrorKind::NullableAssignedToNonnull, N,
+ ValueRegion, C);
+ }
+ return;
+ }
+
+ const auto *BinOp = dyn_cast<BinaryOperator>(S);
+
+ if (ValNullability == Nullability::Nullable) {
+ // Trust the static information of the value more than the static
+ // information on the location.
+ const Stmt *NullabilitySource = BinOp ? BinOp->getRHS() : S;
+ State = State->set<NullabilityMap>(
+ ValueRegion, NullabilityState(ValNullability, NullabilitySource));
+ C.addTransition(State);
+ return;
+ }
+
+ if (LocNullability == Nullability::Nullable) {
+ const Stmt *NullabilitySource = BinOp ? BinOp->getLHS() : S;
+ State = State->set<NullabilityMap>(
+ ValueRegion, NullabilityState(LocNullability, NullabilitySource));
+ C.addTransition(State);
+ }
+}
+
+void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+
+ NullabilityMapTy B = State->get<NullabilityMap>();
+
+ if (State->get<InvariantViolated>())
+ Out << Sep << NL
+ << "Nullability invariant was violated, warnings suppressed." << NL;
+
+ if (B.isEmpty())
+ return;
+
+ if (!State->get<InvariantViolated>())
+ Out << Sep << NL;
+
+ for (NullabilityMapTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ Out << I->first << " : ";
+ I->second.print(Out);
+ Out << NL;
+ }
+}
+
+#define REGISTER_CHECKER(name, trackingRequired) \
+ void ento::register##name##Checker(CheckerManager &mgr) { \
+ NullabilityChecker *checker = mgr.registerChecker<NullabilityChecker>(); \
+ checker->Filter.Check##name = true; \
+ checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \
+ checker->NeedTracking = checker->NeedTracking || trackingRequired; \
+ checker->NoDiagnoseCallsToSystemHeaders = \
+ checker->NoDiagnoseCallsToSystemHeaders || \
+ mgr.getAnalyzerOptions().getCheckerBooleanOption( \
+ "NoDiagnoseCallsToSystemHeaders", false, checker, true); \
+ }
+
+// The checks are likely to be turned on by default and it is possible to do
+// them without tracking any nullability related information. As an optimization
+// no nullability information will be tracked when only these two checks are
+// enables.
+REGISTER_CHECKER(NullPassedToNonnull, false)
+REGISTER_CHECKER(NullReturnedFromNonnull, false)
+
+REGISTER_CHECKER(NullableDereferenced, true)
+REGISTER_CHECKER(NullablePassedToNonnull, true)
+REGISTER_CHECKER(NullableReturnedFromNonnull, true)
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
new file mode 100644
index 000000000000..4e3a7205f1f4
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
@@ -0,0 +1,351 @@
+//===- NumberObjectConversionChecker.cpp -------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines NumberObjectConversionChecker, which checks for a
+// particular common mistake when dealing with numbers represented as objects
+// passed around by pointers. Namely, the language allows to reinterpret the
+// pointer as a number directly, often without throwing any warnings,
+// but in most cases the result of such conversion is clearly unexpected,
+// as pointer value, rather than number value represented by the pointee object,
+// becomes the result of such operation.
+//
+// Currently the checker supports the Objective-C NSNumber class,
+// and the OSBoolean class found in macOS low-level code; the latter
+// can only hold boolean values.
+//
+// This checker has an option "Pedantic" (boolean), which enables detection of
+// more conversion patterns (which are most likely more harmless, and therefore
+// are more likely to produce false positives) - disabled by default,
+// enabled with `-analyzer-config osx.NumberObjectConversion:Pedantic=true'.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/APSInt.h"
+
+using namespace clang;
+using namespace ento;
+using namespace ast_matchers;
+
+namespace {
+
+class NumberObjectConversionChecker : public Checker<check::ASTCodeBody> {
+public:
+ bool Pedantic;
+
+ void checkASTCodeBody(const Decl *D, AnalysisManager &AM,
+ BugReporter &BR) const;
+};
+
+class Callback : public MatchFinder::MatchCallback {
+ const NumberObjectConversionChecker *C;
+ BugReporter &BR;
+ AnalysisDeclContext *ADC;
+
+public:
+ Callback(const NumberObjectConversionChecker *C,
+ BugReporter &BR, AnalysisDeclContext *ADC)
+ : C(C), BR(BR), ADC(ADC) {}
+ virtual void run(const MatchFinder::MatchResult &Result);
+};
+} // end of anonymous namespace
+
+void Callback::run(const MatchFinder::MatchResult &Result) {
+ bool IsPedanticMatch =
+ (Result.Nodes.getNodeAs<Stmt>("pedantic") != nullptr);
+ if (IsPedanticMatch && !C->Pedantic)
+ return;
+
+ ASTContext &ACtx = ADC->getASTContext();
+
+ if (const Expr *CheckIfNull =
+ Result.Nodes.getNodeAs<Expr>("check_if_null")) {
+ // Unless the macro indicates that the intended type is clearly not
+ // a pointer type, we should avoid warning on comparing pointers
+ // to zero literals in non-pedantic mode.
+ // FIXME: Introduce an AST matcher to implement the macro-related logic?
+ bool MacroIndicatesWeShouldSkipTheCheck = false;
+ SourceLocation Loc = CheckIfNull->getBeginLoc();
+ if (Loc.isMacroID()) {
+ StringRef MacroName = Lexer::getImmediateMacroName(
+ Loc, ACtx.getSourceManager(), ACtx.getLangOpts());
+ if (MacroName == "NULL" || MacroName == "nil")
+ return;
+ if (MacroName == "YES" || MacroName == "NO")
+ MacroIndicatesWeShouldSkipTheCheck = true;
+ }
+ if (!MacroIndicatesWeShouldSkipTheCheck) {
+ Expr::EvalResult EVResult;
+ if (CheckIfNull->IgnoreParenCasts()->EvaluateAsInt(
+ EVResult, ACtx, Expr::SE_AllowSideEffects)) {
+ llvm::APSInt Result = EVResult.Val.getInt();
+ if (Result == 0) {
+ if (!C->Pedantic)
+ return;
+ IsPedanticMatch = true;
+ }
+ }
+ }
+ }
+
+ const Stmt *Conv = Result.Nodes.getNodeAs<Stmt>("conv");
+ assert(Conv);
+
+ const Expr *ConvertedCObject = Result.Nodes.getNodeAs<Expr>("c_object");
+ const Expr *ConvertedCppObject = Result.Nodes.getNodeAs<Expr>("cpp_object");
+ const Expr *ConvertedObjCObject = Result.Nodes.getNodeAs<Expr>("objc_object");
+ bool IsCpp = (ConvertedCppObject != nullptr);
+ bool IsObjC = (ConvertedObjCObject != nullptr);
+ const Expr *Obj = IsObjC ? ConvertedObjCObject
+ : IsCpp ? ConvertedCppObject
+ : ConvertedCObject;
+ assert(Obj);
+
+ bool IsComparison =
+ (Result.Nodes.getNodeAs<Stmt>("comparison") != nullptr);
+
+ bool IsOSNumber =
+ (Result.Nodes.getNodeAs<Decl>("osnumber") != nullptr);
+
+ bool IsInteger =
+ (Result.Nodes.getNodeAs<QualType>("int_type") != nullptr);
+ bool IsObjCBool =
+ (Result.Nodes.getNodeAs<QualType>("objc_bool_type") != nullptr);
+ bool IsCppBool =
+ (Result.Nodes.getNodeAs<QualType>("cpp_bool_type") != nullptr);
+
+ llvm::SmallString<64> Msg;
+ llvm::raw_svector_ostream OS(Msg);
+
+ // Remove ObjC ARC qualifiers.
+ QualType ObjT = Obj->getType().getUnqualifiedType();
+
+ // Remove consts from pointers.
+ if (IsCpp) {
+ assert(ObjT.getCanonicalType()->isPointerType());
+ ObjT = ACtx.getPointerType(
+ ObjT->getPointeeType().getCanonicalType().getUnqualifiedType());
+ }
+
+ if (IsComparison)
+ OS << "Comparing ";
+ else
+ OS << "Converting ";
+
+ OS << "a pointer value of type '" << ObjT.getAsString() << "' to a ";
+
+ std::string EuphemismForPlain = "primitive";
+ std::string SuggestedApi = IsObjC ? (IsInteger ? "" : "-boolValue")
+ : IsCpp ? (IsOSNumber ? "" : "getValue()")
+ : "CFNumberGetValue()";
+ if (SuggestedApi.empty()) {
+ // A generic message if we're not sure what API should be called.
+ // FIXME: Pattern-match the integer type to make a better guess?
+ SuggestedApi =
+ "a method on '" + ObjT.getAsString() + "' to get the scalar value";
+ // "scalar" is not quite correct or common, but some documentation uses it
+ // when describing object methods we suggest. For consistency, we use
+ // "scalar" in the whole sentence when we need to use this word in at least
+ // one place, otherwise we use "primitive".
+ EuphemismForPlain = "scalar";
+ }
+
+ if (IsInteger)
+ OS << EuphemismForPlain << " integer value";
+ else if (IsObjCBool)
+ OS << EuphemismForPlain << " BOOL value";
+ else if (IsCppBool)
+ OS << EuphemismForPlain << " bool value";
+ else // Branch condition?
+ OS << EuphemismForPlain << " boolean value";
+
+
+ if (IsPedanticMatch)
+ OS << "; instead, either compare the pointer to "
+ << (IsObjC ? "nil" : IsCpp ? "nullptr" : "NULL") << " or ";
+ else
+ OS << "; did you mean to ";
+
+ if (IsComparison)
+ OS << "compare the result of calling " << SuggestedApi;
+ else
+ OS << "call " << SuggestedApi;
+
+ if (!IsPedanticMatch)
+ OS << "?";
+
+ BR.EmitBasicReport(
+ ADC->getDecl(), C, "Suspicious number object conversion", "Logic error",
+ OS.str(),
+ PathDiagnosticLocation::createBegin(Obj, BR.getSourceManager(), ADC),
+ Conv->getSourceRange());
+}
+
+void NumberObjectConversionChecker::checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const {
+ // Currently this matches CoreFoundation opaque pointer typedefs.
+ auto CSuspiciousNumberObjectExprM =
+ expr(ignoringParenImpCasts(
+ expr(hasType(
+ typedefType(hasDeclaration(anyOf(
+ typedefDecl(hasName("CFNumberRef")),
+ typedefDecl(hasName("CFBooleanRef")))))))
+ .bind("c_object")));
+
+ // Currently this matches XNU kernel number-object pointers.
+ auto CppSuspiciousNumberObjectExprM =
+ expr(ignoringParenImpCasts(
+ expr(hasType(hasCanonicalType(
+ pointerType(pointee(hasCanonicalType(
+ recordType(hasDeclaration(
+ anyOf(
+ cxxRecordDecl(hasName("OSBoolean")),
+ cxxRecordDecl(hasName("OSNumber"))
+ .bind("osnumber"))))))))))
+ .bind("cpp_object")));
+
+ // Currently this matches NeXTSTEP number objects.
+ auto ObjCSuspiciousNumberObjectExprM =
+ expr(ignoringParenImpCasts(
+ expr(hasType(hasCanonicalType(
+ objcObjectPointerType(pointee(
+ qualType(hasCanonicalType(
+ qualType(hasDeclaration(
+ objcInterfaceDecl(hasName("NSNumber")))))))))))
+ .bind("objc_object")));
+
+ auto SuspiciousNumberObjectExprM = anyOf(
+ CSuspiciousNumberObjectExprM,
+ CppSuspiciousNumberObjectExprM,
+ ObjCSuspiciousNumberObjectExprM);
+
+ // Useful for predicates like "Unless we've seen the same object elsewhere".
+ auto AnotherSuspiciousNumberObjectExprM =
+ expr(anyOf(
+ equalsBoundNode("c_object"),
+ equalsBoundNode("objc_object"),
+ equalsBoundNode("cpp_object")));
+
+ // The .bind here is in order to compose the error message more accurately.
+ auto ObjCSuspiciousScalarBooleanTypeM =
+ qualType(typedefType(hasDeclaration(
+ typedefDecl(hasName("BOOL"))))).bind("objc_bool_type");
+
+ // The .bind here is in order to compose the error message more accurately.
+ auto SuspiciousScalarBooleanTypeM =
+ qualType(anyOf(qualType(booleanType()).bind("cpp_bool_type"),
+ ObjCSuspiciousScalarBooleanTypeM));
+
+ // The .bind here is in order to compose the error message more accurately.
+ // Also avoid intptr_t and uintptr_t because they were specifically created
+ // for storing pointers.
+ auto SuspiciousScalarNumberTypeM =
+ qualType(hasCanonicalType(isInteger()),
+ unless(typedefType(hasDeclaration(
+ typedefDecl(matchesName("^::u?intptr_t$"))))))
+ .bind("int_type");
+
+ auto SuspiciousScalarTypeM =
+ qualType(anyOf(SuspiciousScalarBooleanTypeM,
+ SuspiciousScalarNumberTypeM));
+
+ auto SuspiciousScalarExprM =
+ expr(ignoringParenImpCasts(expr(hasType(SuspiciousScalarTypeM))));
+
+ auto ConversionThroughAssignmentM =
+ binaryOperator(allOf(hasOperatorName("="),
+ hasLHS(SuspiciousScalarExprM),
+ hasRHS(SuspiciousNumberObjectExprM)));
+
+ auto ConversionThroughBranchingM =
+ ifStmt(allOf(
+ hasCondition(SuspiciousNumberObjectExprM),
+ unless(hasConditionVariableStatement(declStmt())
+ ))).bind("pedantic");
+
+ auto ConversionThroughCallM =
+ callExpr(hasAnyArgument(allOf(hasType(SuspiciousScalarTypeM),
+ ignoringParenImpCasts(
+ SuspiciousNumberObjectExprM))));
+
+ // We bind "check_if_null" to modify the warning message
+ // in case it was intended to compare a pointer to 0 with a relatively-ok
+ // construct "x == 0" or "x != 0".
+ auto ConversionThroughEquivalenceM =
+ binaryOperator(allOf(anyOf(hasOperatorName("=="), hasOperatorName("!=")),
+ hasEitherOperand(SuspiciousNumberObjectExprM),
+ hasEitherOperand(SuspiciousScalarExprM
+ .bind("check_if_null"))))
+ .bind("comparison");
+
+ auto ConversionThroughComparisonM =
+ binaryOperator(allOf(anyOf(hasOperatorName(">="), hasOperatorName(">"),
+ hasOperatorName("<="), hasOperatorName("<")),
+ hasEitherOperand(SuspiciousNumberObjectExprM),
+ hasEitherOperand(SuspiciousScalarExprM)))
+ .bind("comparison");
+
+ auto ConversionThroughConditionalOperatorM =
+ conditionalOperator(allOf(
+ hasCondition(SuspiciousNumberObjectExprM),
+ unless(hasTrueExpression(
+ hasDescendant(AnotherSuspiciousNumberObjectExprM))),
+ unless(hasFalseExpression(
+ hasDescendant(AnotherSuspiciousNumberObjectExprM)))))
+ .bind("pedantic");
+
+ auto ConversionThroughExclamationMarkM =
+ unaryOperator(allOf(hasOperatorName("!"),
+ has(expr(SuspiciousNumberObjectExprM))))
+ .bind("pedantic");
+
+ auto ConversionThroughExplicitBooleanCastM =
+ explicitCastExpr(allOf(hasType(SuspiciousScalarBooleanTypeM),
+ has(expr(SuspiciousNumberObjectExprM))));
+
+ auto ConversionThroughExplicitNumberCastM =
+ explicitCastExpr(allOf(hasType(SuspiciousScalarNumberTypeM),
+ has(expr(SuspiciousNumberObjectExprM))));
+
+ auto ConversionThroughInitializerM =
+ declStmt(hasSingleDecl(
+ varDecl(hasType(SuspiciousScalarTypeM),
+ hasInitializer(SuspiciousNumberObjectExprM))));
+
+ auto FinalM = stmt(anyOf(ConversionThroughAssignmentM,
+ ConversionThroughBranchingM,
+ ConversionThroughCallM,
+ ConversionThroughComparisonM,
+ ConversionThroughConditionalOperatorM,
+ ConversionThroughEquivalenceM,
+ ConversionThroughExclamationMarkM,
+ ConversionThroughExplicitBooleanCastM,
+ ConversionThroughExplicitNumberCastM,
+ ConversionThroughInitializerM)).bind("conv");
+
+ MatchFinder F;
+ Callback CB(this, BR, AM.getAnalysisDeclContext(D));
+
+ F.addMatcher(stmt(forEachDescendant(FinalM)), &CB);
+ F.match(*D->getBody(), AM.getASTContext());
+}
+
+void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) {
+ NumberObjectConversionChecker *Chk =
+ Mgr.registerChecker<NumberObjectConversionChecker>();
+ Chk->Pedantic =
+ Mgr.getAnalyzerOptions().getCheckerBooleanOption("Pedantic", false, Chk);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
new file mode 100644
index 000000000000..185b57575cb0
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
@@ -0,0 +1,94 @@
+//== ObjCAtSyncChecker.cpp - nil mutex checker for @synchronized -*- C++ -*--=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines ObjCAtSyncChecker, a builtin check that checks for null pointers
+// used as mutexes for @synchronized.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/StmtObjC.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ObjCAtSyncChecker
+ : public Checker< check::PreStmt<ObjCAtSynchronizedStmt> > {
+ mutable std::unique_ptr<BuiltinBug> BT_null;
+ mutable std::unique_ptr<BuiltinBug> BT_undef;
+
+public:
+ void checkPreStmt(const ObjCAtSynchronizedStmt *S, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
+ CheckerContext &C) const {
+
+ const Expr *Ex = S->getSynchExpr();
+ ProgramStateRef state = C.getState();
+ SVal V = C.getSVal(Ex);
+
+ // Uninitialized value used for the mutex?
+ if (V.getAs<UndefinedVal>()) {
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT_undef)
+ BT_undef.reset(new BuiltinBug(this, "Uninitialized value used as mutex "
+ "for @synchronized"));
+ auto report =
+ llvm::make_unique<BugReport>(*BT_undef, BT_undef->getDescription(), N);
+ bugreporter::trackExpressionValue(N, Ex, *report);
+ C.emitReport(std::move(report));
+ }
+ return;
+ }
+
+ if (V.isUnknown())
+ return;
+
+ // Check for null mutexes.
+ ProgramStateRef notNullState, nullState;
+ std::tie(notNullState, nullState) = state->assume(V.castAs<DefinedSVal>());
+
+ if (nullState) {
+ if (!notNullState) {
+ // Generate an error node. This isn't a sink since
+ // a null mutex just means no synchronization occurs.
+ if (ExplodedNode *N = C.generateNonFatalErrorNode(nullState)) {
+ if (!BT_null)
+ BT_null.reset(new BuiltinBug(
+ this, "Nil value used as mutex for @synchronized() "
+ "(no synchronization will occur)"));
+ auto report =
+ llvm::make_unique<BugReport>(*BT_null, BT_null->getDescription(), N);
+ bugreporter::trackExpressionValue(N, Ex, *report);
+
+ C.emitReport(std::move(report));
+ return;
+ }
+ }
+ // Don't add a transition for 'nullState'. If the value is
+ // under-constrained to be null or non-null, assume it is non-null
+ // afterwards.
+ }
+
+ if (notNullState)
+ C.addTransition(notNullState);
+}
+
+void ento::registerObjCAtSyncChecker(CheckerManager &mgr) {
+ if (mgr.getLangOpts().ObjC)
+ mgr.registerChecker<ObjCAtSyncChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
new file mode 100644
index 000000000000..0424958f8e65
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
@@ -0,0 +1,209 @@
+//===- ObjCAutoreleaseWriteChecker.cpp ----------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ObjCAutoreleaseWriteChecker which warns against writes
+// into autoreleased out parameters which cause crashes.
+// An example of a problematic write is a write to {@code error} in the example
+// below:
+//
+// - (BOOL) mymethod:(NSError *__autoreleasing *)error list:(NSArray*) list {
+// [list enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
+// NSString *myString = obj;
+// if ([myString isEqualToString:@"error"] && error)
+// *error = [NSError errorWithDomain:@"MyDomain" code:-1];
+// }];
+// return false;
+// }
+//
+// Such code will crash on read from `*error` due to the autorelease pool
+// in `enumerateObjectsUsingBlock` implementation freeing the error object
+// on exit from the function.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/Twine.h"
+
+using namespace clang;
+using namespace ento;
+using namespace ast_matchers;
+
+namespace {
+
+const char *ProblematicWriteBind = "problematicwrite";
+const char *CapturedBind = "capturedbind";
+const char *ParamBind = "parambind";
+const char *IsMethodBind = "ismethodbind";
+
+class ObjCAutoreleaseWriteChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const;
+private:
+ std::vector<std::string> SelectorsWithAutoreleasingPool = {
+ // Common to NSArray, NSSet, NSOrderedSet
+ "enumerateObjectsUsingBlock:",
+ "enumerateObjectsWithOptions:usingBlock:",
+
+ // Common to NSArray and NSOrderedSet
+ "enumerateObjectsAtIndexes:options:usingBlock:",
+ "indexOfObjectAtIndexes:options:passingTest:",
+ "indexesOfObjectsAtIndexes:options:passingTest:",
+ "indexOfObjectPassingTest:",
+ "indexOfObjectWithOptions:passingTest:",
+ "indexesOfObjectsPassingTest:",
+ "indexesOfObjectsWithOptions:passingTest:",
+
+ // NSDictionary
+ "enumerateKeysAndObjectsUsingBlock:",
+ "enumerateKeysAndObjectsWithOptions:usingBlock:",
+ "keysOfEntriesPassingTest:",
+ "keysOfEntriesWithOptions:passingTest:",
+
+ // NSSet
+ "objectsPassingTest:",
+ "objectsWithOptions:passingTest:",
+ "enumerateIndexPathsWithOptions:usingBlock:",
+
+ // NSIndexSet
+ "enumerateIndexesWithOptions:usingBlock:",
+ "enumerateIndexesUsingBlock:",
+ "enumerateIndexesInRange:options:usingBlock:",
+ "enumerateRangesUsingBlock:",
+ "enumerateRangesWithOptions:usingBlock:",
+ "enumerateRangesInRange:options:usingBlock:",
+ "indexPassingTest:",
+ "indexesPassingTest:",
+ "indexWithOptions:passingTest:",
+ "indexesWithOptions:passingTest:",
+ "indexInRange:options:passingTest:",
+ "indexesInRange:options:passingTest:"
+ };
+
+ std::vector<std::string> FunctionsWithAutoreleasingPool = {
+ "dispatch_async", "dispatch_group_async", "dispatch_barrier_async"};
+};
+}
+
+static inline std::vector<llvm::StringRef> toRefs(std::vector<std::string> V) {
+ return std::vector<llvm::StringRef>(V.begin(), V.end());
+}
+
+static auto callsNames(std::vector<std::string> FunctionNames)
+ -> decltype(callee(functionDecl())) {
+ return callee(functionDecl(hasAnyName(toRefs(FunctionNames))));
+}
+
+static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR,
+ AnalysisManager &AM,
+ const ObjCAutoreleaseWriteChecker *Checker) {
+ AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
+
+ const auto *PVD = Match.getNodeAs<ParmVarDecl>(ParamBind);
+ QualType Ty = PVD->getType();
+ if (Ty->getPointeeType().getObjCLifetime() != Qualifiers::OCL_Autoreleasing)
+ return;
+ const char *ActionMsg = "Write to";
+ const auto *MarkedStmt = Match.getNodeAs<Expr>(ProblematicWriteBind);
+ bool IsCapture = false;
+
+ // Prefer to warn on write, but if not available, warn on capture.
+ if (!MarkedStmt) {
+ MarkedStmt = Match.getNodeAs<Expr>(CapturedBind);
+ assert(MarkedStmt);
+ ActionMsg = "Capture of";
+ IsCapture = true;
+ }
+
+ SourceRange Range = MarkedStmt->getSourceRange();
+ PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin(
+ MarkedStmt, BR.getSourceManager(), ADC);
+ bool IsMethod = Match.getNodeAs<ObjCMethodDecl>(IsMethodBind) != nullptr;
+ const char *Name = IsMethod ? "method" : "function";
+
+ BR.EmitBasicReport(
+ ADC->getDecl(), Checker,
+ /*Name=*/(llvm::Twine(ActionMsg)
+ + " autoreleasing out parameter inside autorelease pool").str(),
+ /*Category=*/"Memory",
+ (llvm::Twine(ActionMsg) + " autoreleasing out parameter " +
+ (IsCapture ? "'" + PVD->getName() + "'" + " " : "") + "inside " +
+ "autorelease pool that may exit before " + Name + " returns; consider "
+ "writing first to a strong local variable declared outside of the block")
+ .str(),
+ Location,
+ Range);
+}
+
+void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const {
+
+ auto DoublePointerParamM =
+ parmVarDecl(hasType(hasCanonicalType(pointerType(
+ pointee(hasCanonicalType(objcObjectPointerType()))))))
+ .bind(ParamBind);
+
+ auto ReferencedParamM =
+ declRefExpr(to(parmVarDecl(DoublePointerParamM))).bind(CapturedBind);
+
+ // Write into a binded object, e.g. *ParamBind = X.
+ auto WritesIntoM = binaryOperator(
+ hasLHS(unaryOperator(
+ hasOperatorName("*"),
+ hasUnaryOperand(
+ ignoringParenImpCasts(ReferencedParamM))
+ )),
+ hasOperatorName("=")
+ ).bind(ProblematicWriteBind);
+
+ auto ArgumentCaptureM = hasAnyArgument(
+ ignoringParenImpCasts(ReferencedParamM));
+ auto CapturedInParamM = stmt(anyOf(
+ callExpr(ArgumentCaptureM),
+ objcMessageExpr(ArgumentCaptureM)));
+
+ // WritesIntoM happens inside a block passed as an argument.
+ auto WritesOrCapturesInBlockM = hasAnyArgument(allOf(
+ hasType(hasCanonicalType(blockPointerType())),
+ forEachDescendant(
+ stmt(anyOf(WritesIntoM, CapturedInParamM))
+ )));
+
+ auto BlockPassedToMarkedFuncM = stmt(anyOf(
+ callExpr(allOf(
+ callsNames(FunctionsWithAutoreleasingPool), WritesOrCapturesInBlockM)),
+ objcMessageExpr(allOf(
+ hasAnySelector(toRefs(SelectorsWithAutoreleasingPool)),
+ WritesOrCapturesInBlockM))
+ ));
+
+ auto HasParamAndWritesInMarkedFuncM = allOf(
+ hasAnyParameter(DoublePointerParamM),
+ forEachDescendant(BlockPassedToMarkedFuncM));
+
+ auto MatcherM = decl(anyOf(
+ objcMethodDecl(HasParamAndWritesInMarkedFuncM).bind(IsMethodBind),
+ functionDecl(HasParamAndWritesInMarkedFuncM),
+ blockDecl(HasParamAndWritesInMarkedFuncM)));
+
+ auto Matches = match(MatcherM, *D, AM.getASTContext());
+ for (BoundNodes Match : Matches)
+ emitDiagnostics(Match, D, BR, AM, this);
+}
+
+void ento::registerAutoreleaseWriteChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<ObjCAutoreleaseWriteChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
new file mode 100644
index 000000000000..34ce47823d51
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
@@ -0,0 +1,174 @@
+//== ObjCContainersASTChecker.cpp - CoreFoundation containers API *- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// An AST checker that looks for common pitfalls when using 'CFArray',
+// 'CFDictionary', 'CFSet' APIs.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/StmtVisitor.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class WalkAST : public StmtVisitor<WalkAST> {
+ BugReporter &BR;
+ const CheckerBase *Checker;
+ AnalysisDeclContext* AC;
+ ASTContext &ASTC;
+ uint64_t PtrWidth;
+
+ /// Check if the type has pointer size (very conservative).
+ inline bool isPointerSize(const Type *T) {
+ if (!T)
+ return true;
+ if (T->isIncompleteType())
+ return true;
+ return (ASTC.getTypeSize(T) == PtrWidth);
+ }
+
+ /// Check if the type is a pointer/array to pointer sized values.
+ inline bool hasPointerToPointerSizedType(const Expr *E) {
+ QualType T = E->getType();
+
+ // The type could be either a pointer or array.
+ const Type *TP = T.getTypePtr();
+ QualType PointeeT = TP->getPointeeType();
+ if (!PointeeT.isNull()) {
+ // If the type is a pointer to an array, check the size of the array
+ // elements. To avoid false positives coming from assumption that the
+ // values x and &x are equal when x is an array.
+ if (const Type *TElem = PointeeT->getArrayElementTypeNoTypeQual())
+ if (isPointerSize(TElem))
+ return true;
+
+ // Else, check the pointee size.
+ return isPointerSize(PointeeT.getTypePtr());
+ }
+
+ if (const Type *TElem = TP->getArrayElementTypeNoTypeQual())
+ return isPointerSize(TElem);
+
+ // The type must be an array/pointer type.
+
+ // This could be a null constant, which is allowed.
+ return static_cast<bool>(
+ E->isNullPointerConstant(ASTC, Expr::NPC_ValueDependentIsNull));
+ }
+
+public:
+ WalkAST(BugReporter &br, const CheckerBase *checker, AnalysisDeclContext *ac)
+ : BR(br), Checker(checker), AC(ac), ASTC(AC->getASTContext()),
+ PtrWidth(ASTC.getTargetInfo().getPointerWidth(0)) {}
+
+ // Statement visitor methods.
+ void VisitChildren(Stmt *S);
+ void VisitStmt(Stmt *S) { VisitChildren(S); }
+ void VisitCallExpr(CallExpr *CE);
+};
+} // end anonymous namespace
+
+static StringRef getCalleeName(CallExpr *CE) {
+ const FunctionDecl *FD = CE->getDirectCallee();
+ if (!FD)
+ return StringRef();
+
+ IdentifierInfo *II = FD->getIdentifier();
+ if (!II) // if no identifier, not a simple C function
+ return StringRef();
+
+ return II->getName();
+}
+
+void WalkAST::VisitCallExpr(CallExpr *CE) {
+ StringRef Name = getCalleeName(CE);
+ if (Name.empty())
+ return;
+
+ const Expr *Arg = nullptr;
+ unsigned ArgNum;
+
+ if (Name.equals("CFArrayCreate") || Name.equals("CFSetCreate")) {
+ if (CE->getNumArgs() != 4)
+ return;
+ ArgNum = 1;
+ Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
+ if (hasPointerToPointerSizedType(Arg))
+ return;
+ } else if (Name.equals("CFDictionaryCreate")) {
+ if (CE->getNumArgs() != 6)
+ return;
+ // Check first argument.
+ ArgNum = 1;
+ Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
+ if (hasPointerToPointerSizedType(Arg)) {
+ // Check second argument.
+ ArgNum = 2;
+ Arg = CE->getArg(ArgNum)->IgnoreParenCasts();
+ if (hasPointerToPointerSizedType(Arg))
+ // Both are good, return.
+ return;
+ }
+ }
+
+ if (Arg) {
+ assert(ArgNum == 1 || ArgNum == 2);
+
+ SmallString<64> BufName;
+ llvm::raw_svector_ostream OsName(BufName);
+ OsName << " Invalid use of '" << Name << "'" ;
+
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+ // Use "second" and "third" since users will expect 1-based indexing
+ // for parameter names when mentioned in prose.
+ Os << " The "<< ((ArgNum == 1) ? "second" : "third") << " argument to '"
+ << Name << "' must be a C array of pointer-sized values, not '"
+ << Arg->getType().getAsString() << "'";
+
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(), Checker, OsName.str(),
+ categories::CoreFoundationObjectiveC, Os.str(), CELoc,
+ Arg->getSourceRange());
+ }
+
+ // Recurse and check children.
+ VisitChildren(CE);
+}
+
+void WalkAST::VisitChildren(Stmt *S) {
+ for (Stmt *Child : S->children())
+ if (Child)
+ Visit(Child);
+}
+
+namespace {
+class ObjCContainersASTChecker : public Checker<check::ASTCodeBody> {
+public:
+
+ void checkASTCodeBody(const Decl *D, AnalysisManager& Mgr,
+ BugReporter &BR) const {
+ WalkAST walker(BR, this, Mgr.getAnalysisDeclContext(D));
+ walker.Visit(D->getBody());
+ }
+};
+}
+
+void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ObjCContainersASTChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
new file mode 100644
index 000000000000..1c8c0d8dedda
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
@@ -0,0 +1,189 @@
+//== ObjCContainersChecker.cpp - Path sensitive checker for CFArray *- C++ -*=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Performs path sensitive checks of Core Foundation static containers like
+// CFArray.
+// 1) Check for buffer overflows:
+// In CFArrayGetArrayAtIndex( myArray, index), if the index is outside the
+// index space of theArray (0 to N-1 inclusive (where N is the count of
+// theArray), the behavior is undefined.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ObjCContainersChecker : public Checker< check::PreStmt<CallExpr>,
+ check::PostStmt<CallExpr>,
+ check::PointerEscape> {
+ mutable std::unique_ptr<BugType> BT;
+ inline void initBugType() const {
+ if (!BT)
+ BT.reset(new BugType(this, "CFArray API",
+ categories::CoreFoundationObjectiveC));
+ }
+
+ inline SymbolRef getArraySym(const Expr *E, CheckerContext &C) const {
+ SVal ArrayRef = C.getSVal(E);
+ SymbolRef ArraySym = ArrayRef.getAsSymbol();
+ return ArraySym;
+ }
+
+ void addSizeInfo(const Expr *Array, const Expr *Size,
+ CheckerContext &C) const;
+
+public:
+ /// A tag to id this checker.
+ static void *getTag() { static int Tag; return &Tag; }
+
+ void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
+ void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+ ProgramStateRef checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
+
+ void printState(raw_ostream &OS, ProgramStateRef State,
+ const char *NL, const char *Sep) const;
+};
+} // end anonymous namespace
+
+// ProgramState trait - a map from array symbol to its state.
+REGISTER_MAP_WITH_PROGRAMSTATE(ArraySizeMap, SymbolRef, DefinedSVal)
+
+void ObjCContainersChecker::addSizeInfo(const Expr *Array, const Expr *Size,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SVal SizeV = C.getSVal(Size);
+ // Undefined is reported by another checker.
+ if (SizeV.isUnknownOrUndef())
+ return;
+
+ // Get the ArrayRef symbol.
+ SVal ArrayRef = C.getSVal(Array);
+ SymbolRef ArraySym = ArrayRef.getAsSymbol();
+ if (!ArraySym)
+ return;
+
+ C.addTransition(
+ State->set<ArraySizeMap>(ArraySym, SizeV.castAs<DefinedSVal>()));
+}
+
+void ObjCContainersChecker::checkPostStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ StringRef Name = C.getCalleeName(CE);
+ if (Name.empty() || CE->getNumArgs() < 1)
+ return;
+
+ // Add array size information to the state.
+ if (Name.equals("CFArrayCreate")) {
+ if (CE->getNumArgs() < 3)
+ return;
+ // Note, we can visit the Create method in the post-visit because
+ // the CFIndex parameter is passed in by value and will not be invalidated
+ // by the call.
+ addSizeInfo(CE, CE->getArg(2), C);
+ return;
+ }
+
+ if (Name.equals("CFArrayGetCount")) {
+ addSizeInfo(CE->getArg(0), CE, C);
+ return;
+ }
+}
+
+void ObjCContainersChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ StringRef Name = C.getCalleeName(CE);
+ if (Name.empty() || CE->getNumArgs() < 2)
+ return;
+
+ // Check the array access.
+ if (Name.equals("CFArrayGetValueAtIndex")) {
+ ProgramStateRef State = C.getState();
+ // Retrieve the size.
+ // Find out if we saw this array symbol before and have information about
+ // it.
+ const Expr *ArrayExpr = CE->getArg(0);
+ SymbolRef ArraySym = getArraySym(ArrayExpr, C);
+ if (!ArraySym)
+ return;
+
+ const DefinedSVal *Size = State->get<ArraySizeMap>(ArraySym);
+
+ if (!Size)
+ return;
+
+ // Get the index.
+ const Expr *IdxExpr = CE->getArg(1);
+ SVal IdxVal = C.getSVal(IdxExpr);
+ if (IdxVal.isUnknownOrUndef())
+ return;
+ DefinedSVal Idx = IdxVal.castAs<DefinedSVal>();
+
+ // Now, check if 'Idx in [0, Size-1]'.
+ const QualType T = IdxExpr->getType();
+ ProgramStateRef StInBound = State->assumeInBound(Idx, *Size, true, T);
+ ProgramStateRef StOutBound = State->assumeInBound(Idx, *Size, false, T);
+ if (StOutBound && !StInBound) {
+ ExplodedNode *N = C.generateErrorNode(StOutBound);
+ if (!N)
+ return;
+ initBugType();
+ auto R = llvm::make_unique<BugReport>(*BT, "Index is out of bounds", N);
+ R->addRange(IdxExpr->getSourceRange());
+ bugreporter::trackExpressionValue(N, IdxExpr, *R,
+ /*EnableNullFPSuppression=*/false);
+ C.emitReport(std::move(R));
+ return;
+ }
+ }
+}
+
+ProgramStateRef
+ObjCContainersChecker::checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const {
+ for (const auto &Sym : Escaped) {
+ // When a symbol for a mutable array escapes, we can't reason precisely
+ // about its size any more -- so remove it from the map.
+ // Note that we aren't notified here when a CFMutableArrayRef escapes as a
+ // CFArrayRef. This is because CFArrayRef is typedef'd as a pointer to a
+ // const-qualified type.
+ State = State->remove<ArraySizeMap>(Sym);
+ }
+ return State;
+}
+
+void ObjCContainersChecker::printState(raw_ostream &OS, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+ ArraySizeMapTy Map = State->get<ArraySizeMap>();
+ if (Map.isEmpty())
+ return;
+
+ OS << Sep << "ObjC container sizes :" << NL;
+ for (auto I : Map) {
+ OS << I.first << " : " << I.second << NL;
+ }
+}
+
+/// Register checker.
+void ento::registerObjCContainersChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ObjCContainersChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
new file mode 100644
index 000000000000..d383302b2790
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
@@ -0,0 +1,263 @@
+//==- ObjCMissingSuperCallChecker.cpp - Check missing super-calls in ObjC --==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a ObjCMissingSuperCallChecker, a checker that
+// analyzes a UIViewController implementation to determine if it
+// correctly calls super in the methods where this is mandatory.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+struct SelectorDescriptor {
+ const char *SelectorName;
+ unsigned ArgumentCount;
+};
+
+//===----------------------------------------------------------------------===//
+// FindSuperCallVisitor - Identify specific calls to the superclass.
+//===----------------------------------------------------------------------===//
+
+class FindSuperCallVisitor : public RecursiveASTVisitor<FindSuperCallVisitor> {
+public:
+ explicit FindSuperCallVisitor(Selector S) : DoesCallSuper(false), Sel(S) {}
+
+ bool VisitObjCMessageExpr(ObjCMessageExpr *E) {
+ if (E->getSelector() == Sel)
+ if (E->getReceiverKind() == ObjCMessageExpr::SuperInstance)
+ DoesCallSuper = true;
+
+ // Recurse if we didn't find the super call yet.
+ return !DoesCallSuper;
+ }
+
+ bool DoesCallSuper;
+
+private:
+ Selector Sel;
+};
+
+//===----------------------------------------------------------------------===//
+// ObjCSuperCallChecker
+//===----------------------------------------------------------------------===//
+
+class ObjCSuperCallChecker : public Checker<
+ check::ASTDecl<ObjCImplementationDecl> > {
+public:
+ ObjCSuperCallChecker() : IsInitialized(false) {}
+
+ void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const;
+private:
+ bool isCheckableClass(const ObjCImplementationDecl *D,
+ StringRef &SuperclassName) const;
+ void initializeSelectors(ASTContext &Ctx) const;
+ void fillSelectors(ASTContext &Ctx, ArrayRef<SelectorDescriptor> Sel,
+ StringRef ClassName) const;
+ mutable llvm::StringMap<llvm::SmallSet<Selector, 16> > SelectorsForClass;
+ mutable bool IsInitialized;
+};
+
+}
+
+/// Determine whether the given class has a superclass that we want
+/// to check. The name of the found superclass is stored in SuperclassName.
+///
+/// \param D The declaration to check for superclasses.
+/// \param[out] SuperclassName On return, the found superclass name.
+bool ObjCSuperCallChecker::isCheckableClass(const ObjCImplementationDecl *D,
+ StringRef &SuperclassName) const {
+ const ObjCInterfaceDecl *ID = D->getClassInterface()->getSuperClass();
+ for ( ; ID ; ID = ID->getSuperClass())
+ {
+ SuperclassName = ID->getIdentifier()->getName();
+ if (SelectorsForClass.count(SuperclassName))
+ return true;
+ }
+ return false;
+}
+
+void ObjCSuperCallChecker::fillSelectors(ASTContext &Ctx,
+ ArrayRef<SelectorDescriptor> Sel,
+ StringRef ClassName) const {
+ llvm::SmallSet<Selector, 16> &ClassSelectors = SelectorsForClass[ClassName];
+ // Fill the Selectors SmallSet with all selectors we want to check.
+ for (ArrayRef<SelectorDescriptor>::iterator I = Sel.begin(), E = Sel.end();
+ I != E; ++I) {
+ SelectorDescriptor Descriptor = *I;
+ assert(Descriptor.ArgumentCount <= 1); // No multi-argument selectors yet.
+
+ // Get the selector.
+ IdentifierInfo *II = &Ctx.Idents.get(Descriptor.SelectorName);
+
+ Selector Sel = Ctx.Selectors.getSelector(Descriptor.ArgumentCount, &II);
+ ClassSelectors.insert(Sel);
+ }
+}
+
+void ObjCSuperCallChecker::initializeSelectors(ASTContext &Ctx) const {
+
+ { // Initialize selectors for: UIViewController
+ const SelectorDescriptor Selectors[] = {
+ { "addChildViewController", 1 },
+ { "viewDidAppear", 1 },
+ { "viewDidDisappear", 1 },
+ { "viewWillAppear", 1 },
+ { "viewWillDisappear", 1 },
+ { "removeFromParentViewController", 0 },
+ { "didReceiveMemoryWarning", 0 },
+ { "viewDidUnload", 0 },
+ { "viewDidLoad", 0 },
+ { "viewWillUnload", 0 },
+ { "updateViewConstraints", 0 },
+ { "encodeRestorableStateWithCoder", 1 },
+ { "restoreStateWithCoder", 1 }};
+
+ fillSelectors(Ctx, Selectors, "UIViewController");
+ }
+
+ { // Initialize selectors for: UIResponder
+ const SelectorDescriptor Selectors[] = {
+ { "resignFirstResponder", 0 }};
+
+ fillSelectors(Ctx, Selectors, "UIResponder");
+ }
+
+ { // Initialize selectors for: NSResponder
+ const SelectorDescriptor Selectors[] = {
+ { "encodeRestorableStateWithCoder", 1 },
+ { "restoreStateWithCoder", 1 }};
+
+ fillSelectors(Ctx, Selectors, "NSResponder");
+ }
+
+ { // Initialize selectors for: NSDocument
+ const SelectorDescriptor Selectors[] = {
+ { "encodeRestorableStateWithCoder", 1 },
+ { "restoreStateWithCoder", 1 }};
+
+ fillSelectors(Ctx, Selectors, "NSDocument");
+ }
+
+ IsInitialized = true;
+}
+
+void ObjCSuperCallChecker::checkASTDecl(const ObjCImplementationDecl *D,
+ AnalysisManager &Mgr,
+ BugReporter &BR) const {
+ ASTContext &Ctx = BR.getContext();
+
+ // We need to initialize the selector table once.
+ if (!IsInitialized)
+ initializeSelectors(Ctx);
+
+ // Find out whether this class has a superclass that we are supposed to check.
+ StringRef SuperclassName;
+ if (!isCheckableClass(D, SuperclassName))
+ return;
+
+
+ // Iterate over all instance methods.
+ for (auto *MD : D->instance_methods()) {
+ Selector S = MD->getSelector();
+ // Find out whether this is a selector that we want to check.
+ if (!SelectorsForClass[SuperclassName].count(S))
+ continue;
+
+ // Check if the method calls its superclass implementation.
+ if (MD->getBody())
+ {
+ FindSuperCallVisitor Visitor(S);
+ Visitor.TraverseDecl(MD);
+
+ // It doesn't call super, emit a diagnostic.
+ if (!Visitor.DoesCallSuper) {
+ PathDiagnosticLocation DLoc =
+ PathDiagnosticLocation::createEnd(MD->getBody(),
+ BR.getSourceManager(),
+ Mgr.getAnalysisDeclContext(D));
+
+ const char *Name = "Missing call to superclass";
+ SmallString<320> Buf;
+ llvm::raw_svector_ostream os(Buf);
+
+ os << "The '" << S.getAsString()
+ << "' instance method in " << SuperclassName.str() << " subclass '"
+ << *D << "' is missing a [super " << S.getAsString() << "] call";
+
+ BR.EmitBasicReport(MD, this, Name, categories::CoreFoundationObjectiveC,
+ os.str(), DLoc);
+ }
+ }
+ }
+}
+
+
+//===----------------------------------------------------------------------===//
+// Check registration.
+//===----------------------------------------------------------------------===//
+
+void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<ObjCSuperCallChecker>();
+}
+
+
+/*
+ ToDo list for expanding this check in the future, the list is not exhaustive.
+ There are also cases where calling super is suggested but not "mandatory".
+ In addition to be able to check the classes and methods below, architectural
+ improvements like being able to allow for the super-call to be done in a called
+ method would be good too.
+
+UIDocument subclasses
+- finishedHandlingError:recovered: (is multi-arg)
+- finishedHandlingError:recovered: (is multi-arg)
+
+UIViewController subclasses
+- loadView (should *never* call super)
+- transitionFromViewController:toViewController:
+ duration:options:animations:completion: (is multi-arg)
+
+UICollectionViewController subclasses
+- loadView (take care because UIViewController subclasses should NOT call super
+ in loadView, but UICollectionViewController subclasses should)
+
+NSObject subclasses
+- doesNotRecognizeSelector (it only has to call super if it doesn't throw)
+
+UIPopoverBackgroundView subclasses (some of those are class methods)
+- arrowDirection (should *never* call super)
+- arrowOffset (should *never* call super)
+- arrowBase (should *never* call super)
+- arrowHeight (should *never* call super)
+- contentViewInsets (should *never* call super)
+
+UITextSelectionRect subclasses (some of those are properties)
+- rect (should *never* call super)
+- range (should *never* call super)
+- writingDirection (should *never* call super)
+- isVertical (should *never* call super)
+- containsStart (should *never* call super)
+- containsEnd (should *never* call super)
+*/
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp
new file mode 100644
index 000000000000..018d3fcfceb9
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp
@@ -0,0 +1,81 @@
+//==- ObjCPropertyChecker.cpp - Check ObjC properties ------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker finds issues with Objective-C properties.
+// Currently finds only one kind of issue:
+// - Find synthesized properties with copy attribute of mutable NS collection
+// types. Calling -copy on such collections produces an immutable copy,
+// which contradicts the type of the property.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ObjCPropertyChecker
+ : public Checker<check::ASTDecl<ObjCPropertyDecl>> {
+ void checkCopyMutable(const ObjCPropertyDecl *D, BugReporter &BR) const;
+
+public:
+ void checkASTDecl(const ObjCPropertyDecl *D, AnalysisManager &Mgr,
+ BugReporter &BR) const;
+};
+} // end anonymous namespace.
+
+void ObjCPropertyChecker::checkASTDecl(const ObjCPropertyDecl *D,
+ AnalysisManager &Mgr,
+ BugReporter &BR) const {
+ checkCopyMutable(D, BR);
+}
+
+void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D,
+ BugReporter &BR) const {
+ if (D->isReadOnly() || D->getSetterKind() != ObjCPropertyDecl::Copy)
+ return;
+
+ QualType T = D->getType();
+ if (!T->isObjCObjectPointerType())
+ return;
+
+ const std::string &PropTypeName(T->getPointeeType().getCanonicalType()
+ .getUnqualifiedType()
+ .getAsString());
+ if (!StringRef(PropTypeName).startswith("NSMutable"))
+ return;
+
+ const ObjCImplDecl *ImplD = nullptr;
+ if (const ObjCInterfaceDecl *IntD =
+ dyn_cast<ObjCInterfaceDecl>(D->getDeclContext())) {
+ ImplD = IntD->getImplementation();
+ } else if (auto *CatD = dyn_cast<ObjCCategoryDecl>(D->getDeclContext())) {
+ ImplD = CatD->getClassInterface()->getImplementation();
+ }
+
+ if (!ImplD || ImplD->HasUserDeclaredSetterMethod(D))
+ return;
+
+ SmallString<128> Str;
+ llvm::raw_svector_ostream OS(Str);
+ OS << "Property of mutable type '" << PropTypeName
+ << "' has 'copy' attribute; an immutable object will be stored instead";
+
+ BR.EmitBasicReport(
+ D, this, "Objective-C property misuse", "Logic error", OS.str(),
+ PathDiagnosticLocation::createBegin(D, BR.getSourceManager()),
+ D->getSourceRange());
+}
+
+void ento::registerObjCPropertyChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<ObjCPropertyChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
new file mode 100644
index 000000000000..efa804220765
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
@@ -0,0 +1,439 @@
+//== ObjCSelfInitChecker.cpp - Checker for 'self' initialization -*- C++ -*--=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines ObjCSelfInitChecker, a builtin check that checks for uses of
+// 'self' before proper initialization.
+//
+//===----------------------------------------------------------------------===//
+
+// This checks initialization methods to verify that they assign 'self' to the
+// result of an initialization call (e.g. [super init], or [self initWith..])
+// before using 'self' or any instance variable.
+//
+// To perform the required checking, values are tagged with flags that indicate
+// 1) if the object is the one pointed to by 'self', and 2) if the object
+// is the result of an initializer (e.g. [super init]).
+//
+// Uses of an object that is true for 1) but not 2) trigger a diagnostic.
+// The uses that are currently checked are:
+// - Using instance variables.
+// - Returning the object.
+//
+// Note that we don't check for an invalid 'self' that is the receiver of an
+// obj-c message expression to cut down false positives where logging functions
+// get information from self (like its class) or doing "invalidation" on self
+// when the initialization fails.
+//
+// Because the object that 'self' points to gets invalidated when a call
+// receives a reference to 'self', the checker keeps track and passes the flags
+// for 1) and 2) to the new object that 'self' points to after the call.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND);
+static bool isInitializationMethod(const ObjCMethodDecl *MD);
+static bool isInitMessage(const ObjCMethodCall &Msg);
+static bool isSelfVar(SVal location, CheckerContext &C);
+
+namespace {
+class ObjCSelfInitChecker : public Checker< check::PostObjCMessage,
+ check::PostStmt<ObjCIvarRefExpr>,
+ check::PreStmt<ReturnStmt>,
+ check::PreCall,
+ check::PostCall,
+ check::Location,
+ check::Bind > {
+ mutable std::unique_ptr<BugType> BT;
+
+ void checkForInvalidSelf(const Expr *E, CheckerContext &C,
+ const char *errorStr) const;
+
+public:
+ ObjCSelfInitChecker() {}
+ void checkPostObjCMessage(const ObjCMethodCall &Msg, CheckerContext &C) const;
+ void checkPostStmt(const ObjCIvarRefExpr *E, CheckerContext &C) const;
+ void checkPreStmt(const ReturnStmt *S, CheckerContext &C) const;
+ void checkLocation(SVal location, bool isLoad, const Stmt *S,
+ CheckerContext &C) const;
+ void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
+
+ void checkPreCall(const CallEvent &CE, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &CE, CheckerContext &C) const;
+
+ void printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const override;
+};
+} // end anonymous namespace
+
+namespace {
+enum SelfFlagEnum {
+ /// No flag set.
+ SelfFlag_None = 0x0,
+ /// Value came from 'self'.
+ SelfFlag_Self = 0x1,
+ /// Value came from the result of an initializer (e.g. [super init]).
+ SelfFlag_InitRes = 0x2
+};
+}
+
+REGISTER_MAP_WITH_PROGRAMSTATE(SelfFlag, SymbolRef, unsigned)
+REGISTER_TRAIT_WITH_PROGRAMSTATE(CalledInit, bool)
+
+/// A call receiving a reference to 'self' invalidates the object that
+/// 'self' contains. This keeps the "self flags" assigned to the 'self'
+/// object before the call so we can assign them to the new object that 'self'
+/// points to after the call.
+REGISTER_TRAIT_WITH_PROGRAMSTATE(PreCallSelfFlags, unsigned)
+
+static SelfFlagEnum getSelfFlags(SVal val, ProgramStateRef state) {
+ if (SymbolRef sym = val.getAsSymbol())
+ if (const unsigned *attachedFlags = state->get<SelfFlag>(sym))
+ return (SelfFlagEnum)*attachedFlags;
+ return SelfFlag_None;
+}
+
+static SelfFlagEnum getSelfFlags(SVal val, CheckerContext &C) {
+ return getSelfFlags(val, C.getState());
+}
+
+static void addSelfFlag(ProgramStateRef state, SVal val,
+ SelfFlagEnum flag, CheckerContext &C) {
+ // We tag the symbol that the SVal wraps.
+ if (SymbolRef sym = val.getAsSymbol()) {
+ state = state->set<SelfFlag>(sym, getSelfFlags(val, state) | flag);
+ C.addTransition(state);
+ }
+}
+
+static bool hasSelfFlag(SVal val, SelfFlagEnum flag, CheckerContext &C) {
+ return getSelfFlags(val, C) & flag;
+}
+
+/// Returns true of the value of the expression is the object that 'self'
+/// points to and is an object that did not come from the result of calling
+/// an initializer.
+static bool isInvalidSelf(const Expr *E, CheckerContext &C) {
+ SVal exprVal = C.getSVal(E);
+ if (!hasSelfFlag(exprVal, SelfFlag_Self, C))
+ return false; // value did not come from 'self'.
+ if (hasSelfFlag(exprVal, SelfFlag_InitRes, C))
+ return false; // 'self' is properly initialized.
+
+ return true;
+}
+
+void ObjCSelfInitChecker::checkForInvalidSelf(const Expr *E, CheckerContext &C,
+ const char *errorStr) const {
+ if (!E)
+ return;
+
+ if (!C.getState()->get<CalledInit>())
+ return;
+
+ if (!isInvalidSelf(E, C))
+ return;
+
+ // Generate an error node.
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+
+ if (!BT)
+ BT.reset(new BugType(this, "Missing \"self = [(super or self) init...]\"",
+ categories::CoreFoundationObjectiveC));
+ C.emitReport(llvm::make_unique<BugReport>(*BT, errorStr, N));
+}
+
+void ObjCSelfInitChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
+ CheckerContext &C) const {
+ // When encountering a message that does initialization (init rule),
+ // tag the return value so that we know later on that if self has this value
+ // then it is properly initialized.
+
+ // FIXME: A callback should disable checkers at the start of functions.
+ if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
+ C.getCurrentAnalysisDeclContext()->getDecl())))
+ return;
+
+ if (isInitMessage(Msg)) {
+ // Tag the return value as the result of an initializer.
+ ProgramStateRef state = C.getState();
+
+ // FIXME this really should be context sensitive, where we record
+ // the current stack frame (for IPA). Also, we need to clean this
+ // value out when we return from this method.
+ state = state->set<CalledInit>(true);
+
+ SVal V = C.getSVal(Msg.getOriginExpr());
+ addSelfFlag(state, V, SelfFlag_InitRes, C);
+ return;
+ }
+
+ // We don't check for an invalid 'self' in an obj-c message expression to cut
+ // down false positives where logging functions get information from self
+ // (like its class) or doing "invalidation" on self when the initialization
+ // fails.
+}
+
+void ObjCSelfInitChecker::checkPostStmt(const ObjCIvarRefExpr *E,
+ CheckerContext &C) const {
+ // FIXME: A callback should disable checkers at the start of functions.
+ if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
+ C.getCurrentAnalysisDeclContext()->getDecl())))
+ return;
+
+ checkForInvalidSelf(
+ E->getBase(), C,
+ "Instance variable used while 'self' is not set to the result of "
+ "'[(super or self) init...]'");
+}
+
+void ObjCSelfInitChecker::checkPreStmt(const ReturnStmt *S,
+ CheckerContext &C) const {
+ // FIXME: A callback should disable checkers at the start of functions.
+ if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
+ C.getCurrentAnalysisDeclContext()->getDecl())))
+ return;
+
+ checkForInvalidSelf(S->getRetValue(), C,
+ "Returning 'self' while it is not set to the result of "
+ "'[(super or self) init...]'");
+}
+
+// When a call receives a reference to 'self', [Pre/Post]Call pass
+// the SelfFlags from the object 'self' points to before the call to the new
+// object after the call. This is to avoid invalidation of 'self' by logging
+// functions.
+// Another common pattern in classes with multiple initializers is to put the
+// subclass's common initialization bits into a static function that receives
+// the value of 'self', e.g:
+// @code
+// if (!(self = [super init]))
+// return nil;
+// if (!(self = _commonInit(self)))
+// return nil;
+// @endcode
+// Until we can use inter-procedural analysis, in such a call, transfer the
+// SelfFlags to the result of the call.
+
+void ObjCSelfInitChecker::checkPreCall(const CallEvent &CE,
+ CheckerContext &C) const {
+ // FIXME: A callback should disable checkers at the start of functions.
+ if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
+ C.getCurrentAnalysisDeclContext()->getDecl())))
+ return;
+
+ ProgramStateRef state = C.getState();
+ unsigned NumArgs = CE.getNumArgs();
+ // If we passed 'self' as and argument to the call, record it in the state
+ // to be propagated after the call.
+ // Note, we could have just given up, but try to be more optimistic here and
+ // assume that the functions are going to continue initialization or will not
+ // modify self.
+ for (unsigned i = 0; i < NumArgs; ++i) {
+ SVal argV = CE.getArgSVal(i);
+ if (isSelfVar(argV, C)) {
+ unsigned selfFlags = getSelfFlags(state->getSVal(argV.castAs<Loc>()), C);
+ C.addTransition(state->set<PreCallSelfFlags>(selfFlags));
+ return;
+ } else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
+ unsigned selfFlags = getSelfFlags(argV, C);
+ C.addTransition(state->set<PreCallSelfFlags>(selfFlags));
+ return;
+ }
+ }
+}
+
+void ObjCSelfInitChecker::checkPostCall(const CallEvent &CE,
+ CheckerContext &C) const {
+ // FIXME: A callback should disable checkers at the start of functions.
+ if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
+ C.getCurrentAnalysisDeclContext()->getDecl())))
+ return;
+
+ ProgramStateRef state = C.getState();
+ SelfFlagEnum prevFlags = (SelfFlagEnum)state->get<PreCallSelfFlags>();
+ if (!prevFlags)
+ return;
+ state = state->remove<PreCallSelfFlags>();
+
+ unsigned NumArgs = CE.getNumArgs();
+ for (unsigned i = 0; i < NumArgs; ++i) {
+ SVal argV = CE.getArgSVal(i);
+ if (isSelfVar(argV, C)) {
+ // If the address of 'self' is being passed to the call, assume that the
+ // 'self' after the call will have the same flags.
+ // EX: log(&self)
+ addSelfFlag(state, state->getSVal(argV.castAs<Loc>()), prevFlags, C);
+ return;
+ } else if (hasSelfFlag(argV, SelfFlag_Self, C)) {
+ // If 'self' is passed to the call by value, assume that the function
+ // returns 'self'. So assign the flags, which were set on 'self' to the
+ // return value.
+ // EX: self = performMoreInitialization(self)
+ addSelfFlag(state, CE.getReturnValue(), prevFlags, C);
+ return;
+ }
+ }
+
+ C.addTransition(state);
+}
+
+void ObjCSelfInitChecker::checkLocation(SVal location, bool isLoad,
+ const Stmt *S,
+ CheckerContext &C) const {
+ if (!shouldRunOnFunctionOrMethod(dyn_cast<NamedDecl>(
+ C.getCurrentAnalysisDeclContext()->getDecl())))
+ return;
+
+ // Tag the result of a load from 'self' so that we can easily know that the
+ // value is the object that 'self' points to.
+ ProgramStateRef state = C.getState();
+ if (isSelfVar(location, C))
+ addSelfFlag(state, state->getSVal(location.castAs<Loc>()), SelfFlag_Self,
+ C);
+}
+
+
+void ObjCSelfInitChecker::checkBind(SVal loc, SVal val, const Stmt *S,
+ CheckerContext &C) const {
+ // Allow assignment of anything to self. Self is a local variable in the
+ // initializer, so it is legal to assign anything to it, like results of
+ // static functions/method calls. After self is assigned something we cannot
+ // reason about, stop enforcing the rules.
+ // (Only continue checking if the assigned value should be treated as self.)
+ if ((isSelfVar(loc, C)) &&
+ !hasSelfFlag(val, SelfFlag_InitRes, C) &&
+ !hasSelfFlag(val, SelfFlag_Self, C) &&
+ !isSelfVar(val, C)) {
+
+ // Stop tracking the checker-specific state in the state.
+ ProgramStateRef State = C.getState();
+ State = State->remove<CalledInit>();
+ if (SymbolRef sym = loc.getAsSymbol())
+ State = State->remove<SelfFlag>(sym);
+ C.addTransition(State);
+ }
+}
+
+void ObjCSelfInitChecker::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+ SelfFlagTy FlagMap = State->get<SelfFlag>();
+ bool DidCallInit = State->get<CalledInit>();
+ SelfFlagEnum PreCallFlags = (SelfFlagEnum)State->get<PreCallSelfFlags>();
+
+ if (FlagMap.isEmpty() && !DidCallInit && !PreCallFlags)
+ return;
+
+ Out << Sep << NL << *this << " :" << NL;
+
+ if (DidCallInit)
+ Out << " An init method has been called." << NL;
+
+ if (PreCallFlags != SelfFlag_None) {
+ if (PreCallFlags & SelfFlag_Self) {
+ Out << " An argument of the current call came from the 'self' variable."
+ << NL;
+ }
+ if (PreCallFlags & SelfFlag_InitRes) {
+ Out << " An argument of the current call came from an init method."
+ << NL;
+ }
+ }
+
+ Out << NL;
+ for (SelfFlagTy::iterator I = FlagMap.begin(), E = FlagMap.end();
+ I != E; ++I) {
+ Out << I->first << " : ";
+
+ if (I->second == SelfFlag_None)
+ Out << "none";
+
+ if (I->second & SelfFlag_Self)
+ Out << "self variable";
+
+ if (I->second & SelfFlag_InitRes) {
+ if (I->second != SelfFlag_InitRes)
+ Out << " | ";
+ Out << "result of init method";
+ }
+
+ Out << NL;
+ }
+}
+
+
+// FIXME: A callback should disable checkers at the start of functions.
+static bool shouldRunOnFunctionOrMethod(const NamedDecl *ND) {
+ if (!ND)
+ return false;
+
+ const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(ND);
+ if (!MD)
+ return false;
+ if (!isInitializationMethod(MD))
+ return false;
+
+ // self = [super init] applies only to NSObject subclasses.
+ // For instance, NSProxy doesn't implement -init.
+ ASTContext &Ctx = MD->getASTContext();
+ IdentifierInfo* NSObjectII = &Ctx.Idents.get("NSObject");
+ ObjCInterfaceDecl *ID = MD->getClassInterface()->getSuperClass();
+ for ( ; ID ; ID = ID->getSuperClass()) {
+ IdentifierInfo *II = ID->getIdentifier();
+
+ if (II == NSObjectII)
+ break;
+ }
+ return ID != nullptr;
+}
+
+/// Returns true if the location is 'self'.
+static bool isSelfVar(SVal location, CheckerContext &C) {
+ AnalysisDeclContext *analCtx = C.getCurrentAnalysisDeclContext();
+ if (!analCtx->getSelfDecl())
+ return false;
+ if (!location.getAs<loc::MemRegionVal>())
+ return false;
+
+ loc::MemRegionVal MRV = location.castAs<loc::MemRegionVal>();
+ if (const DeclRegion *DR = dyn_cast<DeclRegion>(MRV.stripCasts()))
+ return (DR->getDecl() == analCtx->getSelfDecl());
+
+ return false;
+}
+
+static bool isInitializationMethod(const ObjCMethodDecl *MD) {
+ return MD->getMethodFamily() == OMF_init;
+}
+
+static bool isInitMessage(const ObjCMethodCall &Call) {
+ return Call.getMethodFamily() == OMF_init;
+}
+
+//===----------------------------------------------------------------------===//
+// Registration.
+//===----------------------------------------------------------------------===//
+
+void ento::registerObjCSelfInitChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ObjCSelfInitChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
new file mode 100644
index 000000000000..9058784dd345
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
@@ -0,0 +1,289 @@
+//===- ObjCSuperDeallocChecker.cpp - Check correct use of [super dealloc] -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines ObjCSuperDeallocChecker, a builtin check that warns when
+// self is used after a call to [super dealloc] in MRR mode.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ObjCSuperDeallocChecker
+ : public Checker<check::PostObjCMessage, check::PreObjCMessage,
+ check::PreCall, check::Location> {
+
+ mutable IdentifierInfo *IIdealloc, *IINSObject;
+ mutable Selector SELdealloc;
+
+ std::unique_ptr<BugType> DoubleSuperDeallocBugType;
+
+ void initIdentifierInfoAndSelectors(ASTContext &Ctx) const;
+
+ bool isSuperDeallocMessage(const ObjCMethodCall &M) const;
+
+public:
+ ObjCSuperDeallocChecker();
+ void checkPostObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+ void checkPreObjCMessage(const ObjCMethodCall &M, CheckerContext &C) const;
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+ void checkLocation(SVal l, bool isLoad, const Stmt *S,
+ CheckerContext &C) const;
+
+private:
+
+ void diagnoseCallArguments(const CallEvent &CE, CheckerContext &C) const;
+
+ void reportUseAfterDealloc(SymbolRef Sym, StringRef Desc, const Stmt *S,
+ CheckerContext &C) const;
+};
+
+} // End anonymous namespace.
+
+// Remember whether [super dealloc] has previously been called on the
+// SymbolRef for the receiver.
+REGISTER_SET_WITH_PROGRAMSTATE(CalledSuperDealloc, SymbolRef)
+
+namespace {
+class SuperDeallocBRVisitor final : public BugReporterVisitor {
+ SymbolRef ReceiverSymbol;
+ bool Satisfied;
+
+public:
+ SuperDeallocBRVisitor(SymbolRef ReceiverSymbol)
+ : ReceiverSymbol(ReceiverSymbol),
+ Satisfied(false) {}
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.Add(ReceiverSymbol);
+ }
+};
+} // End anonymous namespace.
+
+void ObjCSuperDeallocChecker::checkPreObjCMessage(const ObjCMethodCall &M,
+ CheckerContext &C) const {
+
+ ProgramStateRef State = C.getState();
+ SymbolRef ReceiverSymbol = M.getReceiverSVal().getAsSymbol();
+ if (!ReceiverSymbol) {
+ diagnoseCallArguments(M, C);
+ return;
+ }
+
+ bool AlreadyCalled = State->contains<CalledSuperDealloc>(ReceiverSymbol);
+ if (!AlreadyCalled)
+ return;
+
+ StringRef Desc;
+
+ if (isSuperDeallocMessage(M)) {
+ Desc = "[super dealloc] should not be called multiple times";
+ } else {
+ Desc = StringRef();
+ }
+
+ reportUseAfterDealloc(ReceiverSymbol, Desc, M.getOriginExpr(), C);
+}
+
+void ObjCSuperDeallocChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ diagnoseCallArguments(Call, C);
+}
+
+void ObjCSuperDeallocChecker::checkPostObjCMessage(const ObjCMethodCall &M,
+ CheckerContext &C) const {
+ // Check for [super dealloc] method call.
+ if (!isSuperDeallocMessage(M))
+ return;
+
+ ProgramStateRef State = C.getState();
+ SymbolRef ReceiverSymbol = M.getSelfSVal().getAsSymbol();
+ assert(ReceiverSymbol && "No receiver symbol at call to [super dealloc]?");
+
+ // We add this transition in checkPostObjCMessage to avoid warning when
+ // we inline a call to [super dealloc] where the inlined call itself
+ // calls [super dealloc].
+ State = State->add<CalledSuperDealloc>(ReceiverSymbol);
+ C.addTransition(State);
+}
+
+void ObjCSuperDeallocChecker::checkLocation(SVal L, bool IsLoad, const Stmt *S,
+ CheckerContext &C) const {
+ SymbolRef BaseSym = L.getLocSymbolInBase();
+ if (!BaseSym)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ if (!State->contains<CalledSuperDealloc>(BaseSym))
+ return;
+
+ const MemRegion *R = L.getAsRegion();
+ if (!R)
+ return;
+
+ // Climb the super regions to find the base symbol while recording
+ // the second-to-last region for error reporting.
+ const MemRegion *PriorSubRegion = nullptr;
+ while (const SubRegion *SR = dyn_cast<SubRegion>(R)) {
+ if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(SR)) {
+ BaseSym = SymR->getSymbol();
+ break;
+ } else {
+ R = SR->getSuperRegion();
+ PriorSubRegion = SR;
+ }
+ }
+
+ StringRef Desc = StringRef();
+ auto *IvarRegion = dyn_cast_or_null<ObjCIvarRegion>(PriorSubRegion);
+
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ if (IvarRegion) {
+ OS << "Use of instance variable '" << *IvarRegion->getDecl() <<
+ "' after 'self' has been deallocated";
+ Desc = OS.str();
+ }
+
+ reportUseAfterDealloc(BaseSym, Desc, S, C);
+}
+
+/// Report a use-after-dealloc on Sym. If not empty,
+/// Desc will be used to describe the error; otherwise,
+/// a default warning will be used.
+void ObjCSuperDeallocChecker::reportUseAfterDealloc(SymbolRef Sym,
+ StringRef Desc,
+ const Stmt *S,
+ CheckerContext &C) const {
+ // We have a use of self after free.
+ // This likely causes a crash, so stop exploring the
+ // path by generating a sink.
+ ExplodedNode *ErrNode = C.generateErrorNode();
+ // If we've already reached this node on another path, return.
+ if (!ErrNode)
+ return;
+
+ if (Desc.empty())
+ Desc = "Use of 'self' after it has been deallocated";
+
+ // Generate the report.
+ std::unique_ptr<BugReport> BR(
+ new BugReport(*DoubleSuperDeallocBugType, Desc, ErrNode));
+ BR->addRange(S->getSourceRange());
+ BR->addVisitor(llvm::make_unique<SuperDeallocBRVisitor>(Sym));
+ C.emitReport(std::move(BR));
+}
+
+/// Diagnose if any of the arguments to CE have already been
+/// dealloc'd.
+void ObjCSuperDeallocChecker::diagnoseCallArguments(const CallEvent &CE,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ unsigned ArgCount = CE.getNumArgs();
+ for (unsigned I = 0; I < ArgCount; I++) {
+ SymbolRef Sym = CE.getArgSVal(I).getAsSymbol();
+ if (!Sym)
+ continue;
+
+ if (State->contains<CalledSuperDealloc>(Sym)) {
+ reportUseAfterDealloc(Sym, StringRef(), CE.getArgExpr(I), C);
+ return;
+ }
+ }
+}
+
+ObjCSuperDeallocChecker::ObjCSuperDeallocChecker()
+ : IIdealloc(nullptr), IINSObject(nullptr) {
+
+ DoubleSuperDeallocBugType.reset(
+ new BugType(this, "[super dealloc] should not be called more than once",
+ categories::CoreFoundationObjectiveC));
+}
+
+void
+ObjCSuperDeallocChecker::initIdentifierInfoAndSelectors(ASTContext &Ctx) const {
+ if (IIdealloc)
+ return;
+
+ IIdealloc = &Ctx.Idents.get("dealloc");
+ IINSObject = &Ctx.Idents.get("NSObject");
+
+ SELdealloc = Ctx.Selectors.getSelector(0, &IIdealloc);
+}
+
+bool
+ObjCSuperDeallocChecker::isSuperDeallocMessage(const ObjCMethodCall &M) const {
+ if (M.getOriginExpr()->getReceiverKind() != ObjCMessageExpr::SuperInstance)
+ return false;
+
+ ASTContext &Ctx = M.getState()->getStateManager().getContext();
+ initIdentifierInfoAndSelectors(Ctx);
+
+ return M.getSelector() == SELdealloc;
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC, BugReport &) {
+ if (Satisfied)
+ return nullptr;
+
+ ProgramStateRef State = Succ->getState();
+
+ bool CalledNow =
+ Succ->getState()->contains<CalledSuperDealloc>(ReceiverSymbol);
+ bool CalledBefore =
+ Succ->getFirstPred()->getState()->contains<CalledSuperDealloc>(
+ ReceiverSymbol);
+
+ // Is Succ the node on which the analyzer noted that [super dealloc] was
+ // called on ReceiverSymbol?
+ if (CalledNow && !CalledBefore) {
+ Satisfied = true;
+
+ ProgramPoint P = Succ->getLocation();
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(P, BRC.getSourceManager());
+
+ if (!L.isValid() || !L.asLocation().isValid())
+ return nullptr;
+
+ return std::make_shared<PathDiagnosticEventPiece>(
+ L, "[super dealloc] called here");
+ }
+
+ return nullptr;
+}
+
+//===----------------------------------------------------------------------===//
+// Checker Registration.
+//===----------------------------------------------------------------------===//
+
+void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {
+ const LangOptions &LangOpts = Mgr.getLangOpts();
+ if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount)
+ return;
+ Mgr.registerChecker<ObjCSuperDeallocChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
new file mode 100644
index 000000000000..7f7b45316087
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
@@ -0,0 +1,188 @@
+//==- ObjCUnusedIVarsChecker.cpp - Check for unused ivars --------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a CheckObjCUnusedIvars, a checker that
+// analyzes an Objective-C class's interface/implementation to determine if it
+// has any ivars that are never accessed.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+
+using namespace clang;
+using namespace ento;
+
+enum IVarState { Unused, Used };
+typedef llvm::DenseMap<const ObjCIvarDecl*,IVarState> IvarUsageMap;
+
+static void Scan(IvarUsageMap& M, const Stmt *S) {
+ if (!S)
+ return;
+
+ if (const ObjCIvarRefExpr *Ex = dyn_cast<ObjCIvarRefExpr>(S)) {
+ const ObjCIvarDecl *D = Ex->getDecl();
+ IvarUsageMap::iterator I = M.find(D);
+ if (I != M.end())
+ I->second = Used;
+ return;
+ }
+
+ // Blocks can reference an instance variable of a class.
+ if (const BlockExpr *BE = dyn_cast<BlockExpr>(S)) {
+ Scan(M, BE->getBody());
+ return;
+ }
+
+ if (const PseudoObjectExpr *POE = dyn_cast<PseudoObjectExpr>(S))
+ for (PseudoObjectExpr::const_semantics_iterator
+ i = POE->semantics_begin(), e = POE->semantics_end(); i != e; ++i) {
+ const Expr *sub = *i;
+ if (const OpaqueValueExpr *OVE = dyn_cast<OpaqueValueExpr>(sub))
+ sub = OVE->getSourceExpr();
+ Scan(M, sub);
+ }
+
+ for (const Stmt *SubStmt : S->children())
+ Scan(M, SubStmt);
+}
+
+static void Scan(IvarUsageMap& M, const ObjCPropertyImplDecl *D) {
+ if (!D)
+ return;
+
+ const ObjCIvarDecl *ID = D->getPropertyIvarDecl();
+
+ if (!ID)
+ return;
+
+ IvarUsageMap::iterator I = M.find(ID);
+ if (I != M.end())
+ I->second = Used;
+}
+
+static void Scan(IvarUsageMap& M, const ObjCContainerDecl *D) {
+ // Scan the methods for accesses.
+ for (const auto *I : D->instance_methods())
+ Scan(M, I->getBody());
+
+ if (const ObjCImplementationDecl *ID = dyn_cast<ObjCImplementationDecl>(D)) {
+ // Scan for @synthesized property methods that act as setters/getters
+ // to an ivar.
+ for (const auto *I : ID->property_impls())
+ Scan(M, I);
+
+ // Scan the associated categories as well.
+ for (const auto *Cat : ID->getClassInterface()->visible_categories()) {
+ if (const ObjCCategoryImplDecl *CID = Cat->getImplementation())
+ Scan(M, CID);
+ }
+ }
+}
+
+static void Scan(IvarUsageMap &M, const DeclContext *C, const FileID FID,
+ SourceManager &SM) {
+ for (const auto *I : C->decls())
+ if (const auto *FD = dyn_cast<FunctionDecl>(I)) {
+ SourceLocation L = FD->getBeginLoc();
+ if (SM.getFileID(L) == FID)
+ Scan(M, FD->getBody());
+ }
+}
+
+static void checkObjCUnusedIvar(const ObjCImplementationDecl *D,
+ BugReporter &BR,
+ const CheckerBase *Checker) {
+
+ const ObjCInterfaceDecl *ID = D->getClassInterface();
+ IvarUsageMap M;
+
+ // Iterate over the ivars.
+ for (const auto *Ivar : ID->ivars()) {
+ // Ignore ivars that...
+ // (a) aren't private
+ // (b) explicitly marked unused
+ // (c) are iboutlets
+ // (d) are unnamed bitfields
+ if (Ivar->getAccessControl() != ObjCIvarDecl::Private ||
+ Ivar->hasAttr<UnusedAttr>() || Ivar->hasAttr<IBOutletAttr>() ||
+ Ivar->hasAttr<IBOutletCollectionAttr>() ||
+ Ivar->isUnnamedBitfield())
+ continue;
+
+ M[Ivar] = Unused;
+ }
+
+ if (M.empty())
+ return;
+
+ // Now scan the implementation declaration.
+ Scan(M, D);
+
+ // Any potentially unused ivars?
+ bool hasUnused = false;
+ for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
+ if (I->second == Unused) {
+ hasUnused = true;
+ break;
+ }
+
+ if (!hasUnused)
+ return;
+
+ // We found some potentially unused ivars. Scan the entire translation unit
+ // for functions inside the @implementation that reference these ivars.
+ // FIXME: In the future hopefully we can just use the lexical DeclContext
+ // to go from the ObjCImplementationDecl to the lexically "nested"
+ // C functions.
+ SourceManager &SM = BR.getSourceManager();
+ Scan(M, D->getDeclContext(), SM.getFileID(D->getLocation()), SM);
+
+ // Find ivars that are unused.
+ for (IvarUsageMap::iterator I = M.begin(), E = M.end(); I!=E; ++I)
+ if (I->second == Unused) {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ os << "Instance variable '" << *I->first << "' in class '" << *ID
+ << "' is never used by the methods in its @implementation "
+ "(although it may be used by category methods).";
+
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(I->first, BR.getSourceManager());
+ BR.EmitBasicReport(D, Checker, "Unused instance variable", "Optimization",
+ os.str(), L);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// ObjCUnusedIvarsChecker
+//===----------------------------------------------------------------------===//
+
+namespace {
+class ObjCUnusedIvarsChecker : public Checker<
+ check::ASTDecl<ObjCImplementationDecl> > {
+public:
+ void checkASTDecl(const ObjCImplementationDecl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ checkObjCUnusedIvar(D, BR, this);
+ }
+};
+}
+
+void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ObjCUnusedIvarsChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
new file mode 100644
index 000000000000..211db392bf71
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
@@ -0,0 +1,353 @@
+//=======- PaddingChecker.cpp ------------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a checker that checks for padding that could be
+// removed by re-ordering members.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/MathExtras.h"
+#include "llvm/Support/raw_ostream.h"
+#include <numeric>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
+private:
+ mutable std::unique_ptr<BugType> PaddingBug;
+ mutable int64_t AllowedPad;
+ mutable BugReporter *BR;
+
+public:
+ void checkASTDecl(const TranslationUnitDecl *TUD, AnalysisManager &MGR,
+ BugReporter &BRArg) const {
+ BR = &BRArg;
+ AllowedPad =
+ MGR.getAnalyzerOptions()
+ .getCheckerIntegerOption("AllowedPad", 24, this);
+ assert(AllowedPad >= 0 && "AllowedPad option should be non-negative");
+
+ // The calls to checkAST* from AnalysisConsumer don't
+ // visit template instantiations or lambda classes. We
+ // want to visit those, so we make our own RecursiveASTVisitor.
+ struct LocalVisitor : public RecursiveASTVisitor<LocalVisitor> {
+ const PaddingChecker *Checker;
+ bool shouldVisitTemplateInstantiations() const { return true; }
+ bool shouldVisitImplicitCode() const { return true; }
+ explicit LocalVisitor(const PaddingChecker *Checker) : Checker(Checker) {}
+ bool VisitRecordDecl(const RecordDecl *RD) {
+ Checker->visitRecord(RD);
+ return true;
+ }
+ bool VisitVarDecl(const VarDecl *VD) {
+ Checker->visitVariable(VD);
+ return true;
+ }
+ // TODO: Visit array new and mallocs for arrays.
+ };
+
+ LocalVisitor visitor(this);
+ visitor.TraverseDecl(const_cast<TranslationUnitDecl *>(TUD));
+ }
+
+ /// Look for records of overly padded types. If padding *
+ /// PadMultiplier exceeds AllowedPad, then generate a report.
+ /// PadMultiplier is used to share code with the array padding
+ /// checker.
+ void visitRecord(const RecordDecl *RD, uint64_t PadMultiplier = 1) const {
+ if (shouldSkipDecl(RD))
+ return;
+
+ // TODO: Figure out why we are going through declarations and not only
+ // definitions.
+ if (!(RD = RD->getDefinition()))
+ return;
+
+ // This is the simplest correct case: a class with no fields and one base
+ // class. Other cases are more complicated because of how the base classes
+ // & fields might interact, so we don't bother dealing with them.
+ // TODO: Support other combinations of base classes and fields.
+ if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD))
+ if (CXXRD->field_empty() && CXXRD->getNumBases() == 1)
+ return visitRecord(CXXRD->bases().begin()->getType()->getAsRecordDecl(),
+ PadMultiplier);
+
+ auto &ASTContext = RD->getASTContext();
+ const ASTRecordLayout &RL = ASTContext.getASTRecordLayout(RD);
+ assert(llvm::isPowerOf2_64(RL.getAlignment().getQuantity()));
+
+ CharUnits BaselinePad = calculateBaselinePad(RD, ASTContext, RL);
+ if (BaselinePad.isZero())
+ return;
+
+ CharUnits OptimalPad;
+ SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
+ std::tie(OptimalPad, OptimalFieldsOrder) =
+ calculateOptimalPad(RD, ASTContext, RL);
+
+ CharUnits DiffPad = PadMultiplier * (BaselinePad - OptimalPad);
+ if (DiffPad.getQuantity() <= AllowedPad) {
+ assert(!DiffPad.isNegative() && "DiffPad should not be negative");
+ // There is not enough excess padding to trigger a warning.
+ return;
+ }
+ reportRecord(RD, BaselinePad, OptimalPad, OptimalFieldsOrder);
+ }
+
+ /// Look for arrays of overly padded types. If the padding of the
+ /// array type exceeds AllowedPad, then generate a report.
+ void visitVariable(const VarDecl *VD) const {
+ const ArrayType *ArrTy = VD->getType()->getAsArrayTypeUnsafe();
+ if (ArrTy == nullptr)
+ return;
+ uint64_t Elts = 0;
+ if (const ConstantArrayType *CArrTy = dyn_cast<ConstantArrayType>(ArrTy))
+ Elts = CArrTy->getSize().getZExtValue();
+ if (Elts == 0)
+ return;
+ const RecordType *RT = ArrTy->getElementType()->getAs<RecordType>();
+ if (RT == nullptr)
+ return;
+
+ // TODO: Recurse into the fields to see if they have excess padding.
+ visitRecord(RT->getDecl(), Elts);
+ }
+
+ bool shouldSkipDecl(const RecordDecl *RD) const {
+ // TODO: Figure out why we are going through declarations and not only
+ // definitions.
+ if (!(RD = RD->getDefinition()))
+ return true;
+ auto Location = RD->getLocation();
+ // If the construct doesn't have a source file, then it's not something
+ // we want to diagnose.
+ if (!Location.isValid())
+ return true;
+ SrcMgr::CharacteristicKind Kind =
+ BR->getSourceManager().getFileCharacteristic(Location);
+ // Throw out all records that come from system headers.
+ if (Kind != SrcMgr::C_User)
+ return true;
+
+ // Not going to attempt to optimize unions.
+ if (RD->isUnion())
+ return true;
+ if (auto *CXXRD = dyn_cast<CXXRecordDecl>(RD)) {
+ // Tail padding with base classes ends up being very complicated.
+ // We will skip objects with base classes for now, unless they do not
+ // have fields.
+ // TODO: Handle more base class scenarios.
+ if (!CXXRD->field_empty() && CXXRD->getNumBases() != 0)
+ return true;
+ if (CXXRD->field_empty() && CXXRD->getNumBases() != 1)
+ return true;
+ // Virtual bases are complicated, skipping those for now.
+ if (CXXRD->getNumVBases() != 0)
+ return true;
+ // Can't layout a template, so skip it. We do still layout the
+ // instantiations though.
+ if (CXXRD->getTypeForDecl()->isDependentType())
+ return true;
+ if (CXXRD->getTypeForDecl()->isInstantiationDependentType())
+ return true;
+ }
+ // How do you reorder fields if you haven't got any?
+ else if (RD->field_empty())
+ return true;
+
+ auto IsTrickyField = [](const FieldDecl *FD) -> bool {
+ // Bitfield layout is hard.
+ if (FD->isBitField())
+ return true;
+
+ // Variable length arrays are tricky too.
+ QualType Ty = FD->getType();
+ if (Ty->isIncompleteArrayType())
+ return true;
+ return false;
+ };
+
+ if (std::any_of(RD->field_begin(), RD->field_end(), IsTrickyField))
+ return true;
+ return false;
+ }
+
+ static CharUnits calculateBaselinePad(const RecordDecl *RD,
+ const ASTContext &ASTContext,
+ const ASTRecordLayout &RL) {
+ CharUnits PaddingSum;
+ CharUnits Offset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));
+ for (const FieldDecl *FD : RD->fields()) {
+ // This checker only cares about the padded size of the
+ // field, and not the data size. If the field is a record
+ // with tail padding, then we won't put that number in our
+ // total because reordering fields won't fix that problem.
+ CharUnits FieldSize = ASTContext.getTypeSizeInChars(FD->getType());
+ auto FieldOffsetBits = RL.getFieldOffset(FD->getFieldIndex());
+ CharUnits FieldOffset = ASTContext.toCharUnitsFromBits(FieldOffsetBits);
+ PaddingSum += (FieldOffset - Offset);
+ Offset = FieldOffset + FieldSize;
+ }
+ PaddingSum += RL.getSize() - Offset;
+ return PaddingSum;
+ }
+
+ /// Optimal padding overview:
+ /// 1. Find a close approximation to where we can place our first field.
+ /// This will usually be at offset 0.
+ /// 2. Try to find the best field that can legally be placed at the current
+ /// offset.
+ /// a. "Best" is the largest alignment that is legal, but smallest size.
+ /// This is to account for overly aligned types.
+ /// 3. If no fields can fit, pad by rounding the current offset up to the
+ /// smallest alignment requirement of our fields. Measure and track the
+ // amount of padding added. Go back to 2.
+ /// 4. Increment the current offset by the size of the chosen field.
+ /// 5. Remove the chosen field from the set of future possibilities.
+ /// 6. Go back to 2 if there are still unplaced fields.
+ /// 7. Add tail padding by rounding the current offset up to the structure
+ /// alignment. Track the amount of padding added.
+
+ static std::pair<CharUnits, SmallVector<const FieldDecl *, 20>>
+ calculateOptimalPad(const RecordDecl *RD, const ASTContext &ASTContext,
+ const ASTRecordLayout &RL) {
+ struct FieldInfo {
+ CharUnits Align;
+ CharUnits Size;
+ const FieldDecl *Field;
+ bool operator<(const FieldInfo &RHS) const {
+ // Order from small alignments to large alignments,
+ // then large sizes to small sizes.
+ // then large field indices to small field indices
+ return std::make_tuple(Align, -Size,
+ Field ? -static_cast<int>(Field->getFieldIndex())
+ : 0) <
+ std::make_tuple(
+ RHS.Align, -RHS.Size,
+ RHS.Field ? -static_cast<int>(RHS.Field->getFieldIndex())
+ : 0);
+ }
+ };
+ SmallVector<FieldInfo, 20> Fields;
+ auto GatherSizesAndAlignments = [](const FieldDecl *FD) {
+ FieldInfo RetVal;
+ RetVal.Field = FD;
+ auto &Ctx = FD->getASTContext();
+ std::tie(RetVal.Size, RetVal.Align) =
+ Ctx.getTypeInfoInChars(FD->getType());
+ assert(llvm::isPowerOf2_64(RetVal.Align.getQuantity()));
+ if (auto Max = FD->getMaxAlignment())
+ RetVal.Align = std::max(Ctx.toCharUnitsFromBits(Max), RetVal.Align);
+ return RetVal;
+ };
+ std::transform(RD->field_begin(), RD->field_end(),
+ std::back_inserter(Fields), GatherSizesAndAlignments);
+ llvm::sort(Fields);
+ // This lets us skip over vptrs and non-virtual bases,
+ // so that we can just worry about the fields in our object.
+ // Note that this does cause us to miss some cases where we
+ // could pack more bytes in to a base class's tail padding.
+ CharUnits NewOffset = ASTContext.toCharUnitsFromBits(RL.getFieldOffset(0));
+ CharUnits NewPad;
+ SmallVector<const FieldDecl *, 20> OptimalFieldsOrder;
+ while (!Fields.empty()) {
+ unsigned TrailingZeros =
+ llvm::countTrailingZeros((unsigned long long)NewOffset.getQuantity());
+ // If NewOffset is zero, then countTrailingZeros will be 64. Shifting
+ // 64 will overflow our unsigned long long. Shifting 63 will turn
+ // our long long (and CharUnits internal type) negative. So shift 62.
+ long long CurAlignmentBits = 1ull << (std::min)(TrailingZeros, 62u);
+ CharUnits CurAlignment = CharUnits::fromQuantity(CurAlignmentBits);
+ FieldInfo InsertPoint = {CurAlignment, CharUnits::Zero(), nullptr};
+ auto CurBegin = Fields.begin();
+ auto CurEnd = Fields.end();
+
+ // In the typical case, this will find the last element
+ // of the vector. We won't find a middle element unless
+ // we started on a poorly aligned address or have an overly
+ // aligned field.
+ auto Iter = std::upper_bound(CurBegin, CurEnd, InsertPoint);
+ if (Iter != CurBegin) {
+ // We found a field that we can layout with the current alignment.
+ --Iter;
+ NewOffset += Iter->Size;
+ OptimalFieldsOrder.push_back(Iter->Field);
+ Fields.erase(Iter);
+ } else {
+ // We are poorly aligned, and we need to pad in order to layout another
+ // field. Round up to at least the smallest field alignment that we
+ // currently have.
+ CharUnits NextOffset = NewOffset.alignTo(Fields[0].Align);
+ NewPad += NextOffset - NewOffset;
+ NewOffset = NextOffset;
+ }
+ }
+ // Calculate tail padding.
+ CharUnits NewSize = NewOffset.alignTo(RL.getAlignment());
+ NewPad += NewSize - NewOffset;
+ return {NewPad, std::move(OptimalFieldsOrder)};
+ }
+
+ void reportRecord(
+ const RecordDecl *RD, CharUnits BaselinePad, CharUnits OptimalPad,
+ const SmallVector<const FieldDecl *, 20> &OptimalFieldsOrder) const {
+ if (!PaddingBug)
+ PaddingBug =
+ llvm::make_unique<BugType>(this, "Excessive Padding", "Performance");
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream Os(Buf);
+ Os << "Excessive padding in '";
+ Os << QualType::getAsString(RD->getTypeForDecl(), Qualifiers(),
+ LangOptions())
+ << "'";
+
+ if (auto *TSD = dyn_cast<ClassTemplateSpecializationDecl>(RD)) {
+ // TODO: make this show up better in the console output and in
+ // the HTML. Maybe just make it show up in HTML like the path
+ // diagnostics show.
+ SourceLocation ILoc = TSD->getPointOfInstantiation();
+ if (ILoc.isValid())
+ Os << " instantiated here: "
+ << ILoc.printToString(BR->getSourceManager());
+ }
+
+ Os << " (" << BaselinePad.getQuantity() << " padding bytes, where "
+ << OptimalPad.getQuantity() << " is optimal). \n"
+ << "Optimal fields order: \n";
+ for (const auto *FD : OptimalFieldsOrder)
+ Os << FD->getName() << ", \n";
+ Os << "consider reordering the fields or adding explicit padding "
+ "members.";
+
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::create(RD, BR->getSourceManager());
+ auto Report = llvm::make_unique<BugReport>(*PaddingBug, Os.str(), CELoc);
+ Report->setDeclWithIssue(RD);
+ Report->addRange(RD->getSourceRange());
+ BR->emitReport(std::move(Report));
+ }
+};
+} // namespace
+
+void ento::registerPaddingChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<PaddingChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
new file mode 100644
index 000000000000..de3a16ebc729
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
@@ -0,0 +1,345 @@
+//=== PointerArithChecker.cpp - Pointer arithmetic checker -----*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This files defines PointerArithChecker, a builtin checker that checks for
+// pointer arithmetic on locations other than array elements.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+enum class AllocKind {
+ SingleObject,
+ Array,
+ Unknown,
+ Reinterpreted // Single object interpreted as an array.
+};
+} // end namespace
+
+namespace llvm {
+template <> struct FoldingSetTrait<AllocKind> {
+ static inline void Profile(AllocKind X, FoldingSetNodeID &ID) {
+ ID.AddInteger(static_cast<int>(X));
+ }
+};
+} // end namespace llvm
+
+namespace {
+class PointerArithChecker
+ : public Checker<
+ check::PreStmt<BinaryOperator>, check::PreStmt<UnaryOperator>,
+ check::PreStmt<ArraySubscriptExpr>, check::PreStmt<CastExpr>,
+ check::PostStmt<CastExpr>, check::PostStmt<CXXNewExpr>,
+ check::PostStmt<CallExpr>, check::DeadSymbols> {
+ AllocKind getKindOfNewOp(const CXXNewExpr *NE, const FunctionDecl *FD) const;
+ const MemRegion *getArrayRegion(const MemRegion *Region, bool &Polymorphic,
+ AllocKind &AKind, CheckerContext &C) const;
+ const MemRegion *getPointedRegion(const MemRegion *Region,
+ CheckerContext &C) const;
+ void reportPointerArithMisuse(const Expr *E, CheckerContext &C,
+ bool PointedNeeded = false) const;
+ void initAllocIdentifiers(ASTContext &C) const;
+
+ mutable std::unique_ptr<BuiltinBug> BT_pointerArith;
+ mutable std::unique_ptr<BuiltinBug> BT_polyArray;
+ mutable llvm::SmallSet<IdentifierInfo *, 8> AllocFunctions;
+
+public:
+ void checkPreStmt(const UnaryOperator *UOp, CheckerContext &C) const;
+ void checkPreStmt(const BinaryOperator *BOp, CheckerContext &C) const;
+ void checkPreStmt(const ArraySubscriptExpr *SubExpr, CheckerContext &C) const;
+ void checkPreStmt(const CastExpr *CE, CheckerContext &C) const;
+ void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
+ void checkPostStmt(const CXXNewExpr *NE, CheckerContext &C) const;
+ void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+};
+} // end namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(RegionState, const MemRegion *, AllocKind)
+
+void PointerArithChecker::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
+ // TODO: intentional leak. Some information is garbage collected too early,
+ // see http://reviews.llvm.org/D14203 for further information.
+ /*ProgramStateRef State = C.getState();
+ RegionStateTy RegionStates = State->get<RegionState>();
+ for (RegionStateTy::iterator I = RegionStates.begin(), E = RegionStates.end();
+ I != E; ++I) {
+ if (!SR.isLiveRegion(I->first))
+ State = State->remove<RegionState>(I->first);
+ }
+ C.addTransition(State);*/
+}
+
+AllocKind PointerArithChecker::getKindOfNewOp(const CXXNewExpr *NE,
+ const FunctionDecl *FD) const {
+ // This checker try not to assume anything about placement and overloaded
+ // new to avoid false positives.
+ if (isa<CXXMethodDecl>(FD))
+ return AllocKind::Unknown;
+ if (FD->getNumParams() != 1 || FD->isVariadic())
+ return AllocKind::Unknown;
+ if (NE->isArray())
+ return AllocKind::Array;
+
+ return AllocKind::SingleObject;
+}
+
+const MemRegion *
+PointerArithChecker::getPointedRegion(const MemRegion *Region,
+ CheckerContext &C) const {
+ assert(Region);
+ ProgramStateRef State = C.getState();
+ SVal S = State->getSVal(Region);
+ return S.getAsRegion();
+}
+
+/// Checks whether a region is the part of an array.
+/// In case there is a derived to base cast above the array element, the
+/// Polymorphic output value is set to true. AKind output value is set to the
+/// allocation kind of the inspected region.
+const MemRegion *PointerArithChecker::getArrayRegion(const MemRegion *Region,
+ bool &Polymorphic,
+ AllocKind &AKind,
+ CheckerContext &C) const {
+ assert(Region);
+ while (Region->getKind() == MemRegion::Kind::CXXBaseObjectRegionKind) {
+ Region = Region->getAs<CXXBaseObjectRegion>()->getSuperRegion();
+ Polymorphic = true;
+ }
+ if (Region->getKind() == MemRegion::Kind::ElementRegionKind) {
+ Region = Region->getAs<ElementRegion>()->getSuperRegion();
+ }
+
+ ProgramStateRef State = C.getState();
+ if (const AllocKind *Kind = State->get<RegionState>(Region)) {
+ AKind = *Kind;
+ if (*Kind == AllocKind::Array)
+ return Region;
+ else
+ return nullptr;
+ }
+ // When the region is symbolic and we do not have any information about it,
+ // assume that this is an array to avoid false positives.
+ if (Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
+ return Region;
+
+ // No AllocKind stored and not symbolic, assume that it points to a single
+ // object.
+ return nullptr;
+}
+
+void PointerArithChecker::reportPointerArithMisuse(const Expr *E,
+ CheckerContext &C,
+ bool PointedNeeded) const {
+ SourceRange SR = E->getSourceRange();
+ if (SR.isInvalid())
+ return;
+
+ ProgramStateRef State = C.getState();
+ const MemRegion *Region = C.getSVal(E).getAsRegion();
+ if (!Region)
+ return;
+ if (PointedNeeded)
+ Region = getPointedRegion(Region, C);
+ if (!Region)
+ return;
+
+ bool IsPolymorphic = false;
+ AllocKind Kind = AllocKind::Unknown;
+ if (const MemRegion *ArrayRegion =
+ getArrayRegion(Region, IsPolymorphic, Kind, C)) {
+ if (!IsPolymorphic)
+ return;
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ if (!BT_polyArray)
+ BT_polyArray.reset(new BuiltinBug(
+ this, "Dangerous pointer arithmetic",
+ "Pointer arithmetic on a pointer to base class is dangerous "
+ "because derived and base class may have different size."));
+ auto R = llvm::make_unique<BugReport>(*BT_polyArray,
+ BT_polyArray->getDescription(), N);
+ R->addRange(E->getSourceRange());
+ R->markInteresting(ArrayRegion);
+ C.emitReport(std::move(R));
+ }
+ return;
+ }
+
+ if (Kind == AllocKind::Reinterpreted)
+ return;
+
+ // We might not have enough information about symbolic regions.
+ if (Kind != AllocKind::SingleObject &&
+ Region->getKind() == MemRegion::Kind::SymbolicRegionKind)
+ return;
+
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ if (!BT_pointerArith)
+ BT_pointerArith.reset(new BuiltinBug(this, "Dangerous pointer arithmetic",
+ "Pointer arithmetic on non-array "
+ "variables relies on memory layout, "
+ "which is dangerous."));
+ auto R = llvm::make_unique<BugReport>(*BT_pointerArith,
+ BT_pointerArith->getDescription(), N);
+ R->addRange(SR);
+ R->markInteresting(Region);
+ C.emitReport(std::move(R));
+ }
+}
+
+void PointerArithChecker::initAllocIdentifiers(ASTContext &C) const {
+ if (!AllocFunctions.empty())
+ return;
+ AllocFunctions.insert(&C.Idents.get("alloca"));
+ AllocFunctions.insert(&C.Idents.get("malloc"));
+ AllocFunctions.insert(&C.Idents.get("realloc"));
+ AllocFunctions.insert(&C.Idents.get("calloc"));
+ AllocFunctions.insert(&C.Idents.get("valloc"));
+}
+
+void PointerArithChecker::checkPostStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD)
+ return;
+ IdentifierInfo *FunI = FD->getIdentifier();
+ initAllocIdentifiers(C.getASTContext());
+ if (AllocFunctions.count(FunI) == 0)
+ return;
+
+ SVal SV = C.getSVal(CE);
+ const MemRegion *Region = SV.getAsRegion();
+ if (!Region)
+ return;
+ // Assume that C allocation functions allocate arrays to avoid false
+ // positives.
+ // TODO: Add heuristics to distinguish alloc calls that allocates single
+ // objecs.
+ State = State->set<RegionState>(Region, AllocKind::Array);
+ C.addTransition(State);
+}
+
+void PointerArithChecker::checkPostStmt(const CXXNewExpr *NE,
+ CheckerContext &C) const {
+ const FunctionDecl *FD = NE->getOperatorNew();
+ if (!FD)
+ return;
+
+ AllocKind Kind = getKindOfNewOp(NE, FD);
+
+ ProgramStateRef State = C.getState();
+ SVal AllocedVal = C.getSVal(NE);
+ const MemRegion *Region = AllocedVal.getAsRegion();
+ if (!Region)
+ return;
+ State = State->set<RegionState>(Region, Kind);
+ C.addTransition(State);
+}
+
+void PointerArithChecker::checkPostStmt(const CastExpr *CE,
+ CheckerContext &C) const {
+ if (CE->getCastKind() != CastKind::CK_BitCast)
+ return;
+
+ const Expr *CastedExpr = CE->getSubExpr();
+ ProgramStateRef State = C.getState();
+ SVal CastedVal = C.getSVal(CastedExpr);
+
+ const MemRegion *Region = CastedVal.getAsRegion();
+ if (!Region)
+ return;
+
+ // Suppress reinterpret casted hits.
+ State = State->set<RegionState>(Region, AllocKind::Reinterpreted);
+ C.addTransition(State);
+}
+
+void PointerArithChecker::checkPreStmt(const CastExpr *CE,
+ CheckerContext &C) const {
+ if (CE->getCastKind() != CastKind::CK_ArrayToPointerDecay)
+ return;
+
+ const Expr *CastedExpr = CE->getSubExpr();
+ ProgramStateRef State = C.getState();
+ SVal CastedVal = C.getSVal(CastedExpr);
+
+ const MemRegion *Region = CastedVal.getAsRegion();
+ if (!Region)
+ return;
+
+ if (const AllocKind *Kind = State->get<RegionState>(Region)) {
+ if (*Kind == AllocKind::Array || *Kind == AllocKind::Reinterpreted)
+ return;
+ }
+ State = State->set<RegionState>(Region, AllocKind::Array);
+ C.addTransition(State);
+}
+
+void PointerArithChecker::checkPreStmt(const UnaryOperator *UOp,
+ CheckerContext &C) const {
+ if (!UOp->isIncrementDecrementOp() || !UOp->getType()->isPointerType())
+ return;
+ reportPointerArithMisuse(UOp->getSubExpr(), C, true);
+}
+
+void PointerArithChecker::checkPreStmt(const ArraySubscriptExpr *SubsExpr,
+ CheckerContext &C) const {
+ SVal Idx = C.getSVal(SubsExpr->getIdx());
+
+ // Indexing with 0 is OK.
+ if (Idx.isZeroConstant())
+ return;
+
+ // Indexing vector-type expressions is also OK.
+ if (SubsExpr->getBase()->getType()->isVectorType())
+ return;
+ reportPointerArithMisuse(SubsExpr->getBase(), C);
+}
+
+void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp,
+ CheckerContext &C) const {
+ BinaryOperatorKind OpKind = BOp->getOpcode();
+ if (!BOp->isAdditiveOp() && OpKind != BO_AddAssign && OpKind != BO_SubAssign)
+ return;
+
+ const Expr *Lhs = BOp->getLHS();
+ const Expr *Rhs = BOp->getRHS();
+ ProgramStateRef State = C.getState();
+
+ if (Rhs->getType()->isIntegerType() && Lhs->getType()->isPointerType()) {
+ SVal RHSVal = C.getSVal(Rhs);
+ if (State->isNull(RHSVal).isConstrainedTrue())
+ return;
+ reportPointerArithMisuse(Lhs, C, !BOp->isAdditiveOp());
+ }
+ // The int += ptr; case is not valid C++.
+ if (Lhs->getType()->isIntegerType() && Rhs->getType()->isPointerType()) {
+ SVal LHSVal = C.getSVal(Lhs);
+ if (State->isNull(LHSVal).isConstrainedTrue())
+ return;
+ reportPointerArithMisuse(Rhs, C);
+ }
+}
+
+void ento::registerPointerArithChecker(CheckerManager &mgr) {
+ mgr.registerChecker<PointerArithChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
new file mode 100644
index 000000000000..41490e45f241
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -0,0 +1,75 @@
+//=== PointerSubChecker.cpp - Pointer subtraction checker ------*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This files defines PointerSubChecker, a builtin checker that checks for
+// pointer subtractions on two pointers pointing to different memory chunks.
+// This check corresponds to CWE-469.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class PointerSubChecker
+ : public Checker< check::PreStmt<BinaryOperator> > {
+ mutable std::unique_ptr<BuiltinBug> BT;
+
+public:
+ void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
+};
+}
+
+void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
+ CheckerContext &C) const {
+ // When doing pointer subtraction, if the two pointers do not point to the
+ // same memory chunk, emit a warning.
+ if (B->getOpcode() != BO_Sub)
+ return;
+
+ SVal LV = C.getSVal(B->getLHS());
+ SVal RV = C.getSVal(B->getRHS());
+
+ const MemRegion *LR = LV.getAsRegion();
+ const MemRegion *RR = RV.getAsRegion();
+
+ if (!(LR && RR))
+ return;
+
+ const MemRegion *BaseLR = LR->getBaseRegion();
+ const MemRegion *BaseRR = RR->getBaseRegion();
+
+ if (BaseLR == BaseRR)
+ return;
+
+ // Allow arithmetic on different symbolic regions.
+ if (isa<SymbolicRegion>(BaseLR) || isa<SymbolicRegion>(BaseRR))
+ return;
+
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ if (!BT)
+ BT.reset(
+ new BuiltinBug(this, "Pointer subtraction",
+ "Subtraction of two pointers that do not point to "
+ "the same memory chunk may cause incorrect result."));
+ auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N);
+ R->addRange(B->getSourceRange());
+ C.emitReport(std::move(R));
+ }
+}
+
+void ento::registerPointerSubChecker(CheckerManager &mgr) {
+ mgr.registerChecker<PointerSubChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
new file mode 100644
index 000000000000..66cc37278809
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -0,0 +1,483 @@
+//===--- PthreadLockChecker.cpp - Check for locking problems ---*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines PthreadLockChecker, a simple lock -> unlock checker.
+// Also handles XNU locks, which behave similarly enough to share code.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+struct LockState {
+ enum Kind {
+ Destroyed,
+ Locked,
+ Unlocked,
+ UntouchedAndPossiblyDestroyed,
+ UnlockedAndPossiblyDestroyed
+ } K;
+
+private:
+ LockState(Kind K) : K(K) {}
+
+public:
+ static LockState getLocked() { return LockState(Locked); }
+ static LockState getUnlocked() { return LockState(Unlocked); }
+ static LockState getDestroyed() { return LockState(Destroyed); }
+ static LockState getUntouchedAndPossiblyDestroyed() {
+ return LockState(UntouchedAndPossiblyDestroyed);
+ }
+ static LockState getUnlockedAndPossiblyDestroyed() {
+ return LockState(UnlockedAndPossiblyDestroyed);
+ }
+
+ bool operator==(const LockState &X) const {
+ return K == X.K;
+ }
+
+ bool isLocked() const { return K == Locked; }
+ bool isUnlocked() const { return K == Unlocked; }
+ bool isDestroyed() const { return K == Destroyed; }
+ bool isUntouchedAndPossiblyDestroyed() const {
+ return K == UntouchedAndPossiblyDestroyed;
+ }
+ bool isUnlockedAndPossiblyDestroyed() const {
+ return K == UnlockedAndPossiblyDestroyed;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(K);
+ }
+};
+
+class PthreadLockChecker
+ : public Checker<check::PostStmt<CallExpr>, check::DeadSymbols> {
+ mutable std::unique_ptr<BugType> BT_doublelock;
+ mutable std::unique_ptr<BugType> BT_doubleunlock;
+ mutable std::unique_ptr<BugType> BT_destroylock;
+ mutable std::unique_ptr<BugType> BT_initlock;
+ mutable std::unique_ptr<BugType> BT_lor;
+ enum LockingSemantics {
+ NotApplicable = 0,
+ PthreadSemantics,
+ XNUSemantics
+ };
+public:
+ void checkPostStmt(const CallExpr *CE, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ void printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const override;
+
+ void AcquireLock(CheckerContext &C, const CallExpr *CE, SVal lock,
+ bool isTryLock, enum LockingSemantics semantics) const;
+
+ void ReleaseLock(CheckerContext &C, const CallExpr *CE, SVal lock) const;
+ void DestroyLock(CheckerContext &C, const CallExpr *CE, SVal Lock,
+ enum LockingSemantics semantics) const;
+ void InitLock(CheckerContext &C, const CallExpr *CE, SVal Lock) const;
+ void reportUseDestroyedBug(CheckerContext &C, const CallExpr *CE) const;
+ ProgramStateRef resolvePossiblyDestroyedMutex(ProgramStateRef state,
+ const MemRegion *lockR,
+ const SymbolRef *sym) const;
+};
+} // end anonymous namespace
+
+// A stack of locks for tracking lock-unlock order.
+REGISTER_LIST_WITH_PROGRAMSTATE(LockSet, const MemRegion *)
+
+// An entry for tracking lock states.
+REGISTER_MAP_WITH_PROGRAMSTATE(LockMap, const MemRegion *, LockState)
+
+// Return values for unresolved calls to pthread_mutex_destroy().
+REGISTER_MAP_WITH_PROGRAMSTATE(DestroyRetVal, const MemRegion *, SymbolRef)
+
+void PthreadLockChecker::checkPostStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ StringRef FName = C.getCalleeName(CE);
+ if (FName.empty())
+ return;
+
+ if (CE->getNumArgs() != 1 && CE->getNumArgs() != 2)
+ return;
+
+ if (FName == "pthread_mutex_lock" ||
+ FName == "pthread_rwlock_rdlock" ||
+ FName == "pthread_rwlock_wrlock")
+ AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, PthreadSemantics);
+ else if (FName == "lck_mtx_lock" ||
+ FName == "lck_rw_lock_exclusive" ||
+ FName == "lck_rw_lock_shared")
+ AcquireLock(C, CE, C.getSVal(CE->getArg(0)), false, XNUSemantics);
+ else if (FName == "pthread_mutex_trylock" ||
+ FName == "pthread_rwlock_tryrdlock" ||
+ FName == "pthread_rwlock_trywrlock")
+ AcquireLock(C, CE, C.getSVal(CE->getArg(0)),
+ true, PthreadSemantics);
+ else if (FName == "lck_mtx_try_lock" ||
+ FName == "lck_rw_try_lock_exclusive" ||
+ FName == "lck_rw_try_lock_shared")
+ AcquireLock(C, CE, C.getSVal(CE->getArg(0)), true, XNUSemantics);
+ else if (FName == "pthread_mutex_unlock" ||
+ FName == "pthread_rwlock_unlock" ||
+ FName == "lck_mtx_unlock" ||
+ FName == "lck_rw_done")
+ ReleaseLock(C, CE, C.getSVal(CE->getArg(0)));
+ else if (FName == "pthread_mutex_destroy")
+ DestroyLock(C, CE, C.getSVal(CE->getArg(0)), PthreadSemantics);
+ else if (FName == "lck_mtx_destroy")
+ DestroyLock(C, CE, C.getSVal(CE->getArg(0)), XNUSemantics);
+ else if (FName == "pthread_mutex_init")
+ InitLock(C, CE, C.getSVal(CE->getArg(0)));
+}
+
+// When a lock is destroyed, in some semantics(like PthreadSemantics) we are not
+// sure if the destroy call has succeeded or failed, and the lock enters one of
+// the 'possibly destroyed' state. There is a short time frame for the
+// programmer to check the return value to see if the lock was successfully
+// destroyed. Before we model the next operation over that lock, we call this
+// function to see if the return value was checked by now and set the lock state
+// - either to destroyed state or back to its previous state.
+
+// In PthreadSemantics, pthread_mutex_destroy() returns zero if the lock is
+// successfully destroyed and it returns a non-zero value otherwise.
+ProgramStateRef PthreadLockChecker::resolvePossiblyDestroyedMutex(
+ ProgramStateRef state, const MemRegion *lockR, const SymbolRef *sym) const {
+ const LockState *lstate = state->get<LockMap>(lockR);
+ // Existence in DestroyRetVal ensures existence in LockMap.
+ // Existence in Destroyed also ensures that the lock state for lockR is either
+ // UntouchedAndPossiblyDestroyed or UnlockedAndPossiblyDestroyed.
+ assert(lstate->isUntouchedAndPossiblyDestroyed() ||
+ lstate->isUnlockedAndPossiblyDestroyed());
+
+ ConstraintManager &CMgr = state->getConstraintManager();
+ ConditionTruthVal retZero = CMgr.isNull(state, *sym);
+ if (retZero.isConstrainedFalse()) {
+ if (lstate->isUntouchedAndPossiblyDestroyed())
+ state = state->remove<LockMap>(lockR);
+ else if (lstate->isUnlockedAndPossiblyDestroyed())
+ state = state->set<LockMap>(lockR, LockState::getUnlocked());
+ } else
+ state = state->set<LockMap>(lockR, LockState::getDestroyed());
+
+ // Removing the map entry (lockR, sym) from DestroyRetVal as the lock state is
+ // now resolved.
+ state = state->remove<DestroyRetVal>(lockR);
+ return state;
+}
+
+void PthreadLockChecker::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+ LockMapTy LM = State->get<LockMap>();
+ if (!LM.isEmpty()) {
+ Out << Sep << "Mutex states:" << NL;
+ for (auto I : LM) {
+ I.first->dumpToStream(Out);
+ if (I.second.isLocked())
+ Out << ": locked";
+ else if (I.second.isUnlocked())
+ Out << ": unlocked";
+ else if (I.second.isDestroyed())
+ Out << ": destroyed";
+ else if (I.second.isUntouchedAndPossiblyDestroyed())
+ Out << ": not tracked, possibly destroyed";
+ else if (I.second.isUnlockedAndPossiblyDestroyed())
+ Out << ": unlocked, possibly destroyed";
+ Out << NL;
+ }
+ }
+
+ LockSetTy LS = State->get<LockSet>();
+ if (!LS.isEmpty()) {
+ Out << Sep << "Mutex lock order:" << NL;
+ for (auto I: LS) {
+ I->dumpToStream(Out);
+ Out << NL;
+ }
+ }
+
+ // TODO: Dump destroyed mutex symbols?
+}
+
+void PthreadLockChecker::AcquireLock(CheckerContext &C, const CallExpr *CE,
+ SVal lock, bool isTryLock,
+ enum LockingSemantics semantics) const {
+
+ const MemRegion *lockR = lock.getAsRegion();
+ if (!lockR)
+ return;
+
+ ProgramStateRef state = C.getState();
+ const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
+ if (sym)
+ state = resolvePossiblyDestroyedMutex(state, lockR, sym);
+
+ SVal X = C.getSVal(CE);
+ if (X.isUnknownOrUndef())
+ return;
+
+ DefinedSVal retVal = X.castAs<DefinedSVal>();
+
+ if (const LockState *LState = state->get<LockMap>(lockR)) {
+ if (LState->isLocked()) {
+ if (!BT_doublelock)
+ BT_doublelock.reset(new BugType(this, "Double locking",
+ "Lock checker"));
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+ auto report = llvm::make_unique<BugReport>(
+ *BT_doublelock, "This lock has already been acquired", N);
+ report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(std::move(report));
+ return;
+ } else if (LState->isDestroyed()) {
+ reportUseDestroyedBug(C, CE);
+ return;
+ }
+ }
+
+ ProgramStateRef lockSucc = state;
+ if (isTryLock) {
+ // Bifurcate the state, and allow a mode where the lock acquisition fails.
+ ProgramStateRef lockFail;
+ switch (semantics) {
+ case PthreadSemantics:
+ std::tie(lockFail, lockSucc) = state->assume(retVal);
+ break;
+ case XNUSemantics:
+ std::tie(lockSucc, lockFail) = state->assume(retVal);
+ break;
+ default:
+ llvm_unreachable("Unknown tryLock locking semantics");
+ }
+ assert(lockFail && lockSucc);
+ C.addTransition(lockFail);
+
+ } else if (semantics == PthreadSemantics) {
+ // Assume that the return value was 0.
+ lockSucc = state->assume(retVal, false);
+ assert(lockSucc);
+
+ } else {
+ // XNU locking semantics return void on non-try locks
+ assert((semantics == XNUSemantics) && "Unknown locking semantics");
+ lockSucc = state;
+ }
+
+ // Record that the lock was acquired.
+ lockSucc = lockSucc->add<LockSet>(lockR);
+ lockSucc = lockSucc->set<LockMap>(lockR, LockState::getLocked());
+ C.addTransition(lockSucc);
+}
+
+void PthreadLockChecker::ReleaseLock(CheckerContext &C, const CallExpr *CE,
+ SVal lock) const {
+
+ const MemRegion *lockR = lock.getAsRegion();
+ if (!lockR)
+ return;
+
+ ProgramStateRef state = C.getState();
+ const SymbolRef *sym = state->get<DestroyRetVal>(lockR);
+ if (sym)
+ state = resolvePossiblyDestroyedMutex(state, lockR, sym);
+
+ if (const LockState *LState = state->get<LockMap>(lockR)) {
+ if (LState->isUnlocked()) {
+ if (!BT_doubleunlock)
+ BT_doubleunlock.reset(new BugType(this, "Double unlocking",
+ "Lock checker"));
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+ auto Report = llvm::make_unique<BugReport>(
+ *BT_doubleunlock, "This lock has already been unlocked", N);
+ Report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(std::move(Report));
+ return;
+ } else if (LState->isDestroyed()) {
+ reportUseDestroyedBug(C, CE);
+ return;
+ }
+ }
+
+ LockSetTy LS = state->get<LockSet>();
+
+ // FIXME: Better analysis requires IPA for wrappers.
+
+ if (!LS.isEmpty()) {
+ const MemRegion *firstLockR = LS.getHead();
+ if (firstLockR != lockR) {
+ if (!BT_lor)
+ BT_lor.reset(new BugType(this, "Lock order reversal", "Lock checker"));
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+ auto report = llvm::make_unique<BugReport>(
+ *BT_lor, "This was not the most recently acquired lock. Possible "
+ "lock order reversal", N);
+ report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(std::move(report));
+ return;
+ }
+ // Record that the lock was released.
+ state = state->set<LockSet>(LS.getTail());
+ }
+
+ state = state->set<LockMap>(lockR, LockState::getUnlocked());
+ C.addTransition(state);
+}
+
+void PthreadLockChecker::DestroyLock(CheckerContext &C, const CallExpr *CE,
+ SVal Lock,
+ enum LockingSemantics semantics) const {
+
+ const MemRegion *LockR = Lock.getAsRegion();
+ if (!LockR)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
+ if (sym)
+ State = resolvePossiblyDestroyedMutex(State, LockR, sym);
+
+ const LockState *LState = State->get<LockMap>(LockR);
+ // Checking the return value of the destroy method only in the case of
+ // PthreadSemantics
+ if (semantics == PthreadSemantics) {
+ if (!LState || LState->isUnlocked()) {
+ SymbolRef sym = C.getSVal(CE).getAsSymbol();
+ if (!sym) {
+ State = State->remove<LockMap>(LockR);
+ C.addTransition(State);
+ return;
+ }
+ State = State->set<DestroyRetVal>(LockR, sym);
+ if (LState && LState->isUnlocked())
+ State = State->set<LockMap>(
+ LockR, LockState::getUnlockedAndPossiblyDestroyed());
+ else
+ State = State->set<LockMap>(
+ LockR, LockState::getUntouchedAndPossiblyDestroyed());
+ C.addTransition(State);
+ return;
+ }
+ } else {
+ if (!LState || LState->isUnlocked()) {
+ State = State->set<LockMap>(LockR, LockState::getDestroyed());
+ C.addTransition(State);
+ return;
+ }
+ }
+ StringRef Message;
+
+ if (LState->isLocked()) {
+ Message = "This lock is still locked";
+ } else {
+ Message = "This lock has already been destroyed";
+ }
+
+ if (!BT_destroylock)
+ BT_destroylock.reset(new BugType(this, "Destroy invalid lock",
+ "Lock checker"));
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+ auto Report = llvm::make_unique<BugReport>(*BT_destroylock, Message, N);
+ Report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(std::move(Report));
+}
+
+void PthreadLockChecker::InitLock(CheckerContext &C, const CallExpr *CE,
+ SVal Lock) const {
+
+ const MemRegion *LockR = Lock.getAsRegion();
+ if (!LockR)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ const SymbolRef *sym = State->get<DestroyRetVal>(LockR);
+ if (sym)
+ State = resolvePossiblyDestroyedMutex(State, LockR, sym);
+
+ const struct LockState *LState = State->get<LockMap>(LockR);
+ if (!LState || LState->isDestroyed()) {
+ State = State->set<LockMap>(LockR, LockState::getUnlocked());
+ C.addTransition(State);
+ return;
+ }
+
+ StringRef Message;
+
+ if (LState->isLocked()) {
+ Message = "This lock is still being held";
+ } else {
+ Message = "This lock has already been initialized";
+ }
+
+ if (!BT_initlock)
+ BT_initlock.reset(new BugType(this, "Init invalid lock",
+ "Lock checker"));
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+ auto Report = llvm::make_unique<BugReport>(*BT_initlock, Message, N);
+ Report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(std::move(Report));
+}
+
+void PthreadLockChecker::reportUseDestroyedBug(CheckerContext &C,
+ const CallExpr *CE) const {
+ if (!BT_destroylock)
+ BT_destroylock.reset(new BugType(this, "Use destroyed lock",
+ "Lock checker"));
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+ auto Report = llvm::make_unique<BugReport>(
+ *BT_destroylock, "This lock has already been destroyed", N);
+ Report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(std::move(Report));
+}
+
+void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // TODO: Clean LockMap when a mutex region dies.
+
+ DestroyRetValTy TrackedSymbols = State->get<DestroyRetVal>();
+ for (DestroyRetValTy::iterator I = TrackedSymbols.begin(),
+ E = TrackedSymbols.end();
+ I != E; ++I) {
+ const SymbolRef Sym = I->second;
+ const MemRegion *lockR = I->first;
+ bool IsSymDead = SymReaper.isDead(Sym);
+ // Remove the dead symbol from the return value symbols map.
+ if (IsSymDead)
+ State = resolvePossiblyDestroyedMutex(State, lockR, &Sym);
+ }
+ C.addTransition(State);
+}
+
+void ento::registerPthreadLockChecker(CheckerManager &mgr) {
+ mgr.registerChecker<PthreadLockChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
new file mode 100644
index 000000000000..0652af856643
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
@@ -0,0 +1,1547 @@
+//==-- RetainCountChecker.cpp - Checks for leaks and other issues -*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the methods for RetainCountChecker, which implements
+// a reference count checker for Core Foundation and Cocoa on (Mac OS X).
+//
+//===----------------------------------------------------------------------===//
+
+#include "RetainCountChecker.h"
+
+using namespace clang;
+using namespace ento;
+using namespace retaincountchecker;
+using llvm::StrInStrNoCase;
+
+REGISTER_MAP_WITH_PROGRAMSTATE(RefBindings, SymbolRef, RefVal)
+
+namespace clang {
+namespace ento {
+namespace retaincountchecker {
+
+const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym) {
+ return State->get<RefBindings>(Sym);
+}
+
+ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym,
+ RefVal Val) {
+ assert(Sym != nullptr);
+ return State->set<RefBindings>(Sym, Val);
+}
+
+ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) {
+ return State->remove<RefBindings>(Sym);
+}
+
+class UseAfterRelease : public RefCountBug {
+public:
+ UseAfterRelease(const CheckerBase *checker)
+ : RefCountBug(checker, "Use-after-release") {}
+
+ const char *getDescription() const override {
+ return "Reference-counted object is used after it is released";
+ }
+};
+
+class BadRelease : public RefCountBug {
+public:
+ BadRelease(const CheckerBase *checker) : RefCountBug(checker, "Bad release") {}
+
+ const char *getDescription() const override {
+ return "Incorrect decrement of the reference count of an object that is "
+ "not owned at this point by the caller";
+ }
+};
+
+class DeallocNotOwned : public RefCountBug {
+public:
+ DeallocNotOwned(const CheckerBase *checker)
+ : RefCountBug(checker, "-dealloc sent to non-exclusively owned object") {}
+
+ const char *getDescription() const override {
+ return "-dealloc sent to object that may be referenced elsewhere";
+ }
+};
+
+class OverAutorelease : public RefCountBug {
+public:
+ OverAutorelease(const CheckerBase *checker)
+ : RefCountBug(checker, "Object autoreleased too many times") {}
+
+ const char *getDescription() const override {
+ return "Object autoreleased too many times";
+ }
+};
+
+class ReturnedNotOwnedForOwned : public RefCountBug {
+public:
+ ReturnedNotOwnedForOwned(const CheckerBase *checker)
+ : RefCountBug(checker, "Method should return an owned object") {}
+
+ const char *getDescription() const override {
+ return "Object with a +0 retain count returned to caller where a +1 "
+ "(owning) retain count is expected";
+ }
+};
+
+class Leak : public RefCountBug {
+public:
+ Leak(const CheckerBase *checker, StringRef name) : RefCountBug(checker, name) {
+ // Leaks should not be reported if they are post-dominated by a sink.
+ setSuppressOnSink(true);
+ }
+
+ const char *getDescription() const override { return ""; }
+
+ bool isLeak() const override { return true; }
+};
+
+} // end namespace retaincountchecker
+} // end namespace ento
+} // end namespace clang
+
+void RefVal::print(raw_ostream &Out) const {
+ if (!T.isNull())
+ Out << "Tracked " << T.getAsString() << " | ";
+
+ switch (getKind()) {
+ default: llvm_unreachable("Invalid RefVal kind");
+ case Owned: {
+ Out << "Owned";
+ unsigned cnt = getCount();
+ if (cnt) Out << " (+ " << cnt << ")";
+ break;
+ }
+
+ case NotOwned: {
+ Out << "NotOwned";
+ unsigned cnt = getCount();
+ if (cnt) Out << " (+ " << cnt << ")";
+ break;
+ }
+
+ case ReturnedOwned: {
+ Out << "ReturnedOwned";
+ unsigned cnt = getCount();
+ if (cnt) Out << " (+ " << cnt << ")";
+ break;
+ }
+
+ case ReturnedNotOwned: {
+ Out << "ReturnedNotOwned";
+ unsigned cnt = getCount();
+ if (cnt) Out << " (+ " << cnt << ")";
+ break;
+ }
+
+ case Released:
+ Out << "Released";
+ break;
+
+ case ErrorDeallocNotOwned:
+ Out << "-dealloc (not-owned)";
+ break;
+
+ case ErrorLeak:
+ Out << "Leaked";
+ break;
+
+ case ErrorLeakReturned:
+ Out << "Leaked (Bad naming)";
+ break;
+
+ case ErrorUseAfterRelease:
+ Out << "Use-After-Release [ERROR]";
+ break;
+
+ case ErrorReleaseNotOwned:
+ Out << "Release of Not-Owned [ERROR]";
+ break;
+
+ case RefVal::ErrorOverAutorelease:
+ Out << "Over-autoreleased";
+ break;
+
+ case RefVal::ErrorReturnedNotOwned:
+ Out << "Non-owned object returned instead of owned";
+ break;
+ }
+
+ switch (getIvarAccessHistory()) {
+ case IvarAccessHistory::None:
+ break;
+ case IvarAccessHistory::AccessedDirectly:
+ Out << " [direct ivar access]";
+ break;
+ case IvarAccessHistory::ReleasedAfterDirectAccess:
+ Out << " [released after direct ivar access]";
+ }
+
+ if (ACnt) {
+ Out << " [autorelease -" << ACnt << ']';
+ }
+}
+
+namespace {
+class StopTrackingCallback final : public SymbolVisitor {
+ ProgramStateRef state;
+public:
+ StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {}
+ ProgramStateRef getState() const { return state; }
+
+ bool VisitSymbol(SymbolRef sym) override {
+ state = state->remove<RefBindings>(sym);
+ return true;
+ }
+};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Handle statements that may have an effect on refcounts.
+//===----------------------------------------------------------------------===//
+
+void RetainCountChecker::checkPostStmt(const BlockExpr *BE,
+ CheckerContext &C) const {
+
+ // Scan the BlockDecRefExprs for any object the retain count checker
+ // may be tracking.
+ if (!BE->getBlockDecl()->hasCaptures())
+ return;
+
+ ProgramStateRef state = C.getState();
+ auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
+
+ BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
+ E = R->referenced_vars_end();
+
+ if (I == E)
+ return;
+
+ // FIXME: For now we invalidate the tracking of all symbols passed to blocks
+ // via captured variables, even though captured variables result in a copy
+ // and in implicit increment/decrement of a retain count.
+ SmallVector<const MemRegion*, 10> Regions;
+ const LocationContext *LC = C.getLocationContext();
+ MemRegionManager &MemMgr = C.getSValBuilder().getRegionManager();
+
+ for ( ; I != E; ++I) {
+ const VarRegion *VR = I.getCapturedRegion();
+ if (VR->getSuperRegion() == R) {
+ VR = MemMgr.getVarRegion(VR->getDecl(), LC);
+ }
+ Regions.push_back(VR);
+ }
+
+ state = state->scanReachableSymbols<StopTrackingCallback>(Regions).getState();
+ C.addTransition(state);
+}
+
+void RetainCountChecker::checkPostStmt(const CastExpr *CE,
+ CheckerContext &C) const {
+ const ObjCBridgedCastExpr *BE = dyn_cast<ObjCBridgedCastExpr>(CE);
+ if (!BE)
+ return;
+
+ ArgEffect AE = ArgEffect(IncRef, ObjKind::ObjC);
+
+ switch (BE->getBridgeKind()) {
+ case OBC_Bridge:
+ // Do nothing.
+ return;
+ case OBC_BridgeRetained:
+ AE = AE.withKind(IncRef);
+ break;
+ case OBC_BridgeTransfer:
+ AE = AE.withKind(DecRefBridgedTransferred);
+ break;
+ }
+
+ ProgramStateRef state = C.getState();
+ SymbolRef Sym = C.getSVal(CE).getAsLocSymbol();
+ if (!Sym)
+ return;
+ const RefVal* T = getRefBinding(state, Sym);
+ if (!T)
+ return;
+
+ RefVal::Kind hasErr = (RefVal::Kind) 0;
+ state = updateSymbol(state, Sym, *T, AE, hasErr, C);
+
+ if (hasErr) {
+ // FIXME: If we get an error during a bridge cast, should we report it?
+ return;
+ }
+
+ C.addTransition(state);
+}
+
+void RetainCountChecker::processObjCLiterals(CheckerContext &C,
+ const Expr *Ex) const {
+ ProgramStateRef state = C.getState();
+ const ExplodedNode *pred = C.getPredecessor();
+ for (const Stmt *Child : Ex->children()) {
+ SVal V = pred->getSVal(Child);
+ if (SymbolRef sym = V.getAsSymbol())
+ if (const RefVal* T = getRefBinding(state, sym)) {
+ RefVal::Kind hasErr = (RefVal::Kind) 0;
+ state = updateSymbol(state, sym, *T,
+ ArgEffect(MayEscape, ObjKind::ObjC), hasErr, C);
+ if (hasErr) {
+ processNonLeakError(state, Child->getSourceRange(), hasErr, sym, C);
+ return;
+ }
+ }
+ }
+
+ // Return the object as autoreleased.
+ // RetEffect RE = RetEffect::MakeNotOwned(ObjKind::ObjC);
+ if (SymbolRef sym =
+ state->getSVal(Ex, pred->getLocationContext()).getAsSymbol()) {
+ QualType ResultTy = Ex->getType();
+ state = setRefBinding(state, sym,
+ RefVal::makeNotOwned(ObjKind::ObjC, ResultTy));
+ }
+
+ C.addTransition(state);
+}
+
+void RetainCountChecker::checkPostStmt(const ObjCArrayLiteral *AL,
+ CheckerContext &C) const {
+ // Apply the 'MayEscape' to all values.
+ processObjCLiterals(C, AL);
+}
+
+void RetainCountChecker::checkPostStmt(const ObjCDictionaryLiteral *DL,
+ CheckerContext &C) const {
+ // Apply the 'MayEscape' to all keys and values.
+ processObjCLiterals(C, DL);
+}
+
+void RetainCountChecker::checkPostStmt(const ObjCBoxedExpr *Ex,
+ CheckerContext &C) const {
+ const ExplodedNode *Pred = C.getPredecessor();
+ ProgramStateRef State = Pred->getState();
+
+ if (SymbolRef Sym = Pred->getSVal(Ex).getAsSymbol()) {
+ QualType ResultTy = Ex->getType();
+ State = setRefBinding(State, Sym,
+ RefVal::makeNotOwned(ObjKind::ObjC, ResultTy));
+ }
+
+ C.addTransition(State);
+}
+
+void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
+ CheckerContext &C) const {
+ Optional<Loc> IVarLoc = C.getSVal(IRE).getAs<Loc>();
+ if (!IVarLoc)
+ return;
+
+ ProgramStateRef State = C.getState();
+ SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol();
+ if (!Sym || !dyn_cast_or_null<ObjCIvarRegion>(Sym->getOriginRegion()))
+ return;
+
+ // Accessing an ivar directly is unusual. If we've done that, be more
+ // forgiving about what the surrounding code is allowed to do.
+
+ QualType Ty = Sym->getType();
+ ObjKind Kind;
+ if (Ty->isObjCRetainableType())
+ Kind = ObjKind::ObjC;
+ else if (coreFoundation::isCFObjectRef(Ty))
+ Kind = ObjKind::CF;
+ else
+ return;
+
+ // If the value is already known to be nil, don't bother tracking it.
+ ConstraintManager &CMgr = State->getConstraintManager();
+ if (CMgr.isNull(State, Sym).isConstrainedTrue())
+ return;
+
+ if (const RefVal *RV = getRefBinding(State, Sym)) {
+ // If we've seen this symbol before, or we're only seeing it now because
+ // of something the analyzer has synthesized, don't do anything.
+ if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None ||
+ isSynthesizedAccessor(C.getStackFrame())) {
+ return;
+ }
+
+ // Note that this value has been loaded from an ivar.
+ C.addTransition(setRefBinding(State, Sym, RV->withIvarAccess()));
+ return;
+ }
+
+ RefVal PlusZero = RefVal::makeNotOwned(Kind, Ty);
+
+ // In a synthesized accessor, the effective retain count is +0.
+ if (isSynthesizedAccessor(C.getStackFrame())) {
+ C.addTransition(setRefBinding(State, Sym, PlusZero));
+ return;
+ }
+
+ State = setRefBinding(State, Sym, PlusZero.withIvarAccess());
+ C.addTransition(State);
+}
+
+void RetainCountChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ RetainSummaryManager &Summaries = getSummaryManager(C);
+
+ // Leave null if no receiver.
+ QualType ReceiverType;
+ if (const auto *MC = dyn_cast<ObjCMethodCall>(&Call)) {
+ if (MC->isInstanceMessage()) {
+ SVal ReceiverV = MC->getReceiverSVal();
+ if (SymbolRef Sym = ReceiverV.getAsLocSymbol())
+ if (const RefVal *T = getRefBinding(C.getState(), Sym))
+ ReceiverType = T->getType();
+ }
+ }
+
+ const RetainSummary *Summ = Summaries.getSummary(Call, ReceiverType);
+
+ if (C.wasInlined) {
+ processSummaryOfInlined(*Summ, Call, C);
+ return;
+ }
+ checkSummary(*Summ, Call, C);
+}
+
+RefCountBug *
+RetainCountChecker::getLeakWithinFunctionBug(const LangOptions &LOpts) const {
+ if (!leakWithinFunction)
+ leakWithinFunction.reset(new Leak(this, "Leak"));
+ return leakWithinFunction.get();
+}
+
+RefCountBug *
+RetainCountChecker::getLeakAtReturnBug(const LangOptions &LOpts) const {
+ if (!leakAtReturn)
+ leakAtReturn.reset(new Leak(this, "Leak of returned object"));
+ return leakAtReturn.get();
+}
+
+/// GetReturnType - Used to get the return type of a message expression or
+/// function call with the intention of affixing that type to a tracked symbol.
+/// While the return type can be queried directly from RetEx, when
+/// invoking class methods we augment to the return type to be that of
+/// a pointer to the class (as opposed it just being id).
+// FIXME: We may be able to do this with related result types instead.
+// This function is probably overestimating.
+static QualType GetReturnType(const Expr *RetE, ASTContext &Ctx) {
+ QualType RetTy = RetE->getType();
+ // If RetE is not a message expression just return its type.
+ // If RetE is a message expression, return its types if it is something
+ /// more specific than id.
+ if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(RetE))
+ if (const ObjCObjectPointerType *PT = RetTy->getAs<ObjCObjectPointerType>())
+ if (PT->isObjCQualifiedIdType() || PT->isObjCIdType() ||
+ PT->isObjCClassType()) {
+ // At this point we know the return type of the message expression is
+ // id, id<...>, or Class. If we have an ObjCInterfaceDecl, we know this
+ // is a call to a class method whose type we can resolve. In such
+ // cases, promote the return type to XXX* (where XXX is the class).
+ const ObjCInterfaceDecl *D = ME->getReceiverInterface();
+ return !D ? RetTy :
+ Ctx.getObjCObjectPointerType(Ctx.getObjCInterfaceType(D));
+ }
+
+ return RetTy;
+}
+
+static Optional<RefVal> refValFromRetEffect(RetEffect RE,
+ QualType ResultTy) {
+ if (RE.isOwned()) {
+ return RefVal::makeOwned(RE.getObjKind(), ResultTy);
+ } else if (RE.notOwned()) {
+ return RefVal::makeNotOwned(RE.getObjKind(), ResultTy);
+ }
+
+ return None;
+}
+
+static bool isPointerToObject(QualType QT) {
+ QualType PT = QT->getPointeeType();
+ if (!PT.isNull())
+ if (PT->getAsCXXRecordDecl())
+ return true;
+ return false;
+}
+
+/// Whether the tracked value should be escaped on a given call.
+/// OSObjects are escaped when passed to void * / etc.
+static bool shouldEscapeOSArgumentOnCall(const CallEvent &CE, unsigned ArgIdx,
+ const RefVal *TrackedValue) {
+ if (TrackedValue->getObjKind() != ObjKind::OS)
+ return false;
+ if (ArgIdx >= CE.parameters().size())
+ return false;
+ return !isPointerToObject(CE.parameters()[ArgIdx]->getType());
+}
+
+// We don't always get the exact modeling of the function with regards to the
+// retain count checker even when the function is inlined. For example, we need
+// to stop tracking the symbols which were marked with StopTrackingHard.
+void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ,
+ const CallEvent &CallOrMsg,
+ CheckerContext &C) const {
+ ProgramStateRef state = C.getState();
+
+ // Evaluate the effect of the arguments.
+ for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) {
+ SVal V = CallOrMsg.getArgSVal(idx);
+
+ if (SymbolRef Sym = V.getAsLocSymbol()) {
+ bool ShouldRemoveBinding = Summ.getArg(idx).getKind() == StopTrackingHard;
+ if (const RefVal *T = getRefBinding(state, Sym))
+ if (shouldEscapeOSArgumentOnCall(CallOrMsg, idx, T))
+ ShouldRemoveBinding = true;
+
+ if (ShouldRemoveBinding)
+ state = removeRefBinding(state, Sym);
+ }
+ }
+
+ // Evaluate the effect on the message receiver.
+ if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg)) {
+ if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {
+ if (Summ.getReceiverEffect().getKind() == StopTrackingHard) {
+ state = removeRefBinding(state, Sym);
+ }
+ }
+ }
+
+ // Consult the summary for the return value.
+ RetEffect RE = Summ.getRetEffect();
+
+ if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) {
+ if (RE.getKind() == RetEffect::NoRetHard)
+ state = removeRefBinding(state, Sym);
+ }
+
+ C.addTransition(state);
+}
+
+static bool shouldEscapeRegion(const MemRegion *R) {
+
+ // We do not currently model what happens when a symbol is
+ // assigned to a struct field, so be conservative here and let the symbol
+ // go. TODO: This could definitely be improved upon.
+ return !R->hasStackStorage() || !isa<VarRegion>(R);
+}
+
+static SmallVector<ProgramStateRef, 2>
+updateOutParameters(ProgramStateRef State, const RetainSummary &Summ,
+ const CallEvent &CE) {
+
+ SVal L = CE.getReturnValue();
+
+ // Splitting is required to support out parameters,
+ // as out parameters might be created only on the "success" branch.
+ // We want to avoid eagerly splitting unless out parameters are actually
+ // needed.
+ bool SplitNecessary = false;
+ for (auto &P : Summ.getArgEffects())
+ if (P.second.getKind() == RetainedOutParameterOnNonZero ||
+ P.second.getKind() == RetainedOutParameterOnZero)
+ SplitNecessary = true;
+
+ ProgramStateRef AssumeNonZeroReturn = State;
+ ProgramStateRef AssumeZeroReturn = State;
+
+ if (SplitNecessary) {
+ if (auto DL = L.getAs<DefinedOrUnknownSVal>()) {
+ AssumeNonZeroReturn = AssumeNonZeroReturn->assume(*DL, true);
+ AssumeZeroReturn = AssumeZeroReturn->assume(*DL, false);
+ }
+ }
+
+ for (unsigned idx = 0, e = CE.getNumArgs(); idx != e; ++idx) {
+ SVal ArgVal = CE.getArgSVal(idx);
+ ArgEffect AE = Summ.getArg(idx);
+
+ auto *ArgRegion = dyn_cast_or_null<TypedValueRegion>(ArgVal.getAsRegion());
+ if (!ArgRegion)
+ continue;
+
+ QualType PointeeTy = ArgRegion->getValueType();
+ SVal PointeeVal = State->getSVal(ArgRegion);
+ SymbolRef Pointee = PointeeVal.getAsLocSymbol();
+ if (!Pointee)
+ continue;
+
+ if (shouldEscapeRegion(ArgRegion))
+ continue;
+
+ auto makeNotOwnedParameter = [&](ProgramStateRef St) {
+ return setRefBinding(St, Pointee,
+ RefVal::makeNotOwned(AE.getObjKind(), PointeeTy));
+ };
+ auto makeOwnedParameter = [&](ProgramStateRef St) {
+ return setRefBinding(St, Pointee,
+ RefVal::makeOwned(ObjKind::OS, PointeeTy));
+ };
+
+ switch (AE.getKind()) {
+ case UnretainedOutParameter:
+ AssumeNonZeroReturn = makeNotOwnedParameter(AssumeNonZeroReturn);
+ AssumeZeroReturn = makeNotOwnedParameter(AssumeZeroReturn);
+ break;
+ case RetainedOutParameter:
+ AssumeNonZeroReturn = makeOwnedParameter(AssumeNonZeroReturn);
+ AssumeZeroReturn = makeOwnedParameter(AssumeZeroReturn);
+ break;
+ case RetainedOutParameterOnNonZero:
+ AssumeNonZeroReturn = makeOwnedParameter(AssumeNonZeroReturn);
+ break;
+ case RetainedOutParameterOnZero:
+ AssumeZeroReturn = makeOwnedParameter(AssumeZeroReturn);
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (SplitNecessary) {
+ return {AssumeNonZeroReturn, AssumeZeroReturn};
+ } else {
+ assert(AssumeZeroReturn == AssumeNonZeroReturn);
+ return {AssumeZeroReturn};
+ }
+}
+
+void RetainCountChecker::checkSummary(const RetainSummary &Summ,
+ const CallEvent &CallOrMsg,
+ CheckerContext &C) const {
+ ProgramStateRef state = C.getState();
+
+ // Evaluate the effect of the arguments.
+ RefVal::Kind hasErr = (RefVal::Kind) 0;
+ SourceRange ErrorRange;
+ SymbolRef ErrorSym = nullptr;
+
+ // Helper tag for providing diagnostics: indicate whether dealloc was sent
+ // at this location.
+ static CheckerProgramPointTag DeallocSentTag(this, DeallocTagDescription);
+ bool DeallocSent = false;
+
+ for (unsigned idx = 0, e = CallOrMsg.getNumArgs(); idx != e; ++idx) {
+ SVal V = CallOrMsg.getArgSVal(idx);
+
+ ArgEffect Effect = Summ.getArg(idx);
+ if (SymbolRef Sym = V.getAsLocSymbol()) {
+ if (const RefVal *T = getRefBinding(state, Sym)) {
+
+ if (shouldEscapeOSArgumentOnCall(CallOrMsg, idx, T))
+ Effect = ArgEffect(StopTrackingHard, ObjKind::OS);
+
+ state = updateSymbol(state, Sym, *T, Effect, hasErr, C);
+ if (hasErr) {
+ ErrorRange = CallOrMsg.getArgSourceRange(idx);
+ ErrorSym = Sym;
+ break;
+ } else if (Effect.getKind() == Dealloc) {
+ DeallocSent = true;
+ }
+ }
+ }
+ }
+
+ // Evaluate the effect on the message receiver / `this` argument.
+ bool ReceiverIsTracked = false;
+ if (!hasErr) {
+ if (const auto *MsgInvocation = dyn_cast<ObjCMethodCall>(&CallOrMsg)) {
+ if (SymbolRef Sym = MsgInvocation->getReceiverSVal().getAsLocSymbol()) {
+ if (const RefVal *T = getRefBinding(state, Sym)) {
+ ReceiverIsTracked = true;
+ state = updateSymbol(state, Sym, *T,
+ Summ.getReceiverEffect(), hasErr, C);
+ if (hasErr) {
+ ErrorRange = MsgInvocation->getOriginExpr()->getReceiverRange();
+ ErrorSym = Sym;
+ } else if (Summ.getReceiverEffect().getKind() == Dealloc) {
+ DeallocSent = true;
+ }
+ }
+ }
+ } else if (const auto *MCall = dyn_cast<CXXMemberCall>(&CallOrMsg)) {
+ if (SymbolRef Sym = MCall->getCXXThisVal().getAsLocSymbol()) {
+ if (const RefVal *T = getRefBinding(state, Sym)) {
+ state = updateSymbol(state, Sym, *T, Summ.getThisEffect(),
+ hasErr, C);
+ if (hasErr) {
+ ErrorRange = MCall->getOriginExpr()->getSourceRange();
+ ErrorSym = Sym;
+ }
+ }
+ }
+ }
+ }
+
+ // Process any errors.
+ if (hasErr) {
+ processNonLeakError(state, ErrorRange, hasErr, ErrorSym, C);
+ return;
+ }
+
+ // Consult the summary for the return value.
+ RetEffect RE = Summ.getRetEffect();
+
+ if (RE.getKind() == RetEffect::OwnedWhenTrackedReceiver) {
+ if (ReceiverIsTracked)
+ RE = getSummaryManager(C).getObjAllocRetEffect();
+ else
+ RE = RetEffect::MakeNoRet();
+ }
+
+ if (SymbolRef Sym = CallOrMsg.getReturnValue().getAsSymbol()) {
+ QualType ResultTy = CallOrMsg.getResultType();
+ if (RE.notOwned()) {
+ const Expr *Ex = CallOrMsg.getOriginExpr();
+ assert(Ex);
+ ResultTy = GetReturnType(Ex, C.getASTContext());
+ }
+ if (Optional<RefVal> updatedRefVal = refValFromRetEffect(RE, ResultTy))
+ state = setRefBinding(state, Sym, *updatedRefVal);
+ }
+
+ SmallVector<ProgramStateRef, 2> Out =
+ updateOutParameters(state, Summ, CallOrMsg);
+
+ for (ProgramStateRef St : Out) {
+ if (DeallocSent) {
+ C.addTransition(St, C.getPredecessor(), &DeallocSentTag);
+ } else {
+ C.addTransition(St);
+ }
+ }
+}
+
+ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state,
+ SymbolRef sym, RefVal V,
+ ArgEffect AE,
+ RefVal::Kind &hasErr,
+ CheckerContext &C) const {
+ bool IgnoreRetainMsg = (bool)C.getASTContext().getLangOpts().ObjCAutoRefCount;
+ if (AE.getObjKind() == ObjKind::ObjC && IgnoreRetainMsg) {
+ switch (AE.getKind()) {
+ default:
+ break;
+ case IncRef:
+ AE = AE.withKind(DoNothing);
+ break;
+ case DecRef:
+ AE = AE.withKind(DoNothing);
+ break;
+ case DecRefAndStopTrackingHard:
+ AE = AE.withKind(StopTracking);
+ break;
+ }
+ }
+
+ // Handle all use-after-releases.
+ if (V.getKind() == RefVal::Released) {
+ V = V ^ RefVal::ErrorUseAfterRelease;
+ hasErr = V.getKind();
+ return setRefBinding(state, sym, V);
+ }
+
+ switch (AE.getKind()) {
+ case UnretainedOutParameter:
+ case RetainedOutParameter:
+ case RetainedOutParameterOnZero:
+ case RetainedOutParameterOnNonZero:
+ llvm_unreachable("Applies to pointer-to-pointer parameters, which should "
+ "not have ref state.");
+
+ case Dealloc: // NB. we only need to add a note in a non-error case.
+ switch (V.getKind()) {
+ default:
+ llvm_unreachable("Invalid RefVal state for an explicit dealloc.");
+ case RefVal::Owned:
+ // The object immediately transitions to the released state.
+ V = V ^ RefVal::Released;
+ V.clearCounts();
+ return setRefBinding(state, sym, V);
+ case RefVal::NotOwned:
+ V = V ^ RefVal::ErrorDeallocNotOwned;
+ hasErr = V.getKind();
+ break;
+ }
+ break;
+
+ case MayEscape:
+ if (V.getKind() == RefVal::Owned) {
+ V = V ^ RefVal::NotOwned;
+ break;
+ }
+
+ LLVM_FALLTHROUGH;
+
+ case DoNothing:
+ return state;
+
+ case Autorelease:
+ // Update the autorelease counts.
+ V = V.autorelease();
+ break;
+
+ case StopTracking:
+ case StopTrackingHard:
+ return removeRefBinding(state, sym);
+
+ case IncRef:
+ switch (V.getKind()) {
+ default:
+ llvm_unreachable("Invalid RefVal state for a retain.");
+ case RefVal::Owned:
+ case RefVal::NotOwned:
+ V = V + 1;
+ break;
+ }
+ break;
+
+ case DecRef:
+ case DecRefBridgedTransferred:
+ case DecRefAndStopTrackingHard:
+ switch (V.getKind()) {
+ default:
+ // case 'RefVal::Released' handled above.
+ llvm_unreachable("Invalid RefVal state for a release.");
+
+ case RefVal::Owned:
+ assert(V.getCount() > 0);
+ if (V.getCount() == 1) {
+ if (AE.getKind() == DecRefBridgedTransferred ||
+ V.getIvarAccessHistory() ==
+ RefVal::IvarAccessHistory::AccessedDirectly)
+ V = V ^ RefVal::NotOwned;
+ else
+ V = V ^ RefVal::Released;
+ } else if (AE.getKind() == DecRefAndStopTrackingHard) {
+ return removeRefBinding(state, sym);
+ }
+
+ V = V - 1;
+ break;
+
+ case RefVal::NotOwned:
+ if (V.getCount() > 0) {
+ if (AE.getKind() == DecRefAndStopTrackingHard)
+ return removeRefBinding(state, sym);
+ V = V - 1;
+ } else if (V.getIvarAccessHistory() ==
+ RefVal::IvarAccessHistory::AccessedDirectly) {
+ // Assume that the instance variable was holding on the object at
+ // +1, and we just didn't know.
+ if (AE.getKind() == DecRefAndStopTrackingHard)
+ return removeRefBinding(state, sym);
+ V = V.releaseViaIvar() ^ RefVal::Released;
+ } else {
+ V = V ^ RefVal::ErrorReleaseNotOwned;
+ hasErr = V.getKind();
+ }
+ break;
+ }
+ break;
+ }
+ return setRefBinding(state, sym, V);
+}
+
+void RetainCountChecker::processNonLeakError(ProgramStateRef St,
+ SourceRange ErrorRange,
+ RefVal::Kind ErrorKind,
+ SymbolRef Sym,
+ CheckerContext &C) const {
+ // HACK: Ignore retain-count issues on values accessed through ivars,
+ // because of cases like this:
+ // [_contentView retain];
+ // [_contentView removeFromSuperview];
+ // [self addSubview:_contentView]; // invalidates 'self'
+ // [_contentView release];
+ if (const RefVal *RV = getRefBinding(St, Sym))
+ if (RV->getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
+ return;
+
+ ExplodedNode *N = C.generateErrorNode(St);
+ if (!N)
+ return;
+
+ RefCountBug *BT;
+ switch (ErrorKind) {
+ default:
+ llvm_unreachable("Unhandled error.");
+ case RefVal::ErrorUseAfterRelease:
+ if (!useAfterRelease)
+ useAfterRelease.reset(new UseAfterRelease(this));
+ BT = useAfterRelease.get();
+ break;
+ case RefVal::ErrorReleaseNotOwned:
+ if (!releaseNotOwned)
+ releaseNotOwned.reset(new BadRelease(this));
+ BT = releaseNotOwned.get();
+ break;
+ case RefVal::ErrorDeallocNotOwned:
+ if (!deallocNotOwned)
+ deallocNotOwned.reset(new DeallocNotOwned(this));
+ BT = deallocNotOwned.get();
+ break;
+ }
+
+ assert(BT);
+ auto report = llvm::make_unique<RefCountReport>(
+ *BT, C.getASTContext().getLangOpts(), N, Sym);
+ report->addRange(ErrorRange);
+ C.emitReport(std::move(report));
+}
+
+//===----------------------------------------------------------------------===//
+// Handle the return values of retain-count-related functions.
+//===----------------------------------------------------------------------===//
+
+bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
+ // Get the callee. We're only interested in simple C functions.
+ ProgramStateRef state = C.getState();
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD)
+ return false;
+
+ RetainSummaryManager &SmrMgr = getSummaryManager(C);
+ QualType ResultTy = CE->getCallReturnType(C.getASTContext());
+
+ // See if the function has 'rc_ownership_trusted_implementation'
+ // annotate attribute. If it does, we will not inline it.
+ bool hasTrustedImplementationAnnotation = false;
+
+ const LocationContext *LCtx = C.getLocationContext();
+
+ using BehaviorSummary = RetainSummaryManager::BehaviorSummary;
+ Optional<BehaviorSummary> BSmr =
+ SmrMgr.canEval(CE, FD, hasTrustedImplementationAnnotation);
+
+ // See if it's one of the specific functions we know how to eval.
+ if (!BSmr)
+ return false;
+
+ // Bind the return value.
+ if (BSmr == BehaviorSummary::Identity ||
+ BSmr == BehaviorSummary::IdentityOrZero) {
+ SVal RetVal = state->getSVal(CE->getArg(0), LCtx);
+
+ // If the receiver is unknown or the function has
+ // 'rc_ownership_trusted_implementation' annotate attribute, conjure a
+ // return value.
+ if (RetVal.isUnknown() ||
+ (hasTrustedImplementationAnnotation && !ResultTy.isNull())) {
+ SValBuilder &SVB = C.getSValBuilder();
+ RetVal =
+ SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount());
+ }
+ state = state->BindExpr(CE, LCtx, RetVal, /*Invalidate=*/false);
+
+ if (BSmr == BehaviorSummary::IdentityOrZero) {
+ // Add a branch where the output is zero.
+ ProgramStateRef NullOutputState = C.getState();
+
+ // Assume that output is zero on the other branch.
+ NullOutputState = NullOutputState->BindExpr(
+ CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false);
+
+ C.addTransition(NullOutputState);
+
+ // And on the original branch assume that both input and
+ // output are non-zero.
+ if (auto L = RetVal.getAs<DefinedOrUnknownSVal>())
+ state = state->assume(*L, /*Assumption=*/true);
+
+ }
+ }
+
+ C.addTransition(state);
+ return true;
+}
+
+ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S,
+ CheckerContext &C) const {
+ ExplodedNode *Pred = C.getPredecessor();
+
+ // Only adjust the reference count if this is the top-level call frame,
+ // and not the result of inlining. In the future, we should do
+ // better checking even for inlined calls, and see if they match
+ // with their expected semantics (e.g., the method should return a retained
+ // object, etc.).
+ if (!C.inTopFrame())
+ return Pred;
+
+ if (!S)
+ return Pred;
+
+ const Expr *RetE = S->getRetValue();
+ if (!RetE)
+ return Pred;
+
+ ProgramStateRef state = C.getState();
+ SymbolRef Sym =
+ state->getSValAsScalarOrLoc(RetE, C.getLocationContext()).getAsLocSymbol();
+ if (!Sym)
+ return Pred;
+
+ // Get the reference count binding (if any).
+ const RefVal *T = getRefBinding(state, Sym);
+ if (!T)
+ return Pred;
+
+ // Change the reference count.
+ RefVal X = *T;
+
+ switch (X.getKind()) {
+ case RefVal::Owned: {
+ unsigned cnt = X.getCount();
+ assert(cnt > 0);
+ X.setCount(cnt - 1);
+ X = X ^ RefVal::ReturnedOwned;
+ break;
+ }
+
+ case RefVal::NotOwned: {
+ unsigned cnt = X.getCount();
+ if (cnt) {
+ X.setCount(cnt - 1);
+ X = X ^ RefVal::ReturnedOwned;
+ } else {
+ X = X ^ RefVal::ReturnedNotOwned;
+ }
+ break;
+ }
+
+ default:
+ return Pred;
+ }
+
+ // Update the binding.
+ state = setRefBinding(state, Sym, X);
+ Pred = C.addTransition(state);
+
+ // At this point we have updated the state properly.
+ // Everything after this is merely checking to see if the return value has
+ // been over- or under-retained.
+
+ // Did we cache out?
+ if (!Pred)
+ return nullptr;
+
+ // Update the autorelease counts.
+ static CheckerProgramPointTag AutoreleaseTag(this, "Autorelease");
+ state = handleAutoreleaseCounts(state, Pred, &AutoreleaseTag, C, Sym, X, S);
+
+ // Have we generated a sink node?
+ if (!state)
+ return nullptr;
+
+ // Get the updated binding.
+ T = getRefBinding(state, Sym);
+ assert(T);
+ X = *T;
+
+ // Consult the summary of the enclosing method.
+ RetainSummaryManager &Summaries = getSummaryManager(C);
+ const Decl *CD = &Pred->getCodeDecl();
+ RetEffect RE = RetEffect::MakeNoRet();
+
+ // FIXME: What is the convention for blocks? Is there one?
+ if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) {
+ const RetainSummary *Summ = Summaries.getMethodSummary(MD);
+ RE = Summ->getRetEffect();
+ } else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) {
+ if (!isa<CXXMethodDecl>(FD)) {
+ const RetainSummary *Summ = Summaries.getFunctionSummary(FD);
+ RE = Summ->getRetEffect();
+ }
+ }
+
+ return checkReturnWithRetEffect(S, C, Pred, RE, X, Sym, state);
+}
+
+ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
+ CheckerContext &C,
+ ExplodedNode *Pred,
+ RetEffect RE, RefVal X,
+ SymbolRef Sym,
+ ProgramStateRef state) const {
+ // HACK: Ignore retain-count issues on values accessed through ivars,
+ // because of cases like this:
+ // [_contentView retain];
+ // [_contentView removeFromSuperview];
+ // [self addSubview:_contentView]; // invalidates 'self'
+ // [_contentView release];
+ if (X.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
+ return Pred;
+
+ // Any leaks or other errors?
+ if (X.isReturnedOwned() && X.getCount() == 0) {
+ if (RE.getKind() != RetEffect::NoRet) {
+ if (!RE.isOwned()) {
+
+ // The returning type is a CF, we expect the enclosing method should
+ // return ownership.
+ X = X ^ RefVal::ErrorLeakReturned;
+
+ // Generate an error node.
+ state = setRefBinding(state, Sym, X);
+
+ static CheckerProgramPointTag ReturnOwnLeakTag(this, "ReturnsOwnLeak");
+ ExplodedNode *N = C.addTransition(state, Pred, &ReturnOwnLeakTag);
+ if (N) {
+ const LangOptions &LOpts = C.getASTContext().getLangOpts();
+ auto R = llvm::make_unique<RefLeakReport>(
+ *getLeakAtReturnBug(LOpts), LOpts, N, Sym, C);
+ C.emitReport(std::move(R));
+ }
+ return N;
+ }
+ }
+ } else if (X.isReturnedNotOwned()) {
+ if (RE.isOwned()) {
+ if (X.getIvarAccessHistory() ==
+ RefVal::IvarAccessHistory::AccessedDirectly) {
+ // Assume the method was trying to transfer a +1 reference from a
+ // strong ivar to the caller.
+ state = setRefBinding(state, Sym,
+ X.releaseViaIvar() ^ RefVal::ReturnedOwned);
+ } else {
+ // Trying to return a not owned object to a caller expecting an
+ // owned object.
+ state = setRefBinding(state, Sym, X ^ RefVal::ErrorReturnedNotOwned);
+
+ static CheckerProgramPointTag
+ ReturnNotOwnedTag(this, "ReturnNotOwnedForOwned");
+
+ ExplodedNode *N = C.addTransition(state, Pred, &ReturnNotOwnedTag);
+ if (N) {
+ if (!returnNotOwnedForOwned)
+ returnNotOwnedForOwned.reset(new ReturnedNotOwnedForOwned(this));
+
+ auto R = llvm::make_unique<RefCountReport>(
+ *returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym);
+ C.emitReport(std::move(R));
+ }
+ return N;
+ }
+ }
+ }
+ return Pred;
+}
+
+//===----------------------------------------------------------------------===//
+// Check various ways a symbol can be invalidated.
+//===----------------------------------------------------------------------===//
+
+void RetainCountChecker::checkBind(SVal loc, SVal val, const Stmt *S,
+ CheckerContext &C) const {
+ // Are we storing to something that causes the value to "escape"?
+ bool escapes = true;
+
+ // A value escapes in three possible cases (this may change):
+ //
+ // (1) we are binding to something that is not a memory region.
+ // (2) we are binding to a memregion that does not have stack storage
+ ProgramStateRef state = C.getState();
+
+ if (auto regionLoc = loc.getAs<loc::MemRegionVal>()) {
+ escapes = shouldEscapeRegion(regionLoc->getRegion());
+ }
+
+ // If we are storing the value into an auto function scope variable annotated
+ // with (__attribute__((cleanup))), stop tracking the value to avoid leak
+ // false positives.
+ if (const auto *LVR = dyn_cast_or_null<VarRegion>(loc.getAsRegion())) {
+ const VarDecl *VD = LVR->getDecl();
+ if (VD->hasAttr<CleanupAttr>()) {
+ escapes = true;
+ }
+ }
+
+ // If our store can represent the binding and we aren't storing to something
+ // that doesn't have local storage then just return and have the simulation
+ // state continue as is.
+ if (!escapes)
+ return;
+
+ // Otherwise, find all symbols referenced by 'val' that we are tracking
+ // and stop tracking them.
+ state = state->scanReachableSymbols<StopTrackingCallback>(val).getState();
+ C.addTransition(state);
+}
+
+ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state,
+ SVal Cond,
+ bool Assumption) const {
+ // FIXME: We may add to the interface of evalAssume the list of symbols
+ // whose assumptions have changed. For now we just iterate through the
+ // bindings and check if any of the tracked symbols are NULL. This isn't
+ // too bad since the number of symbols we will track in practice are
+ // probably small and evalAssume is only called at branches and a few
+ // other places.
+ RefBindingsTy B = state->get<RefBindings>();
+
+ if (B.isEmpty())
+ return state;
+
+ bool changed = false;
+ RefBindingsTy::Factory &RefBFactory = state->get_context<RefBindings>();
+
+ for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ // Check if the symbol is null stop tracking the symbol.
+ ConstraintManager &CMgr = state->getConstraintManager();
+ ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey());
+ if (AllocFailed.isConstrainedTrue()) {
+ changed = true;
+ B = RefBFactory.remove(B, I.getKey());
+ }
+ }
+
+ if (changed)
+ state = state->set<RefBindings>(B);
+
+ return state;
+}
+
+ProgramStateRef
+RetainCountChecker::checkRegionChanges(ProgramStateRef state,
+ const InvalidatedSymbols *invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call) const {
+ if (!invalidated)
+ return state;
+
+ llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols;
+ for (ArrayRef<const MemRegion *>::iterator I = ExplicitRegions.begin(),
+ E = ExplicitRegions.end(); I != E; ++I) {
+ if (const SymbolicRegion *SR = (*I)->StripCasts()->getAs<SymbolicRegion>())
+ WhitelistedSymbols.insert(SR->getSymbol());
+ }
+
+ for (SymbolRef sym :
+ llvm::make_range(invalidated->begin(), invalidated->end())) {
+ if (WhitelistedSymbols.count(sym))
+ continue;
+ // Remove any existing reference-count binding.
+ state = removeRefBinding(state, sym);
+ }
+ return state;
+}
+
+ProgramStateRef
+RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
+ ExplodedNode *Pred,
+ const ProgramPointTag *Tag,
+ CheckerContext &Ctx,
+ SymbolRef Sym,
+ RefVal V,
+ const ReturnStmt *S) const {
+ unsigned ACnt = V.getAutoreleaseCount();
+
+ // No autorelease counts? Nothing to be done.
+ if (!ACnt)
+ return state;
+
+ unsigned Cnt = V.getCount();
+
+ // FIXME: Handle sending 'autorelease' to already released object.
+
+ if (V.getKind() == RefVal::ReturnedOwned)
+ ++Cnt;
+
+ // If we would over-release here, but we know the value came from an ivar,
+ // assume it was a strong ivar that's just been relinquished.
+ if (ACnt > Cnt &&
+ V.getIvarAccessHistory() == RefVal::IvarAccessHistory::AccessedDirectly) {
+ V = V.releaseViaIvar();
+ --ACnt;
+ }
+
+ if (ACnt <= Cnt) {
+ if (ACnt == Cnt) {
+ V.clearCounts();
+ if (V.getKind() == RefVal::ReturnedOwned) {
+ V = V ^ RefVal::ReturnedNotOwned;
+ } else {
+ V = V ^ RefVal::NotOwned;
+ }
+ } else {
+ V.setCount(V.getCount() - ACnt);
+ V.setAutoreleaseCount(0);
+ }
+ return setRefBinding(state, Sym, V);
+ }
+
+ // HACK: Ignore retain-count issues on values accessed through ivars,
+ // because of cases like this:
+ // [_contentView retain];
+ // [_contentView removeFromSuperview];
+ // [self addSubview:_contentView]; // invalidates 'self'
+ // [_contentView release];
+ if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
+ return state;
+
+ // Woah! More autorelease counts then retain counts left.
+ // Emit hard error.
+ V = V ^ RefVal::ErrorOverAutorelease;
+ state = setRefBinding(state, Sym, V);
+
+ ExplodedNode *N = Ctx.generateSink(state, Pred, Tag);
+ if (N) {
+ SmallString<128> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ os << "Object was autoreleased ";
+ if (V.getAutoreleaseCount() > 1)
+ os << V.getAutoreleaseCount() << " times but the object ";
+ else
+ os << "but ";
+ os << "has a +" << V.getCount() << " retain count";
+
+ if (!overAutorelease)
+ overAutorelease.reset(new OverAutorelease(this));
+
+ const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
+ auto R = llvm::make_unique<RefCountReport>(*overAutorelease, LOpts, N, Sym,
+ os.str());
+ Ctx.emitReport(std::move(R));
+ }
+
+ return nullptr;
+}
+
+ProgramStateRef
+RetainCountChecker::handleSymbolDeath(ProgramStateRef state,
+ SymbolRef sid, RefVal V,
+ SmallVectorImpl<SymbolRef> &Leaked) const {
+ bool hasLeak;
+
+ // HACK: Ignore retain-count issues on values accessed through ivars,
+ // because of cases like this:
+ // [_contentView retain];
+ // [_contentView removeFromSuperview];
+ // [self addSubview:_contentView]; // invalidates 'self'
+ // [_contentView release];
+ if (V.getIvarAccessHistory() != RefVal::IvarAccessHistory::None)
+ hasLeak = false;
+ else if (V.isOwned())
+ hasLeak = true;
+ else if (V.isNotOwned() || V.isReturnedOwned())
+ hasLeak = (V.getCount() > 0);
+ else
+ hasLeak = false;
+
+ if (!hasLeak)
+ return removeRefBinding(state, sid);
+
+ Leaked.push_back(sid);
+ return setRefBinding(state, sid, V ^ RefVal::ErrorLeak);
+}
+
+ExplodedNode *
+RetainCountChecker::processLeaks(ProgramStateRef state,
+ SmallVectorImpl<SymbolRef> &Leaked,
+ CheckerContext &Ctx,
+ ExplodedNode *Pred) const {
+ // Generate an intermediate node representing the leak point.
+ ExplodedNode *N = Ctx.addTransition(state, Pred);
+
+ if (N) {
+ for (SmallVectorImpl<SymbolRef>::iterator
+ I = Leaked.begin(), E = Leaked.end(); I != E; ++I) {
+
+ const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
+ RefCountBug *BT = Pred ? getLeakWithinFunctionBug(LOpts)
+ : getLeakAtReturnBug(LOpts);
+ assert(BT && "BugType not initialized.");
+
+ Ctx.emitReport(
+ llvm::make_unique<RefLeakReport>(*BT, LOpts, N, *I, Ctx));
+ }
+ }
+
+ return N;
+}
+
+static bool isISLObjectRef(QualType Ty) {
+ return StringRef(Ty.getAsString()).startswith("isl_");
+}
+
+void RetainCountChecker::checkBeginFunction(CheckerContext &Ctx) const {
+ if (!Ctx.inTopFrame())
+ return;
+
+ RetainSummaryManager &SmrMgr = getSummaryManager(Ctx);
+ const LocationContext *LCtx = Ctx.getLocationContext();
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(LCtx->getDecl());
+
+ if (!FD || SmrMgr.isTrustedReferenceCountImplementation(FD))
+ return;
+
+ ProgramStateRef state = Ctx.getState();
+ const RetainSummary *FunctionSummary = SmrMgr.getFunctionSummary(FD);
+ ArgEffects CalleeSideArgEffects = FunctionSummary->getArgEffects();
+
+ for (unsigned idx = 0, e = FD->getNumParams(); idx != e; ++idx) {
+ const ParmVarDecl *Param = FD->getParamDecl(idx);
+ SymbolRef Sym = state->getSVal(state->getRegion(Param, LCtx)).getAsSymbol();
+
+ QualType Ty = Param->getType();
+ const ArgEffect *AE = CalleeSideArgEffects.lookup(idx);
+ if (AE && AE->getKind() == DecRef && isISLObjectRef(Ty)) {
+ state = setRefBinding(
+ state, Sym, RefVal::makeOwned(ObjKind::Generalized, Ty));
+ } else if (isISLObjectRef(Ty)) {
+ state = setRefBinding(
+ state, Sym,
+ RefVal::makeNotOwned(ObjKind::Generalized, Ty));
+ }
+ }
+
+ Ctx.addTransition(state);
+}
+
+void RetainCountChecker::checkEndFunction(const ReturnStmt *RS,
+ CheckerContext &Ctx) const {
+ ExplodedNode *Pred = processReturn(RS, Ctx);
+
+ // Created state cached out.
+ if (!Pred) {
+ return;
+ }
+
+ ProgramStateRef state = Pred->getState();
+ RefBindingsTy B = state->get<RefBindings>();
+
+ // Don't process anything within synthesized bodies.
+ const LocationContext *LCtx = Pred->getLocationContext();
+ if (LCtx->getAnalysisDeclContext()->isBodyAutosynthesized()) {
+ assert(!LCtx->inTopFrame());
+ return;
+ }
+
+ for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ state = handleAutoreleaseCounts(state, Pred, /*Tag=*/nullptr, Ctx,
+ I->first, I->second);
+ if (!state)
+ return;
+ }
+
+ // If the current LocationContext has a parent, don't check for leaks.
+ // We will do that later.
+ // FIXME: we should instead check for imbalances of the retain/releases,
+ // and suggest annotations.
+ if (LCtx->getParent())
+ return;
+
+ B = state->get<RefBindings>();
+ SmallVector<SymbolRef, 10> Leaked;
+
+ for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I)
+ state = handleSymbolDeath(state, I->first, I->second, Leaked);
+
+ processLeaks(state, Leaked, Ctx, Pred);
+}
+
+void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ExplodedNode *Pred = C.getPredecessor();
+
+ ProgramStateRef state = C.getState();
+ RefBindingsTy B = state->get<RefBindings>();
+ SmallVector<SymbolRef, 10> Leaked;
+
+ // Update counts from autorelease pools
+ for (const auto &I: state->get<RefBindings>()) {
+ SymbolRef Sym = I.first;
+ if (SymReaper.isDead(Sym)) {
+ static CheckerProgramPointTag Tag(this, "DeadSymbolAutorelease");
+ const RefVal &V = I.second;
+ state = handleAutoreleaseCounts(state, Pred, &Tag, C, Sym, V);
+ if (!state)
+ return;
+
+ // Fetch the new reference count from the state, and use it to handle
+ // this symbol.
+ state = handleSymbolDeath(state, Sym, *getRefBinding(state, Sym), Leaked);
+ }
+ }
+
+ if (Leaked.empty()) {
+ C.addTransition(state);
+ return;
+ }
+
+ Pred = processLeaks(state, Leaked, C, Pred);
+
+ // Did we cache out?
+ if (!Pred)
+ return;
+
+ // Now generate a new node that nukes the old bindings.
+ // The only bindings left at this point are the leaked symbols.
+ RefBindingsTy::Factory &F = state->get_context<RefBindings>();
+ B = state->get<RefBindings>();
+
+ for (SmallVectorImpl<SymbolRef>::iterator I = Leaked.begin(),
+ E = Leaked.end();
+ I != E; ++I)
+ B = F.remove(B, *I);
+
+ state = state->set<RefBindings>(B);
+ C.addTransition(state, Pred);
+}
+
+void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+
+ RefBindingsTy B = State->get<RefBindings>();
+
+ if (B.isEmpty())
+ return;
+
+ Out << Sep << NL;
+
+ for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ Out << I->first << " : ";
+ I->second.print(Out);
+ Out << NL;
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Checker registration.
+//===----------------------------------------------------------------------===//
+
+void ento::registerRetainCountChecker(CheckerManager &Mgr) {
+ auto *Chk = Mgr.registerChecker<RetainCountChecker>();
+ Chk->TrackObjCAndCFObjects = true;
+}
+
+// FIXME: remove this, hack for backwards compatibility:
+// it should be possible to enable the NS/CF retain count checker as
+// osx.cocoa.RetainCount, and it should be possible to disable
+// osx.OSObjectRetainCount using osx.cocoa.RetainCount:CheckOSObject=false.
+static bool hasPrevCheckOSObjectOptionDisabled(AnalyzerOptions &Options) {
+ auto I = Options.Config.find("osx.cocoa.RetainCount:CheckOSObject");
+ if (I != Options.Config.end())
+ return I->getValue() == "false";
+ return false;
+}
+
+void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) {
+ auto *Chk = Mgr.registerChecker<RetainCountChecker>();
+ if (!hasPrevCheckOSObjectOptionDisabled(Mgr.getAnalyzerOptions()))
+ Chk->TrackOSObjects = true;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
new file mode 100644
index 000000000000..31e2d9ae4932
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
@@ -0,0 +1,393 @@
+//==--- RetainCountChecker.h - Checks for leaks and other issues -*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the methods for RetainCountChecker, which implements
+// a reference count checker for Core Foundation and Cocoa on (Mac OS X).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_H
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "RetainCountDiagnostics.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Analysis/SelectorExtras.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "clang/StaticAnalyzer/Core/RetainSummaryManager.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableList.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include <cstdarg>
+#include <utility>
+
+namespace clang {
+namespace ento {
+namespace retaincountchecker {
+
+/// Metadata on reference.
+class RefVal {
+public:
+ enum Kind {
+ Owned = 0, // Owning reference.
+ NotOwned, // Reference is not owned by still valid (not freed).
+ Released, // Object has been released.
+ ReturnedOwned, // Returned object passes ownership to caller.
+ ReturnedNotOwned, // Return object does not pass ownership to caller.
+ ERROR_START,
+ ErrorDeallocNotOwned, // -dealloc called on non-owned object.
+ ErrorUseAfterRelease, // Object used after released.
+ ErrorReleaseNotOwned, // Release of an object that was not owned.
+ ERROR_LEAK_START,
+ ErrorLeak, // A memory leak due to excessive reference counts.
+ ErrorLeakReturned, // A memory leak due to the returning method not having
+ // the correct naming conventions.
+ ErrorOverAutorelease,
+ ErrorReturnedNotOwned
+ };
+
+ /// Tracks how an object referenced by an ivar has been used.
+ ///
+ /// This accounts for us not knowing if an arbitrary ivar is supposed to be
+ /// stored at +0 or +1.
+ enum class IvarAccessHistory {
+ None,
+ AccessedDirectly,
+ ReleasedAfterDirectAccess
+ };
+
+private:
+ /// The number of outstanding retains.
+ unsigned Cnt;
+ /// The number of outstanding autoreleases.
+ unsigned ACnt;
+ /// The (static) type of the object at the time we started tracking it.
+ QualType T;
+
+ /// The current state of the object.
+ ///
+ /// See the RefVal::Kind enum for possible values.
+ unsigned RawKind : 5;
+
+ /// The kind of object being tracked (CF or ObjC or OSObject), if known.
+ ///
+ /// See the ObjKind enum for possible values.
+ unsigned RawObjectKind : 3;
+
+ /// True if the current state and/or retain count may turn out to not be the
+ /// best possible approximation of the reference counting state.
+ ///
+ /// If true, the checker may decide to throw away ("override") this state
+ /// in favor of something else when it sees the object being used in new ways.
+ ///
+ /// This setting should not be propagated to state derived from this state.
+ /// Once we start deriving new states, it would be inconsistent to override
+ /// them.
+ unsigned RawIvarAccessHistory : 2;
+
+ RefVal(Kind k, ObjKind o, unsigned cnt, unsigned acnt, QualType t,
+ IvarAccessHistory IvarAccess)
+ : Cnt(cnt), ACnt(acnt), T(t), RawKind(static_cast<unsigned>(k)),
+ RawObjectKind(static_cast<unsigned>(o)),
+ RawIvarAccessHistory(static_cast<unsigned>(IvarAccess)) {
+ assert(getKind() == k && "not enough bits for the kind");
+ assert(getObjKind() == o && "not enough bits for the object kind");
+ assert(getIvarAccessHistory() == IvarAccess && "not enough bits");
+ }
+
+public:
+ Kind getKind() const { return static_cast<Kind>(RawKind); }
+
+ ObjKind getObjKind() const {
+ return static_cast<ObjKind>(RawObjectKind);
+ }
+
+ unsigned getCount() const { return Cnt; }
+ unsigned getAutoreleaseCount() const { return ACnt; }
+ unsigned getCombinedCounts() const { return Cnt + ACnt; }
+ void clearCounts() {
+ Cnt = 0;
+ ACnt = 0;
+ }
+ void setCount(unsigned i) {
+ Cnt = i;
+ }
+ void setAutoreleaseCount(unsigned i) {
+ ACnt = i;
+ }
+
+ QualType getType() const { return T; }
+
+ /// Returns what the analyzer knows about direct accesses to a particular
+ /// instance variable.
+ ///
+ /// If the object with this refcount wasn't originally from an Objective-C
+ /// ivar region, this should always return IvarAccessHistory::None.
+ IvarAccessHistory getIvarAccessHistory() const {
+ return static_cast<IvarAccessHistory>(RawIvarAccessHistory);
+ }
+
+ bool isOwned() const {
+ return getKind() == Owned;
+ }
+
+ bool isNotOwned() const {
+ return getKind() == NotOwned;
+ }
+
+ bool isReturnedOwned() const {
+ return getKind() == ReturnedOwned;
+ }
+
+ bool isReturnedNotOwned() const {
+ return getKind() == ReturnedNotOwned;
+ }
+
+ /// Create a state for an object whose lifetime is the responsibility of the
+ /// current function, at least partially.
+ ///
+ /// Most commonly, this is an owned object with a retain count of +1.
+ static RefVal makeOwned(ObjKind o, QualType t) {
+ return RefVal(Owned, o, /*Count=*/1, 0, t, IvarAccessHistory::None);
+ }
+
+ /// Create a state for an object whose lifetime is not the responsibility of
+ /// the current function.
+ ///
+ /// Most commonly, this is an unowned object with a retain count of +0.
+ static RefVal makeNotOwned(ObjKind o, QualType t) {
+ return RefVal(NotOwned, o, /*Count=*/0, 0, t, IvarAccessHistory::None);
+ }
+
+ RefVal operator-(size_t i) const {
+ return RefVal(getKind(), getObjKind(), getCount() - i,
+ getAutoreleaseCount(), getType(), getIvarAccessHistory());
+ }
+
+ RefVal operator+(size_t i) const {
+ return RefVal(getKind(), getObjKind(), getCount() + i,
+ getAutoreleaseCount(), getType(), getIvarAccessHistory());
+ }
+
+ RefVal operator^(Kind k) const {
+ return RefVal(k, getObjKind(), getCount(), getAutoreleaseCount(),
+ getType(), getIvarAccessHistory());
+ }
+
+ RefVal autorelease() const {
+ return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount()+1,
+ getType(), getIvarAccessHistory());
+ }
+
+ RefVal withIvarAccess() const {
+ assert(getIvarAccessHistory() == IvarAccessHistory::None);
+ return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(),
+ getType(), IvarAccessHistory::AccessedDirectly);
+ }
+
+ RefVal releaseViaIvar() const {
+ assert(getIvarAccessHistory() == IvarAccessHistory::AccessedDirectly);
+ return RefVal(getKind(), getObjKind(), getCount(), getAutoreleaseCount(),
+ getType(), IvarAccessHistory::ReleasedAfterDirectAccess);
+ }
+
+ // Comparison, profiling, and pretty-printing.
+ bool hasSameState(const RefVal &X) const {
+ return getKind() == X.getKind() && Cnt == X.Cnt && ACnt == X.ACnt &&
+ getIvarAccessHistory() == X.getIvarAccessHistory();
+ }
+
+ bool operator==(const RefVal& X) const {
+ return T == X.T && hasSameState(X) && getObjKind() == X.getObjKind();
+ }
+
+ void Profile(llvm::FoldingSetNodeID& ID) const {
+ ID.Add(T);
+ ID.AddInteger(RawKind);
+ ID.AddInteger(Cnt);
+ ID.AddInteger(ACnt);
+ ID.AddInteger(RawObjectKind);
+ ID.AddInteger(RawIvarAccessHistory);
+ }
+
+ void print(raw_ostream &Out) const;
+};
+
+class RetainCountChecker
+ : public Checker< check::Bind,
+ check::DeadSymbols,
+ check::BeginFunction,
+ check::EndFunction,
+ check::PostStmt<BlockExpr>,
+ check::PostStmt<CastExpr>,
+ check::PostStmt<ObjCArrayLiteral>,
+ check::PostStmt<ObjCDictionaryLiteral>,
+ check::PostStmt<ObjCBoxedExpr>,
+ check::PostStmt<ObjCIvarRefExpr>,
+ check::PostCall,
+ check::RegionChanges,
+ eval::Assume,
+ eval::Call > {
+ mutable std::unique_ptr<RefCountBug> useAfterRelease, releaseNotOwned;
+ mutable std::unique_ptr<RefCountBug> deallocNotOwned;
+ mutable std::unique_ptr<RefCountBug> overAutorelease, returnNotOwnedForOwned;
+ mutable std::unique_ptr<RefCountBug> leakWithinFunction, leakAtReturn;
+
+ mutable std::unique_ptr<RetainSummaryManager> Summaries;
+public:
+ static constexpr const char *DeallocTagDescription = "DeallocSent";
+
+ /// Track Objective-C and CoreFoundation objects.
+ bool TrackObjCAndCFObjects = false;
+
+ /// Track sublcasses of OSObject.
+ bool TrackOSObjects = false;
+
+ RetainCountChecker() {}
+
+ RefCountBug *getLeakWithinFunctionBug(const LangOptions &LOpts) const;
+
+ RefCountBug *getLeakAtReturnBug(const LangOptions &LOpts) const;
+
+ RetainSummaryManager &getSummaryManager(ASTContext &Ctx) const {
+ // FIXME: We don't support ARC being turned on and off during one analysis.
+ // (nor, for that matter, do we support changing ASTContexts)
+ bool ARCEnabled = (bool)Ctx.getLangOpts().ObjCAutoRefCount;
+ if (!Summaries) {
+ Summaries.reset(new RetainSummaryManager(
+ Ctx, ARCEnabled, TrackObjCAndCFObjects, TrackOSObjects));
+ } else {
+ assert(Summaries->isARCEnabled() == ARCEnabled);
+ }
+ return *Summaries;
+ }
+
+ RetainSummaryManager &getSummaryManager(CheckerContext &C) const {
+ return getSummaryManager(C.getASTContext());
+ }
+
+ void printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const override;
+
+ void checkBind(SVal loc, SVal val, const Stmt *S, CheckerContext &C) const;
+ void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
+ void checkPostStmt(const CastExpr *CE, CheckerContext &C) const;
+
+ void checkPostStmt(const ObjCArrayLiteral *AL, CheckerContext &C) const;
+ void checkPostStmt(const ObjCDictionaryLiteral *DL, CheckerContext &C) const;
+ void checkPostStmt(const ObjCBoxedExpr *BE, CheckerContext &C) const;
+
+ void checkPostStmt(const ObjCIvarRefExpr *IRE, CheckerContext &C) const;
+
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+ void checkSummary(const RetainSummary &Summ, const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void processSummaryOfInlined(const RetainSummary &Summ,
+ const CallEvent &Call,
+ CheckerContext &C) const;
+
+ bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+
+ ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
+ bool Assumption) const;
+
+ ProgramStateRef
+ checkRegionChanges(ProgramStateRef state,
+ const InvalidatedSymbols *invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext* LCtx,
+ const CallEvent *Call) const;
+
+ ExplodedNode* checkReturnWithRetEffect(const ReturnStmt *S, CheckerContext &C,
+ ExplodedNode *Pred, RetEffect RE, RefVal X,
+ SymbolRef Sym, ProgramStateRef state) const;
+
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+ void checkBeginFunction(CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+
+ ProgramStateRef updateSymbol(ProgramStateRef state, SymbolRef sym,
+ RefVal V, ArgEffect E, RefVal::Kind &hasErr,
+ CheckerContext &C) const;
+
+ void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange,
+ RefVal::Kind ErrorKind, SymbolRef Sym,
+ CheckerContext &C) const;
+
+ void processObjCLiterals(CheckerContext &C, const Expr *Ex) const;
+
+ ProgramStateRef handleSymbolDeath(ProgramStateRef state,
+ SymbolRef sid, RefVal V,
+ SmallVectorImpl<SymbolRef> &Leaked) const;
+
+ ProgramStateRef
+ handleAutoreleaseCounts(ProgramStateRef state, ExplodedNode *Pred,
+ const ProgramPointTag *Tag, CheckerContext &Ctx,
+ SymbolRef Sym,
+ RefVal V,
+ const ReturnStmt *S=nullptr) const;
+
+ ExplodedNode *processLeaks(ProgramStateRef state,
+ SmallVectorImpl<SymbolRef> &Leaked,
+ CheckerContext &Ctx,
+ ExplodedNode *Pred = nullptr) const;
+
+private:
+ /// Perform the necessary checks and state adjustments at the end of the
+ /// function.
+ /// \p S Return statement, may be null.
+ ExplodedNode * processReturn(const ReturnStmt *S, CheckerContext &C) const;
+};
+
+//===----------------------------------------------------------------------===//
+// RefBindings - State used to track object reference counts.
+//===----------------------------------------------------------------------===//
+
+const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym);
+
+ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym,
+ RefVal Val);
+
+ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym);
+
+/// Returns true if this stack frame is for an Objective-C method that is a
+/// property getter or setter whose body has been synthesized by the analyzer.
+inline bool isSynthesizedAccessor(const StackFrameContext *SFC) {
+ auto Method = dyn_cast_or_null<ObjCMethodDecl>(SFC->getDecl());
+ if (!Method || !Method->isPropertyAccessor())
+ return false;
+
+ return SFC->getAnalysisDeclContext()->isBodyAutosynthesized();
+}
+
+} // end namespace retaincountchecker
+} // end namespace ento
+} // end namespace clang
+
+#endif
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
new file mode 100644
index 000000000000..cda1a928de13
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
@@ -0,0 +1,794 @@
+// RetainCountDiagnostics.cpp - Checks for leaks and other issues -*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines diagnostics for RetainCountChecker, which implements
+// a reference count checker for Core Foundation and Cocoa on (Mac OS X).
+//
+//===----------------------------------------------------------------------===//
+
+#include "RetainCountDiagnostics.h"
+#include "RetainCountChecker.h"
+
+using namespace clang;
+using namespace ento;
+using namespace retaincountchecker;
+
+static bool isNumericLiteralExpression(const Expr *E) {
+ // FIXME: This set of cases was copied from SemaExprObjC.
+ return isa<IntegerLiteral>(E) ||
+ isa<CharacterLiteral>(E) ||
+ isa<FloatingLiteral>(E) ||
+ isa<ObjCBoolLiteralExpr>(E) ||
+ isa<CXXBoolLiteralExpr>(E);
+}
+
+/// If type represents a pointer to CXXRecordDecl,
+/// and is not a typedef, return the decl name.
+/// Otherwise, return the serialization of type.
+static std::string getPrettyTypeName(QualType QT) {
+ QualType PT = QT->getPointeeType();
+ if (!PT.isNull() && !QT->getAs<TypedefType>())
+ if (const auto *RD = PT->getAsCXXRecordDecl())
+ return RD->getName();
+ return QT.getAsString();
+}
+
+/// Write information about the type state change to {@code os},
+/// return whether the note should be generated.
+static bool shouldGenerateNote(llvm::raw_string_ostream &os,
+ const RefVal *PrevT, const RefVal &CurrV,
+ bool DeallocSent) {
+ // Get the previous type state.
+ RefVal PrevV = *PrevT;
+
+ // Specially handle -dealloc.
+ if (DeallocSent) {
+ // Determine if the object's reference count was pushed to zero.
+ assert(!PrevV.hasSameState(CurrV) && "The state should have changed.");
+ // We may not have transitioned to 'release' if we hit an error.
+ // This case is handled elsewhere.
+ if (CurrV.getKind() == RefVal::Released) {
+ assert(CurrV.getCombinedCounts() == 0);
+ os << "Object released by directly sending the '-dealloc' message";
+ return true;
+ }
+ }
+
+ // Determine if the typestate has changed.
+ if (!PrevV.hasSameState(CurrV))
+ switch (CurrV.getKind()) {
+ case RefVal::Owned:
+ case RefVal::NotOwned:
+ if (PrevV.getCount() == CurrV.getCount()) {
+ // Did an autorelease message get sent?
+ if (PrevV.getAutoreleaseCount() == CurrV.getAutoreleaseCount())
+ return false;
+
+ assert(PrevV.getAutoreleaseCount() < CurrV.getAutoreleaseCount());
+ os << "Object autoreleased";
+ return true;
+ }
+
+ if (PrevV.getCount() > CurrV.getCount())
+ os << "Reference count decremented.";
+ else
+ os << "Reference count incremented.";
+
+ if (unsigned Count = CurrV.getCount())
+ os << " The object now has a +" << Count << " retain count.";
+
+ return true;
+
+ case RefVal::Released:
+ if (CurrV.getIvarAccessHistory() ==
+ RefVal::IvarAccessHistory::ReleasedAfterDirectAccess &&
+ CurrV.getIvarAccessHistory() != PrevV.getIvarAccessHistory()) {
+ os << "Strong instance variable relinquished. ";
+ }
+ os << "Object released.";
+ return true;
+
+ case RefVal::ReturnedOwned:
+ // Autoreleases can be applied after marking a node ReturnedOwned.
+ if (CurrV.getAutoreleaseCount())
+ return false;
+
+ os << "Object returned to caller as an owning reference (single "
+ "retain count transferred to caller)";
+ return true;
+
+ case RefVal::ReturnedNotOwned:
+ os << "Object returned to caller with a +0 retain count";
+ return true;
+
+ default:
+ return false;
+ }
+ return true;
+}
+
+/// Finds argument index of the out paramter in the call {@code S}
+/// corresponding to the symbol {@code Sym}.
+/// If none found, returns None.
+static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
+ const LocationContext *LCtx,
+ SymbolRef &Sym,
+ Optional<CallEventRef<>> CE) {
+ if (!CE)
+ return None;
+
+ for (unsigned Idx = 0; Idx < (*CE)->getNumArgs(); Idx++)
+ if (const MemRegion *MR = (*CE)->getArgSVal(Idx).getAsRegion())
+ if (const auto *TR = dyn_cast<TypedValueRegion>(MR))
+ if (CurrSt->getSVal(MR, TR->getValueType()).getAsSymExpr() == Sym)
+ return Idx;
+
+ return None;
+}
+
+static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
+ const LocationContext *LCtx,
+ const RefVal &CurrV, SymbolRef &Sym,
+ const Stmt *S,
+ llvm::raw_string_ostream &os) {
+ CallEventManager &Mgr = CurrSt->getStateManager().getCallEventManager();
+ if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
+ // Get the name of the callee (if it is available)
+ // from the tracked SVal.
+ SVal X = CurrSt->getSValAsScalarOrLoc(CE->getCallee(), LCtx);
+ const FunctionDecl *FD = X.getAsFunctionDecl();
+
+ // If failed, try to get it from AST.
+ if (!FD)
+ FD = dyn_cast<FunctionDecl>(CE->getCalleeDecl());
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(CE->getCalleeDecl())) {
+ os << "Call to method '" << MD->getQualifiedNameAsString() << '\'';
+ } else if (FD) {
+ os << "Call to function '" << FD->getQualifiedNameAsString() << '\'';
+ } else {
+ os << "function call";
+ }
+ } else if (isa<CXXNewExpr>(S)) {
+ os << "Operator 'new'";
+ } else {
+ assert(isa<ObjCMessageExpr>(S));
+ CallEventRef<ObjCMethodCall> Call =
+ Mgr.getObjCMethodCall(cast<ObjCMessageExpr>(S), CurrSt, LCtx);
+
+ switch (Call->getMessageKind()) {
+ case OCM_Message:
+ os << "Method";
+ break;
+ case OCM_PropertyAccess:
+ os << "Property";
+ break;
+ case OCM_Subscript:
+ os << "Subscript";
+ break;
+ }
+ }
+
+ Optional<CallEventRef<>> CE = Mgr.getCall(S, CurrSt, LCtx);
+ auto Idx = findArgIdxOfSymbol(CurrSt, LCtx, Sym, CE);
+
+ // If index is not found, we assume that the symbol was returned.
+ if (!Idx) {
+ os << " returns ";
+ } else {
+ os << " writes ";
+ }
+
+ if (CurrV.getObjKind() == ObjKind::CF) {
+ os << "a Core Foundation object of type '"
+ << Sym->getType().getAsString() << "' with a ";
+ } else if (CurrV.getObjKind() == ObjKind::OS) {
+ os << "an OSObject of type '" << getPrettyTypeName(Sym->getType())
+ << "' with a ";
+ } else if (CurrV.getObjKind() == ObjKind::Generalized) {
+ os << "an object of type '" << Sym->getType().getAsString()
+ << "' with a ";
+ } else {
+ assert(CurrV.getObjKind() == ObjKind::ObjC);
+ QualType T = Sym->getType();
+ if (!isa<ObjCObjectPointerType>(T)) {
+ os << "an Objective-C object with a ";
+ } else {
+ const ObjCObjectPointerType *PT = cast<ObjCObjectPointerType>(T);
+ os << "an instance of " << PT->getPointeeType().getAsString()
+ << " with a ";
+ }
+ }
+
+ if (CurrV.isOwned()) {
+ os << "+1 retain count";
+ } else {
+ assert(CurrV.isNotOwned());
+ os << "+0 retain count";
+ }
+
+ if (Idx) {
+ os << " into an out parameter '";
+ const ParmVarDecl *PVD = (*CE)->parameters()[*Idx];
+ PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
+ /*Qualified=*/false);
+ os << "'";
+
+ QualType RT = (*CE)->getResultType();
+ if (!RT.isNull() && !RT->isVoidType()) {
+ SVal RV = (*CE)->getReturnValue();
+ if (CurrSt->isNull(RV).isConstrainedTrue()) {
+ os << " (assuming the call returns zero)";
+ } else if (CurrSt->isNonNull(RV).isConstrainedTrue()) {
+ os << " (assuming the call returns non-zero)";
+ }
+
+ }
+ }
+}
+
+namespace clang {
+namespace ento {
+namespace retaincountchecker {
+
+class RefCountReportVisitor : public BugReporterVisitor {
+protected:
+ SymbolRef Sym;
+
+public:
+ RefCountReportVisitor(SymbolRef sym) : Sym(sym) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int x = 0;
+ ID.AddPointer(&x);
+ ID.AddPointer(Sym);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *N,
+ BugReport &BR) override;
+};
+
+class RefLeakReportVisitor : public RefCountReportVisitor {
+public:
+ RefLeakReportVisitor(SymbolRef sym) : RefCountReportVisitor(sym) {}
+
+ std::shared_ptr<PathDiagnosticPiece> getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *N,
+ BugReport &BR) override;
+};
+
+} // end namespace retaincountchecker
+} // end namespace ento
+} // end namespace clang
+
+
+/// Find the first node with the parent stack frame.
+static const ExplodedNode *getCalleeNode(const ExplodedNode *Pred) {
+ const StackFrameContext *SC = Pred->getStackFrame();
+ if (SC->inTopFrame())
+ return nullptr;
+ const StackFrameContext *PC = SC->getParent()->getStackFrame();
+ if (!PC)
+ return nullptr;
+
+ const ExplodedNode *N = Pred;
+ while (N && N->getStackFrame() != PC) {
+ N = N->getFirstPred();
+ }
+ return N;
+}
+
+
+/// Insert a diagnostic piece at function exit
+/// if a function parameter is annotated as "os_consumed",
+/// but it does not actually consume the reference.
+static std::shared_ptr<PathDiagnosticEventPiece>
+annotateConsumedSummaryMismatch(const ExplodedNode *N,
+ CallExitBegin &CallExitLoc,
+ const SourceManager &SM,
+ CallEventManager &CEMgr) {
+
+ const ExplodedNode *CN = getCalleeNode(N);
+ if (!CN)
+ return nullptr;
+
+ CallEventRef<> Call = CEMgr.getCaller(N->getStackFrame(), N->getState());
+
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ ArrayRef<const ParmVarDecl *> Parameters = Call->parameters();
+ for (unsigned I=0; I < Call->getNumArgs() && I < Parameters.size(); ++I) {
+ const ParmVarDecl *PVD = Parameters[I];
+
+ if (!PVD->hasAttr<OSConsumedAttr>())
+ continue;
+
+ if (SymbolRef SR = Call->getArgSVal(I).getAsLocSymbol()) {
+ const RefVal *CountBeforeCall = getRefBinding(CN->getState(), SR);
+ const RefVal *CountAtExit = getRefBinding(N->getState(), SR);
+
+ if (!CountBeforeCall || !CountAtExit)
+ continue;
+
+ unsigned CountBefore = CountBeforeCall->getCount();
+ unsigned CountAfter = CountAtExit->getCount();
+
+ bool AsExpected = CountBefore > 0 && CountAfter == CountBefore - 1;
+ if (!AsExpected) {
+ os << "Parameter '";
+ PVD->getNameForDiagnostic(os, PVD->getASTContext().getPrintingPolicy(),
+ /*Qualified=*/false);
+ os << "' is marked as consuming, but the function did not consume "
+ << "the reference\n";
+ }
+ }
+ }
+
+ if (os.str().empty())
+ return nullptr;
+
+ // FIXME: remove the code duplication with NoStoreFuncVisitor.
+ PathDiagnosticLocation L;
+ if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) {
+ L = PathDiagnosticLocation::createBegin(RS, SM, N->getLocationContext());
+ } else {
+ L = PathDiagnosticLocation(
+ Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);
+ }
+
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+RefCountReportVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC, BugReport &BR) {
+
+ const SourceManager &SM = BRC.getSourceManager();
+ CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
+ if (auto CE = N->getLocationAs<CallExitBegin>())
+ if (auto PD = annotateConsumedSummaryMismatch(N, *CE, SM, CEMgr))
+ return PD;
+
+ // FIXME: We will eventually need to handle non-statement-based events
+ // (__attribute__((cleanup))).
+ if (!N->getLocation().getAs<StmtPoint>())
+ return nullptr;
+
+ // Check if the type state has changed.
+ const ExplodedNode *PrevNode = N->getFirstPred();
+ ProgramStateRef PrevSt = PrevNode->getState();
+ ProgramStateRef CurrSt = N->getState();
+ const LocationContext *LCtx = N->getLocationContext();
+
+ const RefVal* CurrT = getRefBinding(CurrSt, Sym);
+ if (!CurrT) return nullptr;
+
+ const RefVal &CurrV = *CurrT;
+ const RefVal *PrevT = getRefBinding(PrevSt, Sym);
+
+ // Create a string buffer to constain all the useful things we want
+ // to tell the user.
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+
+ // This is the allocation site since the previous node had no bindings
+ // for this symbol.
+ if (!PrevT) {
+ const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
+
+ if (isa<ObjCIvarRefExpr>(S) &&
+ isSynthesizedAccessor(LCtx->getStackFrame())) {
+ S = LCtx->getStackFrame()->getCallSite();
+ }
+
+ if (isa<ObjCArrayLiteral>(S)) {
+ os << "NSArray literal is an object with a +0 retain count";
+ } else if (isa<ObjCDictionaryLiteral>(S)) {
+ os << "NSDictionary literal is an object with a +0 retain count";
+ } else if (const ObjCBoxedExpr *BL = dyn_cast<ObjCBoxedExpr>(S)) {
+ if (isNumericLiteralExpression(BL->getSubExpr()))
+ os << "NSNumber literal is an object with a +0 retain count";
+ else {
+ const ObjCInterfaceDecl *BoxClass = nullptr;
+ if (const ObjCMethodDecl *Method = BL->getBoxingMethod())
+ BoxClass = Method->getClassInterface();
+
+ // We should always be able to find the boxing class interface,
+ // but consider this future-proofing.
+ if (BoxClass) {
+ os << *BoxClass << " b";
+ } else {
+ os << "B";
+ }
+
+ os << "oxed expression produces an object with a +0 retain count";
+ }
+ } else if (isa<ObjCIvarRefExpr>(S)) {
+ os << "Object loaded from instance variable";
+ } else {
+ generateDiagnosticsForCallLike(CurrSt, LCtx, CurrV, Sym, S, os);
+ }
+
+ PathDiagnosticLocation Pos(S, SM, N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
+ }
+
+ // Gather up the effects that were performed on the object at this
+ // program point
+ bool DeallocSent = false;
+
+ if (N->getLocation().getTag() &&
+ N->getLocation().getTag()->getTagDescription().contains(
+ RetainCountChecker::DeallocTagDescription)) {
+ // We only have summaries attached to nodes after evaluating CallExpr and
+ // ObjCMessageExprs.
+ const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
+
+ if (const CallExpr *CE = dyn_cast<CallExpr>(S)) {
+ // Iterate through the parameter expressions and see if the symbol
+ // was ever passed as an argument.
+ unsigned i = 0;
+
+ for (auto AI=CE->arg_begin(), AE=CE->arg_end(); AI!=AE; ++AI, ++i) {
+
+ // Retrieve the value of the argument. Is it the symbol
+ // we are interested in?
+ if (CurrSt->getSValAsScalarOrLoc(*AI, LCtx).getAsLocSymbol() != Sym)
+ continue;
+
+ // We have an argument. Get the effect!
+ DeallocSent = true;
+ }
+ } else if (const ObjCMessageExpr *ME = dyn_cast<ObjCMessageExpr>(S)) {
+ if (const Expr *receiver = ME->getInstanceReceiver()) {
+ if (CurrSt->getSValAsScalarOrLoc(receiver, LCtx)
+ .getAsLocSymbol() == Sym) {
+ // The symbol we are tracking is the receiver.
+ DeallocSent = true;
+ }
+ }
+ }
+ }
+
+ if (!shouldGenerateNote(os, PrevT, CurrV, DeallocSent))
+ return nullptr;
+
+ if (os.str().empty())
+ return nullptr; // We have nothing to say!
+
+ const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ auto P = std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
+
+ // Add the range by scanning the children of the statement for any bindings
+ // to Sym.
+ for (const Stmt *Child : S->children())
+ if (const Expr *Exp = dyn_cast_or_null<Expr>(Child))
+ if (CurrSt->getSValAsScalarOrLoc(Exp, LCtx).getAsLocSymbol() == Sym) {
+ P->addRange(Exp->getSourceRange());
+ break;
+ }
+
+ return std::move(P);
+}
+
+static Optional<std::string> describeRegion(const MemRegion *MR) {
+ if (const auto *VR = dyn_cast_or_null<VarRegion>(MR))
+ return std::string(VR->getDecl()->getName());
+ // Once we support more storage locations for bindings,
+ // this would need to be improved.
+ return None;
+}
+
+namespace {
+// Find the first node in the current function context that referred to the
+// tracked symbol and the memory location that value was stored to. Note, the
+// value is only reported if the allocation occurred in the same function as
+// the leak. The function can also return a location context, which should be
+// treated as interesting.
+struct AllocationInfo {
+ const ExplodedNode* N;
+ const MemRegion *R;
+ const LocationContext *InterestingMethodContext;
+ AllocationInfo(const ExplodedNode *InN,
+ const MemRegion *InR,
+ const LocationContext *InInterestingMethodContext) :
+ N(InN), R(InR), InterestingMethodContext(InInterestingMethodContext) {}
+};
+} // end anonymous namespace
+
+static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
+ const ExplodedNode *N, SymbolRef Sym) {
+ const ExplodedNode *AllocationNode = N;
+ const ExplodedNode *AllocationNodeInCurrentOrParentContext = N;
+ const MemRegion *FirstBinding = nullptr;
+ const LocationContext *LeakContext = N->getLocationContext();
+
+ // The location context of the init method called on the leaked object, if
+ // available.
+ const LocationContext *InitMethodContext = nullptr;
+
+ while (N) {
+ ProgramStateRef St = N->getState();
+ const LocationContext *NContext = N->getLocationContext();
+
+ if (!getRefBinding(St, Sym))
+ break;
+
+ StoreManager::FindUniqueBinding FB(Sym);
+ StateMgr.iterBindings(St, FB);
+
+ if (FB) {
+ const MemRegion *R = FB.getRegion();
+ // Do not show local variables belonging to a function other than
+ // where the error is reported.
+ if (auto MR = dyn_cast<StackSpaceRegion>(R->getMemorySpace()))
+ if (MR->getStackFrame() == LeakContext->getStackFrame())
+ FirstBinding = R;
+ }
+
+ // AllocationNode is the last node in which the symbol was tracked.
+ AllocationNode = N;
+
+ // AllocationNodeInCurrentContext, is the last node in the current or
+ // parent context in which the symbol was tracked.
+ //
+ // Note that the allocation site might be in the parent context. For example,
+ // the case where an allocation happens in a block that captures a reference
+ // to it and that reference is overwritten/dropped by another call to
+ // the block.
+ if (NContext == LeakContext || NContext->isParentOf(LeakContext))
+ AllocationNodeInCurrentOrParentContext = N;
+
+ // Find the last init that was called on the given symbol and store the
+ // init method's location context.
+ if (!InitMethodContext)
+ if (auto CEP = N->getLocation().getAs<CallEnter>()) {
+ const Stmt *CE = CEP->getCallExpr();
+ if (const auto *ME = dyn_cast_or_null<ObjCMessageExpr>(CE)) {
+ const Stmt *RecExpr = ME->getInstanceReceiver();
+ if (RecExpr) {
+ SVal RecV = St->getSVal(RecExpr, NContext);
+ if (ME->getMethodFamily() == OMF_init && RecV.getAsSymbol() == Sym)
+ InitMethodContext = CEP->getCalleeContext();
+ }
+ }
+ }
+
+ N = N->getFirstPred();
+ }
+
+ // If we are reporting a leak of the object that was allocated with alloc,
+ // mark its init method as interesting.
+ const LocationContext *InterestingMethodContext = nullptr;
+ if (InitMethodContext) {
+ const ProgramPoint AllocPP = AllocationNode->getLocation();
+ if (Optional<StmtPoint> SP = AllocPP.getAs<StmtPoint>())
+ if (const ObjCMessageExpr *ME = SP->getStmtAs<ObjCMessageExpr>())
+ if (ME->getMethodFamily() == OMF_alloc)
+ InterestingMethodContext = InitMethodContext;
+ }
+
+ // If allocation happened in a function different from the leak node context,
+ // do not report the binding.
+ assert(N && "Could not find allocation node");
+
+ if (AllocationNodeInCurrentOrParentContext &&
+ AllocationNodeInCurrentOrParentContext->getLocationContext() !=
+ LeakContext)
+ FirstBinding = nullptr;
+
+ return AllocationInfo(AllocationNodeInCurrentOrParentContext,
+ FirstBinding,
+ InterestingMethodContext);
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+RefCountReportVisitor::getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *EndN, BugReport &BR) {
+ BR.markInteresting(Sym);
+ return BugReporterVisitor::getDefaultEndPath(BRC, EndN, BR);
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
+ const ExplodedNode *EndN, BugReport &BR) {
+
+ // Tell the BugReporterContext to report cases when the tracked symbol is
+ // assigned to different variables, etc.
+ BR.markInteresting(Sym);
+
+ // We are reporting a leak. Walk up the graph to get to the first node where
+ // the symbol appeared, and also get the first VarDecl that tracked object
+ // is stored to.
+ AllocationInfo AllocI = GetAllocationSite(BRC.getStateManager(), EndN, Sym);
+
+ const MemRegion* FirstBinding = AllocI.R;
+ BR.markInteresting(AllocI.InterestingMethodContext);
+
+ SourceManager& SM = BRC.getSourceManager();
+
+ // Compute an actual location for the leak. Sometimes a leak doesn't
+ // occur at an actual statement (e.g., transition between blocks; end
+ // of function) so we need to walk the graph and compute a real location.
+ const ExplodedNode *LeakN = EndN;
+ PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(LeakN, SM);
+
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+
+ os << "Object leaked: ";
+
+ Optional<std::string> RegionDescription = describeRegion(FirstBinding);
+ if (RegionDescription) {
+ os << "object allocated and stored into '" << *RegionDescription << '\'';
+ } else {
+ os << "allocated object of type '" << getPrettyTypeName(Sym->getType())
+ << "'";
+ }
+
+ // Get the retain count.
+ const RefVal* RV = getRefBinding(EndN->getState(), Sym);
+ assert(RV);
+
+ if (RV->getKind() == RefVal::ErrorLeakReturned) {
+ // FIXME: Per comments in rdar://6320065, "create" only applies to CF
+ // objects. Only "copy", "alloc", "retain" and "new" transfer ownership
+ // to the caller for NS objects.
+ const Decl *D = &EndN->getCodeDecl();
+
+ os << (isa<ObjCMethodDecl>(D) ? " is returned from a method "
+ : " is returned from a function ");
+
+ if (D->hasAttr<CFReturnsNotRetainedAttr>()) {
+ os << "that is annotated as CF_RETURNS_NOT_RETAINED";
+ } else if (D->hasAttr<NSReturnsNotRetainedAttr>()) {
+ os << "that is annotated as NS_RETURNS_NOT_RETAINED";
+ } else if (D->hasAttr<OSReturnsNotRetainedAttr>()) {
+ os << "that is annotated as OS_RETURNS_NOT_RETAINED";
+ } else {
+ if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ if (BRC.getASTContext().getLangOpts().ObjCAutoRefCount) {
+ os << "managed by Automatic Reference Counting";
+ } else {
+ os << "whose name ('" << MD->getSelector().getAsString()
+ << "') does not start with "
+ "'copy', 'mutableCopy', 'alloc' or 'new'."
+ " This violates the naming convention rules"
+ " given in the Memory Management Guide for Cocoa";
+ }
+ } else {
+ const FunctionDecl *FD = cast<FunctionDecl>(D);
+ os << "whose name ('" << *FD
+ << "') does not contain 'Copy' or 'Create'. This violates the naming"
+ " convention rules given in the Memory Management Guide for Core"
+ " Foundation";
+ }
+ }
+ } else {
+ os << " is not referenced later in this execution path and has a retain "
+ "count of +" << RV->getCount();
+ }
+
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+}
+
+RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
+ ExplodedNode *n, SymbolRef sym,
+ bool registerVisitor)
+ : BugReport(D, D.getDescription(), n), Sym(sym) {
+ if (registerVisitor)
+ addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
+}
+
+RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
+ ExplodedNode *n, SymbolRef sym,
+ StringRef endText)
+ : BugReport(D, D.getDescription(), endText, n) {
+
+ addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
+}
+
+void RefLeakReport::deriveParamLocation(CheckerContext &Ctx, SymbolRef sym) {
+ const SourceManager& SMgr = Ctx.getSourceManager();
+
+ if (!sym->getOriginRegion())
+ return;
+
+ auto *Region = dyn_cast<DeclRegion>(sym->getOriginRegion());
+ if (Region) {
+ const Decl *PDecl = Region->getDecl();
+ if (PDecl && isa<ParmVarDecl>(PDecl)) {
+ PathDiagnosticLocation ParamLocation =
+ PathDiagnosticLocation::create(PDecl, SMgr);
+ Location = ParamLocation;
+ UniqueingLocation = ParamLocation;
+ UniqueingDecl = Ctx.getLocationContext()->getDecl();
+ }
+ }
+}
+
+void RefLeakReport::deriveAllocLocation(CheckerContext &Ctx,
+ SymbolRef sym) {
+ // Most bug reports are cached at the location where they occurred.
+ // With leaks, we want to unique them by the location where they were
+ // allocated, and only report a single path. To do this, we need to find
+ // the allocation site of a piece of tracked memory, which we do via a
+ // call to GetAllocationSite. This will walk the ExplodedGraph backwards.
+ // Note that this is *not* the trimmed graph; we are guaranteed, however,
+ // that all ancestor nodes that represent the allocation site have the
+ // same SourceLocation.
+ const ExplodedNode *AllocNode = nullptr;
+
+ const SourceManager& SMgr = Ctx.getSourceManager();
+
+ AllocationInfo AllocI =
+ GetAllocationSite(Ctx.getStateManager(), getErrorNode(), sym);
+
+ AllocNode = AllocI.N;
+ AllocBinding = AllocI.R;
+ markInteresting(AllocI.InterestingMethodContext);
+
+ // Get the SourceLocation for the allocation site.
+ // FIXME: This will crash the analyzer if an allocation comes from an
+ // implicit call (ex: a destructor call).
+ // (Currently there are no such allocations in Cocoa, though.)
+ AllocStmt = PathDiagnosticLocation::getStmt(AllocNode);
+
+ if (!AllocStmt) {
+ AllocBinding = nullptr;
+ return;
+ }
+
+ PathDiagnosticLocation AllocLocation =
+ PathDiagnosticLocation::createBegin(AllocStmt, SMgr,
+ AllocNode->getLocationContext());
+ Location = AllocLocation;
+
+ // Set uniqieing info, which will be used for unique the bug reports. The
+ // leaks should be uniqued on the allocation site.
+ UniqueingLocation = AllocLocation;
+ UniqueingDecl = AllocNode->getLocationContext()->getDecl();
+}
+
+void RefLeakReport::createDescription(CheckerContext &Ctx) {
+ assert(Location.isValid() && UniqueingDecl && UniqueingLocation.isValid());
+ Description.clear();
+ llvm::raw_string_ostream os(Description);
+ os << "Potential leak of an object";
+
+ Optional<std::string> RegionDescription = describeRegion(AllocBinding);
+ if (RegionDescription) {
+ os << " stored into '" << *RegionDescription << '\'';
+ } else {
+
+ // If we can't figure out the name, just supply the type information.
+ os << " of type '" << getPrettyTypeName(Sym->getType()) << "'";
+ }
+}
+
+RefLeakReport::RefLeakReport(RefCountBug &D, const LangOptions &LOpts,
+ ExplodedNode *n, SymbolRef sym,
+ CheckerContext &Ctx)
+ : RefCountReport(D, LOpts, n, sym, false) {
+
+ deriveAllocLocation(Ctx, sym);
+ if (!AllocBinding)
+ deriveParamLocation(Ctx, sym);
+
+ createDescription(Ctx);
+
+ addVisitor(llvm::make_unique<RefLeakReportVisitor>(sym));
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h
new file mode 100644
index 000000000000..9f796abe8eae
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h
@@ -0,0 +1,85 @@
+//== RetainCountDiagnostics.h - Checks for leaks and other issues -*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines diagnostics for RetainCountChecker, which implements
+// a reference count checker for Core Foundation and Cocoa on (Mac OS X).
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H
+
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/RetainSummaryManager.h"
+
+namespace clang {
+namespace ento {
+namespace retaincountchecker {
+
+class RefCountBug : public BugType {
+protected:
+ RefCountBug(const CheckerBase *checker, StringRef name)
+ : BugType(checker, name, categories::MemoryRefCount) {}
+
+public:
+ virtual const char *getDescription() const = 0;
+
+ virtual bool isLeak() const { return false; }
+};
+
+class RefCountReport : public BugReport {
+protected:
+ SymbolRef Sym;
+
+public:
+ RefCountReport(RefCountBug &D, const LangOptions &LOpts,
+ ExplodedNode *n, SymbolRef sym,
+ bool registerVisitor = true);
+
+ RefCountReport(RefCountBug &D, const LangOptions &LOpts,
+ ExplodedNode *n, SymbolRef sym,
+ StringRef endText);
+
+ llvm::iterator_range<ranges_iterator> getRanges() override {
+ const RefCountBug& BugTy = static_cast<RefCountBug&>(getBugType());
+ if (!BugTy.isLeak())
+ return BugReport::getRanges();
+ return llvm::make_range(ranges_iterator(), ranges_iterator());
+ }
+};
+
+class RefLeakReport : public RefCountReport {
+ const MemRegion* AllocBinding;
+ const Stmt *AllocStmt;
+
+ // Finds the function declaration where a leak warning for the parameter
+ // 'sym' should be raised.
+ void deriveParamLocation(CheckerContext &Ctx, SymbolRef sym);
+ // Finds the location where a leak warning for 'sym' should be raised.
+ void deriveAllocLocation(CheckerContext &Ctx, SymbolRef sym);
+ // Produces description of a leak warning which is printed on the console.
+ void createDescription(CheckerContext &Ctx);
+
+public:
+ RefLeakReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n,
+ SymbolRef sym, CheckerContext &Ctx);
+
+ PathDiagnosticLocation getLocation(const SourceManager &SM) const override {
+ assert(Location.isValid());
+ return Location;
+ }
+};
+
+} // end namespace retaincountchecker
+} // end namespace ento
+} // end namespace clang
+
+#endif
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
new file mode 100644
index 000000000000..17ef39531628
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -0,0 +1,92 @@
+//== ReturnPointerRangeChecker.cpp ------------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ReturnPointerRangeChecker, which is a path-sensitive check
+// which looks for an out-of-bound pointer being returned to callers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ReturnPointerRangeChecker :
+ public Checker< check::PreStmt<ReturnStmt> > {
+ mutable std::unique_ptr<BuiltinBug> BT;
+
+public:
+ void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
+};
+}
+
+void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
+ CheckerContext &C) const {
+ ProgramStateRef state = C.getState();
+
+ const Expr *RetE = RS->getRetValue();
+ if (!RetE)
+ return;
+
+ SVal V = C.getSVal(RetE);
+ const MemRegion *R = V.getAsRegion();
+
+ const ElementRegion *ER = dyn_cast_or_null<ElementRegion>(R);
+ if (!ER)
+ return;
+
+ DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
+ // Zero index is always in bound, this also passes ElementRegions created for
+ // pointer casts.
+ if (Idx.isZeroConstant())
+ return;
+ // FIXME: All of this out-of-bounds checking should eventually be refactored
+ // into a common place.
+
+ DefinedOrUnknownSVal NumElements
+ = C.getStoreManager().getSizeInElements(state, ER->getSuperRegion(),
+ ER->getValueType());
+
+ ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
+ ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
+ if (StOutBound && !StInBound) {
+ ExplodedNode *N = C.generateErrorNode(StOutBound);
+
+ if (!N)
+ return;
+
+ // FIXME: This bug correspond to CWE-466. Eventually we should have bug
+ // types explicitly reference such exploit categories (when applicable).
+ if (!BT)
+ BT.reset(new BuiltinBug(
+ this, "Return of pointer value outside of expected range",
+ "Returned pointer value points outside the original object "
+ "(potential buffer overflow)"));
+
+ // FIXME: It would be nice to eventually make this diagnostic more clear,
+ // e.g., by referencing the original declaration or by saying *why* this
+ // reference is outside the range.
+
+ // Generate a report for this bug.
+ auto report = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N);
+
+ report->addRange(RetE->getSourceRange());
+ C.emitReport(std::move(report));
+ }
+}
+
+void ento::registerReturnPointerRangeChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ReturnPointerRangeChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
new file mode 100644
index 000000000000..3e0613e8ba68
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
@@ -0,0 +1,123 @@
+//== ReturnUndefChecker.cpp -------------------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ReturnUndefChecker, which is a path-sensitive
+// check which looks for undefined or garbage values being returned to the
+// caller.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ReturnUndefChecker : public Checker< check::PreStmt<ReturnStmt> > {
+ mutable std::unique_ptr<BuiltinBug> BT_Undef;
+ mutable std::unique_ptr<BuiltinBug> BT_NullReference;
+
+ void emitUndef(CheckerContext &C, const Expr *RetE) const;
+ void checkReference(CheckerContext &C, const Expr *RetE,
+ DefinedOrUnknownSVal RetVal) const;
+public:
+ void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
+};
+}
+
+void ReturnUndefChecker::checkPreStmt(const ReturnStmt *RS,
+ CheckerContext &C) const {
+ const Expr *RetE = RS->getRetValue();
+ if (!RetE)
+ return;
+ SVal RetVal = C.getSVal(RetE);
+
+ const StackFrameContext *SFC = C.getStackFrame();
+ QualType RT = CallEvent::getDeclaredResultType(SFC->getDecl());
+
+ if (RetVal.isUndef()) {
+ // "return;" is modeled to evaluate to an UndefinedVal. Allow UndefinedVal
+ // to be returned in functions returning void to support this pattern:
+ // void foo() {
+ // return;
+ // }
+ // void test() {
+ // return foo();
+ // }
+ if (!RT.isNull() && RT->isVoidType())
+ return;
+
+ // Not all blocks have explicitly-specified return types; if the return type
+ // is not available, but the return value expression has 'void' type, assume
+ // Sema already checked it.
+ if (RT.isNull() && isa<BlockDecl>(SFC->getDecl()) &&
+ RetE->getType()->isVoidType())
+ return;
+
+ emitUndef(C, RetE);
+ return;
+ }
+
+ if (RT.isNull())
+ return;
+
+ if (RT->isReferenceType()) {
+ checkReference(C, RetE, RetVal.castAs<DefinedOrUnknownSVal>());
+ return;
+ }
+}
+
+static void emitBug(CheckerContext &C, BuiltinBug &BT, const Expr *RetE,
+ const Expr *TrackingE = nullptr) {
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+
+ auto Report = llvm::make_unique<BugReport>(BT, BT.getDescription(), N);
+
+ Report->addRange(RetE->getSourceRange());
+ bugreporter::trackExpressionValue(N, TrackingE ? TrackingE : RetE, *Report);
+
+ C.emitReport(std::move(Report));
+}
+
+void ReturnUndefChecker::emitUndef(CheckerContext &C, const Expr *RetE) const {
+ if (!BT_Undef)
+ BT_Undef.reset(
+ new BuiltinBug(this, "Garbage return value",
+ "Undefined or garbage value returned to caller"));
+ emitBug(C, *BT_Undef, RetE);
+}
+
+void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE,
+ DefinedOrUnknownSVal RetVal) const {
+ ProgramStateRef StNonNull, StNull;
+ std::tie(StNonNull, StNull) = C.getState()->assume(RetVal);
+
+ if (StNonNull) {
+ // Going forward, assume the location is non-null.
+ C.addTransition(StNonNull);
+ return;
+ }
+
+ // The return value is known to be null. Emit a bug report.
+ if (!BT_NullReference)
+ BT_NullReference.reset(new BuiltinBug(this, "Returning null reference"));
+
+ emitBug(C, *BT_NullReference, RetE, bugreporter::getDerefExpr(RetE));
+}
+
+void ento::registerReturnUndefChecker(CheckerManager &mgr) {
+ mgr.registerChecker<ReturnUndefChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
new file mode 100644
index 000000000000..cf03b3c21132
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
@@ -0,0 +1,205 @@
+//=- RunLoopAutoreleaseLeakChecker.cpp --------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//
+//===----------------------------------------------------------------------===//
+//
+// A checker for detecting leaks resulting from allocating temporary
+// autoreleased objects before starting the main run loop.
+//
+// Checks for two antipatterns:
+// 1. ObjCMessageExpr followed by [[NSRunLoop mainRunLoop] run] in the same
+// autorelease pool.
+// 2. ObjCMessageExpr followed by [[NSRunLoop mainRunLoop] run] in no
+// autorelease pool.
+//
+// Any temporary objects autoreleased in code called in those expressions
+// will not be deallocated until the program exits, and are effectively leaks.
+//
+//===----------------------------------------------------------------------===//
+//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+
+using namespace clang;
+using namespace ento;
+using namespace ast_matchers;
+
+namespace {
+
+const char * RunLoopBind = "NSRunLoopM";
+const char * RunLoopRunBind = "RunLoopRunM";
+const char * OtherMsgBind = "OtherMessageSentM";
+const char * AutoreleasePoolBind = "AutoreleasePoolM";
+const char * OtherStmtAutoreleasePoolBind = "OtherAutoreleasePoolM";
+
+class RunLoopAutoreleaseLeakChecker : public Checker<check::ASTCodeBody> {
+
+public:
+ void checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const;
+
+};
+
+} // end anonymous namespace
+
+/// \return Whether {@code A} occurs before {@code B} in traversal of
+/// {@code Parent}.
+/// Conceptually a very incomplete/unsound approximation of happens-before
+/// relationship (A is likely to be evaluated before B),
+/// but useful enough in this case.
+static bool seenBefore(const Stmt *Parent, const Stmt *A, const Stmt *B) {
+ for (const Stmt *C : Parent->children()) {
+ if (!C) continue;
+
+ if (C == A)
+ return true;
+
+ if (C == B)
+ return false;
+
+ return seenBefore(C, A, B);
+ }
+ return false;
+}
+
+static void emitDiagnostics(BoundNodes &Match,
+ const Decl *D,
+ BugReporter &BR,
+ AnalysisManager &AM,
+ const RunLoopAutoreleaseLeakChecker *Checker) {
+
+ assert(D->hasBody());
+ const Stmt *DeclBody = D->getBody();
+
+ AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
+
+ const auto *ME = Match.getNodeAs<ObjCMessageExpr>(OtherMsgBind);
+ assert(ME);
+
+ const auto *AP =
+ Match.getNodeAs<ObjCAutoreleasePoolStmt>(AutoreleasePoolBind);
+ const auto *OAP =
+ Match.getNodeAs<ObjCAutoreleasePoolStmt>(OtherStmtAutoreleasePoolBind);
+ bool HasAutoreleasePool = (AP != nullptr);
+
+ const auto *RL = Match.getNodeAs<ObjCMessageExpr>(RunLoopBind);
+ const auto *RLR = Match.getNodeAs<Stmt>(RunLoopRunBind);
+ assert(RLR && "Run loop launch not found");
+ assert(ME != RLR);
+
+ // Launch of run loop occurs before the message-sent expression is seen.
+ if (seenBefore(DeclBody, RLR, ME))
+ return;
+
+ if (HasAutoreleasePool && (OAP != AP))
+ return;
+
+ PathDiagnosticLocation Location = PathDiagnosticLocation::createBegin(
+ ME, BR.getSourceManager(), ADC);
+ SourceRange Range = ME->getSourceRange();
+
+ BR.EmitBasicReport(ADC->getDecl(), Checker,
+ /*Name=*/"Memory leak inside autorelease pool",
+ /*Category=*/"Memory",
+ /*Name=*/
+ (Twine("Temporary objects allocated in the") +
+ " autorelease pool " +
+ (HasAutoreleasePool ? "" : "of last resort ") +
+ "followed by the launch of " +
+ (RL ? "main run loop " : "xpc_main ") +
+ "may never get released; consider moving them to a "
+ "separate autorelease pool")
+ .str(),
+ Location, Range);
+}
+
+static StatementMatcher getRunLoopRunM(StatementMatcher Extra = anything()) {
+ StatementMatcher MainRunLoopM =
+ objcMessageExpr(hasSelector("mainRunLoop"),
+ hasReceiverType(asString("NSRunLoop")),
+ Extra)
+ .bind(RunLoopBind);
+
+ StatementMatcher MainRunLoopRunM = objcMessageExpr(hasSelector("run"),
+ hasReceiver(MainRunLoopM),
+ Extra).bind(RunLoopRunBind);
+
+ StatementMatcher XPCRunM =
+ callExpr(callee(functionDecl(hasName("xpc_main")))).bind(RunLoopRunBind);
+ return anyOf(MainRunLoopRunM, XPCRunM);
+}
+
+static StatementMatcher getOtherMessageSentM(StatementMatcher Extra = anything()) {
+ return objcMessageExpr(unless(anyOf(equalsBoundNode(RunLoopBind),
+ equalsBoundNode(RunLoopRunBind))),
+ Extra)
+ .bind(OtherMsgBind);
+}
+
+static void
+checkTempObjectsInSamePool(const Decl *D, AnalysisManager &AM, BugReporter &BR,
+ const RunLoopAutoreleaseLeakChecker *Chkr) {
+ StatementMatcher RunLoopRunM = getRunLoopRunM();
+ StatementMatcher OtherMessageSentM = getOtherMessageSentM(
+ hasAncestor(autoreleasePoolStmt().bind(OtherStmtAutoreleasePoolBind)));
+
+ StatementMatcher RunLoopInAutorelease =
+ autoreleasePoolStmt(
+ hasDescendant(RunLoopRunM),
+ hasDescendant(OtherMessageSentM)).bind(AutoreleasePoolBind);
+
+ DeclarationMatcher GroupM = decl(hasDescendant(RunLoopInAutorelease));
+
+ auto Matches = match(GroupM, *D, AM.getASTContext());
+ for (BoundNodes Match : Matches)
+ emitDiagnostics(Match, D, BR, AM, Chkr);
+}
+
+static void
+checkTempObjectsInNoPool(const Decl *D, AnalysisManager &AM, BugReporter &BR,
+ const RunLoopAutoreleaseLeakChecker *Chkr) {
+
+ auto NoPoolM = unless(hasAncestor(autoreleasePoolStmt()));
+
+ StatementMatcher RunLoopRunM = getRunLoopRunM(NoPoolM);
+ StatementMatcher OtherMessageSentM = getOtherMessageSentM(NoPoolM);
+
+ DeclarationMatcher GroupM = functionDecl(
+ isMain(),
+ hasDescendant(RunLoopRunM),
+ hasDescendant(OtherMessageSentM)
+ );
+
+ auto Matches = match(GroupM, *D, AM.getASTContext());
+
+ for (BoundNodes Match : Matches)
+ emitDiagnostics(Match, D, BR, AM, Chkr);
+
+}
+
+void RunLoopAutoreleaseLeakChecker::checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const {
+ checkTempObjectsInSamePool(D, AM, BR, this);
+ checkTempObjectsInNoPool(D, AM, BR, this);
+}
+
+void ento::registerRunLoopAutoreleaseLeakChecker(CheckerManager &mgr) {
+ mgr.registerChecker<RunLoopAutoreleaseLeakChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
new file mode 100644
index 000000000000..819d437e6883
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
@@ -0,0 +1,271 @@
+//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines a checker for proper use of fopen/fclose APIs.
+// - If a file has been closed with fclose, it should not be accessed again.
+// Accessing a closed file results in undefined behavior.
+// - If a file was opened with fopen, it must be closed with fclose before
+// the execution ends. Failing to do so results in a resource leak.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+typedef SmallVector<SymbolRef, 2> SymbolVector;
+
+struct StreamState {
+private:
+ enum Kind { Opened, Closed } K;
+ StreamState(Kind InK) : K(InK) { }
+
+public:
+ bool isOpened() const { return K == Opened; }
+ bool isClosed() const { return K == Closed; }
+
+ static StreamState getOpened() { return StreamState(Opened); }
+ static StreamState getClosed() { return StreamState(Closed); }
+
+ bool operator==(const StreamState &X) const {
+ return K == X.K;
+ }
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(K);
+ }
+};
+
+class SimpleStreamChecker : public Checker<check::PostCall,
+ check::PreCall,
+ check::DeadSymbols,
+ check::PointerEscape> {
+ CallDescription OpenFn, CloseFn;
+
+ std::unique_ptr<BugType> DoubleCloseBugType;
+ std::unique_ptr<BugType> LeakBugType;
+
+ void reportDoubleClose(SymbolRef FileDescSym,
+ const CallEvent &Call,
+ CheckerContext &C) const;
+
+ void reportLeaks(ArrayRef<SymbolRef> LeakedStreams, CheckerContext &C,
+ ExplodedNode *ErrNode) const;
+
+ bool guaranteedNotToCloseFile(const CallEvent &Call) const;
+
+public:
+ SimpleStreamChecker();
+
+ /// Process fopen.
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ /// Process fclose.
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+
+ /// Stop tracking addresses which escape.
+ ProgramStateRef checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const;
+};
+
+} // end anonymous namespace
+
+/// The state of the checker is a map from tracked stream symbols to their
+/// state. Let's store it in the ProgramState.
+REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
+
+namespace {
+class StopTrackingCallback final : public SymbolVisitor {
+ ProgramStateRef state;
+public:
+ StopTrackingCallback(ProgramStateRef st) : state(std::move(st)) {}
+ ProgramStateRef getState() const { return state; }
+
+ bool VisitSymbol(SymbolRef sym) override {
+ state = state->remove<StreamMap>(sym);
+ return true;
+ }
+};
+} // end anonymous namespace
+
+SimpleStreamChecker::SimpleStreamChecker()
+ : OpenFn("fopen"), CloseFn("fclose", 1) {
+ // Initialize the bug types.
+ DoubleCloseBugType.reset(
+ new BugType(this, "Double fclose", "Unix Stream API Error"));
+
+ LeakBugType.reset(
+ new BugType(this, "Resource Leak", "Unix Stream API Error"));
+ // Sinks are higher importance bugs as well as calls to assert() or exit(0).
+ LeakBugType->setSuppressOnSink(true);
+}
+
+void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!Call.isGlobalCFunction())
+ return;
+
+ if (!Call.isCalled(OpenFn))
+ return;
+
+ // Get the symbolic value corresponding to the file handle.
+ SymbolRef FileDesc = Call.getReturnValue().getAsSymbol();
+ if (!FileDesc)
+ return;
+
+ // Generate the next transition (an edge in the exploded graph).
+ ProgramStateRef State = C.getState();
+ State = State->set<StreamMap>(FileDesc, StreamState::getOpened());
+ C.addTransition(State);
+}
+
+void SimpleStreamChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!Call.isGlobalCFunction())
+ return;
+
+ if (!Call.isCalled(CloseFn))
+ return;
+
+ // Get the symbolic value corresponding to the file handle.
+ SymbolRef FileDesc = Call.getArgSVal(0).getAsSymbol();
+ if (!FileDesc)
+ return;
+
+ // Check if the stream has already been closed.
+ ProgramStateRef State = C.getState();
+ const StreamState *SS = State->get<StreamMap>(FileDesc);
+ if (SS && SS->isClosed()) {
+ reportDoubleClose(FileDesc, Call, C);
+ return;
+ }
+
+ // Generate the next transition, in which the stream is closed.
+ State = State->set<StreamMap>(FileDesc, StreamState::getClosed());
+ C.addTransition(State);
+}
+
+static bool isLeaked(SymbolRef Sym, const StreamState &SS,
+ bool IsSymDead, ProgramStateRef State) {
+ if (IsSymDead && SS.isOpened()) {
+ // If a symbol is NULL, assume that fopen failed on this path.
+ // A symbol should only be considered leaked if it is non-null.
+ ConstraintManager &CMgr = State->getConstraintManager();
+ ConditionTruthVal OpenFailed = CMgr.isNull(State, Sym);
+ return !OpenFailed.isConstrainedTrue();
+ }
+ return false;
+}
+
+void SimpleStreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ SymbolVector LeakedStreams;
+ StreamMapTy TrackedStreams = State->get<StreamMap>();
+ for (StreamMapTy::iterator I = TrackedStreams.begin(),
+ E = TrackedStreams.end(); I != E; ++I) {
+ SymbolRef Sym = I->first;
+ bool IsSymDead = SymReaper.isDead(Sym);
+
+ // Collect leaked symbols.
+ if (isLeaked(Sym, I->second, IsSymDead, State))
+ LeakedStreams.push_back(Sym);
+
+ // Remove the dead symbol from the streams map.
+ if (IsSymDead)
+ State = State->remove<StreamMap>(Sym);
+ }
+
+ ExplodedNode *N = C.generateNonFatalErrorNode(State);
+ if (!N)
+ return;
+ reportLeaks(LeakedStreams, C, N);
+}
+
+void SimpleStreamChecker::reportDoubleClose(SymbolRef FileDescSym,
+ const CallEvent &Call,
+ CheckerContext &C) const {
+ // We reached a bug, stop exploring the path here by generating a sink.
+ ExplodedNode *ErrNode = C.generateErrorNode();
+ // If we've already reached this node on another path, return.
+ if (!ErrNode)
+ return;
+
+ // Generate the report.
+ auto R = llvm::make_unique<BugReport>(*DoubleCloseBugType,
+ "Closing a previously closed file stream", ErrNode);
+ R->addRange(Call.getSourceRange());
+ R->markInteresting(FileDescSym);
+ C.emitReport(std::move(R));
+}
+
+void SimpleStreamChecker::reportLeaks(ArrayRef<SymbolRef> LeakedStreams,
+ CheckerContext &C,
+ ExplodedNode *ErrNode) const {
+ // Attach bug reports to the leak node.
+ // TODO: Identify the leaked file descriptor.
+ for (SymbolRef LeakedStream : LeakedStreams) {
+ auto R = llvm::make_unique<BugReport>(*LeakBugType,
+ "Opened file is never closed; potential resource leak", ErrNode);
+ R->markInteresting(LeakedStream);
+ C.emitReport(std::move(R));
+ }
+}
+
+bool SimpleStreamChecker::guaranteedNotToCloseFile(const CallEvent &Call) const{
+ // If it's not in a system header, assume it might close a file.
+ if (!Call.isInSystemHeader())
+ return false;
+
+ // Handle cases where we know a buffer's /address/ can escape.
+ if (Call.argumentsMayEscape())
+ return false;
+
+ // Note, even though fclose closes the file, we do not list it here
+ // since the checker is modeling the call.
+
+ return true;
+}
+
+// If the pointer we are tracking escaped, do not track the symbol as
+// we cannot reason about it anymore.
+ProgramStateRef
+SimpleStreamChecker::checkPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind) const {
+ // If we know that the call cannot close a file, there is nothing to do.
+ if (Kind == PSK_DirectEscapeOnCall && guaranteedNotToCloseFile(*Call)) {
+ return State;
+ }
+
+ for (InvalidatedSymbols::const_iterator I = Escaped.begin(),
+ E = Escaped.end();
+ I != E; ++I) {
+ SymbolRef Sym = *I;
+
+ // The symbol escaped. Optimistically, assume that the corresponding file
+ // handle will be closed somewhere else.
+ State = State->remove<StreamMap>(Sym);
+ }
+ return State;
+}
+
+void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
+ mgr.registerChecker<SimpleStreamChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
new file mode 100644
index 000000000000..0f53d826a5f6
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -0,0 +1,371 @@
+//=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines stack address leak checker, which checks if an invalid
+// stack address is stored into a global or heap location. See CERT DCL30-C.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+using namespace clang;
+using namespace ento;
+
+namespace {
+class StackAddrEscapeChecker
+ : public Checker<check::PreCall, check::PreStmt<ReturnStmt>,
+ check::EndFunction> {
+ mutable IdentifierInfo *dispatch_semaphore_tII;
+ mutable std::unique_ptr<BuiltinBug> BT_stackleak;
+ mutable std::unique_ptr<BuiltinBug> BT_returnstack;
+ mutable std::unique_ptr<BuiltinBug> BT_capturedstackasync;
+ mutable std::unique_ptr<BuiltinBug> BT_capturedstackret;
+
+public:
+ enum CheckKind {
+ CK_StackAddrEscapeChecker,
+ CK_StackAddrAsyncEscapeChecker,
+ CK_NumCheckKinds
+ };
+
+ DefaultBool ChecksEnabled[CK_NumCheckKinds];
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &Ctx) const;
+
+private:
+ void checkReturnedBlockCaptures(const BlockDataRegion &B,
+ CheckerContext &C) const;
+ void checkAsyncExecutedBlockCaptures(const BlockDataRegion &B,
+ CheckerContext &C) const;
+ void EmitStackError(CheckerContext &C, const MemRegion *R,
+ const Expr *RetE) const;
+ bool isSemaphoreCaptured(const BlockDecl &B) const;
+ static SourceRange genName(raw_ostream &os, const MemRegion *R,
+ ASTContext &Ctx);
+ static SmallVector<const MemRegion *, 4>
+ getCapturedStackRegions(const BlockDataRegion &B, CheckerContext &C);
+ static bool isArcManagedBlock(const MemRegion *R, CheckerContext &C);
+ static bool isNotInCurrentFrame(const MemRegion *R, CheckerContext &C);
+};
+} // namespace
+
+SourceRange StackAddrEscapeChecker::genName(raw_ostream &os, const MemRegion *R,
+ ASTContext &Ctx) {
+ // Get the base region, stripping away fields and elements.
+ R = R->getBaseRegion();
+ SourceManager &SM = Ctx.getSourceManager();
+ SourceRange range;
+ os << "Address of ";
+
+ // Check if the region is a compound literal.
+ if (const auto *CR = dyn_cast<CompoundLiteralRegion>(R)) {
+ const CompoundLiteralExpr *CL = CR->getLiteralExpr();
+ os << "stack memory associated with a compound literal "
+ "declared on line "
+ << SM.getExpansionLineNumber(CL->getBeginLoc()) << " returned to caller";
+ range = CL->getSourceRange();
+ } else if (const auto *AR = dyn_cast<AllocaRegion>(R)) {
+ const Expr *ARE = AR->getExpr();
+ SourceLocation L = ARE->getBeginLoc();
+ range = ARE->getSourceRange();
+ os << "stack memory allocated by call to alloca() on line "
+ << SM.getExpansionLineNumber(L);
+ } else if (const auto *BR = dyn_cast<BlockDataRegion>(R)) {
+ const BlockDecl *BD = BR->getCodeRegion()->getDecl();
+ SourceLocation L = BD->getBeginLoc();
+ range = BD->getSourceRange();
+ os << "stack-allocated block declared on line "
+ << SM.getExpansionLineNumber(L);
+ } else if (const auto *VR = dyn_cast<VarRegion>(R)) {
+ os << "stack memory associated with local variable '" << VR->getString()
+ << '\'';
+ range = VR->getDecl()->getSourceRange();
+ } else if (const auto *TOR = dyn_cast<CXXTempObjectRegion>(R)) {
+ QualType Ty = TOR->getValueType().getLocalUnqualifiedType();
+ os << "stack memory associated with temporary object of type '";
+ Ty.print(os, Ctx.getPrintingPolicy());
+ os << "'";
+ range = TOR->getExpr()->getSourceRange();
+ } else {
+ llvm_unreachable("Invalid region in ReturnStackAddressChecker.");
+ }
+
+ return range;
+}
+
+bool StackAddrEscapeChecker::isArcManagedBlock(const MemRegion *R,
+ CheckerContext &C) {
+ assert(R && "MemRegion should not be null");
+ return C.getASTContext().getLangOpts().ObjCAutoRefCount &&
+ isa<BlockDataRegion>(R);
+}
+
+bool StackAddrEscapeChecker::isNotInCurrentFrame(const MemRegion *R,
+ CheckerContext &C) {
+ const StackSpaceRegion *S = cast<StackSpaceRegion>(R->getMemorySpace());
+ return S->getStackFrame() != C.getStackFrame();
+}
+
+bool StackAddrEscapeChecker::isSemaphoreCaptured(const BlockDecl &B) const {
+ if (!dispatch_semaphore_tII)
+ dispatch_semaphore_tII = &B.getASTContext().Idents.get("dispatch_semaphore_t");
+ for (const auto &C : B.captures()) {
+ const auto *T = C.getVariable()->getType()->getAs<TypedefType>();
+ if (T && T->getDecl()->getIdentifier() == dispatch_semaphore_tII)
+ return true;
+ }
+ return false;
+}
+
+SmallVector<const MemRegion *, 4>
+StackAddrEscapeChecker::getCapturedStackRegions(const BlockDataRegion &B,
+ CheckerContext &C) {
+ SmallVector<const MemRegion *, 4> Regions;
+ BlockDataRegion::referenced_vars_iterator I = B.referenced_vars_begin();
+ BlockDataRegion::referenced_vars_iterator E = B.referenced_vars_end();
+ for (; I != E; ++I) {
+ SVal Val = C.getState()->getSVal(I.getCapturedRegion());
+ const MemRegion *Region = Val.getAsRegion();
+ if (Region && isa<StackSpaceRegion>(Region->getMemorySpace()))
+ Regions.push_back(Region);
+ }
+ return Regions;
+}
+
+void StackAddrEscapeChecker::EmitStackError(CheckerContext &C,
+ const MemRegion *R,
+ const Expr *RetE) const {
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (!N)
+ return;
+ if (!BT_returnstack)
+ BT_returnstack = llvm::make_unique<BuiltinBug>(
+ this, "Return of address to stack-allocated memory");
+ // Generate a report for this bug.
+ SmallString<128> buf;
+ llvm::raw_svector_ostream os(buf);
+ SourceRange range = genName(os, R, C.getASTContext());
+ os << " returned to caller";
+ auto report = llvm::make_unique<BugReport>(*BT_returnstack, os.str(), N);
+ report->addRange(RetE->getSourceRange());
+ if (range.isValid())
+ report->addRange(range);
+ C.emitReport(std::move(report));
+}
+
+void StackAddrEscapeChecker::checkAsyncExecutedBlockCaptures(
+ const BlockDataRegion &B, CheckerContext &C) const {
+ // There is a not-too-uncommon idiom
+ // where a block passed to dispatch_async captures a semaphore
+ // and then the thread (which called dispatch_async) is blocked on waiting
+ // for the completion of the execution of the block
+ // via dispatch_semaphore_wait. To avoid false-positives (for now)
+ // we ignore all the blocks which have captured
+ // a variable of the type "dispatch_semaphore_t".
+ if (isSemaphoreCaptured(*B.getDecl()))
+ return;
+ for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
+ // The block passed to dispatch_async may capture another block
+ // created on the stack. However, there is no leak in this situaton,
+ // no matter if ARC or no ARC is enabled:
+ // dispatch_async copies the passed "outer" block (via Block_copy)
+ // and if the block has captured another "inner" block,
+ // the "inner" block will be copied as well.
+ if (isa<BlockDataRegion>(Region))
+ continue;
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (!N)
+ continue;
+ if (!BT_capturedstackasync)
+ BT_capturedstackasync = llvm::make_unique<BuiltinBug>(
+ this, "Address of stack-allocated memory is captured");
+ SmallString<128> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+ SourceRange Range = genName(Out, Region, C.getASTContext());
+ Out << " is captured by an asynchronously-executed block";
+ auto Report =
+ llvm::make_unique<BugReport>(*BT_capturedstackasync, Out.str(), N);
+ if (Range.isValid())
+ Report->addRange(Range);
+ C.emitReport(std::move(Report));
+ }
+}
+
+void StackAddrEscapeChecker::checkReturnedBlockCaptures(
+ const BlockDataRegion &B, CheckerContext &C) const {
+ for (const MemRegion *Region : getCapturedStackRegions(B, C)) {
+ if (isArcManagedBlock(Region, C) || isNotInCurrentFrame(Region, C))
+ continue;
+ ExplodedNode *N = C.generateNonFatalErrorNode();
+ if (!N)
+ continue;
+ if (!BT_capturedstackret)
+ BT_capturedstackret = llvm::make_unique<BuiltinBug>(
+ this, "Address of stack-allocated memory is captured");
+ SmallString<128> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+ SourceRange Range = genName(Out, Region, C.getASTContext());
+ Out << " is captured by a returned block";
+ auto Report =
+ llvm::make_unique<BugReport>(*BT_capturedstackret, Out.str(), N);
+ if (Range.isValid())
+ Report->addRange(Range);
+ C.emitReport(std::move(Report));
+ }
+}
+
+void StackAddrEscapeChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!ChecksEnabled[CK_StackAddrAsyncEscapeChecker])
+ return;
+ if (!Call.isGlobalCFunction("dispatch_after") &&
+ !Call.isGlobalCFunction("dispatch_async"))
+ return;
+ for (unsigned Idx = 0, NumArgs = Call.getNumArgs(); Idx < NumArgs; ++Idx) {
+ if (const BlockDataRegion *B = dyn_cast_or_null<BlockDataRegion>(
+ Call.getArgSVal(Idx).getAsRegion()))
+ checkAsyncExecutedBlockCaptures(*B, C);
+ }
+}
+
+void StackAddrEscapeChecker::checkPreStmt(const ReturnStmt *RS,
+ CheckerContext &C) const {
+ if (!ChecksEnabled[CK_StackAddrEscapeChecker])
+ return;
+
+ const Expr *RetE = RS->getRetValue();
+ if (!RetE)
+ return;
+ RetE = RetE->IgnoreParens();
+
+ SVal V = C.getSVal(RetE);
+ const MemRegion *R = V.getAsRegion();
+ if (!R)
+ return;
+
+ if (const BlockDataRegion *B = dyn_cast<BlockDataRegion>(R))
+ checkReturnedBlockCaptures(*B, C);
+
+ if (!isa<StackSpaceRegion>(R->getMemorySpace()) ||
+ isNotInCurrentFrame(R, C) || isArcManagedBlock(R, C))
+ return;
+
+ // Returning a record by value is fine. (In this case, the returned
+ // expression will be a copy-constructor, possibly wrapped in an
+ // ExprWithCleanups node.)
+ if (const ExprWithCleanups *Cleanup = dyn_cast<ExprWithCleanups>(RetE))
+ RetE = Cleanup->getSubExpr();
+ if (isa<CXXConstructExpr>(RetE) && RetE->getType()->isRecordType())
+ return;
+
+ // The CK_CopyAndAutoreleaseBlockObject cast causes the block to be copied
+ // so the stack address is not escaping here.
+ if (auto *ICE = dyn_cast<ImplicitCastExpr>(RetE)) {
+ if (isa<BlockDataRegion>(R) &&
+ ICE->getCastKind() == CK_CopyAndAutoreleaseBlockObject) {
+ return;
+ }
+ }
+
+ EmitStackError(C, R, RetE);
+}
+
+void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
+ CheckerContext &Ctx) const {
+ if (!ChecksEnabled[CK_StackAddrEscapeChecker])
+ return;
+
+ ProgramStateRef State = Ctx.getState();
+
+ // Iterate over all bindings to global variables and see if it contains
+ // a memory region in the stack space.
+ class CallBack : public StoreManager::BindingsHandler {
+ private:
+ CheckerContext &Ctx;
+ const StackFrameContext *CurSFC;
+
+ public:
+ SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
+
+ CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {}
+
+ bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
+ SVal Val) override {
+
+ if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
+ return true;
+ const MemRegion *VR = Val.getAsRegion();
+ if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) &&
+ !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx))
+ V.emplace_back(Region, VR);
+ return true;
+ }
+ };
+
+ CallBack Cb(Ctx);
+ State->getStateManager().getStoreManager().iterBindings(State->getStore(),
+ Cb);
+
+ if (Cb.V.empty())
+ return;
+
+ // Generate an error node.
+ ExplodedNode *N = Ctx.generateNonFatalErrorNode(State);
+ if (!N)
+ return;
+
+ if (!BT_stackleak)
+ BT_stackleak = llvm::make_unique<BuiltinBug>(
+ this, "Stack address stored into global variable",
+ "Stack address was saved into a global variable. "
+ "This is dangerous because the address will become "
+ "invalid after returning from the function");
+
+ for (const auto &P : Cb.V) {
+ // Generate a report for this bug.
+ SmallString<128> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+ SourceRange Range = genName(Out, P.second, Ctx.getASTContext());
+ Out << " is still referred to by the ";
+ if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace()))
+ Out << "static";
+ else
+ Out << "global";
+ Out << " variable '";
+ const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion());
+ Out << *VR->getDecl()
+ << "' upon returning to the caller. This will be a dangling reference";
+ auto Report = llvm::make_unique<BugReport>(*BT_stackleak, Out.str(), N);
+ if (Range.isValid())
+ Report->addRange(Range);
+
+ Ctx.emitReport(std::move(Report));
+ }
+}
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &Mgr) { \
+ StackAddrEscapeChecker *Chk = \
+ Mgr.registerChecker<StackAddrEscapeChecker>(); \
+ Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
+ }
+
+REGISTER_CHECKER(StackAddrEscapeChecker)
+REGISTER_CHECKER(StackAddrAsyncEscapeChecker)
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
new file mode 100644
index 000000000000..6478128ce954
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -0,0 +1,1058 @@
+//=== StdLibraryFunctionsChecker.cpp - Model standard functions -*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker improves modeling of a few simple library functions.
+// It does not generate warnings.
+//
+// This checker provides a specification format - `FunctionSummaryTy' - and
+// contains descriptions of some library functions in this format. Each
+// specification contains a list of branches for splitting the program state
+// upon call, and range constraints on argument and return-value symbols that
+// are satisfied on each branch. This spec can be expanded to include more
+// items, like external effects of the function.
+//
+// The main difference between this approach and the body farms technique is
+// in more explicit control over how many branches are produced. For example,
+// consider standard C function `ispunct(int x)', which returns a non-zero value
+// iff `x' is a punctuation character, that is, when `x' is in range
+// ['!', '/'] [':', '@'] U ['[', '\`'] U ['{', '~'].
+// `FunctionSummaryTy' provides only two branches for this function. However,
+// any attempt to describe this range with if-statements in the body farm
+// would result in many more branches. Because each branch needs to be analyzed
+// independently, this significantly reduces performance. Additionally,
+// once we consider a branch on which `x' is in range, say, ['!', '/'],
+// we assume that such branch is an important separate path through the program,
+// which may lead to false positives because considering this particular path
+// was not consciously intended, and therefore it might have been unreachable.
+//
+// This checker uses eval::Call for modeling "pure" functions, for which
+// their `FunctionSummaryTy' is a precise model. This avoids unnecessary
+// invalidation passes. Conflicts with other checkers are unlikely because
+// if the function has no other effects, other checkers would probably never
+// want to improve upon the modeling done by this checker.
+//
+// Non-"pure" functions, for which only partial improvement over the default
+// behavior is expected, are modeled via check::PostCall, non-intrusively.
+//
+// The following standard C functions are currently supported:
+//
+// fgetc getline isdigit isupper
+// fread isalnum isgraph isxdigit
+// fwrite isalpha islower read
+// getc isascii isprint write
+// getchar isblank ispunct
+// getdelim iscntrl isspace
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace clang::ento;
+
+namespace {
+class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> {
+ /// Below is a series of typedefs necessary to define function specs.
+ /// We avoid nesting types here because each additional qualifier
+ /// would need to be repeated in every function spec.
+ struct FunctionSummaryTy;
+
+ /// Specify how much the analyzer engine should entrust modeling this function
+ /// to us. If he doesn't, he performs additional invalidations.
+ enum InvalidationKindTy { NoEvalCall, EvalCallAsPure };
+
+ /// A pair of ValueRangeKindTy and IntRangeVectorTy would describe a range
+ /// imposed on a particular argument or return value symbol.
+ ///
+ /// Given a range, should the argument stay inside or outside this range?
+ /// The special `ComparesToArgument' value indicates that we should
+ /// impose a constraint that involves other argument or return value symbols.
+ enum ValueRangeKindTy { OutOfRange, WithinRange, ComparesToArgument };
+
+ // The universal integral type to use in value range descriptions.
+ // Unsigned to make sure overflows are well-defined.
+ typedef uint64_t RangeIntTy;
+
+ /// Normally, describes a single range constraint, eg. {{0, 1}, {3, 4}} is
+ /// a non-negative integer, which less than 5 and not equal to 2. For
+ /// `ComparesToArgument', holds information about how exactly to compare to
+ /// the argument.
+ typedef std::vector<std::pair<RangeIntTy, RangeIntTy>> IntRangeVectorTy;
+
+ /// A reference to an argument or return value by its number.
+ /// ArgNo in CallExpr and CallEvent is defined as Unsigned, but
+ /// obviously uint32_t should be enough for all practical purposes.
+ typedef uint32_t ArgNoTy;
+ static const ArgNoTy Ret = std::numeric_limits<ArgNoTy>::max();
+
+ /// Incapsulates a single range on a single symbol within a branch.
+ class ValueRange {
+ ArgNoTy ArgNo; // Argument to which we apply the range.
+ ValueRangeKindTy Kind; // Kind of range definition.
+ IntRangeVectorTy Args; // Polymorphic arguments.
+
+ public:
+ ValueRange(ArgNoTy ArgNo, ValueRangeKindTy Kind,
+ const IntRangeVectorTy &Args)
+ : ArgNo(ArgNo), Kind(Kind), Args(Args) {}
+
+ ArgNoTy getArgNo() const { return ArgNo; }
+ ValueRangeKindTy getKind() const { return Kind; }
+
+ BinaryOperator::Opcode getOpcode() const {
+ assert(Kind == ComparesToArgument);
+ assert(Args.size() == 1);
+ BinaryOperator::Opcode Op =
+ static_cast<BinaryOperator::Opcode>(Args[0].first);
+ assert(BinaryOperator::isComparisonOp(Op) &&
+ "Only comparison ops are supported for ComparesToArgument");
+ return Op;
+ }
+
+ ArgNoTy getOtherArgNo() const {
+ assert(Kind == ComparesToArgument);
+ assert(Args.size() == 1);
+ return static_cast<ArgNoTy>(Args[0].second);
+ }
+
+ const IntRangeVectorTy &getRanges() const {
+ assert(Kind != ComparesToArgument);
+ return Args;
+ }
+
+ // We avoid creating a virtual apply() method because
+ // it makes initializer lists harder to write.
+ private:
+ ProgramStateRef
+ applyAsOutOfRange(ProgramStateRef State, const CallEvent &Call,
+ const FunctionSummaryTy &Summary) const;
+ ProgramStateRef
+ applyAsWithinRange(ProgramStateRef State, const CallEvent &Call,
+ const FunctionSummaryTy &Summary) const;
+ ProgramStateRef
+ applyAsComparesToArgument(ProgramStateRef State, const CallEvent &Call,
+ const FunctionSummaryTy &Summary) const;
+
+ public:
+ ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call,
+ const FunctionSummaryTy &Summary) const {
+ switch (Kind) {
+ case OutOfRange:
+ return applyAsOutOfRange(State, Call, Summary);
+ case WithinRange:
+ return applyAsWithinRange(State, Call, Summary);
+ case ComparesToArgument:
+ return applyAsComparesToArgument(State, Call, Summary);
+ }
+ llvm_unreachable("Unknown ValueRange kind!");
+ }
+ };
+
+ /// The complete list of ranges that defines a single branch.
+ typedef std::vector<ValueRange> ValueRangeSet;
+
+ /// Includes information about function prototype (which is necessary to
+ /// ensure we're modeling the right function and casting values properly),
+ /// approach to invalidation, and a list of branches - essentially, a list
+ /// of list of ranges - essentially, a list of lists of lists of segments.
+ struct FunctionSummaryTy {
+ const std::vector<QualType> ArgTypes;
+ const QualType RetType;
+ const InvalidationKindTy InvalidationKind;
+ const std::vector<ValueRangeSet> Ranges;
+
+ private:
+ static void assertTypeSuitableForSummary(QualType T) {
+ assert(!T->isVoidType() &&
+ "We should have had no significant void types in the spec");
+ assert(T.isCanonical() &&
+ "We should only have canonical types in the spec");
+ // FIXME: lift this assert (but not the ones above!)
+ assert(T->isIntegralOrEnumerationType() &&
+ "We only support integral ranges in the spec");
+ }
+
+ public:
+ QualType getArgType(ArgNoTy ArgNo) const {
+ QualType T = (ArgNo == Ret) ? RetType : ArgTypes[ArgNo];
+ assertTypeSuitableForSummary(T);
+ return T;
+ }
+
+ /// Try our best to figure out if the call expression is the call of
+ /// *the* library function to which this specification applies.
+ bool matchesCall(const CallExpr *CE) const;
+ };
+
+ // The same function (as in, function identifier) may have different
+ // summaries assigned to it, with different argument and return value types.
+ // We call these "variants" of the function. This can be useful for handling
+ // C++ function overloads, and also it can be used when the same function
+ // may have different definitions on different platforms.
+ typedef std::vector<FunctionSummaryTy> FunctionVariantsTy;
+
+ // The map of all functions supported by the checker. It is initialized
+ // lazily, and it doesn't change after initialization.
+ typedef llvm::StringMap<FunctionVariantsTy> FunctionSummaryMapTy;
+ mutable FunctionSummaryMapTy FunctionSummaryMap;
+
+ // Auxiliary functions to support ArgNoTy within all structures
+ // in a unified manner.
+ static QualType getArgType(const FunctionSummaryTy &Summary, ArgNoTy ArgNo) {
+ return Summary.getArgType(ArgNo);
+ }
+ static QualType getArgType(const CallEvent &Call, ArgNoTy ArgNo) {
+ return ArgNo == Ret ? Call.getResultType().getCanonicalType()
+ : Call.getArgExpr(ArgNo)->getType().getCanonicalType();
+ }
+ static QualType getArgType(const CallExpr *CE, ArgNoTy ArgNo) {
+ return ArgNo == Ret ? CE->getType().getCanonicalType()
+ : CE->getArg(ArgNo)->getType().getCanonicalType();
+ }
+ static SVal getArgSVal(const CallEvent &Call, ArgNoTy ArgNo) {
+ return ArgNo == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgNo);
+ }
+
+public:
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+
+private:
+ Optional<FunctionSummaryTy> findFunctionSummary(const FunctionDecl *FD,
+ const CallExpr *CE,
+ CheckerContext &C) const;
+
+ void initFunctionSummaries(BasicValueFactory &BVF) const;
+};
+} // end of anonymous namespace
+
+ProgramStateRef StdLibraryFunctionsChecker::ValueRange::applyAsOutOfRange(
+ ProgramStateRef State, const CallEvent &Call,
+ const FunctionSummaryTy &Summary) const {
+
+ ProgramStateManager &Mgr = State->getStateManager();
+ SValBuilder &SVB = Mgr.getSValBuilder();
+ BasicValueFactory &BVF = SVB.getBasicValueFactory();
+ ConstraintManager &CM = Mgr.getConstraintManager();
+ QualType T = getArgType(Summary, getArgNo());
+ SVal V = getArgSVal(Call, getArgNo());
+
+ if (auto N = V.getAs<NonLoc>()) {
+ const IntRangeVectorTy &R = getRanges();
+ size_t E = R.size();
+ for (size_t I = 0; I != E; ++I) {
+ const llvm::APSInt &Min = BVF.getValue(R[I].first, T);
+ const llvm::APSInt &Max = BVF.getValue(R[I].second, T);
+ assert(Min <= Max);
+ State = CM.assumeInclusiveRange(State, *N, Min, Max, false);
+ if (!State)
+ break;
+ }
+ }
+
+ return State;
+}
+
+ProgramStateRef
+StdLibraryFunctionsChecker::ValueRange::applyAsWithinRange(
+ ProgramStateRef State, const CallEvent &Call,
+ const FunctionSummaryTy &Summary) const {
+
+ ProgramStateManager &Mgr = State->getStateManager();
+ SValBuilder &SVB = Mgr.getSValBuilder();
+ BasicValueFactory &BVF = SVB.getBasicValueFactory();
+ ConstraintManager &CM = Mgr.getConstraintManager();
+ QualType T = getArgType(Summary, getArgNo());
+ SVal V = getArgSVal(Call, getArgNo());
+
+ // "WithinRange R" is treated as "outside [T_MIN, T_MAX] \ R".
+ // We cut off [T_MIN, min(R) - 1] and [max(R) + 1, T_MAX] if necessary,
+ // and then cut away all holes in R one by one.
+ if (auto N = V.getAs<NonLoc>()) {
+ const IntRangeVectorTy &R = getRanges();
+ size_t E = R.size();
+
+ const llvm::APSInt &MinusInf = BVF.getMinValue(T);
+ const llvm::APSInt &PlusInf = BVF.getMaxValue(T);
+
+ const llvm::APSInt &Left = BVF.getValue(R[0].first - 1ULL, T);
+ if (Left != PlusInf) {
+ assert(MinusInf <= Left);
+ State = CM.assumeInclusiveRange(State, *N, MinusInf, Left, false);
+ if (!State)
+ return nullptr;
+ }
+
+ const llvm::APSInt &Right = BVF.getValue(R[E - 1].second + 1ULL, T);
+ if (Right != MinusInf) {
+ assert(Right <= PlusInf);
+ State = CM.assumeInclusiveRange(State, *N, Right, PlusInf, false);
+ if (!State)
+ return nullptr;
+ }
+
+ for (size_t I = 1; I != E; ++I) {
+ const llvm::APSInt &Min = BVF.getValue(R[I - 1].second + 1ULL, T);
+ const llvm::APSInt &Max = BVF.getValue(R[I].first - 1ULL, T);
+ assert(Min <= Max);
+ State = CM.assumeInclusiveRange(State, *N, Min, Max, false);
+ if (!State)
+ return nullptr;
+ }
+ }
+
+ return State;
+}
+
+ProgramStateRef
+StdLibraryFunctionsChecker::ValueRange::applyAsComparesToArgument(
+ ProgramStateRef State, const CallEvent &Call,
+ const FunctionSummaryTy &Summary) const {
+
+ ProgramStateManager &Mgr = State->getStateManager();
+ SValBuilder &SVB = Mgr.getSValBuilder();
+ QualType CondT = SVB.getConditionType();
+ QualType T = getArgType(Summary, getArgNo());
+ SVal V = getArgSVal(Call, getArgNo());
+
+ BinaryOperator::Opcode Op = getOpcode();
+ ArgNoTy OtherArg = getOtherArgNo();
+ SVal OtherV = getArgSVal(Call, OtherArg);
+ QualType OtherT = getArgType(Call, OtherArg);
+ // Note: we avoid integral promotion for comparison.
+ OtherV = SVB.evalCast(OtherV, T, OtherT);
+ if (auto CompV = SVB.evalBinOp(State, Op, V, OtherV, CondT)
+ .getAs<DefinedOrUnknownSVal>())
+ State = State->assume(*CompV, true);
+ return State;
+}
+
+void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!FD)
+ return;
+
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return;
+
+ Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C);
+ if (!FoundSummary)
+ return;
+
+ // Now apply ranges.
+ const FunctionSummaryTy &Summary = *FoundSummary;
+ ProgramStateRef State = C.getState();
+
+ for (const auto &VRS: Summary.Ranges) {
+ ProgramStateRef NewState = State;
+ for (const auto &VR: VRS) {
+ NewState = VR.apply(NewState, Call, Summary);
+ if (!NewState)
+ break;
+ }
+
+ if (NewState && NewState != State)
+ C.addTransition(NewState);
+ }
+}
+
+bool StdLibraryFunctionsChecker::evalCall(const CallExpr *CE,
+ CheckerContext &C) const {
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl());
+ if (!FD)
+ return false;
+
+ Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C);
+ if (!FoundSummary)
+ return false;
+
+ const FunctionSummaryTy &Summary = *FoundSummary;
+ switch (Summary.InvalidationKind) {
+ case EvalCallAsPure: {
+ ProgramStateRef State = C.getState();
+ const LocationContext *LC = C.getLocationContext();
+ SVal V = C.getSValBuilder().conjureSymbolVal(
+ CE, LC, CE->getType().getCanonicalType(), C.blockCount());
+ State = State->BindExpr(CE, LC, V);
+ C.addTransition(State);
+ return true;
+ }
+ case NoEvalCall:
+ // Summary tells us to avoid performing eval::Call. The function is possibly
+ // evaluated by another checker, or evaluated conservatively.
+ return false;
+ }
+ llvm_unreachable("Unknown invalidation kind!");
+}
+
+bool StdLibraryFunctionsChecker::FunctionSummaryTy::matchesCall(
+ const CallExpr *CE) const {
+ // Check number of arguments:
+ if (CE->getNumArgs() != ArgTypes.size())
+ return false;
+
+ // Check return type if relevant:
+ if (!RetType.isNull() && RetType != CE->getType().getCanonicalType())
+ return false;
+
+ // Check argument types when relevant:
+ for (size_t I = 0, E = ArgTypes.size(); I != E; ++I) {
+ QualType FormalT = ArgTypes[I];
+ // Null type marks irrelevant arguments.
+ if (FormalT.isNull())
+ continue;
+
+ assertTypeSuitableForSummary(FormalT);
+
+ QualType ActualT = StdLibraryFunctionsChecker::getArgType(CE, I);
+ assert(ActualT.isCanonical());
+ if (ActualT != FormalT)
+ return false;
+ }
+
+ return true;
+}
+
+Optional<StdLibraryFunctionsChecker::FunctionSummaryTy>
+StdLibraryFunctionsChecker::findFunctionSummary(const FunctionDecl *FD,
+ const CallExpr *CE,
+ CheckerContext &C) const {
+ // Note: we cannot always obtain FD from CE
+ // (eg. virtual call, or call by pointer).
+ assert(CE);
+
+ if (!FD)
+ return None;
+
+ SValBuilder &SVB = C.getSValBuilder();
+ BasicValueFactory &BVF = SVB.getBasicValueFactory();
+ initFunctionSummaries(BVF);
+
+ IdentifierInfo *II = FD->getIdentifier();
+ if (!II)
+ return None;
+ StringRef Name = II->getName();
+ if (Name.empty() || !C.isCLibraryFunction(FD, Name))
+ return None;
+
+ auto FSMI = FunctionSummaryMap.find(Name);
+ if (FSMI == FunctionSummaryMap.end())
+ return None;
+
+ // Verify that function signature matches the spec in advance.
+ // Otherwise we might be modeling the wrong function.
+ // Strict checking is important because we will be conducting
+ // very integral-type-sensitive operations on arguments and
+ // return values.
+ const FunctionVariantsTy &SpecVariants = FSMI->second;
+ for (const FunctionSummaryTy &Spec : SpecVariants)
+ if (Spec.matchesCall(CE))
+ return Spec;
+
+ return None;
+}
+
+void StdLibraryFunctionsChecker::initFunctionSummaries(
+ BasicValueFactory &BVF) const {
+ if (!FunctionSummaryMap.empty())
+ return;
+
+ ASTContext &ACtx = BVF.getContext();
+
+ // These types are useful for writing specifications quickly,
+ // New specifications should probably introduce more types.
+ // Some types are hard to obtain from the AST, eg. "ssize_t".
+ // In such cases it should be possible to provide multiple variants
+ // of function summary for common cases (eg. ssize_t could be int or long
+ // or long long, so three summary variants would be enough).
+ // Of course, function variants are also useful for C++ overloads.
+ QualType Irrelevant; // A placeholder, whenever we do not care about the type.
+ QualType IntTy = ACtx.IntTy;
+ QualType LongTy = ACtx.LongTy;
+ QualType LongLongTy = ACtx.LongLongTy;
+ QualType SizeTy = ACtx.getSizeType();
+
+ RangeIntTy IntMax = BVF.getMaxValue(IntTy).getLimitedValue();
+ RangeIntTy LongMax = BVF.getMaxValue(LongTy).getLimitedValue();
+ RangeIntTy LongLongMax = BVF.getMaxValue(LongLongTy).getLimitedValue();
+
+ // We are finally ready to define specifications for all supported functions.
+ //
+ // The signature needs to have the correct number of arguments.
+ // However, we insert `Irrelevant' when the type is insignificant.
+ //
+ // Argument ranges should always cover all variants. If return value
+ // is completely unknown, omit it from the respective range set.
+ //
+ // All types in the spec need to be canonical.
+ //
+ // Every item in the list of range sets represents a particular
+ // execution path the analyzer would need to explore once
+ // the call is modeled - a new program state is constructed
+ // for every range set, and each range line in the range set
+ // corresponds to a specific constraint within this state.
+ //
+ // Upon comparing to another argument, the other argument is casted
+ // to the current argument's type. This avoids proper promotion but
+ // seems useful. For example, read() receives size_t argument,
+ // and its return value, which is of type ssize_t, cannot be greater
+ // than this argument. If we made a promotion, and the size argument
+ // is equal to, say, 10, then we'd impose a range of [0, 10] on the
+ // return value, however the correct range is [-1, 10].
+ //
+ // Please update the list of functions in the header after editing!
+ //
+ // The format is as follows:
+ //
+ //{ "function name",
+ // { spec:
+ // { argument types list, ... },
+ // return type, purity, { range set list:
+ // { range list:
+ // { argument index, within or out of, {{from, to}, ...} },
+ // { argument index, compares to argument, {{how, which}} },
+ // ...
+ // }
+ // }
+ // }
+ //}
+
+#define SUMMARY_WITH_VARIANTS(identifier) {#identifier, {
+#define END_SUMMARY_WITH_VARIANTS }},
+#define VARIANT(argument_types, return_type, invalidation_approach) \
+ { argument_types, return_type, invalidation_approach, {
+#define END_VARIANT } },
+#define SUMMARY(identifier, argument_types, return_type, \
+ invalidation_approach) \
+ { #identifier, { { argument_types, return_type, invalidation_approach, {
+#define END_SUMMARY } } } },
+#define ARGUMENT_TYPES(...) { __VA_ARGS__ }
+#define RETURN_TYPE(x) x
+#define INVALIDATION_APPROACH(x) x
+#define CASE {
+#define END_CASE },
+#define ARGUMENT_CONDITION(argument_number, condition_kind) \
+ { argument_number, condition_kind, {
+#define END_ARGUMENT_CONDITION }},
+#define RETURN_VALUE_CONDITION(condition_kind) \
+ { Ret, condition_kind, {
+#define END_RETURN_VALUE_CONDITION }},
+#define ARG_NO(x) x##U
+#define RANGE(x, y) { x, y },
+#define SINGLE_VALUE(x) RANGE(x, x)
+#define IS_LESS_THAN(arg) { BO_LE, arg }
+
+ FunctionSummaryMap = {
+ // The isascii() family of functions.
+ SUMMARY(isalnum, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE // Boils down to isupper() or islower() or isdigit()
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE('0', '9')
+ RANGE('A', 'Z')
+ RANGE('a', 'z')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE // The locale-specific range.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE(128, 255)
+ END_ARGUMENT_CONDITION
+ // No post-condition. We are completely unaware of
+ // locale-specific return values.
+ END_CASE
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE('0', '9')
+ RANGE('A', 'Z')
+ RANGE('a', 'z')
+ RANGE(128, 255)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(isalpha, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE // isupper() or islower(). Note that 'Z' is less than 'a'.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE('A', 'Z')
+ RANGE('a', 'z')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE // The locale-specific range.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE(128, 255)
+ END_ARGUMENT_CONDITION
+ END_CASE
+ CASE // Other.
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE('A', 'Z')
+ RANGE('a', 'z')
+ RANGE(128, 255)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(isascii, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE // Is ASCII.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE(0, 127)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE(0, 127)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(isblank, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ SINGLE_VALUE('\t')
+ SINGLE_VALUE(' ')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ SINGLE_VALUE('\t')
+ SINGLE_VALUE(' ')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(iscntrl, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE // 0..31 or 127
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE(0, 32)
+ SINGLE_VALUE(127)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE(0, 32)
+ SINGLE_VALUE(127)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(isdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE // Is a digit.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE('0', '9')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE('0', '9')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(isgraph, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE(33, 126)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE(33, 126)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(islower, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE // Is certainly lowercase.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE('a', 'z')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE // Is ascii but not lowercase.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE(0, 127)
+ END_ARGUMENT_CONDITION
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE('a', 'z')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE // The locale-specific range.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE(128, 255)
+ END_ARGUMENT_CONDITION
+ END_CASE
+ CASE // Is not an unsigned char.
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE(0, 255)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(isprint, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE(32, 126)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE(32, 126)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(ispunct, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE('!', '/')
+ RANGE(':', '@')
+ RANGE('[', '`')
+ RANGE('{', '~')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE('!', '/')
+ RANGE(':', '@')
+ RANGE('[', '`')
+ RANGE('{', '~')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(isspace, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE // Space, '\f', '\n', '\r', '\t', '\v'.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE(9, 13)
+ SINGLE_VALUE(' ')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE // The locale-specific range.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE(128, 255)
+ END_ARGUMENT_CONDITION
+ END_CASE
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE(9, 13)
+ SINGLE_VALUE(' ')
+ RANGE(128, 255)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(isupper, ARGUMENT_TYPES(IntTy), RETURN_TYPE (IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE // Is certainly uppercase.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE('A', 'Z')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE // The locale-specific range.
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE(128, 255)
+ END_ARGUMENT_CONDITION
+ END_CASE
+ CASE // Other.
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE('A', 'Z') RANGE(128, 255)
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(isxdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(EvalCallAsPure))
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), WithinRange)
+ RANGE('0', '9')
+ RANGE('A', 'F')
+ RANGE('a', 'f')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(OutOfRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ CASE
+ ARGUMENT_CONDITION(ARG_NO(0), OutOfRange)
+ RANGE('0', '9')
+ RANGE('A', 'F')
+ RANGE('a', 'f')
+ END_ARGUMENT_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(0)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+
+ // The getc() family of functions that returns either a char or an EOF.
+ SUMMARY(getc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(NoEvalCall))
+ CASE // FIXME: EOF is assumed to be defined as -1.
+ RETURN_VALUE_CONDITION(WithinRange)
+ RANGE(-1, 255)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(fgetc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(NoEvalCall))
+ CASE // FIXME: EOF is assumed to be defined as -1.
+ RETURN_VALUE_CONDITION(WithinRange)
+ RANGE(-1, 255)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(getchar, ARGUMENT_TYPES(), RETURN_TYPE(IntTy),
+ INVALIDATION_APPROACH(NoEvalCall))
+ CASE // FIXME: EOF is assumed to be defined as -1.
+ RETURN_VALUE_CONDITION(WithinRange)
+ RANGE(-1, 255)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+
+ // read()-like functions that never return more than buffer size.
+ // We are not sure how ssize_t is defined on every platform, so we provide
+ // three variants that should cover common cases.
+ SUMMARY_WITH_VARIANTS(read)
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
+ RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(ComparesToArgument)
+ IS_LESS_THAN(ARG_NO(2))
+ END_RETURN_VALUE_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ RANGE(-1, IntMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
+ RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(ComparesToArgument)
+ IS_LESS_THAN(ARG_NO(2))
+ END_RETURN_VALUE_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ RANGE(-1, LongMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
+ RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(ComparesToArgument)
+ IS_LESS_THAN(ARG_NO(2))
+ END_RETURN_VALUE_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ RANGE(-1, LongLongMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ END_SUMMARY_WITH_VARIANTS
+ SUMMARY_WITH_VARIANTS(write)
+ // Again, due to elusive nature of ssize_t, we have duplicate
+ // our summaries to cover different variants.
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
+ RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(ComparesToArgument)
+ IS_LESS_THAN(ARG_NO(2))
+ END_RETURN_VALUE_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ RANGE(-1, IntMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
+ RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(ComparesToArgument)
+ IS_LESS_THAN(ARG_NO(2))
+ END_RETURN_VALUE_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ RANGE(-1, LongMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy),
+ RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(ComparesToArgument)
+ IS_LESS_THAN(ARG_NO(2))
+ END_RETURN_VALUE_CONDITION
+ RETURN_VALUE_CONDITION(WithinRange)
+ RANGE(-1, LongLongMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ END_SUMMARY_WITH_VARIANTS
+ SUMMARY(fread,
+ ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
+ RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(ComparesToArgument)
+ IS_LESS_THAN(ARG_NO(2))
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+ SUMMARY(fwrite,
+ ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant),
+ RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(ComparesToArgument)
+ IS_LESS_THAN(ARG_NO(2))
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_SUMMARY
+
+ // getline()-like functions either fail or read at least the delimiter.
+ SUMMARY_WITH_VARIANTS(getline)
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
+ RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(-1)
+ RANGE(1, IntMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
+ RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(-1)
+ RANGE(1, LongMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant),
+ RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(-1)
+ RANGE(1, LongLongMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ END_SUMMARY_WITH_VARIANTS
+ SUMMARY_WITH_VARIANTS(getdelim)
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
+ RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(-1)
+ RANGE(1, IntMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
+ RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(-1)
+ RANGE(1, LongMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant),
+ RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall))
+ CASE
+ RETURN_VALUE_CONDITION(WithinRange)
+ SINGLE_VALUE(-1)
+ RANGE(1, LongLongMax)
+ END_RETURN_VALUE_CONDITION
+ END_CASE
+ END_VARIANT
+ END_SUMMARY_WITH_VARIANTS
+ };
+}
+
+void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
+ // If this checker grows large enough to support C++, Objective-C, or other
+ // standard libraries, we could use multiple register...Checker() functions,
+ // which would register various checkers with the help of the same Checker
+ // class, turning on different function summaries.
+ mgr.registerChecker<StdLibraryFunctionsChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
new file mode 100644
index 000000000000..92647f032730
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -0,0 +1,411 @@
+//===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines checkers that model and check stream handling functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+struct StreamState {
+ enum Kind { Opened, Closed, OpenFailed, Escaped } K;
+ const Stmt *S;
+
+ StreamState(Kind k, const Stmt *s) : K(k), S(s) {}
+
+ bool isOpened() const { return K == Opened; }
+ bool isClosed() const { return K == Closed; }
+ //bool isOpenFailed() const { return K == OpenFailed; }
+ //bool isEscaped() const { return K == Escaped; }
+
+ bool operator==(const StreamState &X) const {
+ return K == X.K && S == X.S;
+ }
+
+ static StreamState getOpened(const Stmt *s) { return StreamState(Opened, s); }
+ static StreamState getClosed(const Stmt *s) { return StreamState(Closed, s); }
+ static StreamState getOpenFailed(const Stmt *s) {
+ return StreamState(OpenFailed, s);
+ }
+ static StreamState getEscaped(const Stmt *s) {
+ return StreamState(Escaped, s);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(K);
+ ID.AddPointer(S);
+ }
+};
+
+class StreamChecker : public Checker<eval::Call,
+ check::DeadSymbols > {
+ mutable IdentifierInfo *II_fopen, *II_tmpfile, *II_fclose, *II_fread,
+ *II_fwrite,
+ *II_fseek, *II_ftell, *II_rewind, *II_fgetpos, *II_fsetpos,
+ *II_clearerr, *II_feof, *II_ferror, *II_fileno;
+ mutable std::unique_ptr<BuiltinBug> BT_nullfp, BT_illegalwhence,
+ BT_doubleclose, BT_ResourceLeak;
+
+public:
+ StreamChecker()
+ : II_fopen(nullptr), II_tmpfile(nullptr), II_fclose(nullptr),
+ II_fread(nullptr), II_fwrite(nullptr), II_fseek(nullptr),
+ II_ftell(nullptr), II_rewind(nullptr), II_fgetpos(nullptr),
+ II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr),
+ II_ferror(nullptr), II_fileno(nullptr) {}
+
+ bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
+
+private:
+ void Fopen(CheckerContext &C, const CallExpr *CE) const;
+ void Tmpfile(CheckerContext &C, const CallExpr *CE) const;
+ void Fclose(CheckerContext &C, const CallExpr *CE) const;
+ void Fread(CheckerContext &C, const CallExpr *CE) const;
+ void Fwrite(CheckerContext &C, const CallExpr *CE) const;
+ void Fseek(CheckerContext &C, const CallExpr *CE) const;
+ void Ftell(CheckerContext &C, const CallExpr *CE) const;
+ void Rewind(CheckerContext &C, const CallExpr *CE) const;
+ void Fgetpos(CheckerContext &C, const CallExpr *CE) const;
+ void Fsetpos(CheckerContext &C, const CallExpr *CE) const;
+ void Clearerr(CheckerContext &C, const CallExpr *CE) const;
+ void Feof(CheckerContext &C, const CallExpr *CE) const;
+ void Ferror(CheckerContext &C, const CallExpr *CE) const;
+ void Fileno(CheckerContext &C, const CallExpr *CE) const;
+
+ void OpenFileAux(CheckerContext &C, const CallExpr *CE) const;
+
+ ProgramStateRef CheckNullStream(SVal SV, ProgramStateRef state,
+ CheckerContext &C) const;
+ ProgramStateRef CheckDoubleClose(const CallExpr *CE, ProgramStateRef state,
+ CheckerContext &C) const;
+};
+
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
+
+
+bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD || FD->getKind() != Decl::Function)
+ return false;
+
+ ASTContext &Ctx = C.getASTContext();
+ if (!II_fopen)
+ II_fopen = &Ctx.Idents.get("fopen");
+ if (!II_tmpfile)
+ II_tmpfile = &Ctx.Idents.get("tmpfile");
+ if (!II_fclose)
+ II_fclose = &Ctx.Idents.get("fclose");
+ if (!II_fread)
+ II_fread = &Ctx.Idents.get("fread");
+ if (!II_fwrite)
+ II_fwrite = &Ctx.Idents.get("fwrite");
+ if (!II_fseek)
+ II_fseek = &Ctx.Idents.get("fseek");
+ if (!II_ftell)
+ II_ftell = &Ctx.Idents.get("ftell");
+ if (!II_rewind)
+ II_rewind = &Ctx.Idents.get("rewind");
+ if (!II_fgetpos)
+ II_fgetpos = &Ctx.Idents.get("fgetpos");
+ if (!II_fsetpos)
+ II_fsetpos = &Ctx.Idents.get("fsetpos");
+ if (!II_clearerr)
+ II_clearerr = &Ctx.Idents.get("clearerr");
+ if (!II_feof)
+ II_feof = &Ctx.Idents.get("feof");
+ if (!II_ferror)
+ II_ferror = &Ctx.Idents.get("ferror");
+ if (!II_fileno)
+ II_fileno = &Ctx.Idents.get("fileno");
+
+ if (FD->getIdentifier() == II_fopen) {
+ Fopen(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_tmpfile) {
+ Tmpfile(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_fclose) {
+ Fclose(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_fread) {
+ Fread(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_fwrite) {
+ Fwrite(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_fseek) {
+ Fseek(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_ftell) {
+ Ftell(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_rewind) {
+ Rewind(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_fgetpos) {
+ Fgetpos(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_fsetpos) {
+ Fsetpos(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_clearerr) {
+ Clearerr(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_feof) {
+ Feof(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_ferror) {
+ Ferror(C, CE);
+ return true;
+ }
+ if (FD->getIdentifier() == II_fileno) {
+ Fileno(C, CE);
+ return true;
+ }
+
+ return false;
+}
+
+void StreamChecker::Fopen(CheckerContext &C, const CallExpr *CE) const {
+ OpenFileAux(C, CE);
+}
+
+void StreamChecker::Tmpfile(CheckerContext &C, const CallExpr *CE) const {
+ OpenFileAux(C, CE);
+}
+
+void StreamChecker::OpenFileAux(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ SValBuilder &svalBuilder = C.getSValBuilder();
+ const LocationContext *LCtx = C.getPredecessor()->getLocationContext();
+ DefinedSVal RetVal = svalBuilder.conjureSymbolVal(nullptr, CE, LCtx,
+ C.blockCount())
+ .castAs<DefinedSVal>();
+ state = state->BindExpr(CE, C.getLocationContext(), RetVal);
+
+ ConstraintManager &CM = C.getConstraintManager();
+ // Bifurcate the state into two: one with a valid FILE* pointer, the other
+ // with a NULL.
+ ProgramStateRef stateNotNull, stateNull;
+ std::tie(stateNotNull, stateNull) = CM.assumeDual(state, RetVal);
+
+ if (SymbolRef Sym = RetVal.getAsSymbol()) {
+ // if RetVal is not NULL, set the symbol's state to Opened.
+ stateNotNull =
+ stateNotNull->set<StreamMap>(Sym,StreamState::getOpened(CE));
+ stateNull =
+ stateNull->set<StreamMap>(Sym, StreamState::getOpenFailed(CE));
+
+ C.addTransition(stateNotNull);
+ C.addTransition(stateNull);
+ }
+}
+
+void StreamChecker::Fclose(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = CheckDoubleClose(CE, C.getState(), C);
+ if (state)
+ C.addTransition(state);
+}
+
+void StreamChecker::Fread(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C))
+ return;
+}
+
+void StreamChecker::Fwrite(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ if (!CheckNullStream(C.getSVal(CE->getArg(3)), state, C))
+ return;
+}
+
+void StreamChecker::Fseek(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ if (!(state = CheckNullStream(C.getSVal(CE->getArg(0)), state, C)))
+ return;
+ // Check the legality of the 'whence' argument of 'fseek'.
+ SVal Whence = state->getSVal(CE->getArg(2), C.getLocationContext());
+ Optional<nonloc::ConcreteInt> CI = Whence.getAs<nonloc::ConcreteInt>();
+
+ if (!CI)
+ return;
+
+ int64_t x = CI->getValue().getSExtValue();
+ if (x >= 0 && x <= 2)
+ return;
+
+ if (ExplodedNode *N = C.generateNonFatalErrorNode(state)) {
+ if (!BT_illegalwhence)
+ BT_illegalwhence.reset(
+ new BuiltinBug(this, "Illegal whence argument",
+ "The whence argument to fseek() should be "
+ "SEEK_SET, SEEK_END, or SEEK_CUR."));
+ C.emitReport(llvm::make_unique<BugReport>(
+ *BT_illegalwhence, BT_illegalwhence->getDescription(), N));
+ }
+}
+
+void StreamChecker::Ftell(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
+ return;
+}
+
+void StreamChecker::Rewind(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
+ return;
+}
+
+void StreamChecker::Fgetpos(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
+ return;
+}
+
+void StreamChecker::Fsetpos(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
+ return;
+}
+
+void StreamChecker::Clearerr(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
+ return;
+}
+
+void StreamChecker::Feof(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
+ return;
+}
+
+void StreamChecker::Ferror(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
+ return;
+}
+
+void StreamChecker::Fileno(CheckerContext &C, const CallExpr *CE) const {
+ ProgramStateRef state = C.getState();
+ if (!CheckNullStream(C.getSVal(CE->getArg(0)), state, C))
+ return;
+}
+
+ProgramStateRef StreamChecker::CheckNullStream(SVal SV, ProgramStateRef state,
+ CheckerContext &C) const {
+ Optional<DefinedSVal> DV = SV.getAs<DefinedSVal>();
+ if (!DV)
+ return nullptr;
+
+ ConstraintManager &CM = C.getConstraintManager();
+ ProgramStateRef stateNotNull, stateNull;
+ std::tie(stateNotNull, stateNull) = CM.assumeDual(state, *DV);
+
+ if (!stateNotNull && stateNull) {
+ if (ExplodedNode *N = C.generateErrorNode(stateNull)) {
+ if (!BT_nullfp)
+ BT_nullfp.reset(new BuiltinBug(this, "NULL stream pointer",
+ "Stream pointer might be NULL."));
+ C.emitReport(llvm::make_unique<BugReport>(
+ *BT_nullfp, BT_nullfp->getDescription(), N));
+ }
+ return nullptr;
+ }
+ return stateNotNull;
+}
+
+ProgramStateRef StreamChecker::CheckDoubleClose(const CallExpr *CE,
+ ProgramStateRef state,
+ CheckerContext &C) const {
+ SymbolRef Sym = C.getSVal(CE->getArg(0)).getAsSymbol();
+ if (!Sym)
+ return state;
+
+ const StreamState *SS = state->get<StreamMap>(Sym);
+
+ // If the file stream is not tracked, return.
+ if (!SS)
+ return state;
+
+ // Check: Double close a File Descriptor could cause undefined behaviour.
+ // Conforming to man-pages
+ if (SS->isClosed()) {
+ ExplodedNode *N = C.generateErrorNode();
+ if (N) {
+ if (!BT_doubleclose)
+ BT_doubleclose.reset(new BuiltinBug(
+ this, "Double fclose", "Try to close a file Descriptor already"
+ " closed. Cause undefined behaviour."));
+ C.emitReport(llvm::make_unique<BugReport>(
+ *BT_doubleclose, BT_doubleclose->getDescription(), N));
+ }
+ return nullptr;
+ }
+
+ // Close the File Descriptor.
+ return state->set<StreamMap>(Sym, StreamState::getClosed(CE));
+}
+
+void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
+ CheckerContext &C) const {
+ ProgramStateRef state = C.getState();
+
+ // TODO: Clean up the state.
+ const StreamMapTy &Map = state->get<StreamMap>();
+ for (const auto &I: Map) {
+ SymbolRef Sym = I.first;
+ const StreamState &SS = I.second;
+ if (!SymReaper.isDead(Sym) || !SS.isOpened())
+ continue;
+
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+
+ if (!BT_ResourceLeak)
+ BT_ResourceLeak.reset(
+ new BuiltinBug(this, "Resource Leak",
+ "Opened File never closed. Potential Resource leak."));
+ C.emitReport(llvm::make_unique<BugReport>(
+ *BT_ResourceLeak, BT_ResourceLeak->getDescription(), N));
+ }
+}
+
+void ento::registerStreamChecker(CheckerManager &mgr) {
+ mgr.registerChecker<StreamChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
new file mode 100644
index 000000000000..3aa8e95d0ad0
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
@@ -0,0 +1,62 @@
+//== TaintTesterChecker.cpp ----------------------------------- -*- C++ -*--=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker can be used for testing how taint data is propagated.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class TaintTesterChecker : public Checker< check::PostStmt<Expr> > {
+
+ mutable std::unique_ptr<BugType> BT;
+ void initBugType() const;
+
+ /// Given a pointer argument, get the symbol of the value it contains
+ /// (points to).
+ SymbolRef getPointedToSymbol(CheckerContext &C,
+ const Expr* Arg,
+ bool IssueWarning = true) const;
+
+public:
+ void checkPostStmt(const Expr *E, CheckerContext &C) const;
+};
+}
+
+inline void TaintTesterChecker::initBugType() const {
+ if (!BT)
+ BT.reset(new BugType(this, "Tainted data", "General"));
+}
+
+void TaintTesterChecker::checkPostStmt(const Expr *E,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ if (!State)
+ return;
+
+ if (State->isTainted(E, C.getLocationContext())) {
+ if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
+ initBugType();
+ auto report = llvm::make_unique<BugReport>(*BT, "tainted",N);
+ report->addRange(E->getSourceRange());
+ C.emitReport(std::move(report));
+ }
+ }
+}
+
+void ento::registerTaintTesterChecker(CheckerManager &mgr) {
+ mgr.registerChecker<TaintTesterChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
new file mode 100644
index 000000000000..527e371571f1
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
@@ -0,0 +1,263 @@
+//== TestAfterDivZeroChecker.cpp - Test after division by zero checker --*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines TestAfterDivZeroChecker, a builtin check that performs checks
+// for division by zero where the division occurs before comparison with zero.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/FoldingSet.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class ZeroState {
+private:
+ SymbolRef ZeroSymbol;
+ unsigned BlockID;
+ const StackFrameContext *SFC;
+
+public:
+ ZeroState(SymbolRef S, unsigned B, const StackFrameContext *SFC)
+ : ZeroSymbol(S), BlockID(B), SFC(SFC) {}
+
+ const StackFrameContext *getStackFrameContext() const { return SFC; }
+
+ bool operator==(const ZeroState &X) const {
+ return BlockID == X.BlockID && SFC == X.SFC && ZeroSymbol == X.ZeroSymbol;
+ }
+
+ bool operator<(const ZeroState &X) const {
+ if (BlockID != X.BlockID)
+ return BlockID < X.BlockID;
+ if (SFC != X.SFC)
+ return SFC < X.SFC;
+ return ZeroSymbol < X.ZeroSymbol;
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(BlockID);
+ ID.AddPointer(SFC);
+ ID.AddPointer(ZeroSymbol);
+ }
+};
+
+class DivisionBRVisitor : public BugReporterVisitor {
+private:
+ SymbolRef ZeroSymbol;
+ const StackFrameContext *SFC;
+ bool Satisfied;
+
+public:
+ DivisionBRVisitor(SymbolRef ZeroSymbol, const StackFrameContext *SFC)
+ : ZeroSymbol(ZeroSymbol), SFC(SFC), Satisfied(false) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.Add(ZeroSymbol);
+ ID.Add(SFC);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+};
+
+class TestAfterDivZeroChecker
+ : public Checker<check::PreStmt<BinaryOperator>, check::BranchCondition,
+ check::EndFunction> {
+ mutable std::unique_ptr<BuiltinBug> DivZeroBug;
+ void reportBug(SVal Val, CheckerContext &C) const;
+
+public:
+ void checkPreStmt(const BinaryOperator *B, CheckerContext &C) const;
+ void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+ void setDivZeroMap(SVal Var, CheckerContext &C) const;
+ bool hasDivZeroMap(SVal Var, const CheckerContext &C) const;
+ bool isZero(SVal S, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+REGISTER_SET_WITH_PROGRAMSTATE(DivZeroMap, ZeroState)
+
+std::shared_ptr<PathDiagnosticPiece>
+DivisionBRVisitor::VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC, BugReport &BR) {
+ if (Satisfied)
+ return nullptr;
+
+ const Expr *E = nullptr;
+
+ if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>())
+ if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>()) {
+ BinaryOperator::Opcode Op = BO->getOpcode();
+ if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign ||
+ Op == BO_RemAssign) {
+ E = BO->getRHS();
+ }
+ }
+
+ if (!E)
+ return nullptr;
+
+ SVal S = Succ->getSVal(E);
+ if (ZeroSymbol == S.getAsSymbol() && SFC == Succ->getStackFrame()) {
+ Satisfied = true;
+
+ // Construct a new PathDiagnosticPiece.
+ ProgramPoint P = Succ->getLocation();
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(P, BRC.getSourceManager());
+
+ if (!L.isValid() || !L.asLocation().isValid())
+ return nullptr;
+
+ return std::make_shared<PathDiagnosticEventPiece>(
+ L, "Division with compared value made here");
+ }
+
+ return nullptr;
+}
+
+bool TestAfterDivZeroChecker::isZero(SVal S, CheckerContext &C) const {
+ Optional<DefinedSVal> DSV = S.getAs<DefinedSVal>();
+
+ if (!DSV)
+ return false;
+
+ ConstraintManager &CM = C.getConstraintManager();
+ return !CM.assume(C.getState(), *DSV, true);
+}
+
+void TestAfterDivZeroChecker::setDivZeroMap(SVal Var, CheckerContext &C) const {
+ SymbolRef SR = Var.getAsSymbol();
+ if (!SR)
+ return;
+
+ ProgramStateRef State = C.getState();
+ State =
+ State->add<DivZeroMap>(ZeroState(SR, C.getBlockID(), C.getStackFrame()));
+ C.addTransition(State);
+}
+
+bool TestAfterDivZeroChecker::hasDivZeroMap(SVal Var,
+ const CheckerContext &C) const {
+ SymbolRef SR = Var.getAsSymbol();
+ if (!SR)
+ return false;
+
+ ZeroState ZS(SR, C.getBlockID(), C.getStackFrame());
+ return C.getState()->contains<DivZeroMap>(ZS);
+}
+
+void TestAfterDivZeroChecker::reportBug(SVal Val, CheckerContext &C) const {
+ if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
+ if (!DivZeroBug)
+ DivZeroBug.reset(new BuiltinBug(this, "Division by zero"));
+
+ auto R = llvm::make_unique<BugReport>(
+ *DivZeroBug, "Value being compared against zero has already been used "
+ "for division",
+ N);
+
+ R->addVisitor(llvm::make_unique<DivisionBRVisitor>(Val.getAsSymbol(),
+ C.getStackFrame()));
+ C.emitReport(std::move(R));
+ }
+}
+
+void TestAfterDivZeroChecker::checkEndFunction(const ReturnStmt *,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ DivZeroMapTy DivZeroes = State->get<DivZeroMap>();
+ if (DivZeroes.isEmpty())
+ return;
+
+ DivZeroMapTy::Factory &F = State->get_context<DivZeroMap>();
+ for (llvm::ImmutableSet<ZeroState>::iterator I = DivZeroes.begin(),
+ E = DivZeroes.end();
+ I != E; ++I) {
+ ZeroState ZS = *I;
+ if (ZS.getStackFrameContext() == C.getStackFrame())
+ DivZeroes = F.remove(DivZeroes, ZS);
+ }
+ C.addTransition(State->set<DivZeroMap>(DivZeroes));
+}
+
+void TestAfterDivZeroChecker::checkPreStmt(const BinaryOperator *B,
+ CheckerContext &C) const {
+ BinaryOperator::Opcode Op = B->getOpcode();
+ if (Op == BO_Div || Op == BO_Rem || Op == BO_DivAssign ||
+ Op == BO_RemAssign) {
+ SVal S = C.getSVal(B->getRHS());
+
+ if (!isZero(S, C))
+ setDivZeroMap(S, C);
+ }
+}
+
+void TestAfterDivZeroChecker::checkBranchCondition(const Stmt *Condition,
+ CheckerContext &C) const {
+ if (const BinaryOperator *B = dyn_cast<BinaryOperator>(Condition)) {
+ if (B->isComparisonOp()) {
+ const IntegerLiteral *IntLiteral = dyn_cast<IntegerLiteral>(B->getRHS());
+ bool LRHS = true;
+ if (!IntLiteral) {
+ IntLiteral = dyn_cast<IntegerLiteral>(B->getLHS());
+ LRHS = false;
+ }
+
+ if (!IntLiteral || IntLiteral->getValue() != 0)
+ return;
+
+ SVal Val = C.getSVal(LRHS ? B->getLHS() : B->getRHS());
+ if (hasDivZeroMap(Val, C))
+ reportBug(Val, C);
+ }
+ } else if (const UnaryOperator *U = dyn_cast<UnaryOperator>(Condition)) {
+ if (U->getOpcode() == UO_LNot) {
+ SVal Val;
+ if (const ImplicitCastExpr *I =
+ dyn_cast<ImplicitCastExpr>(U->getSubExpr()))
+ Val = C.getSVal(I->getSubExpr());
+
+ if (hasDivZeroMap(Val, C))
+ reportBug(Val, C);
+ else {
+ Val = C.getSVal(U->getSubExpr());
+ if (hasDivZeroMap(Val, C))
+ reportBug(Val, C);
+ }
+ }
+ } else if (const ImplicitCastExpr *IE =
+ dyn_cast<ImplicitCastExpr>(Condition)) {
+ SVal Val = C.getSVal(IE->getSubExpr());
+
+ if (hasDivZeroMap(Val, C))
+ reportBug(Val, C);
+ else {
+ SVal Val = C.getSVal(Condition);
+
+ if (hasDivZeroMap(Val, C))
+ reportBug(Val, C);
+ }
+ }
+}
+
+void ento::registerTestAfterDivZeroChecker(CheckerManager &mgr) {
+ mgr.registerChecker<TestAfterDivZeroChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
new file mode 100644
index 000000000000..2f06469bb209
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
@@ -0,0 +1,114 @@
+//== TraversalChecker.cpp -------------------------------------- -*- C++ -*--=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// These checkers print various aspects of the ExprEngine's traversal of the CFG
+// as it builds the ExplodedGraph.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/AST/StmtObjC.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class TraversalDumper : public Checker< check::BranchCondition,
+ check::BeginFunction,
+ check::EndFunction > {
+public:
+ void checkBranchCondition(const Stmt *Condition, CheckerContext &C) const;
+ void checkBeginFunction(CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+};
+}
+
+void TraversalDumper::checkBranchCondition(const Stmt *Condition,
+ CheckerContext &C) const {
+ // Special-case Objective-C's for-in loop, which uses the entire loop as its
+ // condition. We just print the collection expression.
+ const Stmt *Parent = dyn_cast<ObjCForCollectionStmt>(Condition);
+ if (!Parent) {
+ const ParentMap &Parents = C.getLocationContext()->getParentMap();
+ Parent = Parents.getParent(Condition);
+ }
+
+ // It is mildly evil to print directly to llvm::outs() rather than emitting
+ // warnings, but this ensures things do not get filtered out by the rest of
+ // the static analyzer machinery.
+ SourceLocation Loc = Parent->getBeginLoc();
+ llvm::outs() << C.getSourceManager().getSpellingLineNumber(Loc) << " "
+ << Parent->getStmtClassName() << "\n";
+}
+
+void TraversalDumper::checkBeginFunction(CheckerContext &C) const {
+ llvm::outs() << "--BEGIN FUNCTION--\n";
+}
+
+void TraversalDumper::checkEndFunction(const ReturnStmt *RS,
+ CheckerContext &C) const {
+ llvm::outs() << "--END FUNCTION--\n";
+}
+
+void ento::registerTraversalDumper(CheckerManager &mgr) {
+ mgr.registerChecker<TraversalDumper>();
+}
+
+//------------------------------------------------------------------------------
+
+namespace {
+class CallDumper : public Checker< check::PreCall,
+ check::PostCall > {
+public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+};
+}
+
+void CallDumper::checkPreCall(const CallEvent &Call, CheckerContext &C) const {
+ unsigned Indentation = 0;
+ for (const LocationContext *LC = C.getLocationContext()->getParent();
+ LC != nullptr; LC = LC->getParent())
+ ++Indentation;
+
+ // It is mildly evil to print directly to llvm::outs() rather than emitting
+ // warnings, but this ensures things do not get filtered out by the rest of
+ // the static analyzer machinery.
+ llvm::outs().indent(Indentation);
+ Call.dump(llvm::outs());
+}
+
+void CallDumper::checkPostCall(const CallEvent &Call, CheckerContext &C) const {
+ const Expr *CallE = Call.getOriginExpr();
+ if (!CallE)
+ return;
+
+ unsigned Indentation = 0;
+ for (const LocationContext *LC = C.getLocationContext()->getParent();
+ LC != nullptr; LC = LC->getParent())
+ ++Indentation;
+
+ // It is mildly evil to print directly to llvm::outs() rather than emitting
+ // warnings, but this ensures things do not get filtered out by the rest of
+ // the static analyzer machinery.
+ llvm::outs().indent(Indentation);
+ if (Call.getResultType()->isVoidType())
+ llvm::outs() << "Returning void\n";
+ else
+ llvm::outs() << "Returning " << C.getSVal(CallE) << "\n";
+}
+
+void ento::registerCallDumper(CheckerManager &mgr) {
+ mgr.registerChecker<CallDumper>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
new file mode 100644
index 000000000000..5e777803af00
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
@@ -0,0 +1,255 @@
+//== TrustNonnullChecker.cpp --------- API nullability modeling -*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker adds nullability-related assumptions:
+//
+// 1. Methods annotated with _Nonnull
+// which come from system headers actually return a non-null pointer.
+//
+// 2. NSDictionary key is non-null after the keyword subscript operation
+// on read if and only if the resulting expression is non-null.
+//
+// 3. NSMutableDictionary index is non-null after a write operation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/Analysis/SelectorExtras.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+
+using namespace clang;
+using namespace ento;
+
+/// Records implications between symbols.
+/// The semantics is:
+/// (antecedent != 0) => (consequent != 0)
+/// These implications are then read during the evaluation of the assumption,
+/// and the appropriate antecedents are applied.
+REGISTER_MAP_WITH_PROGRAMSTATE(NonNullImplicationMap, SymbolRef, SymbolRef)
+
+/// The semantics is:
+/// (antecedent == 0) => (consequent == 0)
+REGISTER_MAP_WITH_PROGRAMSTATE(NullImplicationMap, SymbolRef, SymbolRef)
+
+namespace {
+
+class TrustNonnullChecker : public Checker<check::PostCall,
+ check::PostObjCMessage,
+ check::DeadSymbols,
+ eval::Assume> {
+ // Do not try to iterate over symbols with higher complexity.
+ static unsigned constexpr ComplexityThreshold = 10;
+ Selector ObjectForKeyedSubscriptSel;
+ Selector ObjectForKeySel;
+ Selector SetObjectForKeyedSubscriptSel;
+ Selector SetObjectForKeySel;
+
+public:
+ TrustNonnullChecker(ASTContext &Ctx)
+ : ObjectForKeyedSubscriptSel(
+ getKeywordSelector(Ctx, "objectForKeyedSubscript")),
+ ObjectForKeySel(getKeywordSelector(Ctx, "objectForKey")),
+ SetObjectForKeyedSubscriptSel(
+ getKeywordSelector(Ctx, "setObject", "forKeyedSubscript")),
+ SetObjectForKeySel(getKeywordSelector(Ctx, "setObject", "forKey")) {}
+
+ ProgramStateRef evalAssume(ProgramStateRef State,
+ SVal Cond,
+ bool Assumption) const {
+ const SymbolRef CondS = Cond.getAsSymbol();
+ if (!CondS || CondS->computeComplexity() > ComplexityThreshold)
+ return State;
+
+ for (auto B=CondS->symbol_begin(), E=CondS->symbol_end(); B != E; ++B) {
+ const SymbolRef Antecedent = *B;
+ State = addImplication(Antecedent, State, true);
+ State = addImplication(Antecedent, State, false);
+ }
+
+ return State;
+ }
+
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
+ // Only trust annotations for system headers for non-protocols.
+ if (!Call.isInSystemHeader())
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ if (isNonNullPtr(Call, C))
+ if (auto L = Call.getReturnValue().getAs<Loc>())
+ State = State->assume(*L, /*Assumption=*/true);
+
+ C.addTransition(State);
+ }
+
+ void checkPostObjCMessage(const ObjCMethodCall &Msg,
+ CheckerContext &C) const {
+ const ObjCInterfaceDecl *ID = Msg.getReceiverInterface();
+ if (!ID)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ // Index to setter for NSMutableDictionary is assumed to be non-null,
+ // as an exception is thrown otherwise.
+ if (interfaceHasSuperclass(ID, "NSMutableDictionary") &&
+ (Msg.getSelector() == SetObjectForKeyedSubscriptSel ||
+ Msg.getSelector() == SetObjectForKeySel)) {
+ if (auto L = Msg.getArgSVal(1).getAs<Loc>())
+ State = State->assume(*L, /*Assumption=*/true);
+ }
+
+ // Record an implication: index is non-null if the output is non-null.
+ if (interfaceHasSuperclass(ID, "NSDictionary") &&
+ (Msg.getSelector() == ObjectForKeyedSubscriptSel ||
+ Msg.getSelector() == ObjectForKeySel)) {
+ SymbolRef ArgS = Msg.getArgSVal(0).getAsSymbol();
+ SymbolRef RetS = Msg.getReturnValue().getAsSymbol();
+
+ if (ArgS && RetS) {
+ // Emulate an implication: the argument is non-null if
+ // the return value is non-null.
+ State = State->set<NonNullImplicationMap>(RetS, ArgS);
+
+ // Conversely, when the argument is null, the return value
+ // is definitely null.
+ State = State->set<NullImplicationMap>(ArgS, RetS);
+ }
+ }
+
+ C.addTransition(State);
+ }
+
+ void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ State = dropDeadFromGDM<NullImplicationMap>(SymReaper, State);
+ State = dropDeadFromGDM<NonNullImplicationMap>(SymReaper, State);
+
+ C.addTransition(State);
+ }
+
+private:
+
+ /// \returns State with GDM \p MapName where all dead symbols were
+ // removed.
+ template <typename MapName>
+ ProgramStateRef dropDeadFromGDM(SymbolReaper &SymReaper,
+ ProgramStateRef State) const {
+ for (const std::pair<SymbolRef, SymbolRef> &P : State->get<MapName>())
+ if (!SymReaper.isLive(P.first) || !SymReaper.isLive(P.second))
+ State = State->remove<MapName>(P.first);
+ return State;
+ }
+
+ /// \returns Whether we trust the result of the method call to be
+ /// a non-null pointer.
+ bool isNonNullPtr(const CallEvent &Call, CheckerContext &C) const {
+ QualType ExprRetType = Call.getResultType();
+ if (!ExprRetType->isAnyPointerType())
+ return false;
+
+ if (getNullabilityAnnotation(ExprRetType) == Nullability::Nonnull)
+ return true;
+
+ // The logic for ObjC instance method calls is more complicated,
+ // as the return value is nil when the receiver is nil.
+ if (!isa<ObjCMethodCall>(&Call))
+ return false;
+
+ const auto *MCall = cast<ObjCMethodCall>(&Call);
+ const ObjCMethodDecl *MD = MCall->getDecl();
+
+ // Distrust protocols.
+ if (isa<ObjCProtocolDecl>(MD->getDeclContext()))
+ return false;
+
+ QualType DeclRetType = MD->getReturnType();
+ if (getNullabilityAnnotation(DeclRetType) != Nullability::Nonnull)
+ return false;
+
+ // For class messages it is sufficient for the declaration to be
+ // annotated _Nonnull.
+ if (!MCall->isInstanceMessage())
+ return true;
+
+ // Alternatively, the analyzer could know that the receiver is not null.
+ SVal Receiver = MCall->getReceiverSVal();
+ ConditionTruthVal TV = C.getState()->isNonNull(Receiver);
+ if (TV.isConstrainedTrue())
+ return true;
+
+ return false;
+ }
+
+ /// \return Whether \p ID has a superclass by the name \p ClassName.
+ bool interfaceHasSuperclass(const ObjCInterfaceDecl *ID,
+ StringRef ClassName) const {
+ if (ID->getIdentifier()->getName() == ClassName)
+ return true;
+
+ if (const ObjCInterfaceDecl *Super = ID->getSuperClass())
+ return interfaceHasSuperclass(Super, ClassName);
+
+ return false;
+ }
+
+
+ /// \return a state with an optional implication added (if exists)
+ /// from a map of recorded implications.
+ /// If \p Negated is true, checks NullImplicationMap, and assumes
+ /// the negation of \p Antecedent.
+ /// Checks NonNullImplicationMap and assumes \p Antecedent otherwise.
+ ProgramStateRef addImplication(SymbolRef Antecedent,
+ ProgramStateRef InputState,
+ bool Negated) const {
+ if (!InputState)
+ return nullptr;
+ SValBuilder &SVB = InputState->getStateManager().getSValBuilder();
+ const SymbolRef *Consequent =
+ Negated ? InputState->get<NonNullImplicationMap>(Antecedent)
+ : InputState->get<NullImplicationMap>(Antecedent);
+ if (!Consequent)
+ return InputState;
+
+ SVal AntecedentV = SVB.makeSymbolVal(Antecedent);
+ ProgramStateRef State = InputState;
+
+ if ((Negated && InputState->isNonNull(AntecedentV).isConstrainedTrue())
+ || (!Negated && InputState->isNull(AntecedentV).isConstrainedTrue())) {
+ SVal ConsequentS = SVB.makeSymbolVal(*Consequent);
+ State = InputState->assume(ConsequentS.castAs<DefinedSVal>(), Negated);
+ if (!State)
+ return nullptr;
+
+ // Drop implications from the map.
+ if (Negated) {
+ State = State->remove<NonNullImplicationMap>(Antecedent);
+ State = State->remove<NullImplicationMap>(*Consequent);
+ } else {
+ State = State->remove<NullImplicationMap>(Antecedent);
+ State = State->remove<NonNullImplicationMap>(*Consequent);
+ }
+ }
+
+ return State;
+ }
+};
+
+} // end empty namespace
+
+
+void ento::registerTrustNonnullChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext());
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
new file mode 100644
index 000000000000..d7fad4e475ab
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
@@ -0,0 +1,111 @@
+//=== UndefBranchChecker.cpp -----------------------------------*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines UndefBranchChecker, which checks for undefined branch
+// condition.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class UndefBranchChecker : public Checker<check::BranchCondition> {
+ mutable std::unique_ptr<BuiltinBug> BT;
+
+ struct FindUndefExpr {
+ ProgramStateRef St;
+ const LocationContext *LCtx;
+
+ FindUndefExpr(ProgramStateRef S, const LocationContext *L)
+ : St(std::move(S)), LCtx(L) {}
+
+ const Expr *FindExpr(const Expr *Ex) {
+ if (!MatchesCriteria(Ex))
+ return nullptr;
+
+ for (const Stmt *SubStmt : Ex->children())
+ if (const Expr *ExI = dyn_cast_or_null<Expr>(SubStmt))
+ if (const Expr *E2 = FindExpr(ExI))
+ return E2;
+
+ return Ex;
+ }
+
+ bool MatchesCriteria(const Expr *Ex) {
+ return St->getSVal(Ex, LCtx).isUndef();
+ }
+ };
+
+public:
+ void checkBranchCondition(const Stmt *Condition, CheckerContext &Ctx) const;
+};
+
+}
+
+void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
+ CheckerContext &Ctx) const {
+ SVal X = Ctx.getSVal(Condition);
+ if (X.isUndef()) {
+ // Generate a sink node, which implicitly marks both outgoing branches as
+ // infeasible.
+ ExplodedNode *N = Ctx.generateErrorNode();
+ if (N) {
+ if (!BT)
+ BT.reset(new BuiltinBug(
+ this, "Branch condition evaluates to a garbage value"));
+
+ // What's going on here: we want to highlight the subexpression of the
+ // condition that is the most likely source of the "uninitialized
+ // branch condition." We do a recursive walk of the condition's
+ // subexpressions and roughly look for the most nested subexpression
+ // that binds to Undefined. We then highlight that expression's range.
+
+ // Get the predecessor node and check if is a PostStmt with the Stmt
+ // being the terminator condition. We want to inspect the state
+ // of that node instead because it will contain main information about
+ // the subexpressions.
+
+ // Note: any predecessor will do. They should have identical state,
+ // since all the BlockEdge did was act as an error sink since the value
+ // had to already be undefined.
+ assert (!N->pred_empty());
+ const Expr *Ex = cast<Expr>(Condition);
+ ExplodedNode *PrevN = *N->pred_begin();
+ ProgramPoint P = PrevN->getLocation();
+ ProgramStateRef St = N->getState();
+
+ if (Optional<PostStmt> PS = P.getAs<PostStmt>())
+ if (PS->getStmt() == Ex)
+ St = PrevN->getState();
+
+ FindUndefExpr FindIt(St, Ctx.getLocationContext());
+ Ex = FindIt.FindExpr(Ex);
+
+ // Emit the bug report.
+ auto R = llvm::make_unique<BugReport>(*BT, BT->getDescription(), N);
+ bugreporter::trackExpressionValue(N, Ex, *R);
+ R->addRange(Ex->getSourceRange());
+
+ Ctx.emitReport(std::move(R));
+ }
+ }
+}
+
+void ento::registerUndefBranchChecker(CheckerManager &mgr) {
+ mgr.registerChecker<UndefBranchChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
new file mode 100644
index 000000000000..8a625227b81e
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
@@ -0,0 +1,102 @@
+// UndefCapturedBlockVarChecker.cpp - Uninitialized captured vars -*- C++ -*-=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This checker detects blocks that capture uninitialized values.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/Attr.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class UndefCapturedBlockVarChecker
+ : public Checker< check::PostStmt<BlockExpr> > {
+ mutable std::unique_ptr<BugType> BT;
+
+public:
+ void checkPostStmt(const BlockExpr *BE, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+static const DeclRefExpr *FindBlockDeclRefExpr(const Stmt *S,
+ const VarDecl *VD) {
+ if (const DeclRefExpr *BR = dyn_cast<DeclRefExpr>(S))
+ if (BR->getDecl() == VD)
+ return BR;
+
+ for (const Stmt *Child : S->children())
+ if (Child)
+ if (const DeclRefExpr *BR = FindBlockDeclRefExpr(Child, VD))
+ return BR;
+
+ return nullptr;
+}
+
+void
+UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
+ CheckerContext &C) const {
+ if (!BE->getBlockDecl()->hasCaptures())
+ return;
+
+ ProgramStateRef state = C.getState();
+ auto *R = cast<BlockDataRegion>(C.getSVal(BE).getAsRegion());
+
+ BlockDataRegion::referenced_vars_iterator I = R->referenced_vars_begin(),
+ E = R->referenced_vars_end();
+
+ for (; I != E; ++I) {
+ // This VarRegion is the region associated with the block; we need
+ // the one associated with the encompassing context.
+ const VarRegion *VR = I.getCapturedRegion();
+ const VarDecl *VD = VR->getDecl();
+
+ if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage())
+ continue;
+
+ // Get the VarRegion associated with VD in the local stack frame.
+ if (Optional<UndefinedVal> V =
+ state->getSVal(I.getOriginalRegion()).getAs<UndefinedVal>()) {
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT)
+ BT.reset(
+ new BuiltinBug(this, "uninitialized variable captured by block"));
+
+ // Generate a bug report.
+ SmallString<128> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << "Variable '" << VD->getName()
+ << "' is uninitialized when captured by block";
+
+ auto R = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ if (const Expr *Ex = FindBlockDeclRefExpr(BE->getBody(), VD))
+ R->addRange(Ex->getSourceRange());
+ R->addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
+ *V, VR, /*EnableNullFPSuppression*/ false));
+ R->disablePathPruning();
+ // need location of block
+ C.emitReport(std::move(R));
+ }
+ }
+ }
+}
+
+void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) {
+ mgr.registerChecker<UndefCapturedBlockVarChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
new file mode 100644
index 000000000000..624cff6048fd
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -0,0 +1,188 @@
+//=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines UndefResultChecker, a builtin check in ExprEngine that
+// performs checks for undefined results of non-assignment binary operators.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class UndefResultChecker
+ : public Checker< check::PostStmt<BinaryOperator> > {
+
+ mutable std::unique_ptr<BugType> BT;
+
+public:
+ void checkPostStmt(const BinaryOperator *B, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+static bool isArrayIndexOutOfBounds(CheckerContext &C, const Expr *Ex) {
+ ProgramStateRef state = C.getState();
+
+ if (!isa<ArraySubscriptExpr>(Ex))
+ return false;
+
+ SVal Loc = C.getSVal(Ex);
+ if (!Loc.isValid())
+ return false;
+
+ const MemRegion *MR = Loc.castAs<loc::MemRegionVal>().getRegion();
+ const ElementRegion *ER = dyn_cast<ElementRegion>(MR);
+ if (!ER)
+ return false;
+
+ DefinedOrUnknownSVal Idx = ER->getIndex().castAs<DefinedOrUnknownSVal>();
+ DefinedOrUnknownSVal NumElements = C.getStoreManager().getSizeInElements(
+ state, ER->getSuperRegion(), ER->getValueType());
+ ProgramStateRef StInBound = state->assumeInBound(Idx, NumElements, true);
+ ProgramStateRef StOutBound = state->assumeInBound(Idx, NumElements, false);
+ return StOutBound && !StInBound;
+}
+
+static bool isShiftOverflow(const BinaryOperator *B, CheckerContext &C) {
+ return C.isGreaterOrEqual(
+ B->getRHS(), C.getASTContext().getIntWidth(B->getLHS()->getType()));
+}
+
+static bool isLeftShiftResultUnrepresentable(const BinaryOperator *B,
+ CheckerContext &C) {
+ SValBuilder &SB = C.getSValBuilder();
+ ProgramStateRef State = C.getState();
+ const llvm::APSInt *LHS = SB.getKnownValue(State, C.getSVal(B->getLHS()));
+ const llvm::APSInt *RHS = SB.getKnownValue(State, C.getSVal(B->getRHS()));
+ assert(LHS && RHS && "Values unknown, inconsistent state");
+ return (unsigned)RHS->getZExtValue() > LHS->countLeadingZeros();
+}
+
+void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
+ CheckerContext &C) const {
+ if (C.getSVal(B).isUndef()) {
+
+ // Do not report assignments of uninitialized values inside swap functions.
+ // This should allow to swap partially uninitialized structs
+ // (radar://14129997)
+ if (const FunctionDecl *EnclosingFunctionDecl =
+ dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
+ if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
+ return;
+
+ // Generate an error node.
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+
+ if (!BT)
+ BT.reset(
+ new BuiltinBug(this, "Result of operation is garbage or undefined"));
+
+ SmallString<256> sbuf;
+ llvm::raw_svector_ostream OS(sbuf);
+ const Expr *Ex = nullptr;
+ bool isLeft = true;
+
+ if (C.getSVal(B->getLHS()).isUndef()) {
+ Ex = B->getLHS()->IgnoreParenCasts();
+ isLeft = true;
+ }
+ else if (C.getSVal(B->getRHS()).isUndef()) {
+ Ex = B->getRHS()->IgnoreParenCasts();
+ isLeft = false;
+ }
+
+ if (Ex) {
+ OS << "The " << (isLeft ? "left" : "right") << " operand of '"
+ << BinaryOperator::getOpcodeStr(B->getOpcode())
+ << "' is a garbage value";
+ if (isArrayIndexOutOfBounds(C, Ex))
+ OS << " due to array index out of bounds";
+ } else {
+ // Neither operand was undefined, but the result is undefined.
+ if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
+ B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
+ C.isNegative(B->getRHS())) {
+ OS << "The result of the "
+ << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
+ : "right")
+ << " shift is undefined because the right operand is negative";
+ Ex = B->getRHS();
+ } else if ((B->getOpcode() == BinaryOperatorKind::BO_Shl ||
+ B->getOpcode() == BinaryOperatorKind::BO_Shr) &&
+ isShiftOverflow(B, C)) {
+
+ OS << "The result of the "
+ << ((B->getOpcode() == BinaryOperatorKind::BO_Shl) ? "left"
+ : "right")
+ << " shift is undefined due to shifting by ";
+ Ex = B->getRHS();
+
+ SValBuilder &SB = C.getSValBuilder();
+ const llvm::APSInt *I =
+ SB.getKnownValue(C.getState(), C.getSVal(B->getRHS()));
+ if (!I)
+ OS << "a value that is";
+ else if (I->isUnsigned())
+ OS << '\'' << I->getZExtValue() << "\', which is";
+ else
+ OS << '\'' << I->getSExtValue() << "\', which is";
+
+ OS << " greater or equal to the width of type '"
+ << B->getLHS()->getType().getAsString() << "'.";
+ } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
+ C.isNegative(B->getLHS())) {
+ OS << "The result of the left shift is undefined because the left "
+ "operand is negative";
+ Ex = B->getLHS();
+ } else if (B->getOpcode() == BinaryOperatorKind::BO_Shl &&
+ isLeftShiftResultUnrepresentable(B, C)) {
+ ProgramStateRef State = C.getState();
+ SValBuilder &SB = C.getSValBuilder();
+ const llvm::APSInt *LHS =
+ SB.getKnownValue(State, C.getSVal(B->getLHS()));
+ const llvm::APSInt *RHS =
+ SB.getKnownValue(State, C.getSVal(B->getRHS()));
+ OS << "The result of the left shift is undefined due to shifting \'"
+ << LHS->getSExtValue() << "\' by \'" << RHS->getZExtValue()
+ << "\', which is unrepresentable in the unsigned version of "
+ << "the return type \'" << B->getLHS()->getType().getAsString()
+ << "\'";
+ Ex = B->getLHS();
+ } else {
+ OS << "The result of the '"
+ << BinaryOperator::getOpcodeStr(B->getOpcode())
+ << "' expression is undefined";
+ }
+ }
+ auto report = llvm::make_unique<BugReport>(*BT, OS.str(), N);
+ if (Ex) {
+ report->addRange(Ex->getSourceRange());
+ bugreporter::trackExpressionValue(N, Ex, *report);
+ }
+ else
+ bugreporter::trackExpressionValue(N, B, *report);
+
+ C.emitReport(std::move(report));
+ }
+}
+
+void ento::registerUndefResultChecker(CheckerManager &mgr) {
+ mgr.registerChecker<UndefResultChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
new file mode 100644
index 000000000000..1d78d7cebd67
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
@@ -0,0 +1,64 @@
+//===--- UndefinedArraySubscriptChecker.h ----------------------*- C++ -*--===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines UndefinedArraySubscriptChecker, a builtin check in ExprEngine
+// that performs checks for undefined array subscripts.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class UndefinedArraySubscriptChecker
+ : public Checker< check::PreStmt<ArraySubscriptExpr> > {
+ mutable std::unique_ptr<BugType> BT;
+
+public:
+ void checkPreStmt(const ArraySubscriptExpr *A, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+void
+UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A,
+ CheckerContext &C) const {
+ const Expr *Index = A->getIdx();
+ if (!C.getSVal(Index).isUndef())
+ return;
+
+ // Sema generates anonymous array variables for copying array struct fields.
+ // Don't warn if we're in an implicitly-generated constructor.
+ const Decl *D = C.getLocationContext()->getDecl();
+ if (const CXXConstructorDecl *Ctor = dyn_cast<CXXConstructorDecl>(D))
+ if (Ctor->isDefaulted())
+ return;
+
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+ if (!BT)
+ BT.reset(new BuiltinBug(this, "Array subscript is undefined"));
+
+ // Generate a report for this bug.
+ auto R = llvm::make_unique<BugReport>(*BT, BT->getName(), N);
+ R->addRange(A->getIdx()->getSourceRange());
+ bugreporter::trackExpressionValue(N, A->getIdx(), *R);
+ C.emitReport(std::move(R));
+}
+
+void ento::registerUndefinedArraySubscriptChecker(CheckerManager &mgr) {
+ mgr.registerChecker<UndefinedArraySubscriptChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
new file mode 100644
index 000000000000..8e10bfdd2f3c
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
@@ -0,0 +1,122 @@
+//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines UndefinedAssignmentChecker, a builtin check in ExprEngine that
+// checks for assigning undefined values.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class UndefinedAssignmentChecker
+ : public Checker<check::Bind> {
+ mutable std::unique_ptr<BugType> BT;
+
+public:
+ void checkBind(SVal location, SVal val, const Stmt *S,
+ CheckerContext &C) const;
+};
+}
+
+void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
+ const Stmt *StoreE,
+ CheckerContext &C) const {
+ if (!val.isUndef())
+ return;
+
+ // Do not report assignments of uninitialized values inside swap functions.
+ // This should allow to swap partially uninitialized structs
+ // (radar://14129997)
+ if (const FunctionDecl *EnclosingFunctionDecl =
+ dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
+ if (C.getCalleeName(EnclosingFunctionDecl) == "swap")
+ return;
+
+ ExplodedNode *N = C.generateErrorNode();
+
+ if (!N)
+ return;
+
+ static const char *const DefaultMsg =
+ "Assigned value is garbage or undefined";
+ if (!BT)
+ BT.reset(new BuiltinBug(this, DefaultMsg));
+
+ // Generate a report for this bug.
+ llvm::SmallString<128> Str;
+ llvm::raw_svector_ostream OS(Str);
+
+ const Expr *ex = nullptr;
+
+ while (StoreE) {
+ if (const UnaryOperator *U = dyn_cast<UnaryOperator>(StoreE)) {
+ OS << "The expression is an uninitialized value. "
+ "The computed value will also be garbage";
+
+ ex = U->getSubExpr();
+ break;
+ }
+
+ if (const BinaryOperator *B = dyn_cast<BinaryOperator>(StoreE)) {
+ if (B->isCompoundAssignmentOp()) {
+ if (C.getSVal(B->getLHS()).isUndef()) {
+ OS << "The left expression of the compound assignment is an "
+ "uninitialized value. The computed value will also be garbage";
+ ex = B->getLHS();
+ break;
+ }
+ }
+
+ ex = B->getRHS();
+ break;
+ }
+
+ if (const DeclStmt *DS = dyn_cast<DeclStmt>(StoreE)) {
+ const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
+ ex = VD->getInit();
+ }
+
+ if (const auto *CD =
+ dyn_cast<CXXConstructorDecl>(C.getStackFrame()->getDecl())) {
+ if (CD->isImplicit()) {
+ for (auto I : CD->inits()) {
+ if (I->getInit()->IgnoreImpCasts() == StoreE) {
+ OS << "Value assigned to field '" << I->getMember()->getName()
+ << "' in implicit constructor is garbage or undefined";
+ break;
+ }
+ }
+ }
+ }
+
+ break;
+ }
+
+ if (OS.str().empty())
+ OS << DefaultMsg;
+
+ auto R = llvm::make_unique<BugReport>(*BT, OS.str(), N);
+ if (ex) {
+ R->addRange(ex->getSourceRange());
+ bugreporter::trackExpressionValue(N, ex, *R);
+ }
+ C.emitReport(std::move(R));
+}
+
+void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) {
+ mgr.registerChecker<UndefinedAssignmentChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
new file mode 100644
index 000000000000..c3291a21c164
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
@@ -0,0 +1,349 @@
+//===----- UninitializedObject.h ---------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines helper classes for UninitializedObjectChecker and
+// documentation about the logic of it.
+//
+// The checker reports uninitialized fields in objects created after a
+// constructor call.
+//
+// This checker has several options:
+// - "Pedantic" (boolean). If its not set or is set to false, the checker
+// won't emit warnings for objects that don't have at least one initialized
+// field. This may be set with
+//
+// `-analyzer-config alpha.cplusplus.UninitializedObject:Pedantic=true`.
+//
+// - "NotesAsWarnings" (boolean). If set to true, the checker will emit a
+// warning for each uninitialized field, as opposed to emitting one warning
+// per constructor call, and listing the uninitialized fields that belongs
+// to it in notes. Defaults to false.
+//
+// `-analyzer-config \
+// alpha.cplusplus.UninitializedObject:NotesAsWarnings=true`.
+//
+// - "CheckPointeeInitialization" (boolean). If set to false, the checker will
+// not analyze the pointee of pointer/reference fields, and will only check
+// whether the object itself is initialized. Defaults to false.
+//
+// `-analyzer-config \
+// alpha.cplusplus.UninitializedObject:CheckPointeeInitialization=true`.
+//
+// - "IgnoreRecordsWithField" (string). If supplied, the checker will not
+// analyze structures that have a field with a name or type name that
+// matches the given pattern. Defaults to "".
+//
+// `-analyzer-config \
+// alpha.cplusplus.UninitializedObject:IgnoreRecordsWithField="[Tt]ag|[Kk]ind"`.
+//
+// TODO: With some clever heuristics, some pointers should be dereferenced
+// by default. For example, if the pointee is constructed within the
+// constructor call, it's reasonable to say that no external object
+// references it, and we wouldn't generate multiple report on the same
+// pointee.
+//
+// Most of the following methods as well as the checker itself is defined in
+// UninitializedObjectChecker.cpp.
+//
+// Some methods are implemented in UninitializedPointee.cpp, to reduce the
+// complexity of the main checker file.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H
+#define LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+namespace clang {
+namespace ento {
+
+struct UninitObjCheckerOptions {
+ bool IsPedantic = false;
+ bool ShouldConvertNotesToWarnings = false;
+ bool CheckPointeeInitialization = false;
+ std::string IgnoredRecordsWithFieldPattern;
+};
+
+/// A lightweight polymorphic wrapper around FieldRegion *. We'll use this
+/// interface to store addinitional information about fields. As described
+/// later, a list of these objects (i.e. "fieldchain") will be constructed and
+/// used for printing note messages should an uninitialized value be found.
+class FieldNode {
+protected:
+ const FieldRegion *FR;
+
+ /// FieldNodes are never meant to be created on the heap, see
+ /// FindUninitializedFields::addFieldToUninits().
+ /* non-virtual */ ~FieldNode() = default;
+
+public:
+ FieldNode(const FieldRegion *FR) : FR(FR) {}
+
+ // We'll delete all of these special member functions to force the users of
+ // this interface to only store references to FieldNode objects in containers.
+ FieldNode() = delete;
+ FieldNode(const FieldNode &) = delete;
+ FieldNode(FieldNode &&) = delete;
+ FieldNode &operator=(const FieldNode &) = delete;
+ FieldNode &operator=(const FieldNode &&) = delete;
+
+ void Profile(llvm::FoldingSetNodeID &ID) const { ID.AddPointer(this); }
+
+ /// Helper method for uniqueing.
+ bool isSameRegion(const FieldRegion *OtherFR) const {
+ // Special FieldNode descendants may wrap nullpointers (for example if they
+ // describe a special relationship between two elements of the fieldchain)
+ // -- we wouldn't like to unique these objects.
+ if (FR == nullptr)
+ return false;
+
+ return FR == OtherFR;
+ }
+
+ const FieldRegion *getRegion() const { return FR; }
+ const FieldDecl *getDecl() const {
+ assert(FR);
+ return FR->getDecl();
+ }
+
+ // When a fieldchain is printed, it will have the following format (without
+ // newline, indices are in order of insertion, from 1 to n):
+ //
+ // <note_message_n>'<prefix_n><prefix_n-1>...<prefix_1>
+ // this-><node_1><separator_1><node_2><separator_2>...<node_n>'
+
+ /// If this is the last element of the fieldchain, this method will print the
+ /// note message associated with it.
+ /// The note message should state something like "uninitialized field" or
+ /// "uninitialized pointee" etc.
+ virtual void printNoteMsg(llvm::raw_ostream &Out) const = 0;
+
+ /// Print any prefixes before the fieldchain. Could contain casts, etc.
+ virtual void printPrefix(llvm::raw_ostream &Out) const = 0;
+
+ /// Print the node. Should contain the name of the field stored in FR.
+ virtual void printNode(llvm::raw_ostream &Out) const = 0;
+
+ /// Print the separator. For example, fields may be separated with '.' or
+ /// "->".
+ virtual void printSeparator(llvm::raw_ostream &Out) const = 0;
+
+ virtual bool isBase() const { return false; }
+};
+
+/// Returns with Field's name. This is a helper function to get the correct name
+/// even if Field is a captured lambda variable.
+std::string getVariableName(const FieldDecl *Field);
+
+/// Represents a field chain. A field chain is a list of fields where the first
+/// element of the chain is the object under checking (not stored), and every
+/// other element is a field, and the element that precedes it is the object
+/// that contains it.
+///
+/// Note that this class is immutable (essentially a wrapper around an
+/// ImmutableList), new FieldChainInfo objects may be created by member
+/// functions such as add() and replaceHead().
+class FieldChainInfo {
+public:
+ using FieldChain = llvm::ImmutableList<const FieldNode &>;
+
+private:
+ FieldChain::Factory &ChainFactory;
+ FieldChain Chain;
+
+ FieldChainInfo(FieldChain::Factory &F, FieldChain NewChain)
+ : FieldChainInfo(F) {
+ Chain = NewChain;
+ }
+
+public:
+ FieldChainInfo() = delete;
+ FieldChainInfo(FieldChain::Factory &F) : ChainFactory(F) {}
+ FieldChainInfo(const FieldChainInfo &Other) = default;
+
+ /// Constructs a new FieldChainInfo object with \p FN appended.
+ template <class FieldNodeT> FieldChainInfo add(const FieldNodeT &FN);
+
+ /// Constructs a new FieldChainInfo object with \p FN as the new head of the
+ /// list.
+ template <class FieldNodeT> FieldChainInfo replaceHead(const FieldNodeT &FN);
+
+ bool contains(const FieldRegion *FR) const;
+ bool isEmpty() const { return Chain.isEmpty(); }
+
+ const FieldNode &getHead() const { return Chain.getHead(); }
+ const FieldRegion *getUninitRegion() const { return getHead().getRegion(); }
+
+ void printNoteMsg(llvm::raw_ostream &Out) const;
+};
+
+using UninitFieldMap = std::map<const FieldRegion *, llvm::SmallString<50>>;
+
+/// Searches for and stores uninitialized fields in a non-union object.
+class FindUninitializedFields {
+ ProgramStateRef State;
+ const TypedValueRegion *const ObjectR;
+
+ const UninitObjCheckerOptions Opts;
+ bool IsAnyFieldInitialized = false;
+
+ FieldChainInfo::FieldChain::Factory ChainFactory;
+
+ /// A map for assigning uninitialized regions to note messages. For example,
+ ///
+ /// struct A {
+ /// int x;
+ /// };
+ ///
+ /// A a;
+ ///
+ /// After analyzing `a`, the map will contain a pair for `a.x`'s region and
+ /// the note message "uninitialized field 'this->x'.
+ UninitFieldMap UninitFields;
+
+public:
+ /// Constructs the FindUninitializedField object, searches for and stores
+ /// uninitialized fields in R.
+ FindUninitializedFields(ProgramStateRef State,
+ const TypedValueRegion *const R,
+ const UninitObjCheckerOptions &Opts);
+
+ /// Returns with the modified state and a map of (uninitialized region,
+ /// note message) pairs.
+ std::pair<ProgramStateRef, const UninitFieldMap &> getResults() {
+ return {State, UninitFields};
+ }
+
+ /// Returns whether the analyzed region contains at least one initialized
+ /// field. Note that this includes subfields as well, not just direct ones,
+ /// and will return false if an uninitialized pointee is found with
+ /// CheckPointeeInitialization enabled.
+ bool isAnyFieldInitialized() { return IsAnyFieldInitialized; }
+
+private:
+ // For the purposes of this checker, we'll regard the analyzed region as a
+ // directed tree, where
+ // * the root is the object under checking
+ // * every node is an object that is
+ // - a union
+ // - a non-union record
+ // - dereferenceable (see isDereferencableType())
+ // - an array
+ // - of a primitive type (see isPrimitiveType())
+ // * the parent of each node is the object that contains it
+ // * every leaf is an array, a primitive object, a nullptr or an undefined
+ // pointer.
+ //
+ // Example:
+ //
+ // struct A {
+ // struct B {
+ // int x, y = 0;
+ // };
+ // B b;
+ // int *iptr = new int;
+ // B* bptr;
+ //
+ // A() {}
+ // };
+ //
+ // The directed tree:
+ //
+ // ->x
+ // /
+ // ->b--->y
+ // /
+ // A-->iptr->(int value)
+ // \
+ // ->bptr
+ //
+ // From this we'll construct a vector of fieldchains, where each fieldchain
+ // represents an uninitialized field. An uninitialized field may be a
+ // primitive object, a pointer, a pointee or a union without a single
+ // initialized field.
+ // In the above example, for the default constructor call we'll end up with
+ // these fieldchains:
+ //
+ // this->b.x
+ // this->iptr (pointee uninit)
+ // this->bptr (pointer uninit)
+ //
+ // We'll traverse each node of the above graph with the appropriate one of
+ // these methods:
+
+ /// Checks the region of a union object, and returns true if no field is
+ /// initialized within the region.
+ bool isUnionUninit(const TypedValueRegion *R);
+
+ /// Checks a region of a non-union object, and returns true if an
+ /// uninitialized field is found within the region.
+ bool isNonUnionUninit(const TypedValueRegion *R, FieldChainInfo LocalChain);
+
+ /// Checks a region of a pointer or reference object, and returns true if the
+ /// ptr/ref object itself or any field within the pointee's region is
+ /// uninitialized.
+ bool isDereferencableUninit(const FieldRegion *FR, FieldChainInfo LocalChain);
+
+ /// Returns true if the value of a primitive object is uninitialized.
+ bool isPrimitiveUninit(const SVal &V);
+
+ // Note that we don't have a method for arrays -- the elements of an array are
+ // often left uninitialized intentionally even when it is of a C++ record
+ // type, so we'll assume that an array is always initialized.
+ // TODO: Add a support for nonloc::LocAsInteger.
+
+ /// Processes LocalChain and attempts to insert it into UninitFields. Returns
+ /// true on success. Also adds the head of the list and \p PointeeR (if
+ /// supplied) to the GDM as already analyzed objects.
+ ///
+ /// Since this class analyzes regions with recursion, we'll only store
+ /// references to temporary FieldNode objects created on the stack. This means
+ /// that after analyzing a leaf of the directed tree described above, the
+ /// elements LocalChain references will be destructed, so we can't store it
+ /// directly.
+ bool addFieldToUninits(FieldChainInfo LocalChain,
+ const MemRegion *PointeeR = nullptr);
+};
+
+/// Returns true if T is a primitive type. An object of a primitive type only
+/// needs to be analyzed as much as checking whether their value is undefined.
+inline bool isPrimitiveType(const QualType &T) {
+ return T->isBuiltinType() || T->isEnumeralType() ||
+ T->isMemberPointerType() || T->isBlockPointerType() ||
+ T->isFunctionType();
+}
+
+inline bool isDereferencableType(const QualType &T) {
+ return T->isAnyPointerType() || T->isReferenceType();
+}
+
+// Template method definitions.
+
+template <class FieldNodeT>
+inline FieldChainInfo FieldChainInfo::add(const FieldNodeT &FN) {
+ assert(!contains(FN.getRegion()) &&
+ "Can't add a field that is already a part of the "
+ "fieldchain! Is this a cyclic reference?");
+
+ FieldChainInfo NewChain = *this;
+ NewChain.Chain = ChainFactory.add(FN, Chain);
+ return NewChain;
+}
+
+template <class FieldNodeT>
+inline FieldChainInfo FieldChainInfo::replaceHead(const FieldNodeT &FN) {
+ FieldChainInfo NewChain(ChainFactory, Chain.getTail());
+ return NewChain.add(FN);
+}
+
+} // end of namespace ento
+} // end of namespace clang
+
+#endif // LLVM_CLANG_STATICANALYZER_UNINITIALIZEDOBJECT_H
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
new file mode 100644
index 000000000000..208e303e8295
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
@@ -0,0 +1,538 @@
+//===----- UninitializedObjectChecker.cpp ------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a checker that reports uninitialized fields in objects
+// created after a constructor call.
+//
+// To read about command line options and how the checker works, refer to the
+// top of the file and inline comments in UninitializedObject.h.
+//
+// Some of the logic is implemented in UninitializedPointee.cpp, to reduce the
+// complexity of this file.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "UninitializedObject.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+
+using namespace clang;
+using namespace clang::ento;
+
+/// We'll mark fields (and pointee of fields) that are confirmed to be
+/// uninitialized as already analyzed.
+REGISTER_SET_WITH_PROGRAMSTATE(AnalyzedRegions, const MemRegion *)
+
+namespace {
+
+class UninitializedObjectChecker
+ : public Checker<check::EndFunction, check::DeadSymbols> {
+ std::unique_ptr<BuiltinBug> BT_uninitField;
+
+public:
+ // The fields of this struct will be initialized when registering the checker.
+ UninitObjCheckerOptions Opts;
+
+ UninitializedObjectChecker()
+ : BT_uninitField(new BuiltinBug(this, "Uninitialized fields")) {}
+
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+};
+
+/// A basic field type, that is not a pointer or a reference, it's dynamic and
+/// static type is the same.
+class RegularField final : public FieldNode {
+public:
+ RegularField(const FieldRegion *FR) : FieldNode(FR) {}
+
+ virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ Out << "uninitialized field ";
+ }
+
+ virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+
+ virtual void printNode(llvm::raw_ostream &Out) const override {
+ Out << getVariableName(getDecl());
+ }
+
+ virtual void printSeparator(llvm::raw_ostream &Out) const override {
+ Out << '.';
+ }
+};
+
+/// Represents that the FieldNode that comes after this is declared in a base
+/// of the previous FieldNode. As such, this descendant doesn't wrap a
+/// FieldRegion, and is purely a tool to describe a relation between two other
+/// FieldRegion wrapping descendants.
+class BaseClass final : public FieldNode {
+ const QualType BaseClassT;
+
+public:
+ BaseClass(const QualType &T) : FieldNode(nullptr), BaseClassT(T) {
+ assert(!T.isNull());
+ assert(T->getAsCXXRecordDecl());
+ }
+
+ virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ llvm_unreachable("This node can never be the final node in the "
+ "fieldchain!");
+ }
+
+ virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+
+ virtual void printNode(llvm::raw_ostream &Out) const override {
+ Out << BaseClassT->getAsCXXRecordDecl()->getName() << "::";
+ }
+
+ virtual void printSeparator(llvm::raw_ostream &Out) const override {}
+
+ virtual bool isBase() const override { return true; }
+};
+
+} // end of anonymous namespace
+
+// Utility function declarations.
+
+/// Returns the region that was constructed by CtorDecl, or nullptr if that
+/// isn't possible.
+static const TypedValueRegion *
+getConstructedRegion(const CXXConstructorDecl *CtorDecl,
+ CheckerContext &Context);
+
+/// Checks whether the object constructed by \p Ctor will be analyzed later
+/// (e.g. if the object is a field of another object, in which case we'd check
+/// it multiple times).
+static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
+ CheckerContext &Context);
+
+/// Checks whether RD contains a field with a name or type name that matches
+/// \p Pattern.
+static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern);
+
+//===----------------------------------------------------------------------===//
+// Methods for UninitializedObjectChecker.
+//===----------------------------------------------------------------------===//
+
+void UninitializedObjectChecker::checkEndFunction(
+ const ReturnStmt *RS, CheckerContext &Context) const {
+
+ const auto *CtorDecl = dyn_cast_or_null<CXXConstructorDecl>(
+ Context.getLocationContext()->getDecl());
+ if (!CtorDecl)
+ return;
+
+ if (!CtorDecl->isUserProvided())
+ return;
+
+ if (CtorDecl->getParent()->isUnion())
+ return;
+
+ // This avoids essentially the same error being reported multiple times.
+ if (willObjectBeAnalyzedLater(CtorDecl, Context))
+ return;
+
+ const TypedValueRegion *R = getConstructedRegion(CtorDecl, Context);
+ if (!R)
+ return;
+
+ FindUninitializedFields F(Context.getState(), R, Opts);
+
+ std::pair<ProgramStateRef, const UninitFieldMap &> UninitInfo =
+ F.getResults();
+
+ ProgramStateRef UpdatedState = UninitInfo.first;
+ const UninitFieldMap &UninitFields = UninitInfo.second;
+
+ if (UninitFields.empty()) {
+ Context.addTransition(UpdatedState);
+ return;
+ }
+
+ // There are uninitialized fields in the record.
+
+ ExplodedNode *Node = Context.generateNonFatalErrorNode(UpdatedState);
+ if (!Node)
+ return;
+
+ PathDiagnosticLocation LocUsedForUniqueing;
+ const Stmt *CallSite = Context.getStackFrame()->getCallSite();
+ if (CallSite)
+ LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
+ CallSite, Context.getSourceManager(), Node->getLocationContext());
+
+ // For Plist consumers that don't support notes just yet, we'll convert notes
+ // to warnings.
+ if (Opts.ShouldConvertNotesToWarnings) {
+ for (const auto &Pair : UninitFields) {
+
+ auto Report = llvm::make_unique<BugReport>(
+ *BT_uninitField, Pair.second, Node, LocUsedForUniqueing,
+ Node->getLocationContext()->getDecl());
+ Context.emitReport(std::move(Report));
+ }
+ return;
+ }
+
+ SmallString<100> WarningBuf;
+ llvm::raw_svector_ostream WarningOS(WarningBuf);
+ WarningOS << UninitFields.size() << " uninitialized field"
+ << (UninitFields.size() == 1 ? "" : "s")
+ << " at the end of the constructor call";
+
+ auto Report = llvm::make_unique<BugReport>(
+ *BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
+ Node->getLocationContext()->getDecl());
+
+ for (const auto &Pair : UninitFields) {
+ Report->addNote(Pair.second,
+ PathDiagnosticLocation::create(Pair.first->getDecl(),
+ Context.getSourceManager()));
+ }
+ Context.emitReport(std::move(Report));
+}
+
+void UninitializedObjectChecker::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ for (const MemRegion *R : State->get<AnalyzedRegions>()) {
+ if (!SR.isLiveRegion(R))
+ State = State->remove<AnalyzedRegions>(R);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Methods for FindUninitializedFields.
+//===----------------------------------------------------------------------===//
+
+FindUninitializedFields::FindUninitializedFields(
+ ProgramStateRef State, const TypedValueRegion *const R,
+ const UninitObjCheckerOptions &Opts)
+ : State(State), ObjectR(R), Opts(Opts) {
+
+ isNonUnionUninit(ObjectR, FieldChainInfo(ChainFactory));
+
+ // In non-pedantic mode, if ObjectR doesn't contain a single initialized
+ // field, we'll assume that Object was intentionally left uninitialized.
+ if (!Opts.IsPedantic && !isAnyFieldInitialized())
+ UninitFields.clear();
+}
+
+bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,
+ const MemRegion *PointeeR) {
+ const FieldRegion *FR = Chain.getUninitRegion();
+
+ assert((PointeeR || !isDereferencableType(FR->getDecl()->getType())) &&
+ "One must also pass the pointee region as a parameter for "
+ "dereferenceable fields!");
+
+ if (State->contains<AnalyzedRegions>(FR))
+ return false;
+
+ if (PointeeR) {
+ if (State->contains<AnalyzedRegions>(PointeeR)) {
+ return false;
+ }
+ State = State->add<AnalyzedRegions>(PointeeR);
+ }
+
+ State = State->add<AnalyzedRegions>(FR);
+
+ if (State->getStateManager().getContext().getSourceManager().isInSystemHeader(
+ FR->getDecl()->getLocation()))
+ return false;
+
+ UninitFieldMap::mapped_type NoteMsgBuf;
+ llvm::raw_svector_ostream OS(NoteMsgBuf);
+ Chain.printNoteMsg(OS);
+ return UninitFields.insert({FR, std::move(NoteMsgBuf)}).second;
+}
+
+bool FindUninitializedFields::isNonUnionUninit(const TypedValueRegion *R,
+ FieldChainInfo LocalChain) {
+ assert(R->getValueType()->isRecordType() &&
+ !R->getValueType()->isUnionType() &&
+ "This method only checks non-union record objects!");
+
+ const RecordDecl *RD = R->getValueType()->getAsRecordDecl()->getDefinition();
+
+ if (!RD) {
+ IsAnyFieldInitialized = true;
+ return true;
+ }
+
+ if (!Opts.IgnoredRecordsWithFieldPattern.empty() &&
+ shouldIgnoreRecord(RD, Opts.IgnoredRecordsWithFieldPattern)) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ bool ContainsUninitField = false;
+
+ // Are all of this non-union's fields initialized?
+ for (const FieldDecl *I : RD->fields()) {
+
+ const auto FieldVal =
+ State->getLValue(I, loc::MemRegionVal(R)).castAs<loc::MemRegionVal>();
+ const auto *FR = FieldVal.getRegionAs<FieldRegion>();
+ QualType T = I->getType();
+
+ // If LocalChain already contains FR, then we encountered a cyclic
+ // reference. In this case, region FR is already under checking at an
+ // earlier node in the directed tree.
+ if (LocalChain.contains(FR))
+ return false;
+
+ if (T->isStructureOrClassType()) {
+ if (isNonUnionUninit(FR, LocalChain.add(RegularField(FR))))
+ ContainsUninitField = true;
+ continue;
+ }
+
+ if (T->isUnionType()) {
+ if (isUnionUninit(FR)) {
+ if (addFieldToUninits(LocalChain.add(RegularField(FR))))
+ ContainsUninitField = true;
+ } else
+ IsAnyFieldInitialized = true;
+ continue;
+ }
+
+ if (T->isArrayType()) {
+ IsAnyFieldInitialized = true;
+ continue;
+ }
+
+ SVal V = State->getSVal(FieldVal);
+
+ if (isDereferencableType(T) || V.getAs<nonloc::LocAsInteger>()) {
+ if (isDereferencableUninit(FR, LocalChain))
+ ContainsUninitField = true;
+ continue;
+ }
+
+ if (isPrimitiveType(T)) {
+ if (isPrimitiveUninit(V)) {
+ if (addFieldToUninits(LocalChain.add(RegularField(FR))))
+ ContainsUninitField = true;
+ }
+ continue;
+ }
+
+ llvm_unreachable("All cases are handled!");
+ }
+
+ // Checking bases. The checker will regard inherited data members as direct
+ // fields.
+ const auto *CXXRD = dyn_cast<CXXRecordDecl>(RD);
+ if (!CXXRD)
+ return ContainsUninitField;
+
+ for (const CXXBaseSpecifier &BaseSpec : CXXRD->bases()) {
+ const auto *BaseRegion = State->getLValue(BaseSpec, R)
+ .castAs<loc::MemRegionVal>()
+ .getRegionAs<TypedValueRegion>();
+
+ // If the head of the list is also a BaseClass, we'll overwrite it to avoid
+ // note messages like 'this->A::B::x'.
+ if (!LocalChain.isEmpty() && LocalChain.getHead().isBase()) {
+ if (isNonUnionUninit(BaseRegion, LocalChain.replaceHead(
+ BaseClass(BaseSpec.getType()))))
+ ContainsUninitField = true;
+ } else {
+ if (isNonUnionUninit(BaseRegion,
+ LocalChain.add(BaseClass(BaseSpec.getType()))))
+ ContainsUninitField = true;
+ }
+ }
+
+ return ContainsUninitField;
+}
+
+bool FindUninitializedFields::isUnionUninit(const TypedValueRegion *R) {
+ assert(R->getValueType()->isUnionType() &&
+ "This method only checks union objects!");
+ // TODO: Implement support for union fields.
+ return false;
+}
+
+bool FindUninitializedFields::isPrimitiveUninit(const SVal &V) {
+ if (V.isUndef())
+ return true;
+
+ IsAnyFieldInitialized = true;
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// Methods for FieldChainInfo.
+//===----------------------------------------------------------------------===//
+
+bool FieldChainInfo::contains(const FieldRegion *FR) const {
+ for (const FieldNode &Node : Chain) {
+ if (Node.isSameRegion(FR))
+ return true;
+ }
+ return false;
+}
+
+/// Prints every element except the last to `Out`. Since ImmutableLists store
+/// elements in reverse order, and have no reverse iterators, we use a
+/// recursive function to print the fieldchain correctly. The last element in
+/// the chain is to be printed by `FieldChainInfo::print`.
+static void printTail(llvm::raw_ostream &Out,
+ const FieldChainInfo::FieldChain L);
+
+// FIXME: This function constructs an incorrect string in the following case:
+//
+// struct Base { int x; };
+// struct D1 : Base {}; struct D2 : Base {};
+//
+// struct MostDerived : D1, D2 {
+// MostDerived() {}
+// }
+//
+// A call to MostDerived::MostDerived() will cause two notes that say
+// "uninitialized field 'this->x'", but we can't refer to 'x' directly,
+// we need an explicit namespace resolution whether the uninit field was
+// 'D1::x' or 'D2::x'.
+void FieldChainInfo::printNoteMsg(llvm::raw_ostream &Out) const {
+ if (Chain.isEmpty())
+ return;
+
+ const FieldNode &LastField = getHead();
+
+ LastField.printNoteMsg(Out);
+ Out << '\'';
+
+ for (const FieldNode &Node : Chain)
+ Node.printPrefix(Out);
+
+ Out << "this->";
+ printTail(Out, Chain.getTail());
+ LastField.printNode(Out);
+ Out << '\'';
+}
+
+static void printTail(llvm::raw_ostream &Out,
+ const FieldChainInfo::FieldChain L) {
+ if (L.isEmpty())
+ return;
+
+ printTail(Out, L.getTail());
+
+ L.getHead().printNode(Out);
+ L.getHead().printSeparator(Out);
+}
+
+//===----------------------------------------------------------------------===//
+// Utility functions.
+//===----------------------------------------------------------------------===//
+
+static const TypedValueRegion *
+getConstructedRegion(const CXXConstructorDecl *CtorDecl,
+ CheckerContext &Context) {
+
+ Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl,
+ Context.getStackFrame());
+
+ SVal ObjectV = Context.getState()->getSVal(ThisLoc);
+
+ auto *R = ObjectV.getAsRegion()->getAs<TypedValueRegion>();
+ if (R && !R->getValueType()->getAsCXXRecordDecl())
+ return nullptr;
+
+ return R;
+}
+
+static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
+ CheckerContext &Context) {
+
+ const TypedValueRegion *CurrRegion = getConstructedRegion(Ctor, Context);
+ if (!CurrRegion)
+ return false;
+
+ const LocationContext *LC = Context.getLocationContext();
+ while ((LC = LC->getParent())) {
+
+ // If \p Ctor was called by another constructor.
+ const auto *OtherCtor = dyn_cast<CXXConstructorDecl>(LC->getDecl());
+ if (!OtherCtor)
+ continue;
+
+ const TypedValueRegion *OtherRegion =
+ getConstructedRegion(OtherCtor, Context);
+ if (!OtherRegion)
+ continue;
+
+ // If the CurrRegion is a subregion of OtherRegion, it will be analyzed
+ // during the analysis of OtherRegion.
+ if (CurrRegion->isSubRegionOf(OtherRegion))
+ return true;
+ }
+
+ return false;
+}
+
+static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) {
+ llvm::Regex R(Pattern);
+
+ for (const FieldDecl *FD : RD->fields()) {
+ if (R.match(FD->getType().getAsString()))
+ return true;
+ if (R.match(FD->getName()))
+ return true;
+ }
+
+ return false;
+}
+
+std::string clang::ento::getVariableName(const FieldDecl *Field) {
+ // If Field is a captured lambda variable, Field->getName() will return with
+ // an empty string. We can however acquire it's name from the lambda's
+ // captures.
+ const auto *CXXParent = dyn_cast<CXXRecordDecl>(Field->getParent());
+
+ if (CXXParent && CXXParent->isLambda()) {
+ assert(CXXParent->captures_begin());
+ auto It = CXXParent->captures_begin() + Field->getFieldIndex();
+
+ if (It->capturesVariable())
+ return llvm::Twine("/*captured variable*/" +
+ It->getCapturedVar()->getName())
+ .str();
+
+ if (It->capturesThis())
+ return "/*'this' capture*/";
+
+ llvm_unreachable("No other capture type is expected!");
+ }
+
+ return Field->getName();
+}
+
+void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
+ auto Chk = Mgr.registerChecker<UninitializedObjectChecker>();
+
+ AnalyzerOptions &AnOpts = Mgr.getAnalyzerOptions();
+ UninitObjCheckerOptions &ChOpts = Chk->Opts;
+
+ ChOpts.IsPedantic =
+ AnOpts.getCheckerBooleanOption("Pedantic", /*DefaultVal*/ false, Chk);
+ ChOpts.ShouldConvertNotesToWarnings =
+ AnOpts.getCheckerBooleanOption("NotesAsWarnings", /*DefaultVal*/ false, Chk);
+ ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption(
+ "CheckPointeeInitialization", /*DefaultVal*/ false, Chk);
+ ChOpts.IgnoredRecordsWithFieldPattern =
+ AnOpts.getCheckerStringOption("IgnoreRecordsWithField",
+ /*DefaultVal*/ "", Chk);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
new file mode 100644
index 000000000000..aead59c7bf87
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
@@ -0,0 +1,282 @@
+//===----- UninitializedPointee.cpp ------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines functions and methods for handling pointers and references
+// to reduce the size and complexity of UninitializedObjectChecker.cpp.
+//
+// To read about command line options and documentation about how the checker
+// works, refer to UninitializedObjectChecker.h.
+//
+//===----------------------------------------------------------------------===//
+
+#include "UninitializedObject.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+
+using namespace clang;
+using namespace clang::ento;
+
+namespace {
+
+/// Represents a pointer or a reference field.
+class LocField final : public FieldNode {
+ /// We'll store whether the pointee or the pointer itself is uninitialited.
+ const bool IsDereferenced;
+
+public:
+ LocField(const FieldRegion *FR, const bool IsDereferenced = true)
+ : FieldNode(FR), IsDereferenced(IsDereferenced) {}
+
+ virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ if (IsDereferenced)
+ Out << "uninitialized pointee ";
+ else
+ Out << "uninitialized pointer ";
+ }
+
+ virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+
+ virtual void printNode(llvm::raw_ostream &Out) const override {
+ Out << getVariableName(getDecl());
+ }
+
+ virtual void printSeparator(llvm::raw_ostream &Out) const override {
+ if (getDecl()->getType()->isPointerType())
+ Out << "->";
+ else
+ Out << '.';
+ }
+};
+
+/// Represents a nonloc::LocAsInteger or void* field, that point to objects, but
+/// needs to be casted back to its dynamic type for a correct note message.
+class NeedsCastLocField final : public FieldNode {
+ QualType CastBackType;
+
+public:
+ NeedsCastLocField(const FieldRegion *FR, const QualType &T)
+ : FieldNode(FR), CastBackType(T) {}
+
+ virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ Out << "uninitialized pointee ";
+ }
+
+ virtual void printPrefix(llvm::raw_ostream &Out) const override {
+ // If this object is a nonloc::LocAsInteger.
+ if (getDecl()->getType()->isIntegerType())
+ Out << "reinterpret_cast";
+ // If this pointer's dynamic type is different then it's static type.
+ else
+ Out << "static_cast";
+ Out << '<' << CastBackType.getAsString() << ">(";
+ }
+
+ virtual void printNode(llvm::raw_ostream &Out) const override {
+ Out << getVariableName(getDecl()) << ')';
+ }
+
+ virtual void printSeparator(llvm::raw_ostream &Out) const override {
+ Out << "->";
+ }
+};
+
+/// Represents a Loc field that points to itself.
+class CyclicLocField final : public FieldNode {
+
+public:
+ CyclicLocField(const FieldRegion *FR) : FieldNode(FR) {}
+
+ virtual void printNoteMsg(llvm::raw_ostream &Out) const override {
+ Out << "object references itself ";
+ }
+
+ virtual void printPrefix(llvm::raw_ostream &Out) const override {}
+
+ virtual void printNode(llvm::raw_ostream &Out) const override {
+ Out << getVariableName(getDecl());
+ }
+
+ virtual void printSeparator(llvm::raw_ostream &Out) const override {
+ llvm_unreachable("CyclicLocField objects must be the last node of the "
+ "fieldchain!");
+ }
+};
+
+} // end of anonymous namespace
+
+// Utility function declarations.
+
+struct DereferenceInfo {
+ const TypedValueRegion *R;
+ const bool NeedsCastBack;
+ const bool IsCyclic;
+ DereferenceInfo(const TypedValueRegion *R, bool NCB, bool IC)
+ : R(R), NeedsCastBack(NCB), IsCyclic(IC) {}
+};
+
+/// Dereferences \p FR and returns with the pointee's region, and whether it
+/// needs to be casted back to it's location type. If for whatever reason
+/// dereferencing fails, returns with None.
+static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
+ const FieldRegion *FR);
+
+/// Returns whether \p T can be (transitively) dereferenced to a void pointer
+/// type (void*, void**, ...).
+static bool isVoidPointer(QualType T);
+
+//===----------------------------------------------------------------------===//
+// Methods for FindUninitializedFields.
+//===----------------------------------------------------------------------===//
+
+bool FindUninitializedFields::isDereferencableUninit(
+ const FieldRegion *FR, FieldChainInfo LocalChain) {
+
+ SVal V = State->getSVal(FR);
+
+ assert((isDereferencableType(FR->getDecl()->getType()) ||
+ V.getAs<nonloc::LocAsInteger>()) &&
+ "This method only checks dereferenceable objects!");
+
+ if (V.isUnknown() || V.getAs<loc::ConcreteInt>()) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ if (V.isUndef()) {
+ return addFieldToUninits(
+ LocalChain.add(LocField(FR, /*IsDereferenced*/ false)), FR);
+ }
+
+ if (!Opts.CheckPointeeInitialization) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ // At this point the pointer itself is initialized and points to a valid
+ // location, we'll now check the pointee.
+ llvm::Optional<DereferenceInfo> DerefInfo = dereference(State, FR);
+ if (!DerefInfo) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ if (DerefInfo->IsCyclic)
+ return addFieldToUninits(LocalChain.add(CyclicLocField(FR)), FR);
+
+ const TypedValueRegion *R = DerefInfo->R;
+ const bool NeedsCastBack = DerefInfo->NeedsCastBack;
+
+ QualType DynT = R->getLocationType();
+ QualType PointeeT = DynT->getPointeeType();
+
+ if (PointeeT->isStructureOrClassType()) {
+ if (NeedsCastBack)
+ return isNonUnionUninit(R, LocalChain.add(NeedsCastLocField(FR, DynT)));
+ return isNonUnionUninit(R, LocalChain.add(LocField(FR)));
+ }
+
+ if (PointeeT->isUnionType()) {
+ if (isUnionUninit(R)) {
+ if (NeedsCastBack)
+ return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)),
+ R);
+ return addFieldToUninits(LocalChain.add(LocField(FR)), R);
+ } else {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+ }
+
+ if (PointeeT->isArrayType()) {
+ IsAnyFieldInitialized = true;
+ return false;
+ }
+
+ assert((isPrimitiveType(PointeeT) || isDereferencableType(PointeeT)) &&
+ "At this point FR must either have a primitive dynamic type, or it "
+ "must be a null, undefined, unknown or concrete pointer!");
+
+ SVal PointeeV = State->getSVal(R);
+
+ if (isPrimitiveUninit(PointeeV)) {
+ if (NeedsCastBack)
+ return addFieldToUninits(LocalChain.add(NeedsCastLocField(FR, DynT)), R);
+ return addFieldToUninits(LocalChain.add(LocField(FR)), R);
+ }
+
+ IsAnyFieldInitialized = true;
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// Utility functions.
+//===----------------------------------------------------------------------===//
+
+static llvm::Optional<DereferenceInfo> dereference(ProgramStateRef State,
+ const FieldRegion *FR) {
+
+ llvm::SmallSet<const TypedValueRegion *, 5> VisitedRegions;
+
+ SVal V = State->getSVal(FR);
+ assert(V.getAsRegion() && "V must have an underlying region!");
+
+ // If the static type of the field is a void pointer, or it is a
+ // nonloc::LocAsInteger, we need to cast it back to the dynamic type before
+ // dereferencing.
+ bool NeedsCastBack = isVoidPointer(FR->getDecl()->getType()) ||
+ V.getAs<nonloc::LocAsInteger>();
+
+ // The region we'd like to acquire.
+ const auto *R = V.getAsRegion()->getAs<TypedValueRegion>();
+ if (!R)
+ return None;
+
+ VisitedRegions.insert(R);
+
+ // We acquire the dynamic type of R,
+ QualType DynT = R->getLocationType();
+
+ while (const MemRegion *Tmp = State->getSVal(R, DynT).getAsRegion()) {
+
+ R = Tmp->getAs<TypedValueRegion>();
+ if (!R)
+ return None;
+
+ // We found a cyclic pointer, like int *ptr = (int *)&ptr.
+ if (!VisitedRegions.insert(R).second)
+ return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ true};
+
+ DynT = R->getLocationType();
+ // In order to ensure that this loop terminates, we're also checking the
+ // dynamic type of R, since type hierarchy is finite.
+ if (isDereferencableType(DynT->getPointeeType()))
+ break;
+ }
+
+ while (R->getAs<CXXBaseObjectRegion>()) {
+ NeedsCastBack = true;
+
+ if (!isa<TypedValueRegion>(R->getSuperRegion()))
+ break;
+ R = R->getSuperRegion()->getAs<TypedValueRegion>();
+ }
+
+ return DereferenceInfo{R, NeedsCastBack, /*IsCyclic*/ false};
+}
+
+static bool isVoidPointer(QualType T) {
+ while (!T.isNull()) {
+ if (T->isVoidPointerType())
+ return true;
+ T = T->getPointeeType();
+ }
+ return false;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
new file mode 100644
index 000000000000..bab0c12704fa
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -0,0 +1,479 @@
+//= UnixAPIChecker.h - Checks preconditions for various Unix APIs --*- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines UnixAPIChecker, which is an assortment of checks on calls
+// to various, widely used UNIX/Posix functions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/Support/raw_ostream.h"
+#include <fcntl.h>
+
+using namespace clang;
+using namespace ento;
+
+enum class OpenVariant {
+ /// The standard open() call:
+ /// int open(const char *path, int oflag, ...);
+ Open,
+
+ /// The variant taking a directory file descriptor and a relative path:
+ /// int openat(int fd, const char *path, int oflag, ...);
+ OpenAt
+};
+
+namespace {
+class UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > {
+ mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero;
+ mutable Optional<uint64_t> Val_O_CREAT;
+
+public:
+ DefaultBool CheckMisuse, CheckPortability;
+
+ void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+
+ void CheckOpen(CheckerContext &C, const CallExpr *CE) const;
+ void CheckOpenAt(CheckerContext &C, const CallExpr *CE) const;
+
+ void CheckPthreadOnce(CheckerContext &C, const CallExpr *CE) const;
+ void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
+ void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
+ void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
+ void CheckReallocfZero(CheckerContext &C, const CallExpr *CE) const;
+ void CheckAllocaZero(CheckerContext &C, const CallExpr *CE) const;
+ void CheckAllocaWithAlignZero(CheckerContext &C, const CallExpr *CE) const;
+ void CheckVallocZero(CheckerContext &C, const CallExpr *CE) const;
+
+ typedef void (UnixAPIChecker::*SubChecker)(CheckerContext &,
+ const CallExpr *) const;
+private:
+
+ void CheckOpenVariant(CheckerContext &C,
+ const CallExpr *CE, OpenVariant Variant) const;
+
+ bool ReportZeroByteAllocation(CheckerContext &C,
+ ProgramStateRef falseState,
+ const Expr *arg,
+ const char *fn_name) const;
+ void BasicAllocationCheck(CheckerContext &C,
+ const CallExpr *CE,
+ const unsigned numArgs,
+ const unsigned sizeArg,
+ const char *fn) const;
+ void LazyInitialize(std::unique_ptr<BugType> &BT, const char *name) const {
+ if (BT)
+ return;
+ BT.reset(new BugType(this, name, categories::UnixAPI));
+ }
+ void ReportOpenBug(CheckerContext &C,
+ ProgramStateRef State,
+ const char *Msg,
+ SourceRange SR) const;
+};
+} //end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// "open" (man 2 open)
+//===----------------------------------------------------------------------===//
+
+void UnixAPIChecker::ReportOpenBug(CheckerContext &C,
+ ProgramStateRef State,
+ const char *Msg,
+ SourceRange SR) const {
+ ExplodedNode *N = C.generateErrorNode(State);
+ if (!N)
+ return;
+
+ LazyInitialize(BT_open, "Improper use of 'open'");
+
+ auto Report = llvm::make_unique<BugReport>(*BT_open, Msg, N);
+ Report->addRange(SR);
+ C.emitReport(std::move(Report));
+}
+
+void UnixAPIChecker::CheckOpen(CheckerContext &C, const CallExpr *CE) const {
+ CheckOpenVariant(C, CE, OpenVariant::Open);
+}
+
+void UnixAPIChecker::CheckOpenAt(CheckerContext &C, const CallExpr *CE) const {
+ CheckOpenVariant(C, CE, OpenVariant::OpenAt);
+}
+
+void UnixAPIChecker::CheckOpenVariant(CheckerContext &C,
+ const CallExpr *CE,
+ OpenVariant Variant) const {
+ // The index of the argument taking the flags open flags (O_RDONLY,
+ // O_WRONLY, O_CREAT, etc.),
+ unsigned int FlagsArgIndex;
+ const char *VariantName;
+ switch (Variant) {
+ case OpenVariant::Open:
+ FlagsArgIndex = 1;
+ VariantName = "open";
+ break;
+ case OpenVariant::OpenAt:
+ FlagsArgIndex = 2;
+ VariantName = "openat";
+ break;
+ };
+
+ // All calls should at least provide arguments up to the 'flags' parameter.
+ unsigned int MinArgCount = FlagsArgIndex + 1;
+
+ // If the flags has O_CREAT set then open/openat() require an additional
+ // argument specifying the file mode (permission bits) for the created file.
+ unsigned int CreateModeArgIndex = FlagsArgIndex + 1;
+
+ // The create mode argument should be the last argument.
+ unsigned int MaxArgCount = CreateModeArgIndex + 1;
+
+ ProgramStateRef state = C.getState();
+
+ if (CE->getNumArgs() < MinArgCount) {
+ // The frontend should issue a warning for this case, so this is a sanity
+ // check.
+ return;
+ } else if (CE->getNumArgs() == MaxArgCount) {
+ const Expr *Arg = CE->getArg(CreateModeArgIndex);
+ QualType QT = Arg->getType();
+ if (!QT->isIntegerType()) {
+ SmallString<256> SBuf;
+ llvm::raw_svector_ostream OS(SBuf);
+ OS << "The " << CreateModeArgIndex + 1
+ << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
+ << " argument to '" << VariantName << "' is not an integer";
+
+ ReportOpenBug(C, state,
+ SBuf.c_str(),
+ Arg->getSourceRange());
+ return;
+ }
+ } else if (CE->getNumArgs() > MaxArgCount) {
+ SmallString<256> SBuf;
+ llvm::raw_svector_ostream OS(SBuf);
+ OS << "Call to '" << VariantName << "' with more than " << MaxArgCount
+ << " arguments";
+
+ ReportOpenBug(C, state,
+ SBuf.c_str(),
+ CE->getArg(MaxArgCount)->getSourceRange());
+ return;
+ }
+
+ // The definition of O_CREAT is platform specific. We need a better way
+ // of querying this information from the checking environment.
+ if (!Val_O_CREAT.hasValue()) {
+ if (C.getASTContext().getTargetInfo().getTriple().getVendor()
+ == llvm::Triple::Apple)
+ Val_O_CREAT = 0x0200;
+ else {
+ // FIXME: We need a more general way of getting the O_CREAT value.
+ // We could possibly grovel through the preprocessor state, but
+ // that would require passing the Preprocessor object to the ExprEngine.
+ // See also: MallocChecker.cpp / M_ZERO.
+ return;
+ }
+ }
+
+ // Now check if oflags has O_CREAT set.
+ const Expr *oflagsEx = CE->getArg(FlagsArgIndex);
+ const SVal V = C.getSVal(oflagsEx);
+ if (!V.getAs<NonLoc>()) {
+ // The case where 'V' can be a location can only be due to a bad header,
+ // so in this case bail out.
+ return;
+ }
+ NonLoc oflags = V.castAs<NonLoc>();
+ NonLoc ocreateFlag = C.getSValBuilder()
+ .makeIntVal(Val_O_CREAT.getValue(), oflagsEx->getType()).castAs<NonLoc>();
+ SVal maskedFlagsUC = C.getSValBuilder().evalBinOpNN(state, BO_And,
+ oflags, ocreateFlag,
+ oflagsEx->getType());
+ if (maskedFlagsUC.isUnknownOrUndef())
+ return;
+ DefinedSVal maskedFlags = maskedFlagsUC.castAs<DefinedSVal>();
+
+ // Check if maskedFlags is non-zero.
+ ProgramStateRef trueState, falseState;
+ std::tie(trueState, falseState) = state->assume(maskedFlags);
+
+ // Only emit an error if the value of 'maskedFlags' is properly
+ // constrained;
+ if (!(trueState && !falseState))
+ return;
+
+ if (CE->getNumArgs() < MaxArgCount) {
+ SmallString<256> SBuf;
+ llvm::raw_svector_ostream OS(SBuf);
+ OS << "Call to '" << VariantName << "' requires a "
+ << CreateModeArgIndex + 1
+ << llvm::getOrdinalSuffix(CreateModeArgIndex + 1)
+ << " argument when the 'O_CREAT' flag is set";
+ ReportOpenBug(C, trueState,
+ SBuf.c_str(),
+ oflagsEx->getSourceRange());
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// pthread_once
+//===----------------------------------------------------------------------===//
+
+void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
+ const CallExpr *CE) const {
+
+ // This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
+ // They can possibly be refactored.
+
+ if (CE->getNumArgs() < 1)
+ return;
+
+ // Check if the first argument is stack allocated. If so, issue a warning
+ // because that's likely to be bad news.
+ ProgramStateRef state = C.getState();
+ const MemRegion *R = C.getSVal(CE->getArg(0)).getAsRegion();
+ if (!R || !isa<StackSpaceRegion>(R->getMemorySpace()))
+ return;
+
+ ExplodedNode *N = C.generateErrorNode(state);
+ if (!N)
+ return;
+
+ SmallString<256> S;
+ llvm::raw_svector_ostream os(S);
+ os << "Call to 'pthread_once' uses";
+ if (const VarRegion *VR = dyn_cast<VarRegion>(R))
+ os << " the local variable '" << VR->getDecl()->getName() << '\'';
+ else
+ os << " stack allocated memory";
+ os << " for the \"control\" value. Using such transient memory for "
+ "the control value is potentially dangerous.";
+ if (isa<VarRegion>(R) && isa<StackLocalsSpaceRegion>(R->getMemorySpace()))
+ os << " Perhaps you intended to declare the variable as 'static'?";
+
+ LazyInitialize(BT_pthreadOnce, "Improper use of 'pthread_once'");
+
+ auto report = llvm::make_unique<BugReport>(*BT_pthreadOnce, os.str(), N);
+ report->addRange(CE->getArg(0)->getSourceRange());
+ C.emitReport(std::move(report));
+}
+
+//===----------------------------------------------------------------------===//
+// "calloc", "malloc", "realloc", "reallocf", "alloca" and "valloc"
+// with allocation size 0
+//===----------------------------------------------------------------------===//
+// FIXME: Eventually these should be rolled into the MallocChecker, but right now
+// they're more basic and valuable for widespread use.
+
+// Returns true if we try to do a zero byte allocation, false otherwise.
+// Fills in trueState and falseState.
+static bool IsZeroByteAllocation(ProgramStateRef state,
+ const SVal argVal,
+ ProgramStateRef *trueState,
+ ProgramStateRef *falseState) {
+ std::tie(*trueState, *falseState) =
+ state->assume(argVal.castAs<DefinedSVal>());
+
+ return (*falseState && !*trueState);
+}
+
+// Generates an error report, indicating that the function whose name is given
+// will perform a zero byte allocation.
+// Returns false if an error occurred, true otherwise.
+bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
+ ProgramStateRef falseState,
+ const Expr *arg,
+ const char *fn_name) const {
+ ExplodedNode *N = C.generateErrorNode(falseState);
+ if (!N)
+ return false;
+
+ LazyInitialize(BT_mallocZero,
+ "Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
+
+ SmallString<256> S;
+ llvm::raw_svector_ostream os(S);
+ os << "Call to '" << fn_name << "' has an allocation size of 0 bytes";
+ auto report = llvm::make_unique<BugReport>(*BT_mallocZero, os.str(), N);
+
+ report->addRange(arg->getSourceRange());
+ bugreporter::trackExpressionValue(N, arg, *report);
+ C.emitReport(std::move(report));
+
+ return true;
+}
+
+// Does a basic check for 0-sized allocations suitable for most of the below
+// functions (modulo "calloc")
+void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C,
+ const CallExpr *CE,
+ const unsigned numArgs,
+ const unsigned sizeArg,
+ const char *fn) const {
+ // Sanity check for the correct number of arguments
+ if (CE->getNumArgs() != numArgs)
+ return;
+
+ // Check if the allocation size is 0.
+ ProgramStateRef state = C.getState();
+ ProgramStateRef trueState = nullptr, falseState = nullptr;
+ const Expr *arg = CE->getArg(sizeArg);
+ SVal argVal = C.getSVal(arg);
+
+ if (argVal.isUnknownOrUndef())
+ return;
+
+ // Is the value perfectly constrained to zero?
+ if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
+ (void) ReportZeroByteAllocation(C, falseState, arg, fn);
+ return;
+ }
+ // Assume the value is non-zero going forward.
+ assert(trueState);
+ if (trueState != state)
+ C.addTransition(trueState);
+}
+
+void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
+ const CallExpr *CE) const {
+ unsigned int nArgs = CE->getNumArgs();
+ if (nArgs != 2)
+ return;
+
+ ProgramStateRef state = C.getState();
+ ProgramStateRef trueState = nullptr, falseState = nullptr;
+
+ unsigned int i;
+ for (i = 0; i < nArgs; i++) {
+ const Expr *arg = CE->getArg(i);
+ SVal argVal = C.getSVal(arg);
+ if (argVal.isUnknownOrUndef()) {
+ if (i == 0)
+ continue;
+ else
+ return;
+ }
+
+ if (IsZeroByteAllocation(state, argVal, &trueState, &falseState)) {
+ if (ReportZeroByteAllocation(C, falseState, arg, "calloc"))
+ return;
+ else if (i == 0)
+ continue;
+ else
+ return;
+ }
+ }
+
+ // Assume the value is non-zero going forward.
+ assert(trueState);
+ if (trueState != state)
+ C.addTransition(trueState);
+}
+
+void UnixAPIChecker::CheckMallocZero(CheckerContext &C,
+ const CallExpr *CE) const {
+ BasicAllocationCheck(C, CE, 1, 0, "malloc");
+}
+
+void UnixAPIChecker::CheckReallocZero(CheckerContext &C,
+ const CallExpr *CE) const {
+ BasicAllocationCheck(C, CE, 2, 1, "realloc");
+}
+
+void UnixAPIChecker::CheckReallocfZero(CheckerContext &C,
+ const CallExpr *CE) const {
+ BasicAllocationCheck(C, CE, 2, 1, "reallocf");
+}
+
+void UnixAPIChecker::CheckAllocaZero(CheckerContext &C,
+ const CallExpr *CE) const {
+ BasicAllocationCheck(C, CE, 1, 0, "alloca");
+}
+
+void UnixAPIChecker::CheckAllocaWithAlignZero(CheckerContext &C,
+ const CallExpr *CE) const {
+ BasicAllocationCheck(C, CE, 2, 0, "__builtin_alloca_with_align");
+}
+
+void UnixAPIChecker::CheckVallocZero(CheckerContext &C,
+ const CallExpr *CE) const {
+ BasicAllocationCheck(C, CE, 1, 0, "valloc");
+}
+
+
+//===----------------------------------------------------------------------===//
+// Central dispatch function.
+//===----------------------------------------------------------------------===//
+
+void UnixAPIChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const {
+ const FunctionDecl *FD = C.getCalleeDecl(CE);
+ if (!FD || FD->getKind() != Decl::Function)
+ return;
+
+ // Don't treat functions in namespaces with the same name a Unix function
+ // as a call to the Unix function.
+ const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
+ if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx))
+ return;
+
+ StringRef FName = C.getCalleeName(FD);
+ if (FName.empty())
+ return;
+
+ if (CheckMisuse) {
+ if (SubChecker SC =
+ llvm::StringSwitch<SubChecker>(FName)
+ .Case("open", &UnixAPIChecker::CheckOpen)
+ .Case("openat", &UnixAPIChecker::CheckOpenAt)
+ .Case("pthread_once", &UnixAPIChecker::CheckPthreadOnce)
+ .Default(nullptr)) {
+ (this->*SC)(C, CE);
+ }
+ }
+ if (CheckPortability) {
+ if (SubChecker SC =
+ llvm::StringSwitch<SubChecker>(FName)
+ .Case("calloc", &UnixAPIChecker::CheckCallocZero)
+ .Case("malloc", &UnixAPIChecker::CheckMallocZero)
+ .Case("realloc", &UnixAPIChecker::CheckReallocZero)
+ .Case("reallocf", &UnixAPIChecker::CheckReallocfZero)
+ .Cases("alloca", "__builtin_alloca",
+ &UnixAPIChecker::CheckAllocaZero)
+ .Case("__builtin_alloca_with_align",
+ &UnixAPIChecker::CheckAllocaWithAlignZero)
+ .Case("valloc", &UnixAPIChecker::CheckVallocZero)
+ .Default(nullptr)) {
+ (this->*SC)(C, CE);
+ }
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Registration.
+//===----------------------------------------------------------------------===//
+
+#define REGISTER_CHECKER(Name) \
+ void ento::registerUnixAPI##Name##Checker(CheckerManager &mgr) { \
+ mgr.registerChecker<UnixAPIChecker>()->Check##Name = true; \
+ }
+
+REGISTER_CHECKER(Misuse)
+REGISTER_CHECKER(Portability)
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
new file mode 100644
index 000000000000..16b4d5e925ba
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
@@ -0,0 +1,259 @@
+//==- UnreachableCodeChecker.cpp - Generalized dead code checker -*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+// This file implements a generalized unreachable code checker using a
+// path-sensitive analysis. We mark any path visited, and then walk the CFG as a
+// post-analysis to determine what was never visited.
+//
+// A similar flow-sensitive only check exists in Analysis/ReachableCode.cpp
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/SmallSet.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class UnreachableCodeChecker : public Checker<check::EndAnalysis> {
+public:
+ void checkEndAnalysis(ExplodedGraph &G, BugReporter &B,
+ ExprEngine &Eng) const;
+private:
+ typedef llvm::SmallSet<unsigned, 32> CFGBlocksSet;
+
+ static inline const Stmt *getUnreachableStmt(const CFGBlock *CB);
+ static void FindUnreachableEntryPoints(const CFGBlock *CB,
+ CFGBlocksSet &reachable,
+ CFGBlocksSet &visited);
+ static bool isInvalidPath(const CFGBlock *CB, const ParentMap &PM);
+ static inline bool isEmptyCFGBlock(const CFGBlock *CB);
+};
+}
+
+void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G,
+ BugReporter &B,
+ ExprEngine &Eng) const {
+ CFGBlocksSet reachable, visited;
+
+ if (Eng.hasWorkRemaining())
+ return;
+
+ const Decl *D = nullptr;
+ CFG *C = nullptr;
+ ParentMap *PM = nullptr;
+ const LocationContext *LC = nullptr;
+ // Iterate over ExplodedGraph
+ for (ExplodedGraph::node_iterator I = G.nodes_begin(), E = G.nodes_end();
+ I != E; ++I) {
+ const ProgramPoint &P = I->getLocation();
+ LC = P.getLocationContext();
+ if (!LC->inTopFrame())
+ continue;
+
+ if (!D)
+ D = LC->getAnalysisDeclContext()->getDecl();
+
+ // Save the CFG if we don't have it already
+ if (!C)
+ C = LC->getAnalysisDeclContext()->getUnoptimizedCFG();
+ if (!PM)
+ PM = &LC->getParentMap();
+
+ if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
+ const CFGBlock *CB = BE->getBlock();
+ reachable.insert(CB->getBlockID());
+ }
+ }
+
+ // Bail out if we didn't get the CFG or the ParentMap.
+ if (!D || !C || !PM)
+ return;
+
+ // Don't do anything for template instantiations. Proving that code
+ // in a template instantiation is unreachable means proving that it is
+ // unreachable in all instantiations.
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D))
+ if (FD->isTemplateInstantiation())
+ return;
+
+ // Find CFGBlocks that were not covered by any node
+ for (CFG::const_iterator I = C->begin(), E = C->end(); I != E; ++I) {
+ const CFGBlock *CB = *I;
+ // Check if the block is unreachable
+ if (reachable.count(CB->getBlockID()))
+ continue;
+
+ // Check if the block is empty (an artificial block)
+ if (isEmptyCFGBlock(CB))
+ continue;
+
+ // Find the entry points for this block
+ if (!visited.count(CB->getBlockID()))
+ FindUnreachableEntryPoints(CB, reachable, visited);
+
+ // This block may have been pruned; check if we still want to report it
+ if (reachable.count(CB->getBlockID()))
+ continue;
+
+ // Check for false positives
+ if (isInvalidPath(CB, *PM))
+ continue;
+
+ // It is good practice to always have a "default" label in a "switch", even
+ // if we should never get there. It can be used to detect errors, for
+ // instance. Unreachable code directly under a "default" label is therefore
+ // likely to be a false positive.
+ if (const Stmt *label = CB->getLabel())
+ if (label->getStmtClass() == Stmt::DefaultStmtClass)
+ continue;
+
+ // Special case for __builtin_unreachable.
+ // FIXME: This should be extended to include other unreachable markers,
+ // such as llvm_unreachable.
+ if (!CB->empty()) {
+ bool foundUnreachable = false;
+ for (CFGBlock::const_iterator ci = CB->begin(), ce = CB->end();
+ ci != ce; ++ci) {
+ if (Optional<CFGStmt> S = (*ci).getAs<CFGStmt>())
+ if (const CallExpr *CE = dyn_cast<CallExpr>(S->getStmt())) {
+ if (CE->getBuiltinCallee() == Builtin::BI__builtin_unreachable ||
+ CE->isBuiltinAssumeFalse(Eng.getContext())) {
+ foundUnreachable = true;
+ break;
+ }
+ }
+ }
+ if (foundUnreachable)
+ continue;
+ }
+
+ // We found a block that wasn't covered - find the statement to report
+ SourceRange SR;
+ PathDiagnosticLocation DL;
+ SourceLocation SL;
+ if (const Stmt *S = getUnreachableStmt(CB)) {
+ // In macros, 'do {...} while (0)' is often used. Don't warn about the
+ // condition 0 when it is unreachable.
+ if (S->getBeginLoc().isMacroID())
+ if (const auto *I = dyn_cast<IntegerLiteral>(S))
+ if (I->getValue() == 0ULL)
+ if (const Stmt *Parent = PM->getParent(S))
+ if (isa<DoStmt>(Parent))
+ continue;
+ SR = S->getSourceRange();
+ DL = PathDiagnosticLocation::createBegin(S, B.getSourceManager(), LC);
+ SL = DL.asLocation();
+ if (SR.isInvalid() || !SL.isValid())
+ continue;
+ }
+ else
+ continue;
+
+ // Check if the SourceLocation is in a system header
+ const SourceManager &SM = B.getSourceManager();
+ if (SM.isInSystemHeader(SL) || SM.isInExternCSystemHeader(SL))
+ continue;
+
+ B.EmitBasicReport(D, this, "Unreachable code", "Dead code",
+ "This statement is never executed", DL, SR);
+ }
+}
+
+// Recursively finds the entry point(s) for this dead CFGBlock.
+void UnreachableCodeChecker::FindUnreachableEntryPoints(const CFGBlock *CB,
+ CFGBlocksSet &reachable,
+ CFGBlocksSet &visited) {
+ visited.insert(CB->getBlockID());
+
+ for (CFGBlock::const_pred_iterator I = CB->pred_begin(), E = CB->pred_end();
+ I != E; ++I) {
+ if (!*I)
+ continue;
+
+ if (!reachable.count((*I)->getBlockID())) {
+ // If we find an unreachable predecessor, mark this block as reachable so
+ // we don't report this block
+ reachable.insert(CB->getBlockID());
+ if (!visited.count((*I)->getBlockID()))
+ // If we haven't previously visited the unreachable predecessor, recurse
+ FindUnreachableEntryPoints(*I, reachable, visited);
+ }
+ }
+}
+
+// Find the Stmt* in a CFGBlock for reporting a warning
+const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) {
+ for (CFGBlock::const_iterator I = CB->begin(), E = CB->end(); I != E; ++I) {
+ if (Optional<CFGStmt> S = I->getAs<CFGStmt>()) {
+ if (!isa<DeclStmt>(S->getStmt()))
+ return S->getStmt();
+ }
+ }
+ if (const Stmt *S = CB->getTerminator())
+ return S;
+ else
+ return nullptr;
+}
+
+// Determines if the path to this CFGBlock contained an element that infers this
+// block is a false positive. We assume that FindUnreachableEntryPoints has
+// already marked only the entry points to any dead code, so we need only to
+// find the condition that led to this block (the predecessor of this block.)
+// There will never be more than one predecessor.
+bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB,
+ const ParentMap &PM) {
+ // We only expect a predecessor size of 0 or 1. If it is >1, then an external
+ // condition has broken our assumption (for example, a sink being placed by
+ // another check). In these cases, we choose not to report.
+ if (CB->pred_size() > 1)
+ return true;
+
+ // If there are no predecessors, then this block is trivially unreachable
+ if (CB->pred_size() == 0)
+ return false;
+
+ const CFGBlock *pred = *CB->pred_begin();
+ if (!pred)
+ return false;
+
+ // Get the predecessor block's terminator condition
+ const Stmt *cond = pred->getTerminatorCondition();
+
+ //assert(cond && "CFGBlock's predecessor has a terminator condition");
+ // The previous assertion is invalid in some cases (eg do/while). Leaving
+ // reporting of these situations on at the moment to help triage these cases.
+ if (!cond)
+ return false;
+
+ // Run each of the checks on the conditions
+ return containsMacro(cond) || containsEnum(cond) ||
+ containsStaticLocal(cond) || containsBuiltinOffsetOf(cond) ||
+ containsStmt<UnaryExprOrTypeTraitExpr>(cond);
+}
+
+// Returns true if the given CFGBlock is empty
+bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) {
+ return CB->getLabel() == nullptr // No labels
+ && CB->size() == 0 // No statements
+ && !CB->getTerminator(); // No terminator
+}
+
+void ento::registerUnreachableCodeChecker(CheckerManager &mgr) {
+ mgr.registerChecker<UnreachableCodeChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
new file mode 100644
index 000000000000..e458e0554ee2
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
@@ -0,0 +1,185 @@
+//=== VLASizeChecker.cpp - Undefined dereference checker --------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines VLASizeChecker, a builtin check in ExprEngine that
+// performs checks for declaration of VLA of undefined or zero size.
+// In addition, VLASizeChecker is responsible for defining the extent
+// of the MemRegion that represents a VLA.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > {
+ mutable std::unique_ptr<BugType> BT;
+ enum VLASize_Kind { VLA_Garbage, VLA_Zero, VLA_Tainted, VLA_Negative };
+
+ void reportBug(VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
+ CheckerContext &C,
+ std::unique_ptr<BugReporterVisitor> Visitor = nullptr) const;
+
+public:
+ void checkPreStmt(const DeclStmt *DS, CheckerContext &C) const;
+};
+} // end anonymous namespace
+
+void VLASizeChecker::reportBug(
+ VLASize_Kind Kind, const Expr *SizeE, ProgramStateRef State,
+ CheckerContext &C, std::unique_ptr<BugReporterVisitor> Visitor) const {
+ // Generate an error node.
+ ExplodedNode *N = C.generateErrorNode(State);
+ if (!N)
+ return;
+
+ if (!BT)
+ BT.reset(new BuiltinBug(
+ this, "Dangerous variable-length array (VLA) declaration"));
+
+ SmallString<256> buf;
+ llvm::raw_svector_ostream os(buf);
+ os << "Declared variable-length array (VLA) ";
+ switch (Kind) {
+ case VLA_Garbage:
+ os << "uses a garbage value as its size";
+ break;
+ case VLA_Zero:
+ os << "has zero size";
+ break;
+ case VLA_Tainted:
+ os << "has tainted size";
+ break;
+ case VLA_Negative:
+ os << "has negative size";
+ break;
+ }
+
+ auto report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ report->addVisitor(std::move(Visitor));
+ report->addRange(SizeE->getSourceRange());
+ bugreporter::trackExpressionValue(N, SizeE, *report);
+ C.emitReport(std::move(report));
+}
+
+void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
+ if (!DS->isSingleDecl())
+ return;
+
+ const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
+ if (!VD)
+ return;
+
+ ASTContext &Ctx = C.getASTContext();
+ const VariableArrayType *VLA = Ctx.getAsVariableArrayType(VD->getType());
+ if (!VLA)
+ return;
+
+ // FIXME: Handle multi-dimensional VLAs.
+ const Expr *SE = VLA->getSizeExpr();
+ ProgramStateRef state = C.getState();
+ SVal sizeV = C.getSVal(SE);
+
+ if (sizeV.isUndef()) {
+ reportBug(VLA_Garbage, SE, state, C);
+ return;
+ }
+
+ // See if the size value is known. It can't be undefined because we would have
+ // warned about that already.
+ if (sizeV.isUnknown())
+ return;
+
+ // Check if the size is tainted.
+ if (state->isTainted(sizeV)) {
+ reportBug(VLA_Tainted, SE, nullptr, C,
+ llvm::make_unique<TaintBugVisitor>(sizeV));
+ return;
+ }
+
+ // Check if the size is zero.
+ DefinedSVal sizeD = sizeV.castAs<DefinedSVal>();
+
+ ProgramStateRef stateNotZero, stateZero;
+ std::tie(stateNotZero, stateZero) = state->assume(sizeD);
+
+ if (stateZero && !stateNotZero) {
+ reportBug(VLA_Zero, SE, stateZero, C);
+ return;
+ }
+
+ // From this point on, assume that the size is not zero.
+ state = stateNotZero;
+
+ // VLASizeChecker is responsible for defining the extent of the array being
+ // declared. We do this by multiplying the array length by the element size,
+ // then matching that with the array region's extent symbol.
+
+ // Check if the size is negative.
+ SValBuilder &svalBuilder = C.getSValBuilder();
+
+ QualType Ty = SE->getType();
+ DefinedOrUnknownSVal Zero = svalBuilder.makeZeroVal(Ty);
+
+ SVal LessThanZeroVal = svalBuilder.evalBinOp(state, BO_LT, sizeD, Zero, Ty);
+ if (Optional<DefinedSVal> LessThanZeroDVal =
+ LessThanZeroVal.getAs<DefinedSVal>()) {
+ ConstraintManager &CM = C.getConstraintManager();
+ ProgramStateRef StatePos, StateNeg;
+
+ std::tie(StateNeg, StatePos) = CM.assumeDual(state, *LessThanZeroDVal);
+ if (StateNeg && !StatePos) {
+ reportBug(VLA_Negative, SE, state, C);
+ return;
+ }
+ state = StatePos;
+ }
+
+ // Convert the array length to size_t.
+ QualType SizeTy = Ctx.getSizeType();
+ NonLoc ArrayLength =
+ svalBuilder.evalCast(sizeD, SizeTy, SE->getType()).castAs<NonLoc>();
+
+ // Get the element size.
+ CharUnits EleSize = Ctx.getTypeSizeInChars(VLA->getElementType());
+ SVal EleSizeVal = svalBuilder.makeIntVal(EleSize.getQuantity(), SizeTy);
+
+ // Multiply the array length by the element size.
+ SVal ArraySizeVal = svalBuilder.evalBinOpNN(
+ state, BO_Mul, ArrayLength, EleSizeVal.castAs<NonLoc>(), SizeTy);
+
+ // Finally, assume that the array's extent matches the given size.
+ const LocationContext *LC = C.getLocationContext();
+ DefinedOrUnknownSVal Extent =
+ state->getRegion(VD, LC)->getExtent(svalBuilder);
+ DefinedOrUnknownSVal ArraySize = ArraySizeVal.castAs<DefinedOrUnknownSVal>();
+ DefinedOrUnknownSVal sizeIsKnown =
+ svalBuilder.evalEQ(state, Extent, ArraySize);
+ state = state->assume(sizeIsKnown, true);
+
+ // Assume should not fail at this point.
+ assert(state);
+
+ // Remember our assumptions!
+ C.addTransition(state);
+}
+
+void ento::registerVLASizeChecker(CheckerManager &mgr) {
+ mgr.registerChecker<VLASizeChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
new file mode 100644
index 000000000000..748b226b7a1e
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
@@ -0,0 +1,412 @@
+//== ValistChecker.cpp - stdarg.h macro usage checker -----------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines checkers which detect usage of uninitialized va_list values
+// and va_start calls with no matching va_end.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+REGISTER_SET_WITH_PROGRAMSTATE(InitializedVALists, const MemRegion *)
+
+namespace {
+typedef SmallVector<const MemRegion *, 2> RegionVector;
+
+class ValistChecker : public Checker<check::PreCall, check::PreStmt<VAArgExpr>,
+ check::DeadSymbols> {
+ mutable std::unique_ptr<BugType> BT_leakedvalist, BT_uninitaccess;
+
+ struct VAListAccepter {
+ CallDescription Func;
+ int VAListPos;
+ };
+ static const SmallVector<VAListAccepter, 15> VAListAccepters;
+ static const CallDescription VaStart, VaEnd, VaCopy;
+
+public:
+ enum CheckKind {
+ CK_Uninitialized,
+ CK_Unterminated,
+ CK_CopyToSelf,
+ CK_NumCheckKinds
+ };
+
+ DefaultBool ChecksEnabled[CK_NumCheckKinds];
+ CheckName CheckNames[CK_NumCheckKinds];
+
+ void checkPreStmt(const VAArgExpr *VAA, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
+
+private:
+ const MemRegion *getVAListAsRegion(SVal SV, const Expr *VAExpr,
+ bool &IsSymbolic, CheckerContext &C) const;
+ const ExplodedNode *getStartCallSite(const ExplodedNode *N,
+ const MemRegion *Reg) const;
+
+ void reportUninitializedAccess(const MemRegion *VAList, StringRef Msg,
+ CheckerContext &C) const;
+ void reportLeakedVALists(const RegionVector &LeakedVALists, StringRef Msg1,
+ StringRef Msg2, CheckerContext &C, ExplodedNode *N,
+ bool ReportUninit = false) const;
+
+ void checkVAListStartCall(const CallEvent &Call, CheckerContext &C,
+ bool IsCopy) const;
+ void checkVAListEndCall(const CallEvent &Call, CheckerContext &C) const;
+
+ class ValistBugVisitor : public BugReporterVisitor {
+ public:
+ ValistBugVisitor(const MemRegion *Reg, bool IsLeak = false)
+ : Reg(Reg), IsLeak(IsLeak) {}
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int X = 0;
+ ID.AddPointer(&X);
+ ID.AddPointer(Reg);
+ }
+ std::shared_ptr<PathDiagnosticPiece>
+ getEndPath(BugReporterContext &BRC, const ExplodedNode *EndPathNode,
+ BugReport &BR) override {
+ if (!IsLeak)
+ return nullptr;
+
+ PathDiagnosticLocation L = PathDiagnosticLocation::createEndOfPath(
+ EndPathNode, BRC.getSourceManager());
+ // Do not add the statement itself as a range in case of leak.
+ return std::make_shared<PathDiagnosticEventPiece>(L, BR.getDescription(), false);
+ }
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+
+ private:
+ const MemRegion *Reg;
+ bool IsLeak;
+ };
+};
+
+const SmallVector<ValistChecker::VAListAccepter, 15>
+ ValistChecker::VAListAccepters = {
+ {{"vfprintf", 3}, 2},
+ {{"vfscanf", 3}, 2},
+ {{"vprintf", 2}, 1},
+ {{"vscanf", 2}, 1},
+ {{"vsnprintf", 4}, 3},
+ {{"vsprintf", 3}, 2},
+ {{"vsscanf", 3}, 2},
+ {{"vfwprintf", 3}, 2},
+ {{"vfwscanf", 3}, 2},
+ {{"vwprintf", 2}, 1},
+ {{"vwscanf", 2}, 1},
+ {{"vswprintf", 4}, 3},
+ // vswprintf is the wide version of vsnprintf,
+ // vsprintf has no wide version
+ {{"vswscanf", 3}, 2}};
+const CallDescription ValistChecker::VaStart("__builtin_va_start", 2),
+ ValistChecker::VaCopy("__builtin_va_copy", 2),
+ ValistChecker::VaEnd("__builtin_va_end", 1);
+} // end anonymous namespace
+
+void ValistChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!Call.isGlobalCFunction())
+ return;
+ if (Call.isCalled(VaStart))
+ checkVAListStartCall(Call, C, false);
+ else if (Call.isCalled(VaCopy))
+ checkVAListStartCall(Call, C, true);
+ else if (Call.isCalled(VaEnd))
+ checkVAListEndCall(Call, C);
+ else {
+ for (auto FuncInfo : VAListAccepters) {
+ if (!Call.isCalled(FuncInfo.Func))
+ continue;
+ bool Symbolic;
+ const MemRegion *VAList =
+ getVAListAsRegion(Call.getArgSVal(FuncInfo.VAListPos),
+ Call.getArgExpr(FuncInfo.VAListPos), Symbolic, C);
+ if (!VAList)
+ return;
+
+ if (C.getState()->contains<InitializedVALists>(VAList))
+ return;
+
+ // We did not see va_start call, but the source of the region is unknown.
+ // Be conservative and assume the best.
+ if (Symbolic)
+ return;
+
+ SmallString<80> Errmsg("Function '");
+ Errmsg += FuncInfo.Func.getFunctionName();
+ Errmsg += "' is called with an uninitialized va_list argument";
+ reportUninitializedAccess(VAList, Errmsg.c_str(), C);
+ break;
+ }
+ }
+}
+
+const MemRegion *ValistChecker::getVAListAsRegion(SVal SV, const Expr *E,
+ bool &IsSymbolic,
+ CheckerContext &C) const {
+ const MemRegion *Reg = SV.getAsRegion();
+ if (!Reg)
+ return nullptr;
+ // TODO: In the future this should be abstracted away by the analyzer.
+ bool VaListModelledAsArray = false;
+ if (const auto *Cast = dyn_cast<CastExpr>(E)) {
+ QualType Ty = Cast->getType();
+ VaListModelledAsArray =
+ Ty->isPointerType() && Ty->getPointeeType()->isRecordType();
+ }
+ if (const auto *DeclReg = Reg->getAs<DeclRegion>()) {
+ if (isa<ParmVarDecl>(DeclReg->getDecl()))
+ Reg = C.getState()->getSVal(SV.castAs<Loc>()).getAsRegion();
+ }
+ IsSymbolic = Reg && Reg->getAs<SymbolicRegion>();
+ // Some VarRegion based VA lists reach here as ElementRegions.
+ const auto *EReg = dyn_cast_or_null<ElementRegion>(Reg);
+ return (EReg && VaListModelledAsArray) ? EReg->getSuperRegion() : Reg;
+}
+
+void ValistChecker::checkPreStmt(const VAArgExpr *VAA,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ const Expr *VASubExpr = VAA->getSubExpr();
+ SVal VAListSVal = C.getSVal(VASubExpr);
+ bool Symbolic;
+ const MemRegion *VAList =
+ getVAListAsRegion(VAListSVal, VASubExpr, Symbolic, C);
+ if (!VAList)
+ return;
+ if (Symbolic)
+ return;
+ if (!State->contains<InitializedVALists>(VAList))
+ reportUninitializedAccess(
+ VAList, "va_arg() is called on an uninitialized va_list", C);
+}
+
+void ValistChecker::checkDeadSymbols(SymbolReaper &SR,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ InitializedVAListsTy TrackedVALists = State->get<InitializedVALists>();
+ RegionVector LeakedVALists;
+ for (auto Reg : TrackedVALists) {
+ if (SR.isLiveRegion(Reg))
+ continue;
+ LeakedVALists.push_back(Reg);
+ State = State->remove<InitializedVALists>(Reg);
+ }
+ if (ExplodedNode *N = C.addTransition(State))
+ reportLeakedVALists(LeakedVALists, "Initialized va_list", " is leaked", C,
+ N);
+}
+
+// This function traverses the exploded graph backwards and finds the node where
+// the va_list is initialized. That node is used for uniquing the bug paths.
+// It is not likely that there are several different va_lists that belongs to
+// different stack frames, so that case is not yet handled.
+const ExplodedNode *
+ValistChecker::getStartCallSite(const ExplodedNode *N,
+ const MemRegion *Reg) const {
+ const LocationContext *LeakContext = N->getLocationContext();
+ const ExplodedNode *StartCallNode = N;
+
+ bool FoundInitializedState = false;
+
+ while (N) {
+ ProgramStateRef State = N->getState();
+ if (!State->contains<InitializedVALists>(Reg)) {
+ if (FoundInitializedState)
+ break;
+ } else {
+ FoundInitializedState = true;
+ }
+ const LocationContext *NContext = N->getLocationContext();
+ if (NContext == LeakContext || NContext->isParentOf(LeakContext))
+ StartCallNode = N;
+ N = N->pred_empty() ? nullptr : *(N->pred_begin());
+ }
+
+ return StartCallNode;
+}
+
+void ValistChecker::reportUninitializedAccess(const MemRegion *VAList,
+ StringRef Msg,
+ CheckerContext &C) const {
+ if (!ChecksEnabled[CK_Uninitialized])
+ return;
+ if (ExplodedNode *N = C.generateErrorNode()) {
+ if (!BT_uninitaccess)
+ BT_uninitaccess.reset(new BugType(CheckNames[CK_Uninitialized],
+ "Uninitialized va_list",
+ categories::MemoryError));
+ auto R = llvm::make_unique<BugReport>(*BT_uninitaccess, Msg, N);
+ R->markInteresting(VAList);
+ R->addVisitor(llvm::make_unique<ValistBugVisitor>(VAList));
+ C.emitReport(std::move(R));
+ }
+}
+
+void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
+ StringRef Msg1, StringRef Msg2,
+ CheckerContext &C, ExplodedNode *N,
+ bool ReportUninit) const {
+ if (!(ChecksEnabled[CK_Unterminated] ||
+ (ChecksEnabled[CK_Uninitialized] && ReportUninit)))
+ return;
+ for (auto Reg : LeakedVALists) {
+ if (!BT_leakedvalist) {
+ // FIXME: maybe creating a new check name for this type of bug is a better
+ // solution.
+ BT_leakedvalist.reset(
+ new BugType(CheckNames[CK_Unterminated].getName().empty()
+ ? CheckNames[CK_Uninitialized]
+ : CheckNames[CK_Unterminated],
+ "Leaked va_list", categories::MemoryError));
+ BT_leakedvalist->setSuppressOnSink(true);
+ }
+
+ const ExplodedNode *StartNode = getStartCallSite(N, Reg);
+ PathDiagnosticLocation LocUsedForUniqueing;
+
+ if (const Stmt *StartCallStmt = PathDiagnosticLocation::getStmt(StartNode))
+ LocUsedForUniqueing = PathDiagnosticLocation::createBegin(
+ StartCallStmt, C.getSourceManager(), StartNode->getLocationContext());
+
+ SmallString<100> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+ OS << Msg1;
+ std::string VariableName = Reg->getDescriptiveName();
+ if (!VariableName.empty())
+ OS << " " << VariableName;
+ OS << Msg2;
+
+ auto R = llvm::make_unique<BugReport>(
+ *BT_leakedvalist, OS.str(), N, LocUsedForUniqueing,
+ StartNode->getLocationContext()->getDecl());
+ R->markInteresting(Reg);
+ R->addVisitor(llvm::make_unique<ValistBugVisitor>(Reg, true));
+ C.emitReport(std::move(R));
+ }
+}
+
+void ValistChecker::checkVAListStartCall(const CallEvent &Call,
+ CheckerContext &C, bool IsCopy) const {
+ bool Symbolic;
+ const MemRegion *VAList =
+ getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C);
+ if (!VAList)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ if (IsCopy) {
+ const MemRegion *Arg2 =
+ getVAListAsRegion(Call.getArgSVal(1), Call.getArgExpr(1), Symbolic, C);
+ if (Arg2) {
+ if (ChecksEnabled[CK_CopyToSelf] && VAList == Arg2) {
+ RegionVector LeakedVALists{VAList};
+ if (ExplodedNode *N = C.addTransition(State))
+ reportLeakedVALists(LeakedVALists, "va_list",
+ " is copied onto itself", C, N, true);
+ return;
+ } else if (!State->contains<InitializedVALists>(Arg2) && !Symbolic) {
+ if (State->contains<InitializedVALists>(VAList)) {
+ State = State->remove<InitializedVALists>(VAList);
+ RegionVector LeakedVALists{VAList};
+ if (ExplodedNode *N = C.addTransition(State))
+ reportLeakedVALists(LeakedVALists, "Initialized va_list",
+ " is overwritten by an uninitialized one", C, N,
+ true);
+ } else {
+ reportUninitializedAccess(Arg2, "Uninitialized va_list is copied", C);
+ }
+ return;
+ }
+ }
+ }
+ if (State->contains<InitializedVALists>(VAList)) {
+ RegionVector LeakedVALists{VAList};
+ if (ExplodedNode *N = C.addTransition(State))
+ reportLeakedVALists(LeakedVALists, "Initialized va_list",
+ " is initialized again", C, N);
+ return;
+ }
+
+ State = State->add<InitializedVALists>(VAList);
+ C.addTransition(State);
+}
+
+void ValistChecker::checkVAListEndCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ bool Symbolic;
+ const MemRegion *VAList =
+ getVAListAsRegion(Call.getArgSVal(0), Call.getArgExpr(0), Symbolic, C);
+ if (!VAList)
+ return;
+
+ // We did not see va_start call, but the source of the region is unknown.
+ // Be conservative and assume the best.
+ if (Symbolic)
+ return;
+
+ if (!C.getState()->contains<InitializedVALists>(VAList)) {
+ reportUninitializedAccess(
+ VAList, "va_end() is called on an uninitialized va_list", C);
+ return;
+ }
+ ProgramStateRef State = C.getState();
+ State = State->remove<InitializedVALists>(VAList);
+ C.addTransition(State);
+}
+
+std::shared_ptr<PathDiagnosticPiece> ValistChecker::ValistBugVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BRC,
+ BugReport &) {
+ ProgramStateRef State = N->getState();
+ ProgramStateRef StatePrev = N->getFirstPred()->getState();
+
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ if (!S)
+ return nullptr;
+
+ StringRef Msg;
+ if (State->contains<InitializedVALists>(Reg) &&
+ !StatePrev->contains<InitializedVALists>(Reg))
+ Msg = "Initialized va_list";
+ else if (!State->contains<InitializedVALists>(Reg) &&
+ StatePrev->contains<InitializedVALists>(Reg))
+ Msg = "Ended va_list";
+
+ if (Msg.empty())
+ return nullptr;
+
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true);
+}
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name##Checker(CheckerManager &mgr) { \
+ ValistChecker *checker = mgr.registerChecker<ValistChecker>(); \
+ checker->ChecksEnabled[ValistChecker::CK_##name] = true; \
+ checker->CheckNames[ValistChecker::CK_##name] = mgr.getCurrentCheckName(); \
+ }
+
+REGISTER_CHECKER(Uninitialized)
+REGISTER_CHECKER(Unterminated)
+REGISTER_CHECKER(CopyToSelf)
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
new file mode 100644
index 000000000000..3ee9f1a07fa2
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
@@ -0,0 +1,218 @@
+//===- VforkChecker.cpp -------- Vfork usage checks --------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines vfork checker which checks for dangerous uses of vfork.
+// Vforked process shares memory (including stack) with parent so it's
+// range of actions is significantly limited: can't write variables,
+// can't call functions not in whitelist, etc. For more details, see
+// http://man7.org/linux/man-pages/man2/vfork.2.html
+//
+// This checker checks for prohibited constructs in vforked process.
+// The state transition diagram:
+// PARENT ---(vfork() == 0)--> CHILD
+// |
+// --(*p = ...)--> bug
+// |
+// --foo()--> bug
+// |
+// --return--> bug
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/AST/ParentMap.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class VforkChecker : public Checker<check::PreCall, check::PostCall,
+ check::Bind, check::PreStmt<ReturnStmt>> {
+ mutable std::unique_ptr<BuiltinBug> BT;
+ mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkWhitelist;
+ mutable const IdentifierInfo *II_vfork;
+
+ static bool isChildProcess(const ProgramStateRef State);
+
+ bool isVforkCall(const Decl *D, CheckerContext &C) const;
+ bool isCallWhitelisted(const IdentifierInfo *II, CheckerContext &C) const;
+
+ void reportBug(const char *What, CheckerContext &C,
+ const char *Details = nullptr) const;
+
+public:
+ VforkChecker() : II_vfork(nullptr) {}
+
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkBind(SVal L, SVal V, const Stmt *S, CheckerContext &C) const;
+ void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const;
+};
+
+} // end anonymous namespace
+
+// This trait holds region of variable that is assigned with vfork's
+// return value (this is the only region child is allowed to write).
+// VFORK_RESULT_INVALID means that we are in parent process.
+// VFORK_RESULT_NONE means that vfork's return value hasn't been assigned.
+// Other values point to valid regions.
+REGISTER_TRAIT_WITH_PROGRAMSTATE(VforkResultRegion, const void *)
+#define VFORK_RESULT_INVALID 0
+#define VFORK_RESULT_NONE ((void *)(uintptr_t)1)
+
+bool VforkChecker::isChildProcess(const ProgramStateRef State) {
+ return State->get<VforkResultRegion>() != VFORK_RESULT_INVALID;
+}
+
+bool VforkChecker::isVforkCall(const Decl *D, CheckerContext &C) const {
+ auto FD = dyn_cast_or_null<FunctionDecl>(D);
+ if (!FD || !C.isCLibraryFunction(FD))
+ return false;
+
+ if (!II_vfork) {
+ ASTContext &AC = C.getASTContext();
+ II_vfork = &AC.Idents.get("vfork");
+ }
+
+ return FD->getIdentifier() == II_vfork;
+}
+
+// Returns true iff ok to call function after successful vfork.
+bool VforkChecker::isCallWhitelisted(const IdentifierInfo *II,
+ CheckerContext &C) const {
+ if (VforkWhitelist.empty()) {
+ // According to manpage.
+ const char *ids[] = {
+ "_exit",
+ "_Exit",
+ "execl",
+ "execlp",
+ "execle",
+ "execv",
+ "execvp",
+ "execvpe",
+ nullptr
+ };
+
+ ASTContext &AC = C.getASTContext();
+ for (const char **id = ids; *id; ++id)
+ VforkWhitelist.insert(&AC.Idents.get(*id));
+ }
+
+ return VforkWhitelist.count(II);
+}
+
+void VforkChecker::reportBug(const char *What, CheckerContext &C,
+ const char *Details) const {
+ if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
+ if (!BT)
+ BT.reset(new BuiltinBug(this,
+ "Dangerous construct in a vforked process"));
+
+ SmallString<256> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << What << " is prohibited after a successful vfork";
+
+ if (Details)
+ os << "; " << Details;
+
+ auto Report = llvm::make_unique<BugReport>(*BT, os.str(), N);
+ // TODO: mark vfork call in BugReportVisitor
+ C.emitReport(std::move(Report));
+ }
+}
+
+// Detect calls to vfork and split execution appropriately.
+void VforkChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // We can't call vfork in child so don't bother
+ // (corresponding warning has already been emitted in checkPreCall).
+ ProgramStateRef State = C.getState();
+ if (isChildProcess(State))
+ return;
+
+ if (!isVforkCall(Call.getDecl(), C))
+ return;
+
+ // Get return value of vfork.
+ SVal VforkRetVal = Call.getReturnValue();
+ Optional<DefinedOrUnknownSVal> DVal =
+ VforkRetVal.getAs<DefinedOrUnknownSVal>();
+ if (!DVal)
+ return;
+
+ // Get assigned variable.
+ const ParentMap &PM = C.getLocationContext()->getParentMap();
+ const Stmt *P = PM.getParentIgnoreParenCasts(Call.getOriginExpr());
+ const VarDecl *LhsDecl;
+ std::tie(LhsDecl, std::ignore) = parseAssignment(P);
+
+ // Get assigned memory region.
+ MemRegionManager &M = C.getStoreManager().getRegionManager();
+ const MemRegion *LhsDeclReg =
+ LhsDecl
+ ? M.getVarRegion(LhsDecl, C.getLocationContext())
+ : (const MemRegion *)VFORK_RESULT_NONE;
+
+ // Parent branch gets nonzero return value (according to manpage).
+ ProgramStateRef ParentState, ChildState;
+ std::tie(ParentState, ChildState) = C.getState()->assume(*DVal);
+ C.addTransition(ParentState);
+ ChildState = ChildState->set<VforkResultRegion>(LhsDeclReg);
+ C.addTransition(ChildState);
+}
+
+// Prohibit calls to non-whitelist functions in child process.
+void VforkChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ if (isChildProcess(State)
+ && !isCallWhitelisted(Call.getCalleeIdentifier(), C))
+ reportBug("This function call", C);
+}
+
+// Prohibit writes in child process (except for vfork's lhs).
+void VforkChecker::checkBind(SVal L, SVal V, const Stmt *S,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ if (!isChildProcess(State))
+ return;
+
+ const MemRegion *VforkLhs =
+ static_cast<const MemRegion *>(State->get<VforkResultRegion>());
+ const MemRegion *MR = L.getAsRegion();
+
+ // Child is allowed to modify only vfork's lhs.
+ if (!MR || MR == VforkLhs)
+ return;
+
+ reportBug("This assignment", C);
+}
+
+// Prohibit return from function in child process.
+void VforkChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+ if (isChildProcess(State))
+ reportBug("Return", C, "call _exit() instead");
+}
+
+void ento::registerVforkChecker(CheckerManager &mgr) {
+ mgr.registerChecker<VforkChecker>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
new file mode 100644
index 000000000000..567063197405
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -0,0 +1,285 @@
+//=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a checker that checks virtual function calls during
+// construction or destruction of C++ objects.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+enum class ObjectState : bool { CtorCalled, DtorCalled };
+} // end namespace
+ // FIXME: Ascending over StackFrameContext maybe another method.
+
+namespace llvm {
+template <> struct FoldingSetTrait<ObjectState> {
+ static inline void Profile(ObjectState X, FoldingSetNodeID &ID) {
+ ID.AddInteger(static_cast<int>(X));
+ }
+};
+} // end namespace llvm
+
+namespace {
+class VirtualCallChecker
+ : public Checker<check::BeginFunction, check::EndFunction, check::PreCall> {
+ mutable std::unique_ptr<BugType> BT;
+
+public:
+ // The flag to determine if pure virtual functions should be issued only.
+ DefaultBool IsPureOnly;
+
+ void checkBeginFunction(CheckerContext &C) const;
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+
+private:
+ void registerCtorDtorCallInState(bool IsBeginFunction,
+ CheckerContext &C) const;
+ void reportBug(StringRef Msg, bool PureError, const MemRegion *Reg,
+ CheckerContext &C) const;
+
+ class VirtualBugVisitor : public BugReporterVisitor {
+ private:
+ const MemRegion *ObjectRegion;
+ bool Found;
+
+ public:
+ VirtualBugVisitor(const MemRegion *R) : ObjectRegion(R), Found(false) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int X = 0;
+ ID.AddPointer(&X);
+ ID.AddPointer(ObjectRegion);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+ };
+};
+} // end namespace
+
+// GDM (generic data map) to the memregion of this for the ctor and dtor.
+REGISTER_MAP_WITH_PROGRAMSTATE(CtorDtorMap, const MemRegion *, ObjectState)
+
+std::shared_ptr<PathDiagnosticPiece>
+VirtualCallChecker::VirtualBugVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &) {
+ // We need the last ctor/dtor which call the virtual function.
+ // The visitor walks the ExplodedGraph backwards.
+ if (Found)
+ return nullptr;
+
+ ProgramStateRef State = N->getState();
+ const LocationContext *LCtx = N->getLocationContext();
+ const CXXConstructorDecl *CD =
+ dyn_cast_or_null<CXXConstructorDecl>(LCtx->getDecl());
+ const CXXDestructorDecl *DD =
+ dyn_cast_or_null<CXXDestructorDecl>(LCtx->getDecl());
+
+ if (!CD && !DD)
+ return nullptr;
+
+ ProgramStateManager &PSM = State->getStateManager();
+ auto &SVB = PSM.getSValBuilder();
+ const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
+ if (!MD)
+ return nullptr;
+ auto ThiSVal =
+ State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
+ const MemRegion *Reg = ThiSVal.castAs<loc::MemRegionVal>().getRegion();
+ if (!Reg)
+ return nullptr;
+ if (Reg != ObjectRegion)
+ return nullptr;
+
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ if (!S)
+ return nullptr;
+ Found = true;
+
+ std::string InfoText;
+ if (CD)
+ InfoText = "This constructor of an object of type '" +
+ CD->getNameAsString() +
+ "' has not returned when the virtual method was called";
+ else
+ InfoText = "This destructor of an object of type '" +
+ DD->getNameAsString() +
+ "' has not returned when the virtual method was called";
+
+ // Generate the extra diagnostic.
+ PathDiagnosticLocation Pos(S, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, InfoText, true);
+}
+
+// The function to check if a callexpr is a virtual function.
+static bool isVirtualCall(const CallExpr *CE) {
+ bool CallIsNonVirtual = false;
+
+ if (const MemberExpr *CME = dyn_cast<MemberExpr>(CE->getCallee())) {
+ // The member access is fully qualified (i.e., X::F).
+ // Treat this as a non-virtual call and do not warn.
+ if (CME->getQualifier())
+ CallIsNonVirtual = true;
+
+ if (const Expr *Base = CME->getBase()) {
+ // The most derived class is marked final.
+ if (Base->getBestDynamicClassType()->hasAttr<FinalAttr>())
+ CallIsNonVirtual = true;
+ }
+ }
+
+ const CXXMethodDecl *MD =
+ dyn_cast_or_null<CXXMethodDecl>(CE->getDirectCallee());
+ if (MD && MD->isVirtual() && !CallIsNonVirtual && !MD->hasAttr<FinalAttr>() &&
+ !MD->getParent()->hasAttr<FinalAttr>())
+ return true;
+ return false;
+}
+
+// The BeginFunction callback when enter a constructor or a destructor.
+void VirtualCallChecker::checkBeginFunction(CheckerContext &C) const {
+ registerCtorDtorCallInState(true, C);
+}
+
+// The EndFunction callback when leave a constructor or a destructor.
+void VirtualCallChecker::checkEndFunction(const ReturnStmt *RS,
+ CheckerContext &C) const {
+ registerCtorDtorCallInState(false, C);
+}
+
+void VirtualCallChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto MC = dyn_cast<CXXMemberCall>(&Call);
+ if (!MC)
+ return;
+
+ const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(Call.getDecl());
+ if (!MD)
+ return;
+ ProgramStateRef State = C.getState();
+ const CallExpr *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+
+ if (IsPureOnly && !MD->isPure())
+ return;
+ if (!isVirtualCall(CE))
+ return;
+
+ const MemRegion *Reg = MC->getCXXThisVal().getAsRegion();
+ const ObjectState *ObState = State->get<CtorDtorMap>(Reg);
+ if (!ObState)
+ return;
+ // Check if a virtual method is called.
+ // The GDM of constructor and destructor should be true.
+ if (*ObState == ObjectState::CtorCalled) {
+ if (IsPureOnly && MD->isPure())
+ reportBug("Call to pure virtual function during construction", true, Reg,
+ C);
+ else if (!MD->isPure())
+ reportBug("Call to virtual function during construction", false, Reg, C);
+ else
+ reportBug("Call to pure virtual function during construction", false, Reg,
+ C);
+ }
+
+ if (*ObState == ObjectState::DtorCalled) {
+ if (IsPureOnly && MD->isPure())
+ reportBug("Call to pure virtual function during destruction", true, Reg,
+ C);
+ else if (!MD->isPure())
+ reportBug("Call to virtual function during destruction", false, Reg, C);
+ else
+ reportBug("Call to pure virtual function during construction", false, Reg,
+ C);
+ }
+}
+
+void VirtualCallChecker::registerCtorDtorCallInState(bool IsBeginFunction,
+ CheckerContext &C) const {
+ const auto *LCtx = C.getLocationContext();
+ const auto *MD = dyn_cast_or_null<CXXMethodDecl>(LCtx->getDecl());
+ if (!MD)
+ return;
+
+ ProgramStateRef State = C.getState();
+ auto &SVB = C.getSValBuilder();
+
+ // Enter a constructor, set the corresponding memregion be true.
+ if (isa<CXXConstructorDecl>(MD)) {
+ auto ThiSVal =
+ State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
+ const MemRegion *Reg = ThiSVal.getAsRegion();
+ if (IsBeginFunction)
+ State = State->set<CtorDtorMap>(Reg, ObjectState::CtorCalled);
+ else
+ State = State->remove<CtorDtorMap>(Reg);
+
+ C.addTransition(State);
+ return;
+ }
+
+ // Enter a Destructor, set the corresponding memregion be true.
+ if (isa<CXXDestructorDecl>(MD)) {
+ auto ThiSVal =
+ State->getSVal(SVB.getCXXThis(MD, LCtx->getStackFrame()));
+ const MemRegion *Reg = ThiSVal.getAsRegion();
+ if (IsBeginFunction)
+ State = State->set<CtorDtorMap>(Reg, ObjectState::DtorCalled);
+ else
+ State = State->remove<CtorDtorMap>(Reg);
+
+ C.addTransition(State);
+ return;
+ }
+}
+
+void VirtualCallChecker::reportBug(StringRef Msg, bool IsSink,
+ const MemRegion *Reg,
+ CheckerContext &C) const {
+ ExplodedNode *N;
+ if (IsSink)
+ N = C.generateErrorNode();
+ else
+ N = C.generateNonFatalErrorNode();
+
+ if (!N)
+ return;
+ if (!BT)
+ BT.reset(new BugType(
+ this, "Call to virtual function during construction or destruction",
+ "C++ Object Lifecycle"));
+
+ auto Reporter = llvm::make_unique<BugReport>(*BT, Msg, N);
+ Reporter->addVisitor(llvm::make_unique<VirtualBugVisitor>(Reg));
+ C.emitReport(std::move(Reporter));
+}
+
+void ento::registerVirtualCallChecker(CheckerManager &mgr) {
+ VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
+
+ checker->IsPureOnly =
+ mgr.getAnalyzerOptions().getCheckerBooleanOption("PureOnly", false,
+ checker);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/APSIntType.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/APSIntType.cpp
new file mode 100644
index 000000000000..c7e95268213e
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/APSIntType.cpp
@@ -0,0 +1,49 @@
+//===--- APSIntType.cpp - Simple record of the type of APSInts ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+
+using namespace clang;
+using namespace ento;
+
+APSIntType::RangeTestResultKind
+APSIntType::testInRange(const llvm::APSInt &Value,
+ bool AllowSignConversions) const {
+
+ // Negative numbers cannot be losslessly converted to unsigned type.
+ if (IsUnsigned && !AllowSignConversions &&
+ Value.isSigned() && Value.isNegative())
+ return RTR_Below;
+
+ unsigned MinBits;
+ if (AllowSignConversions) {
+ if (Value.isSigned() && !IsUnsigned)
+ MinBits = Value.getMinSignedBits();
+ else
+ MinBits = Value.getActiveBits();
+
+ } else {
+ // Signed integers can be converted to signed integers of the same width
+ // or (if positive) unsigned integers with one fewer bit.
+ // Unsigned integers can be converted to unsigned integers of the same width
+ // or signed integers with one more bit.
+ if (Value.isSigned())
+ MinBits = Value.getMinSignedBits() - IsUnsigned;
+ else
+ MinBits = Value.getActiveBits() + !IsUnsigned;
+ }
+
+ if (MinBits <= BitWidth)
+ return RTR_Within;
+
+ if (Value.isSigned() && Value.isNegative())
+ return RTR_Below;
+ else
+ return RTR_Above;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
new file mode 100644
index 000000000000..7fb1c09ca049
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -0,0 +1,62 @@
+//===-- AnalysisManager.cpp -------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+
+using namespace clang;
+using namespace ento;
+
+void AnalysisManager::anchor() { }
+
+AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags,
+ const PathDiagnosticConsumers &PDC,
+ StoreManagerCreator storemgr,
+ ConstraintManagerCreator constraintmgr,
+ CheckerManager *checkerMgr,
+ AnalyzerOptions &Options,
+ CodeInjector *injector)
+ : AnaCtxMgr(
+ ASTCtx, Options.UnoptimizedCFG,
+ Options.ShouldIncludeImplicitDtorsInCFG,
+ /*AddInitializers=*/true,
+ Options.ShouldIncludeTemporaryDtorsInCFG,
+ Options.ShouldIncludeLifetimeInCFG,
+ // Adding LoopExit elements to the CFG is a requirement for loop
+ // unrolling.
+ Options.ShouldIncludeLoopExitInCFG ||
+ Options.ShouldUnrollLoops,
+ Options.ShouldIncludeScopesInCFG,
+ Options.ShouldSynthesizeBodies,
+ Options.ShouldConditionalizeStaticInitializers,
+ /*addCXXNewAllocator=*/true,
+ Options.ShouldIncludeRichConstructorsInCFG,
+ Options.ShouldElideConstructors, injector),
+ Ctx(ASTCtx), Diags(diags), LangOpts(ASTCtx.getLangOpts()),
+ PathConsumers(PDC), CreateStoreMgr(storemgr),
+ CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr),
+ options(Options) {
+ AnaCtxMgr.getCFGBuildOptions().setAllAlwaysAdd();
+}
+
+AnalysisManager::~AnalysisManager() {
+ FlushDiagnostics();
+ for (PathDiagnosticConsumers::iterator I = PathConsumers.begin(),
+ E = PathConsumers.end(); I != E; ++I) {
+ delete *I;
+ }
+}
+
+void AnalysisManager::FlushDiagnostics() {
+ PathDiagnosticConsumer::FilesMade filesMade;
+ for (PathDiagnosticConsumers::iterator I = PathConsumers.begin(),
+ E = PathConsumers.end();
+ I != E; ++I) {
+ (*I)->FlushDiagnostics(&filesMade);
+ }
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
new file mode 100644
index 000000000000..0588c2bd3d35
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -0,0 +1,156 @@
+//===- AnalyzerOptions.cpp - Analysis Engine Options ----------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file contains special accessors for analyzer configuration options
+// with string representations.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringSwitch.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstddef>
+#include <utility>
+#include <vector>
+
+using namespace clang;
+using namespace ento;
+using namespace llvm;
+
+std::vector<StringRef>
+AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) {
+ static const StringRef StaticAnalyzerChecks[] = {
+#define GET_CHECKERS
+#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \
+ FULLNAME,
+#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
+#undef CHECKER
+#undef GET_CHECKERS
+ };
+ std::vector<StringRef> Result;
+ for (StringRef CheckName : StaticAnalyzerChecks) {
+ if (!CheckName.startswith("debug.") &&
+ (IncludeExperimental || !CheckName.startswith("alpha.")))
+ Result.push_back(CheckName);
+ }
+ return Result;
+}
+
+ExplorationStrategyKind
+AnalyzerOptions::getExplorationStrategy() const {
+ auto K =
+ llvm::StringSwitch<llvm::Optional<ExplorationStrategyKind>>(
+ ExplorationStrategy)
+ .Case("dfs", ExplorationStrategyKind::DFS)
+ .Case("bfs", ExplorationStrategyKind::BFS)
+ .Case("unexplored_first",
+ ExplorationStrategyKind::UnexploredFirst)
+ .Case("unexplored_first_queue",
+ ExplorationStrategyKind::UnexploredFirstQueue)
+ .Case("unexplored_first_location_queue",
+ ExplorationStrategyKind::UnexploredFirstLocationQueue)
+ .Case("bfs_block_dfs_contents",
+ ExplorationStrategyKind::BFSBlockDFSContents)
+ .Default(None);
+ assert(K.hasValue() && "User mode is invalid.");
+ return K.getValue();
+}
+
+IPAKind AnalyzerOptions::getIPAMode() const {
+ auto K = llvm::StringSwitch<llvm::Optional<IPAKind>>(IPAMode)
+ .Case("none", IPAK_None)
+ .Case("basic-inlining", IPAK_BasicInlining)
+ .Case("inlining", IPAK_Inlining)
+ .Case("dynamic", IPAK_DynamicDispatch)
+ .Case("dynamic-bifurcate", IPAK_DynamicDispatchBifurcate)
+ .Default(None);
+ assert(K.hasValue() && "IPA Mode is invalid.");
+
+ return K.getValue();
+}
+
+bool
+AnalyzerOptions::mayInlineCXXMemberFunction(
+ CXXInlineableMemberKind Param) const {
+ if (getIPAMode() < IPAK_Inlining)
+ return false;
+
+ auto K =
+ llvm::StringSwitch<llvm::Optional<CXXInlineableMemberKind>>(
+ CXXMemberInliningMode)
+ .Case("constructors", CIMK_Constructors)
+ .Case("destructors", CIMK_Destructors)
+ .Case("methods", CIMK_MemberFunctions)
+ .Case("none", CIMK_None)
+ .Default(None);
+
+ assert(K.hasValue() && "Invalid c++ member function inlining mode.");
+
+ return *K >= Param;
+}
+
+StringRef AnalyzerOptions::getCheckerStringOption(StringRef OptionName,
+ StringRef DefaultVal,
+ const CheckerBase *C,
+ bool SearchInParents) const {
+ assert(C);
+ // Search for a package option if the option for the checker is not specified
+ // and search in parents is enabled.
+ StringRef CheckerName = C->getTagDescription();
+
+ assert(!CheckerName.empty() &&
+ "Empty checker name! Make sure the checker object (including it's "
+ "bases!) if fully initialized before calling this function!");
+ ConfigTable::const_iterator E = Config.end();
+ do {
+ ConfigTable::const_iterator I =
+ Config.find((Twine(CheckerName) + ":" + OptionName).str());
+ if (I != E)
+ return StringRef(I->getValue());
+ size_t Pos = CheckerName.rfind('.');
+ if (Pos == StringRef::npos)
+ return DefaultVal;
+ CheckerName = CheckerName.substr(0, Pos);
+ } while (!CheckerName.empty() && SearchInParents);
+ return DefaultVal;
+}
+
+bool AnalyzerOptions::getCheckerBooleanOption(StringRef Name, bool DefaultVal,
+ const CheckerBase *C,
+ bool SearchInParents) const {
+ // FIXME: We should emit a warning here if the value is something other than
+ // "true", "false", or the empty string (meaning the default value),
+ // but the AnalyzerOptions doesn't have access to a diagnostic engine.
+ assert(C);
+ return llvm::StringSwitch<bool>(
+ getCheckerStringOption(Name, DefaultVal ? "true" : "false", C,
+ SearchInParents))
+ .Case("true", true)
+ .Case("false", false)
+ .Default(DefaultVal);
+}
+
+int AnalyzerOptions::getCheckerIntegerOption(StringRef Name, int DefaultVal,
+ const CheckerBase *C,
+ bool SearchInParents) const {
+ int Ret = DefaultVal;
+ bool HasFailed = getCheckerStringOption(Name, std::to_string(DefaultVal), C,
+ SearchInParents)
+ .getAsInteger(10, Ret);
+ assert(!HasFailed && "analyzer-config option should be numeric");
+ (void)HasFailed;
+ return Ret;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
new file mode 100644
index 000000000000..d8ed6942de81
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -0,0 +1,350 @@
+//===- BasicValueFactory.cpp - Basic values for Path Sens analysis --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines BasicValueFactory, a class that manages the lifetime
+// of APSInt objects and symbolic constraints used by ExprEngine
+// and related classes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableList.h"
+#include "llvm/ADT/STLExtras.h"
+#include <cassert>
+#include <cstdint>
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+void CompoundValData::Profile(llvm::FoldingSetNodeID& ID, QualType T,
+ llvm::ImmutableList<SVal> L) {
+ T.Profile(ID);
+ ID.AddPointer(L.getInternalPointer());
+}
+
+void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID,
+ const StoreRef &store,
+ const TypedValueRegion *region) {
+ ID.AddPointer(store.getStore());
+ ID.AddPointer(region);
+}
+
+void PointerToMemberData::Profile(
+ llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D,
+ llvm::ImmutableList<const CXXBaseSpecifier *> L) {
+ ID.AddPointer(D);
+ ID.AddPointer(L.getInternalPointer());
+}
+
+using SValData = std::pair<SVal, uintptr_t>;
+using SValPair = std::pair<SVal, SVal>;
+
+namespace llvm {
+
+template<> struct FoldingSetTrait<SValData> {
+ static inline void Profile(const SValData& X, llvm::FoldingSetNodeID& ID) {
+ X.first.Profile(ID);
+ ID.AddPointer( (void*) X.second);
+ }
+};
+
+template<> struct FoldingSetTrait<SValPair> {
+ static inline void Profile(const SValPair& X, llvm::FoldingSetNodeID& ID) {
+ X.first.Profile(ID);
+ X.second.Profile(ID);
+ }
+};
+
+} // namespace llvm
+
+using PersistentSValsTy =
+ llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValData>>;
+
+using PersistentSValPairsTy =
+ llvm::FoldingSet<llvm::FoldingSetNodeWrapper<SValPair>>;
+
+BasicValueFactory::~BasicValueFactory() {
+ // Note that the dstor for the contents of APSIntSet will never be called,
+ // so we iterate over the set and invoke the dstor for each APSInt. This
+ // frees an aux. memory allocated to represent very large constants.
+ for (const auto &I : APSIntSet)
+ I.getValue().~APSInt();
+
+ delete (PersistentSValsTy*) PersistentSVals;
+ delete (PersistentSValPairsTy*) PersistentSValPairs;
+}
+
+const llvm::APSInt& BasicValueFactory::getValue(const llvm::APSInt& X) {
+ llvm::FoldingSetNodeID ID;
+ void *InsertPos;
+
+ using FoldNodeTy = llvm::FoldingSetNodeWrapper<llvm::APSInt>;
+
+ X.Profile(ID);
+ FoldNodeTy* P = APSIntSet.FindNodeOrInsertPos(ID, InsertPos);
+
+ if (!P) {
+ P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>();
+ new (P) FoldNodeTy(X);
+ APSIntSet.InsertNode(P, InsertPos);
+ }
+
+ return *P;
+}
+
+const llvm::APSInt& BasicValueFactory::getValue(const llvm::APInt& X,
+ bool isUnsigned) {
+ llvm::APSInt V(X, isUnsigned);
+ return getValue(V);
+}
+
+const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, unsigned BitWidth,
+ bool isUnsigned) {
+ llvm::APSInt V(BitWidth, isUnsigned);
+ V = X;
+ return getValue(V);
+}
+
+const llvm::APSInt& BasicValueFactory::getValue(uint64_t X, QualType T) {
+ return getValue(getAPSIntType(T).getValue(X));
+}
+
+const CompoundValData*
+BasicValueFactory::getCompoundValData(QualType T,
+ llvm::ImmutableList<SVal> Vals) {
+ llvm::FoldingSetNodeID ID;
+ CompoundValData::Profile(ID, T, Vals);
+ void *InsertPos;
+
+ CompoundValData* D = CompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos);
+
+ if (!D) {
+ D = (CompoundValData*) BPAlloc.Allocate<CompoundValData>();
+ new (D) CompoundValData(T, Vals);
+ CompoundValDataSet.InsertNode(D, InsertPos);
+ }
+
+ return D;
+}
+
+const LazyCompoundValData*
+BasicValueFactory::getLazyCompoundValData(const StoreRef &store,
+ const TypedValueRegion *region) {
+ llvm::FoldingSetNodeID ID;
+ LazyCompoundValData::Profile(ID, store, region);
+ void *InsertPos;
+
+ LazyCompoundValData *D =
+ LazyCompoundValDataSet.FindNodeOrInsertPos(ID, InsertPos);
+
+ if (!D) {
+ D = (LazyCompoundValData*) BPAlloc.Allocate<LazyCompoundValData>();
+ new (D) LazyCompoundValData(store, region);
+ LazyCompoundValDataSet.InsertNode(D, InsertPos);
+ }
+
+ return D;
+}
+
+const PointerToMemberData *BasicValueFactory::getPointerToMemberData(
+ const DeclaratorDecl *DD, llvm::ImmutableList<const CXXBaseSpecifier *> L) {
+ llvm::FoldingSetNodeID ID;
+ PointerToMemberData::Profile(ID, DD, L);
+ void *InsertPos;
+
+ PointerToMemberData *D =
+ PointerToMemberDataSet.FindNodeOrInsertPos(ID, InsertPos);
+
+ if (!D) {
+ D = (PointerToMemberData*) BPAlloc.Allocate<PointerToMemberData>();
+ new (D) PointerToMemberData(DD, L);
+ PointerToMemberDataSet.InsertNode(D, InsertPos);
+ }
+
+ return D;
+}
+
+const PointerToMemberData *BasicValueFactory::accumCXXBase(
+ llvm::iterator_range<CastExpr::path_const_iterator> PathRange,
+ const nonloc::PointerToMember &PTM) {
+ nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData();
+ const DeclaratorDecl *DD = nullptr;
+ llvm::ImmutableList<const CXXBaseSpecifier *> PathList;
+
+ if (PTMDT.isNull() || PTMDT.is<const DeclaratorDecl *>()) {
+ if (PTMDT.is<const DeclaratorDecl *>())
+ DD = PTMDT.get<const DeclaratorDecl *>();
+
+ PathList = CXXBaseListFactory.getEmptyList();
+ } else { // const PointerToMemberData *
+ const PointerToMemberData *PTMD =
+ PTMDT.get<const PointerToMemberData *>();
+ DD = PTMD->getDeclaratorDecl();
+
+ PathList = PTMD->getCXXBaseList();
+ }
+
+ for (const auto &I : llvm::reverse(PathRange))
+ PathList = prependCXXBase(I, PathList);
+ return getPointerToMemberData(DD, PathList);
+}
+
+const llvm::APSInt*
+BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
+ const llvm::APSInt& V1, const llvm::APSInt& V2) {
+ switch (Op) {
+ default:
+ llvm_unreachable("Invalid Opcode.");
+
+ case BO_Mul:
+ return &getValue( V1 * V2 );
+
+ case BO_Div:
+ if (V2 == 0) // Avoid division by zero
+ return nullptr;
+ return &getValue( V1 / V2 );
+
+ case BO_Rem:
+ if (V2 == 0) // Avoid division by zero
+ return nullptr;
+ return &getValue( V1 % V2 );
+
+ case BO_Add:
+ return &getValue( V1 + V2 );
+
+ case BO_Sub:
+ return &getValue( V1 - V2 );
+
+ case BO_Shl: {
+ // FIXME: This logic should probably go higher up, where we can
+ // test these conditions symbolically.
+
+ if (V1.isSigned() && V1.isNegative())
+ return nullptr;
+
+ if (V2.isSigned() && V2.isNegative())
+ return nullptr;
+
+ uint64_t Amt = V2.getZExtValue();
+
+ if (Amt >= V1.getBitWidth())
+ return nullptr;
+
+ if (V1.isSigned() && Amt > V1.countLeadingZeros())
+ return nullptr;
+
+ return &getValue( V1.operator<<( (unsigned) Amt ));
+ }
+
+ case BO_Shr: {
+ // FIXME: This logic should probably go higher up, where we can
+ // test these conditions symbolically.
+
+ if (V2.isSigned() && V2.isNegative())
+ return nullptr;
+
+ uint64_t Amt = V2.getZExtValue();
+
+ if (Amt >= V1.getBitWidth())
+ return nullptr;
+
+ return &getValue( V1.operator>>( (unsigned) Amt ));
+ }
+
+ case BO_LT:
+ return &getTruthValue( V1 < V2 );
+
+ case BO_GT:
+ return &getTruthValue( V1 > V2 );
+
+ case BO_LE:
+ return &getTruthValue( V1 <= V2 );
+
+ case BO_GE:
+ return &getTruthValue( V1 >= V2 );
+
+ case BO_EQ:
+ return &getTruthValue( V1 == V2 );
+
+ case BO_NE:
+ return &getTruthValue( V1 != V2 );
+
+ // Note: LAnd, LOr, Comma are handled specially by higher-level logic.
+
+ case BO_And:
+ return &getValue( V1 & V2 );
+
+ case BO_Or:
+ return &getValue( V1 | V2 );
+
+ case BO_Xor:
+ return &getValue( V1 ^ V2 );
+ }
+}
+
+const std::pair<SVal, uintptr_t>&
+BasicValueFactory::getPersistentSValWithData(const SVal& V, uintptr_t Data) {
+ // Lazily create the folding set.
+ if (!PersistentSVals) PersistentSVals = new PersistentSValsTy();
+
+ llvm::FoldingSetNodeID ID;
+ void *InsertPos;
+ V.Profile(ID);
+ ID.AddPointer((void*) Data);
+
+ PersistentSValsTy& Map = *((PersistentSValsTy*) PersistentSVals);
+
+ using FoldNodeTy = llvm::FoldingSetNodeWrapper<SValData>;
+
+ FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos);
+
+ if (!P) {
+ P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>();
+ new (P) FoldNodeTy(std::make_pair(V, Data));
+ Map.InsertNode(P, InsertPos);
+ }
+
+ return P->getValue();
+}
+
+const std::pair<SVal, SVal>&
+BasicValueFactory::getPersistentSValPair(const SVal& V1, const SVal& V2) {
+ // Lazily create the folding set.
+ if (!PersistentSValPairs) PersistentSValPairs = new PersistentSValPairsTy();
+
+ llvm::FoldingSetNodeID ID;
+ void *InsertPos;
+ V1.Profile(ID);
+ V2.Profile(ID);
+
+ PersistentSValPairsTy& Map = *((PersistentSValPairsTy*) PersistentSValPairs);
+
+ using FoldNodeTy = llvm::FoldingSetNodeWrapper<SValPair>;
+
+ FoldNodeTy* P = Map.FindNodeOrInsertPos(ID, InsertPos);
+
+ if (!P) {
+ P = (FoldNodeTy*) BPAlloc.Allocate<FoldNodeTy>();
+ new (P) FoldNodeTy(std::make_pair(V1, V2));
+ Map.InsertNode(P, InsertPos);
+ }
+
+ return P->getValue();
+}
+
+const SVal* BasicValueFactory::getPersistentSVal(SVal X) {
+ return &getPersistentSValWithData(X, 0).first;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp
new file mode 100644
index 000000000000..8c99bd808494
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BlockCounter.cpp
@@ -0,0 +1,85 @@
+//==- BlockCounter.h - ADT for counting block visits -------------*- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines BlockCounter, an abstract data type used to count
+// the number of times a given block has been visited along a path
+// analyzed by CoreEngine.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h"
+#include "llvm/ADT/ImmutableMap.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class CountKey {
+ const StackFrameContext *CallSite;
+ unsigned BlockID;
+
+public:
+ CountKey(const StackFrameContext *CS, unsigned ID)
+ : CallSite(CS), BlockID(ID) {}
+
+ bool operator==(const CountKey &RHS) const {
+ return (CallSite == RHS.CallSite) && (BlockID == RHS.BlockID);
+ }
+
+ bool operator<(const CountKey &RHS) const {
+ return std::tie(CallSite, BlockID) < std::tie(RHS.CallSite, RHS.BlockID);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddPointer(CallSite);
+ ID.AddInteger(BlockID);
+ }
+};
+
+}
+
+typedef llvm::ImmutableMap<CountKey, unsigned> CountMap;
+
+static inline CountMap GetMap(void *D) {
+ return CountMap(static_cast<CountMap::TreeTy*>(D));
+}
+
+static inline CountMap::Factory& GetFactory(void *F) {
+ return *static_cast<CountMap::Factory*>(F);
+}
+
+unsigned BlockCounter::getNumVisited(const StackFrameContext *CallSite,
+ unsigned BlockID) const {
+ CountMap M = GetMap(Data);
+ CountMap::data_type* T = M.lookup(CountKey(CallSite, BlockID));
+ return T ? *T : 0;
+}
+
+BlockCounter::Factory::Factory(llvm::BumpPtrAllocator& Alloc) {
+ F = new CountMap::Factory(Alloc);
+}
+
+BlockCounter::Factory::~Factory() {
+ delete static_cast<CountMap::Factory*>(F);
+}
+
+BlockCounter
+BlockCounter::Factory::IncrementCount(BlockCounter BC,
+ const StackFrameContext *CallSite,
+ unsigned BlockID) {
+ return BlockCounter(GetFactory(F).add(GetMap(BC.Data),
+ CountKey(CallSite, BlockID),
+ BC.getNumVisited(CallSite, BlockID)+1).getRoot());
+}
+
+BlockCounter
+BlockCounter::Factory::GetEmptyCounter() {
+ return BlockCounter(GetFactory(F).getEmptyMap().getRoot());
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
new file mode 100644
index 000000000000..fd7f53210490
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -0,0 +1,3142 @@
+//===- BugReporter.cpp - Generate PathDiagnostics for bugs ----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines BugReporter, a utility class for generating
+// PathDiagnostics.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtObjC.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <cstddef>
+#include <iterator>
+#include <memory>
+#include <queue>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+using namespace clang;
+using namespace ento;
+
+#define DEBUG_TYPE "BugReporter"
+
+STATISTIC(MaxBugClassSize,
+ "The maximum number of bug reports in the same equivalence class");
+STATISTIC(MaxValidBugClassSize,
+ "The maximum number of bug reports in the same equivalence class "
+ "where at least one report is valid (not suppressed)");
+
+BugReporterVisitor::~BugReporterVisitor() = default;
+
+void BugReporterContext::anchor() {}
+
+//===----------------------------------------------------------------------===//
+// Helper routines for walking the ExplodedGraph and fetching statements.
+//===----------------------------------------------------------------------===//
+
+static const Stmt *GetPreviousStmt(const ExplodedNode *N) {
+ for (N = N->getFirstPred(); N; N = N->getFirstPred())
+ if (const Stmt *S = PathDiagnosticLocation::getStmt(N))
+ return S;
+
+ return nullptr;
+}
+
+static inline const Stmt*
+GetCurrentOrPreviousStmt(const ExplodedNode *N) {
+ if (const Stmt *S = PathDiagnosticLocation::getStmt(N))
+ return S;
+
+ return GetPreviousStmt(N);
+}
+
+//===----------------------------------------------------------------------===//
+// Diagnostic cleanup.
+//===----------------------------------------------------------------------===//
+
+static PathDiagnosticEventPiece *
+eventsDescribeSameCondition(PathDiagnosticEventPiece *X,
+ PathDiagnosticEventPiece *Y) {
+ // Prefer diagnostics that come from ConditionBRVisitor over
+ // those that came from TrackConstraintBRVisitor,
+ // unless the one from ConditionBRVisitor is
+ // its generic fallback diagnostic.
+ const void *tagPreferred = ConditionBRVisitor::getTag();
+ const void *tagLesser = TrackConstraintBRVisitor::getTag();
+
+ if (X->getLocation() != Y->getLocation())
+ return nullptr;
+
+ if (X->getTag() == tagPreferred && Y->getTag() == tagLesser)
+ return ConditionBRVisitor::isPieceMessageGeneric(X) ? Y : X;
+
+ if (Y->getTag() == tagPreferred && X->getTag() == tagLesser)
+ return ConditionBRVisitor::isPieceMessageGeneric(Y) ? X : Y;
+
+ return nullptr;
+}
+
+/// An optimization pass over PathPieces that removes redundant diagnostics
+/// generated by both ConditionBRVisitor and TrackConstraintBRVisitor. Both
+/// BugReporterVisitors use different methods to generate diagnostics, with
+/// one capable of emitting diagnostics in some cases but not in others. This
+/// can lead to redundant diagnostic pieces at the same point in a path.
+static void removeRedundantMsgs(PathPieces &path) {
+ unsigned N = path.size();
+ if (N < 2)
+ return;
+ // NOTE: this loop intentionally is not using an iterator. Instead, we
+ // are streaming the path and modifying it in place. This is done by
+ // grabbing the front, processing it, and if we decide to keep it append
+ // it to the end of the path. The entire path is processed in this way.
+ for (unsigned i = 0; i < N; ++i) {
+ auto piece = std::move(path.front());
+ path.pop_front();
+
+ switch (piece->getKind()) {
+ case PathDiagnosticPiece::Call:
+ removeRedundantMsgs(cast<PathDiagnosticCallPiece>(*piece).path);
+ break;
+ case PathDiagnosticPiece::Macro:
+ removeRedundantMsgs(cast<PathDiagnosticMacroPiece>(*piece).subPieces);
+ break;
+ case PathDiagnosticPiece::ControlFlow:
+ break;
+ case PathDiagnosticPiece::Event: {
+ if (i == N-1)
+ break;
+
+ if (auto *nextEvent =
+ dyn_cast<PathDiagnosticEventPiece>(path.front().get())) {
+ auto *event = cast<PathDiagnosticEventPiece>(piece.get());
+ // Check to see if we should keep one of the two pieces. If we
+ // come up with a preference, record which piece to keep, and consume
+ // another piece from the path.
+ if (auto *pieceToKeep =
+ eventsDescribeSameCondition(event, nextEvent)) {
+ piece = std::move(pieceToKeep == event ? piece : path.front());
+ path.pop_front();
+ ++i;
+ }
+ }
+ break;
+ }
+ case PathDiagnosticPiece::Note:
+ break;
+ }
+ path.push_back(std::move(piece));
+ }
+}
+
+/// A map from PathDiagnosticPiece to the LocationContext of the inlined
+/// function call it represents.
+using LocationContextMap =
+ llvm::DenseMap<const PathPieces *, const LocationContext *>;
+
+/// Recursively scan through a path and prune out calls and macros pieces
+/// that aren't needed. Return true if afterwards the path contains
+/// "interesting stuff" which means it shouldn't be pruned from the parent path.
+static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
+ LocationContextMap &LCM,
+ bool IsInteresting = false) {
+ bool containsSomethingInteresting = IsInteresting;
+ const unsigned N = pieces.size();
+
+ for (unsigned i = 0 ; i < N ; ++i) {
+ // Remove the front piece from the path. If it is still something we
+ // want to keep once we are done, we will push it back on the end.
+ auto piece = std::move(pieces.front());
+ pieces.pop_front();
+
+ switch (piece->getKind()) {
+ case PathDiagnosticPiece::Call: {
+ auto &call = cast<PathDiagnosticCallPiece>(*piece);
+ // Check if the location context is interesting.
+ assert(LCM.count(&call.path));
+ if (!removeUnneededCalls(call.path, R, LCM,
+ R->isInteresting(LCM[&call.path])))
+ continue;
+
+ containsSomethingInteresting = true;
+ break;
+ }
+ case PathDiagnosticPiece::Macro: {
+ auto &macro = cast<PathDiagnosticMacroPiece>(*piece);
+ if (!removeUnneededCalls(macro.subPieces, R, LCM, IsInteresting))
+ continue;
+ containsSomethingInteresting = true;
+ break;
+ }
+ case PathDiagnosticPiece::Event: {
+ auto &event = cast<PathDiagnosticEventPiece>(*piece);
+
+ // We never throw away an event, but we do throw it away wholesale
+ // as part of a path if we throw the entire path away.
+ containsSomethingInteresting |= !event.isPrunable();
+ break;
+ }
+ case PathDiagnosticPiece::ControlFlow:
+ break;
+
+ case PathDiagnosticPiece::Note:
+ break;
+ }
+
+ pieces.push_back(std::move(piece));
+ }
+
+ return containsSomethingInteresting;
+}
+
+/// Returns true if the given decl has been implicitly given a body, either by
+/// the analyzer or by the compiler proper.
+static bool hasImplicitBody(const Decl *D) {
+ assert(D);
+ return D->isImplicit() || !D->hasBody();
+}
+
+/// Recursively scan through a path and make sure that all call pieces have
+/// valid locations.
+static void
+adjustCallLocations(PathPieces &Pieces,
+ PathDiagnosticLocation *LastCallLocation = nullptr) {
+ for (const auto &I : Pieces) {
+ auto *Call = dyn_cast<PathDiagnosticCallPiece>(I.get());
+
+ if (!Call)
+ continue;
+
+ if (LastCallLocation) {
+ bool CallerIsImplicit = hasImplicitBody(Call->getCaller());
+ if (CallerIsImplicit || !Call->callEnter.asLocation().isValid())
+ Call->callEnter = *LastCallLocation;
+ if (CallerIsImplicit || !Call->callReturn.asLocation().isValid())
+ Call->callReturn = *LastCallLocation;
+ }
+
+ // Recursively clean out the subclass. Keep this call around if
+ // it contains any informative diagnostics.
+ PathDiagnosticLocation *ThisCallLocation;
+ if (Call->callEnterWithin.asLocation().isValid() &&
+ !hasImplicitBody(Call->getCallee()))
+ ThisCallLocation = &Call->callEnterWithin;
+ else
+ ThisCallLocation = &Call->callEnter;
+
+ assert(ThisCallLocation && "Outermost call has an invalid location");
+ adjustCallLocations(Call->path, ThisCallLocation);
+ }
+}
+
+/// Remove edges in and out of C++ default initializer expressions. These are
+/// for fields that have in-class initializers, as opposed to being initialized
+/// explicitly in a constructor or braced list.
+static void removeEdgesToDefaultInitializers(PathPieces &Pieces) {
+ for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
+ if (auto *C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
+ removeEdgesToDefaultInitializers(C->path);
+
+ if (auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
+ removeEdgesToDefaultInitializers(M->subPieces);
+
+ if (auto *CF = dyn_cast<PathDiagnosticControlFlowPiece>(I->get())) {
+ const Stmt *Start = CF->getStartLocation().asStmt();
+ const Stmt *End = CF->getEndLocation().asStmt();
+ if (Start && isa<CXXDefaultInitExpr>(Start)) {
+ I = Pieces.erase(I);
+ continue;
+ } else if (End && isa<CXXDefaultInitExpr>(End)) {
+ PathPieces::iterator Next = std::next(I);
+ if (Next != E) {
+ if (auto *NextCF =
+ dyn_cast<PathDiagnosticControlFlowPiece>(Next->get())) {
+ NextCF->setStartLocation(CF->getStartLocation());
+ }
+ }
+ I = Pieces.erase(I);
+ continue;
+ }
+ }
+
+ I++;
+ }
+}
+
+/// Remove all pieces with invalid locations as these cannot be serialized.
+/// We might have pieces with invalid locations as a result of inlining Body
+/// Farm generated functions.
+static void removePiecesWithInvalidLocations(PathPieces &Pieces) {
+ for (PathPieces::iterator I = Pieces.begin(), E = Pieces.end(); I != E;) {
+ if (auto *C = dyn_cast<PathDiagnosticCallPiece>(I->get()))
+ removePiecesWithInvalidLocations(C->path);
+
+ if (auto *M = dyn_cast<PathDiagnosticMacroPiece>(I->get()))
+ removePiecesWithInvalidLocations(M->subPieces);
+
+ if (!(*I)->getLocation().isValid() ||
+ !(*I)->getLocation().asLocation().isValid()) {
+ I = Pieces.erase(I);
+ continue;
+ }
+ I++;
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// PathDiagnosticBuilder and its associated routines and helper objects.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class PathDiagnosticBuilder : public BugReporterContext {
+ BugReport *R;
+ PathDiagnosticConsumer *PDC;
+
+public:
+ const LocationContext *LC;
+
+ PathDiagnosticBuilder(GRBugReporter &br,
+ BugReport *r, InterExplodedGraphMap &Backmap,
+ PathDiagnosticConsumer *pdc)
+ : BugReporterContext(br, Backmap), R(r), PDC(pdc),
+ LC(r->getErrorNode()->getLocationContext()) {}
+
+ PathDiagnosticLocation ExecutionContinues(const ExplodedNode *N);
+
+ PathDiagnosticLocation ExecutionContinues(llvm::raw_string_ostream &os,
+ const ExplodedNode *N);
+
+ BugReport *getBugReport() { return R; }
+
+ Decl const &getCodeDecl() { return R->getErrorNode()->getCodeDecl(); }
+
+ ParentMap& getParentMap() { return LC->getParentMap(); }
+
+ const Stmt *getParent(const Stmt *S) {
+ return getParentMap().getParent(S);
+ }
+
+ PathDiagnosticLocation getEnclosingStmtLocation(const Stmt *S);
+
+ PathDiagnosticConsumer::PathGenerationScheme getGenerationScheme() const {
+ return PDC ? PDC->getGenerationScheme() : PathDiagnosticConsumer::Minimal;
+ }
+
+ bool supportsLogicalOpControlFlow() const {
+ return PDC ? PDC->supportsLogicalOpControlFlow() : true;
+ }
+};
+
+} // namespace
+
+PathDiagnosticLocation
+PathDiagnosticBuilder::ExecutionContinues(const ExplodedNode *N) {
+ if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N))
+ return PathDiagnosticLocation(S, getSourceManager(), LC);
+
+ return PathDiagnosticLocation::createDeclEnd(N->getLocationContext(),
+ getSourceManager());
+}
+
+PathDiagnosticLocation
+PathDiagnosticBuilder::ExecutionContinues(llvm::raw_string_ostream &os,
+ const ExplodedNode *N) {
+ // Slow, but probably doesn't matter.
+ if (os.str().empty())
+ os << ' ';
+
+ const PathDiagnosticLocation &Loc = ExecutionContinues(N);
+
+ if (Loc.asStmt())
+ os << "Execution continues on line "
+ << getSourceManager().getExpansionLineNumber(Loc.asLocation())
+ << '.';
+ else {
+ os << "Execution jumps to the end of the ";
+ const Decl *D = N->getLocationContext()->getDecl();
+ if (isa<ObjCMethodDecl>(D))
+ os << "method";
+ else if (isa<FunctionDecl>(D))
+ os << "function";
+ else {
+ assert(isa<BlockDecl>(D));
+ os << "anonymous block";
+ }
+ os << '.';
+ }
+
+ return Loc;
+}
+
+static const Stmt *getEnclosingParent(const Stmt *S, const ParentMap &PM) {
+ if (isa<Expr>(S) && PM.isConsumedExpr(cast<Expr>(S)))
+ return PM.getParentIgnoreParens(S);
+
+ const Stmt *Parent = PM.getParentIgnoreParens(S);
+ if (!Parent)
+ return nullptr;
+
+ switch (Parent->getStmtClass()) {
+ case Stmt::ForStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::ObjCForCollectionStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ return Parent;
+ default:
+ break;
+ }
+
+ return nullptr;
+}
+
+static PathDiagnosticLocation
+getEnclosingStmtLocation(const Stmt *S, SourceManager &SMgr, const ParentMap &P,
+ const LocationContext *LC, bool allowNestedContexts) {
+ if (!S)
+ return {};
+
+ while (const Stmt *Parent = getEnclosingParent(S, P)) {
+ switch (Parent->getStmtClass()) {
+ case Stmt::BinaryOperatorClass: {
+ const auto *B = cast<BinaryOperator>(Parent);
+ if (B->isLogicalOp())
+ return PathDiagnosticLocation(allowNestedContexts ? B : S, SMgr, LC);
+ break;
+ }
+ case Stmt::CompoundStmtClass:
+ case Stmt::StmtExprClass:
+ return PathDiagnosticLocation(S, SMgr, LC);
+ case Stmt::ChooseExprClass:
+ // Similar to '?' if we are referring to condition, just have the edge
+ // point to the entire choose expression.
+ if (allowNestedContexts || cast<ChooseExpr>(Parent)->getCond() == S)
+ return PathDiagnosticLocation(Parent, SMgr, LC);
+ else
+ return PathDiagnosticLocation(S, SMgr, LC);
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass:
+ // For '?', if we are referring to condition, just have the edge point
+ // to the entire '?' expression.
+ if (allowNestedContexts ||
+ cast<AbstractConditionalOperator>(Parent)->getCond() == S)
+ return PathDiagnosticLocation(Parent, SMgr, LC);
+ else
+ return PathDiagnosticLocation(S, SMgr, LC);
+ case Stmt::CXXForRangeStmtClass:
+ if (cast<CXXForRangeStmt>(Parent)->getBody() == S)
+ return PathDiagnosticLocation(S, SMgr, LC);
+ break;
+ case Stmt::DoStmtClass:
+ return PathDiagnosticLocation(S, SMgr, LC);
+ case Stmt::ForStmtClass:
+ if (cast<ForStmt>(Parent)->getBody() == S)
+ return PathDiagnosticLocation(S, SMgr, LC);
+ break;
+ case Stmt::IfStmtClass:
+ if (cast<IfStmt>(Parent)->getCond() != S)
+ return PathDiagnosticLocation(S, SMgr, LC);
+ break;
+ case Stmt::ObjCForCollectionStmtClass:
+ if (cast<ObjCForCollectionStmt>(Parent)->getBody() == S)
+ return PathDiagnosticLocation(S, SMgr, LC);
+ break;
+ case Stmt::WhileStmtClass:
+ if (cast<WhileStmt>(Parent)->getCond() != S)
+ return PathDiagnosticLocation(S, SMgr, LC);
+ break;
+ default:
+ break;
+ }
+
+ S = Parent;
+ }
+
+ assert(S && "Cannot have null Stmt for PathDiagnosticLocation");
+
+ return PathDiagnosticLocation(S, SMgr, LC);
+}
+
+PathDiagnosticLocation
+PathDiagnosticBuilder::getEnclosingStmtLocation(const Stmt *S) {
+ assert(S && "Null Stmt passed to getEnclosingStmtLocation");
+ return ::getEnclosingStmtLocation(S, getSourceManager(), getParentMap(), LC,
+ /*allowNestedContexts=*/false);
+}
+
+//===----------------------------------------------------------------------===//
+// "Minimal" path diagnostic generation algorithm.
+//===----------------------------------------------------------------------===//
+using StackDiagPair =
+ std::pair<PathDiagnosticCallPiece *, const ExplodedNode *>;
+using StackDiagVector = SmallVector<StackDiagPair, 6>;
+
+static void updateStackPiecesWithMessage(PathDiagnosticPiece &P,
+ StackDiagVector &CallStack) {
+ // If the piece contains a special message, add it to all the call
+ // pieces on the active stack.
+ if (auto *ep = dyn_cast<PathDiagnosticEventPiece>(&P)) {
+ if (ep->hasCallStackHint())
+ for (const auto &I : CallStack) {
+ PathDiagnosticCallPiece *CP = I.first;
+ const ExplodedNode *N = I.second;
+ std::string stackMsg = ep->getCallStackMessage(N);
+
+ // The last message on the path to final bug is the most important
+ // one. Since we traverse the path backwards, do not add the message
+ // if one has been previously added.
+ if (!CP->hasCallStackMessage())
+ CP->setCallStackMessage(stackMsg);
+ }
+ }
+}
+
+static void CompactMacroExpandedPieces(PathPieces &path,
+ const SourceManager& SM);
+
+
+std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForSwitchOP(
+ const ExplodedNode *N,
+ const CFGBlock *Dst,
+ const SourceManager &SM,
+ const LocationContext *LC,
+ PathDiagnosticBuilder &PDB,
+ PathDiagnosticLocation &Start
+ ) {
+ // Figure out what case arm we took.
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ PathDiagnosticLocation End;
+
+ if (const Stmt *S = Dst->getLabel()) {
+ End = PathDiagnosticLocation(S, SM, LC);
+
+ switch (S->getStmtClass()) {
+ default:
+ os << "No cases match in the switch statement. "
+ "Control jumps to line "
+ << End.asLocation().getExpansionLineNumber();
+ break;
+ case Stmt::DefaultStmtClass:
+ os << "Control jumps to the 'default' case at line "
+ << End.asLocation().getExpansionLineNumber();
+ break;
+
+ case Stmt::CaseStmtClass: {
+ os << "Control jumps to 'case ";
+ const auto *Case = cast<CaseStmt>(S);
+ const Expr *LHS = Case->getLHS()->IgnoreParenCasts();
+
+ // Determine if it is an enum.
+ bool GetRawInt = true;
+
+ if (const auto *DR = dyn_cast<DeclRefExpr>(LHS)) {
+ // FIXME: Maybe this should be an assertion. Are there cases
+ // were it is not an EnumConstantDecl?
+ const auto *D = dyn_cast<EnumConstantDecl>(DR->getDecl());
+
+ if (D) {
+ GetRawInt = false;
+ os << *D;
+ }
+ }
+
+ if (GetRawInt)
+ os << LHS->EvaluateKnownConstInt(PDB.getASTContext());
+
+ os << ":' at line " << End.asLocation().getExpansionLineNumber();
+ break;
+ }
+ }
+ } else {
+ os << "'Default' branch taken. ";
+ End = PDB.ExecutionContinues(os, N);
+ }
+ return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
+ os.str());
+}
+
+
+std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForGotoOP(
+ const Stmt *S,
+ PathDiagnosticBuilder &PDB,
+ PathDiagnosticLocation &Start) {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ const PathDiagnosticLocation &End = PDB.getEnclosingStmtLocation(S);
+ os << "Control jumps to line " << End.asLocation().getExpansionLineNumber();
+ return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str());
+
+}
+
+std::shared_ptr<PathDiagnosticControlFlowPiece> generateDiagForBinaryOP(
+ const ExplodedNode *N,
+ const Stmt *T,
+ const CFGBlock *Src,
+ const CFGBlock *Dst,
+ const SourceManager &SM,
+ PathDiagnosticBuilder &PDB,
+ const LocationContext *LC) {
+ const auto *B = cast<BinaryOperator>(T);
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ os << "Left side of '";
+ PathDiagnosticLocation Start, End;
+
+ if (B->getOpcode() == BO_LAnd) {
+ os << "&&"
+ << "' is ";
+
+ if (*(Src->succ_begin() + 1) == Dst) {
+ os << "false";
+ End = PathDiagnosticLocation(B->getLHS(), SM, LC);
+ Start =
+ PathDiagnosticLocation::createOperatorLoc(B, SM);
+ } else {
+ os << "true";
+ Start = PathDiagnosticLocation(B->getLHS(), SM, LC);
+ End = PDB.ExecutionContinues(N);
+ }
+ } else {
+ assert(B->getOpcode() == BO_LOr);
+ os << "||"
+ << "' is ";
+
+ if (*(Src->succ_begin() + 1) == Dst) {
+ os << "false";
+ Start = PathDiagnosticLocation(B->getLHS(), SM, LC);
+ End = PDB.ExecutionContinues(N);
+ } else {
+ os << "true";
+ End = PathDiagnosticLocation(B->getLHS(), SM, LC);
+ Start =
+ PathDiagnosticLocation::createOperatorLoc(B, SM);
+ }
+ }
+ return std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
+ os.str());
+}
+
+void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE,
+ const SourceManager &SM,
+ PathDiagnosticBuilder &PDB,
+ PathDiagnostic &PD) {
+ const LocationContext *LC = N->getLocationContext();
+ const CFGBlock *Src = BE.getSrc();
+ const CFGBlock *Dst = BE.getDst();
+ const Stmt *T = Src->getTerminator();
+ if (!T)
+ return;
+
+ auto Start = PathDiagnosticLocation::createBegin(T, SM, LC);
+ switch (T->getStmtClass()) {
+ default:
+ break;
+
+ case Stmt::GotoStmtClass:
+ case Stmt::IndirectGotoStmtClass: {
+ if (const Stmt *S = PathDiagnosticLocation::getNextStmt(N))
+ PD.getActivePath().push_front(generateDiagForGotoOP(S, PDB, Start));
+ break;
+ }
+
+ case Stmt::SwitchStmtClass: {
+ PD.getActivePath().push_front(
+ generateDiagForSwitchOP(N, Dst, SM, LC, PDB, Start));
+ break;
+ }
+
+ case Stmt::BreakStmtClass:
+ case Stmt::ContinueStmtClass: {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
+ break;
+ }
+
+ // Determine control-flow for ternary '?'.
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass: {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+ os << "'?' condition is ";
+
+ if (*(Src->succ_begin() + 1) == Dst)
+ os << "false";
+ else
+ os << "true";
+
+ PathDiagnosticLocation End = PDB.ExecutionContinues(N);
+
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
+
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(Start, End, os.str()));
+ break;
+ }
+
+ // Determine control-flow for short-circuited '&&' and '||'.
+ case Stmt::BinaryOperatorClass: {
+ if (!PDB.supportsLogicalOpControlFlow())
+ break;
+
+ std::shared_ptr<PathDiagnosticControlFlowPiece> Diag =
+ generateDiagForBinaryOP(N, T, Src, Dst, SM, PDB, LC);
+ PD.getActivePath().push_front(Diag);
+ break;
+ }
+
+ case Stmt::DoStmtClass:
+ if (*(Src->succ_begin()) == Dst) {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+
+ os << "Loop condition is true. ";
+ PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
+
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
+
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
+ os.str()));
+ } else {
+ PathDiagnosticLocation End = PDB.ExecutionContinues(N);
+
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
+
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(
+ Start, End, "Loop condition is false. Exiting loop"));
+ }
+ break;
+
+ case Stmt::WhileStmtClass:
+ case Stmt::ForStmtClass:
+ if (*(Src->succ_begin() + 1) == Dst) {
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+
+ os << "Loop condition is false. ";
+ PathDiagnosticLocation End = PDB.ExecutionContinues(os, N);
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
+
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(Start, End,
+ os.str()));
+ } else {
+ PathDiagnosticLocation End = PDB.ExecutionContinues(N);
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
+
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(
+ Start, End, "Loop condition is true. Entering loop body"));
+ }
+
+ break;
+
+ case Stmt::IfStmtClass: {
+ PathDiagnosticLocation End = PDB.ExecutionContinues(N);
+
+ if (const Stmt *S = End.asStmt())
+ End = PDB.getEnclosingStmtLocation(S);
+
+ if (*(Src->succ_begin() + 1) == Dst)
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(
+ Start, End, "Taking false branch"));
+ else
+ PD.getActivePath().push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(
+ Start, End, "Taking true branch"));
+
+ break;
+ }
+ }
+}
+
+// Cone-of-influence: support the reverse propagation of "interesting" symbols
+// and values by tracing interesting calculations backwards through evaluated
+// expressions along a path. This is probably overly complicated, but the idea
+// is that if an expression computed an "interesting" value, the child
+// expressions are also likely to be "interesting" as well (which then
+// propagates to the values they in turn compute). This reverse propagation
+// is needed to track interesting correlations across function call boundaries,
+// where formal arguments bind to actual arguments, etc. This is also needed
+// because the constraint solver sometimes simplifies certain symbolic values
+// into constants when appropriate, and this complicates reasoning about
+// interesting values.
+using InterestingExprs = llvm::DenseSet<const Expr *>;
+
+static void reversePropagateIntererstingSymbols(BugReport &R,
+ InterestingExprs &IE,
+ const ProgramState *State,
+ const Expr *Ex,
+ const LocationContext *LCtx) {
+ SVal V = State->getSVal(Ex, LCtx);
+ if (!(R.isInteresting(V) || IE.count(Ex)))
+ return;
+
+ switch (Ex->getStmtClass()) {
+ default:
+ if (!isa<CastExpr>(Ex))
+ break;
+ LLVM_FALLTHROUGH;
+ case Stmt::BinaryOperatorClass:
+ case Stmt::UnaryOperatorClass: {
+ for (const Stmt *SubStmt : Ex->children()) {
+ if (const auto *child = dyn_cast_or_null<Expr>(SubStmt)) {
+ IE.insert(child);
+ SVal ChildV = State->getSVal(child, LCtx);
+ R.markInteresting(ChildV);
+ }
+ }
+ break;
+ }
+ }
+
+ R.markInteresting(V);
+}
+
+static void reversePropagateInterestingSymbols(BugReport &R,
+ InterestingExprs &IE,
+ const ProgramState *State,
+ const LocationContext *CalleeCtx)
+{
+ // FIXME: Handle non-CallExpr-based CallEvents.
+ const StackFrameContext *Callee = CalleeCtx->getStackFrame();
+ const Stmt *CallSite = Callee->getCallSite();
+ if (const auto *CE = dyn_cast_or_null<CallExpr>(CallSite)) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(CalleeCtx->getDecl())) {
+ FunctionDecl::param_const_iterator PI = FD->param_begin(),
+ PE = FD->param_end();
+ CallExpr::const_arg_iterator AI = CE->arg_begin(), AE = CE->arg_end();
+ for (; AI != AE && PI != PE; ++AI, ++PI) {
+ if (const Expr *ArgE = *AI) {
+ if (const ParmVarDecl *PD = *PI) {
+ Loc LV = State->getLValue(PD, CalleeCtx);
+ if (R.isInteresting(LV) || R.isInteresting(State->getRawSVal(LV)))
+ IE.insert(ArgE);
+ }
+ }
+ }
+ }
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Functions for determining if a loop was executed 0 times.
+//===----------------------------------------------------------------------===//
+
+static bool isLoop(const Stmt *Term) {
+ switch (Term->getStmtClass()) {
+ case Stmt::ForStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::ObjCForCollectionStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ return true;
+ default:
+ // Note that we intentionally do not include do..while here.
+ return false;
+ }
+}
+
+static bool isJumpToFalseBranch(const BlockEdge *BE) {
+ const CFGBlock *Src = BE->getSrc();
+ assert(Src->succ_size() == 2);
+ return (*(Src->succ_begin()+1) == BE->getDst());
+}
+
+static bool isContainedByStmt(ParentMap &PM, const Stmt *S, const Stmt *SubS) {
+ while (SubS) {
+ if (SubS == S)
+ return true;
+ SubS = PM.getParent(SubS);
+ }
+ return false;
+}
+
+static const Stmt *getStmtBeforeCond(ParentMap &PM, const Stmt *Term,
+ const ExplodedNode *N) {
+ while (N) {
+ Optional<StmtPoint> SP = N->getLocation().getAs<StmtPoint>();
+ if (SP) {
+ const Stmt *S = SP->getStmt();
+ if (!isContainedByStmt(PM, Term, S))
+ return S;
+ }
+ N = N->getFirstPred();
+ }
+ return nullptr;
+}
+
+static bool isInLoopBody(ParentMap &PM, const Stmt *S, const Stmt *Term) {
+ const Stmt *LoopBody = nullptr;
+ switch (Term->getStmtClass()) {
+ case Stmt::CXXForRangeStmtClass: {
+ const auto *FR = cast<CXXForRangeStmt>(Term);
+ if (isContainedByStmt(PM, FR->getInc(), S))
+ return true;
+ if (isContainedByStmt(PM, FR->getLoopVarStmt(), S))
+ return true;
+ LoopBody = FR->getBody();
+ break;
+ }
+ case Stmt::ForStmtClass: {
+ const auto *FS = cast<ForStmt>(Term);
+ if (isContainedByStmt(PM, FS->getInc(), S))
+ return true;
+ LoopBody = FS->getBody();
+ break;
+ }
+ case Stmt::ObjCForCollectionStmtClass: {
+ const auto *FC = cast<ObjCForCollectionStmt>(Term);
+ LoopBody = FC->getBody();
+ break;
+ }
+ case Stmt::WhileStmtClass:
+ LoopBody = cast<WhileStmt>(Term)->getBody();
+ break;
+ default:
+ return false;
+ }
+ return isContainedByStmt(PM, LoopBody, S);
+}
+
+/// Adds a sanitized control-flow diagnostic edge to a path.
+static void addEdgeToPath(PathPieces &path,
+ PathDiagnosticLocation &PrevLoc,
+ PathDiagnosticLocation NewLoc) {
+ if (!NewLoc.isValid())
+ return;
+
+ SourceLocation NewLocL = NewLoc.asLocation();
+ if (NewLocL.isInvalid())
+ return;
+
+ if (!PrevLoc.isValid() || !PrevLoc.asLocation().isValid()) {
+ PrevLoc = NewLoc;
+ return;
+ }
+
+ // Ignore self-edges, which occur when there are multiple nodes at the same
+ // statement.
+ if (NewLoc.asStmt() && NewLoc.asStmt() == PrevLoc.asStmt())
+ return;
+
+ path.push_front(
+ std::make_shared<PathDiagnosticControlFlowPiece>(NewLoc, PrevLoc));
+ PrevLoc = NewLoc;
+}
+
+/// A customized wrapper for CFGBlock::getTerminatorCondition()
+/// which returns the element for ObjCForCollectionStmts.
+static const Stmt *getTerminatorCondition(const CFGBlock *B) {
+ const Stmt *S = B->getTerminatorCondition();
+ if (const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(S))
+ return FS->getElement();
+ return S;
+}
+
+static const char StrEnteringLoop[] = "Entering loop body";
+static const char StrLoopBodyZero[] = "Loop body executed 0 times";
+static const char StrLoopRangeEmpty[] =
+ "Loop body skipped when range is empty";
+static const char StrLoopCollectionEmpty[] =
+ "Loop body skipped when collection is empty";
+
+static std::unique_ptr<FilesToLineNumsMap>
+findExecutedLines(SourceManager &SM, const ExplodedNode *N);
+
+/// Generate diagnostics for the node \p N,
+/// and write it into \p PD.
+/// \p AddPathEdges Whether diagnostic consumer can generate path arrows
+/// showing both row and column.
+static void generatePathDiagnosticsForNode(const ExplodedNode *N,
+ PathDiagnostic &PD,
+ PathDiagnosticLocation &PrevLoc,
+ PathDiagnosticBuilder &PDB,
+ LocationContextMap &LCM,
+ StackDiagVector &CallStack,
+ InterestingExprs &IE,
+ bool AddPathEdges) {
+ ProgramPoint P = N->getLocation();
+ const SourceManager& SM = PDB.getSourceManager();
+
+ // Have we encountered an entrance to a call? It may be
+ // the case that we have not encountered a matching
+ // call exit before this point. This means that the path
+ // terminated within the call itself.
+ if (auto CE = P.getAs<CallEnter>()) {
+
+ if (AddPathEdges) {
+ // Add an edge to the start of the function.
+ const StackFrameContext *CalleeLC = CE->getCalleeContext();
+ const Decl *D = CalleeLC->getDecl();
+ // Add the edge only when the callee has body. We jump to the beginning
+ // of the *declaration*, however we expect it to be followed by the
+ // body. This isn't the case for autosynthesized property accessors in
+ // Objective-C. No need for a similar extra check for CallExit points
+ // because the exit edge comes from a statement (i.e. return),
+ // not from declaration.
+ if (D->hasBody())
+ addEdgeToPath(PD.getActivePath(), PrevLoc,
+ PathDiagnosticLocation::createBegin(D, SM));
+ }
+
+ // Did we visit an entire call?
+ bool VisitedEntireCall = PD.isWithinCall();
+ PD.popActivePath();
+
+ PathDiagnosticCallPiece *C;
+ if (VisitedEntireCall) {
+ C = cast<PathDiagnosticCallPiece>(PD.getActivePath().front().get());
+ } else {
+ const Decl *Caller = CE->getLocationContext()->getDecl();
+ C = PathDiagnosticCallPiece::construct(PD.getActivePath(), Caller);
+
+ if (AddPathEdges) {
+ // Since we just transferred the path over to the call piece,
+ // reset the mapping from active to location context.
+ assert(PD.getActivePath().size() == 1 &&
+ PD.getActivePath().front().get() == C);
+ LCM[&PD.getActivePath()] = nullptr;
+ }
+
+ // Record the location context mapping for the path within
+ // the call.
+ assert(LCM[&C->path] == nullptr ||
+ LCM[&C->path] == CE->getCalleeContext());
+ LCM[&C->path] = CE->getCalleeContext();
+
+ // If this is the first item in the active path, record
+ // the new mapping from active path to location context.
+ const LocationContext *&NewLC = LCM[&PD.getActivePath()];
+ if (!NewLC)
+ NewLC = N->getLocationContext();
+
+ PDB.LC = NewLC;
+ }
+ C->setCallee(*CE, SM);
+
+ // Update the previous location in the active path.
+ PrevLoc = C->getLocation();
+
+ if (!CallStack.empty()) {
+ assert(CallStack.back().first == C);
+ CallStack.pop_back();
+ }
+ return;
+ }
+
+
+ if (AddPathEdges) {
+ // Query the location context here and the previous location
+ // as processing CallEnter may change the active path.
+ PDB.LC = N->getLocationContext();
+
+ // Record the mapping from the active path to the location
+ // context.
+ assert(!LCM[&PD.getActivePath()] || LCM[&PD.getActivePath()] == PDB.LC);
+ LCM[&PD.getActivePath()] = PDB.LC;
+ }
+
+ // Have we encountered an exit from a function call?
+ if (Optional<CallExitEnd> CE = P.getAs<CallExitEnd>()) {
+
+ // We are descending into a call (backwards). Construct
+ // a new call piece to contain the path pieces for that call.
+ auto C = PathDiagnosticCallPiece::construct(*CE, SM);
+ // Record the mapping from call piece to LocationContext.
+ LCM[&C->path] = CE->getCalleeContext();
+
+ if (AddPathEdges) {
+ const Stmt *S = CE->getCalleeContext()->getCallSite();
+ // Propagate the interesting symbols accordingly.
+ if (const auto *Ex = dyn_cast_or_null<Expr>(S)) {
+ reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
+ N->getState().get(), Ex,
+ N->getLocationContext());
+ }
+ // Add the edge to the return site.
+ addEdgeToPath(PD.getActivePath(), PrevLoc, C->callReturn);
+ PrevLoc.invalidate();
+ }
+
+ auto *P = C.get();
+ PD.getActivePath().push_front(std::move(C));
+
+ // Make the contents of the call the active path for now.
+ PD.pushActivePath(&P->path);
+ CallStack.push_back(StackDiagPair(P, N));
+ return;
+ }
+
+ if (auto PS = P.getAs<PostStmt>()) {
+ if (!AddPathEdges)
+ return;
+
+ // For expressions, make sure we propagate the
+ // interesting symbols correctly.
+ if (const Expr *Ex = PS->getStmtAs<Expr>())
+ reversePropagateIntererstingSymbols(*PDB.getBugReport(), IE,
+ N->getState().get(), Ex,
+ N->getLocationContext());
+
+ // Add an edge. If this is an ObjCForCollectionStmt do
+ // not add an edge here as it appears in the CFG both
+ // as a terminator and as a terminator condition.
+ if (!isa<ObjCForCollectionStmt>(PS->getStmt())) {
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation(PS->getStmt(), SM, PDB.LC);
+ addEdgeToPath(PD.getActivePath(), PrevLoc, L);
+ }
+
+ } else if (auto BE = P.getAs<BlockEdge>()) {
+
+ if (!AddPathEdges) {
+ generateMinimalDiagForBlockEdge(N, *BE, SM, PDB, PD);
+ return;
+ }
+
+ // Does this represent entering a call? If so, look at propagating
+ // interesting symbols across call boundaries.
+ if (const ExplodedNode *NextNode = N->getFirstPred()) {
+ const LocationContext *CallerCtx = NextNode->getLocationContext();
+ const LocationContext *CalleeCtx = PDB.LC;
+ if (CallerCtx != CalleeCtx && AddPathEdges) {
+ reversePropagateInterestingSymbols(*PDB.getBugReport(), IE,
+ N->getState().get(), CalleeCtx);
+ }
+ }
+
+ // Are we jumping to the head of a loop? Add a special diagnostic.
+ if (const Stmt *Loop = BE->getSrc()->getLoopTarget()) {
+ PathDiagnosticLocation L(Loop, SM, PDB.LC);
+ const Stmt *Body = nullptr;
+
+ if (const auto *FS = dyn_cast<ForStmt>(Loop))
+ Body = FS->getBody();
+ else if (const auto *WS = dyn_cast<WhileStmt>(Loop))
+ Body = WS->getBody();
+ else if (const auto *OFS = dyn_cast<ObjCForCollectionStmt>(Loop)) {
+ Body = OFS->getBody();
+ } else if (const auto *FRS = dyn_cast<CXXForRangeStmt>(Loop)) {
+ Body = FRS->getBody();
+ }
+ // do-while statements are explicitly excluded here
+
+ auto p = std::make_shared<PathDiagnosticEventPiece>(
+ L, "Looping back to the head "
+ "of the loop");
+ p->setPrunable(true);
+
+ addEdgeToPath(PD.getActivePath(), PrevLoc, p->getLocation());
+ PD.getActivePath().push_front(std::move(p));
+
+ if (const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
+ addEdgeToPath(PD.getActivePath(), PrevLoc,
+ PathDiagnosticLocation::createEndBrace(CS, SM));
+ }
+ }
+
+ const CFGBlock *BSrc = BE->getSrc();
+ ParentMap &PM = PDB.getParentMap();
+
+ if (const Stmt *Term = BSrc->getTerminator()) {
+ // Are we jumping past the loop body without ever executing the
+ // loop (because the condition was false)?
+ if (isLoop(Term)) {
+ const Stmt *TermCond = getTerminatorCondition(BSrc);
+ bool IsInLoopBody =
+ isInLoopBody(PM, getStmtBeforeCond(PM, TermCond, N), Term);
+
+ const char *str = nullptr;
+
+ if (isJumpToFalseBranch(&*BE)) {
+ if (!IsInLoopBody) {
+ if (isa<ObjCForCollectionStmt>(Term)) {
+ str = StrLoopCollectionEmpty;
+ } else if (isa<CXXForRangeStmt>(Term)) {
+ str = StrLoopRangeEmpty;
+ } else {
+ str = StrLoopBodyZero;
+ }
+ }
+ } else {
+ str = StrEnteringLoop;
+ }
+
+ if (str) {
+ PathDiagnosticLocation L(TermCond ? TermCond : Term, SM, PDB.LC);
+ auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
+ PE->setPrunable(true);
+ addEdgeToPath(PD.getActivePath(), PrevLoc,
+ PE->getLocation());
+ PD.getActivePath().push_front(std::move(PE));
+ }
+ } else if (isa<BreakStmt>(Term) || isa<ContinueStmt>(Term) ||
+ isa<GotoStmt>(Term)) {
+ PathDiagnosticLocation L(Term, SM, PDB.LC);
+ addEdgeToPath(PD.getActivePath(), PrevLoc, L);
+ }
+ }
+ }
+}
+
+static std::unique_ptr<PathDiagnostic>
+generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) {
+ BugType &BT = R->getBugType();
+ return llvm::make_unique<PathDiagnostic>(
+ R->getBugType().getCheckName(), R->getDeclWithIssue(),
+ R->getBugType().getName(), R->getDescription(),
+ R->getShortDescription(/*Fallback=*/false), BT.getCategory(),
+ R->getUniqueingLocation(), R->getUniqueingDecl(),
+ findExecutedLines(SM, R->getErrorNode()));
+}
+
+static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) {
+ if (!S)
+ return nullptr;
+
+ while (true) {
+ S = PM.getParentIgnoreParens(S);
+
+ if (!S)
+ break;
+
+ if (isa<FullExpr>(S) ||
+ isa<CXXBindTemporaryExpr>(S) ||
+ isa<SubstNonTypeTemplateParmExpr>(S))
+ continue;
+
+ break;
+ }
+
+ return S;
+}
+
+static bool isConditionForTerminator(const Stmt *S, const Stmt *Cond) {
+ switch (S->getStmtClass()) {
+ case Stmt::BinaryOperatorClass: {
+ const auto *BO = cast<BinaryOperator>(S);
+ if (!BO->isLogicalOp())
+ return false;
+ return BO->getLHS() == Cond || BO->getRHS() == Cond;
+ }
+ case Stmt::IfStmtClass:
+ return cast<IfStmt>(S)->getCond() == Cond;
+ case Stmt::ForStmtClass:
+ return cast<ForStmt>(S)->getCond() == Cond;
+ case Stmt::WhileStmtClass:
+ return cast<WhileStmt>(S)->getCond() == Cond;
+ case Stmt::DoStmtClass:
+ return cast<DoStmt>(S)->getCond() == Cond;
+ case Stmt::ChooseExprClass:
+ return cast<ChooseExpr>(S)->getCond() == Cond;
+ case Stmt::IndirectGotoStmtClass:
+ return cast<IndirectGotoStmt>(S)->getTarget() == Cond;
+ case Stmt::SwitchStmtClass:
+ return cast<SwitchStmt>(S)->getCond() == Cond;
+ case Stmt::BinaryConditionalOperatorClass:
+ return cast<BinaryConditionalOperator>(S)->getCond() == Cond;
+ case Stmt::ConditionalOperatorClass: {
+ const auto *CO = cast<ConditionalOperator>(S);
+ return CO->getCond() == Cond ||
+ CO->getLHS() == Cond ||
+ CO->getRHS() == Cond;
+ }
+ case Stmt::ObjCForCollectionStmtClass:
+ return cast<ObjCForCollectionStmt>(S)->getElement() == Cond;
+ case Stmt::CXXForRangeStmtClass: {
+ const auto *FRS = cast<CXXForRangeStmt>(S);
+ return FRS->getCond() == Cond || FRS->getRangeInit() == Cond;
+ }
+ default:
+ return false;
+ }
+}
+
+static bool isIncrementOrInitInForLoop(const Stmt *S, const Stmt *FL) {
+ if (const auto *FS = dyn_cast<ForStmt>(FL))
+ return FS->getInc() == S || FS->getInit() == S;
+ if (const auto *FRS = dyn_cast<CXXForRangeStmt>(FL))
+ return FRS->getInc() == S || FRS->getRangeStmt() == S ||
+ FRS->getLoopVarStmt() || FRS->getRangeInit() == S;
+ return false;
+}
+
+using OptimizedCallsSet = llvm::DenseSet<const PathDiagnosticCallPiece *>;
+
+/// Adds synthetic edges from top-level statements to their subexpressions.
+///
+/// This avoids a "swoosh" effect, where an edge from a top-level statement A
+/// points to a sub-expression B.1 that's not at the start of B. In these cases,
+/// we'd like to see an edge from A to B, then another one from B to B.1.
+static void addContextEdges(PathPieces &pieces, SourceManager &SM,
+ const ParentMap &PM, const LocationContext *LCtx) {
+ PathPieces::iterator Prev = pieces.end();
+ for (PathPieces::iterator I = pieces.begin(), E = Prev; I != E;
+ Prev = I, ++I) {
+ auto *Piece = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
+
+ if (!Piece)
+ continue;
+
+ PathDiagnosticLocation SrcLoc = Piece->getStartLocation();
+ SmallVector<PathDiagnosticLocation, 4> SrcContexts;
+
+ PathDiagnosticLocation NextSrcContext = SrcLoc;
+ const Stmt *InnerStmt = nullptr;
+ while (NextSrcContext.isValid() && NextSrcContext.asStmt() != InnerStmt) {
+ SrcContexts.push_back(NextSrcContext);
+ InnerStmt = NextSrcContext.asStmt();
+ NextSrcContext = getEnclosingStmtLocation(InnerStmt, SM, PM, LCtx,
+ /*allowNested=*/true);
+ }
+
+ // Repeatedly split the edge as necessary.
+ // This is important for nested logical expressions (||, &&, ?:) where we
+ // want to show all the levels of context.
+ while (true) {
+ const Stmt *Dst = Piece->getEndLocation().getStmtOrNull();
+
+ // We are looking at an edge. Is the destination within a larger
+ // expression?
+ PathDiagnosticLocation DstContext =
+ getEnclosingStmtLocation(Dst, SM, PM, LCtx, /*allowNested=*/true);
+ if (!DstContext.isValid() || DstContext.asStmt() == Dst)
+ break;
+
+ // If the source is in the same context, we're already good.
+ if (std::find(SrcContexts.begin(), SrcContexts.end(), DstContext) !=
+ SrcContexts.end())
+ break;
+
+ // Update the subexpression node to point to the context edge.
+ Piece->setStartLocation(DstContext);
+
+ // Try to extend the previous edge if it's at the same level as the source
+ // context.
+ if (Prev != E) {
+ auto *PrevPiece = dyn_cast<PathDiagnosticControlFlowPiece>(Prev->get());
+
+ if (PrevPiece) {
+ if (const Stmt *PrevSrc =
+ PrevPiece->getStartLocation().getStmtOrNull()) {
+ const Stmt *PrevSrcParent = getStmtParent(PrevSrc, PM);
+ if (PrevSrcParent ==
+ getStmtParent(DstContext.getStmtOrNull(), PM)) {
+ PrevPiece->setEndLocation(DstContext);
+ break;
+ }
+ }
+ }
+ }
+
+ // Otherwise, split the current edge into a context edge and a
+ // subexpression edge. Note that the context statement may itself have
+ // context.
+ auto P =
+ std::make_shared<PathDiagnosticControlFlowPiece>(SrcLoc, DstContext);
+ Piece = P.get();
+ I = pieces.insert(I, std::move(P));
+ }
+ }
+}
+
+/// Move edges from a branch condition to a branch target
+/// when the condition is simple.
+///
+/// This restructures some of the work of addContextEdges. That function
+/// creates edges this may destroy, but they work together to create a more
+/// aesthetically set of edges around branches. After the call to
+/// addContextEdges, we may have (1) an edge to the branch, (2) an edge from
+/// the branch to the branch condition, and (3) an edge from the branch
+/// condition to the branch target. We keep (1), but may wish to remove (2)
+/// and move the source of (3) to the branch if the branch condition is simple.
+static void simplifySimpleBranches(PathPieces &pieces) {
+ for (PathPieces::iterator I = pieces.begin(), E = pieces.end(); I != E; ++I) {
+ const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
+
+ if (!PieceI)
+ continue;
+
+ const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
+ const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
+
+ if (!s1Start || !s1End)
+ continue;
+
+ PathPieces::iterator NextI = I; ++NextI;
+ if (NextI == E)
+ break;
+
+ PathDiagnosticControlFlowPiece *PieceNextI = nullptr;
+
+ while (true) {
+ if (NextI == E)
+ break;
+
+ const auto *EV = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
+ if (EV) {
+ StringRef S = EV->getString();
+ if (S == StrEnteringLoop || S == StrLoopBodyZero ||
+ S == StrLoopCollectionEmpty || S == StrLoopRangeEmpty) {
+ ++NextI;
+ continue;
+ }
+ break;
+ }
+
+ PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
+ break;
+ }
+
+ if (!PieceNextI)
+ continue;
+
+ const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
+ const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
+
+ if (!s2Start || !s2End || s1End != s2Start)
+ continue;
+
+ // We only perform this transformation for specific branch kinds.
+ // We don't want to do this for do..while, for example.
+ if (!(isa<ForStmt>(s1Start) || isa<WhileStmt>(s1Start) ||
+ isa<IfStmt>(s1Start) || isa<ObjCForCollectionStmt>(s1Start) ||
+ isa<CXXForRangeStmt>(s1Start)))
+ continue;
+
+ // Is s1End the branch condition?
+ if (!isConditionForTerminator(s1Start, s1End))
+ continue;
+
+ // Perform the hoisting by eliminating (2) and changing the start
+ // location of (3).
+ PieceNextI->setStartLocation(PieceI->getStartLocation());
+ I = pieces.erase(I);
+ }
+}
+
+/// Returns the number of bytes in the given (character-based) SourceRange.
+///
+/// If the locations in the range are not on the same line, returns None.
+///
+/// Note that this does not do a precise user-visible character or column count.
+static Optional<size_t> getLengthOnSingleLine(SourceManager &SM,
+ SourceRange Range) {
+ SourceRange ExpansionRange(SM.getExpansionLoc(Range.getBegin()),
+ SM.getExpansionRange(Range.getEnd()).getEnd());
+
+ FileID FID = SM.getFileID(ExpansionRange.getBegin());
+ if (FID != SM.getFileID(ExpansionRange.getEnd()))
+ return None;
+
+ bool Invalid;
+ const llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, &Invalid);
+ if (Invalid)
+ return None;
+
+ unsigned BeginOffset = SM.getFileOffset(ExpansionRange.getBegin());
+ unsigned EndOffset = SM.getFileOffset(ExpansionRange.getEnd());
+ StringRef Snippet = Buffer->getBuffer().slice(BeginOffset, EndOffset);
+
+ // We're searching the raw bytes of the buffer here, which might include
+ // escaped newlines and such. That's okay; we're trying to decide whether the
+ // SourceRange is covering a large or small amount of space in the user's
+ // editor.
+ if (Snippet.find_first_of("\r\n") != StringRef::npos)
+ return None;
+
+ // This isn't Unicode-aware, but it doesn't need to be.
+ return Snippet.size();
+}
+
+/// \sa getLengthOnSingleLine(SourceManager, SourceRange)
+static Optional<size_t> getLengthOnSingleLine(SourceManager &SM,
+ const Stmt *S) {
+ return getLengthOnSingleLine(SM, S->getSourceRange());
+}
+
+/// Eliminate two-edge cycles created by addContextEdges().
+///
+/// Once all the context edges are in place, there are plenty of cases where
+/// there's a single edge from a top-level statement to a subexpression,
+/// followed by a single path note, and then a reverse edge to get back out to
+/// the top level. If the statement is simple enough, the subexpression edges
+/// just add noise and make it harder to understand what's going on.
+///
+/// This function only removes edges in pairs, because removing only one edge
+/// might leave other edges dangling.
+///
+/// This will not remove edges in more complicated situations:
+/// - if there is more than one "hop" leading to or from a subexpression.
+/// - if there is an inlined call between the edges instead of a single event.
+/// - if the whole statement is large enough that having subexpression arrows
+/// might be helpful.
+static void removeContextCycles(PathPieces &Path, SourceManager &SM) {
+ for (PathPieces::iterator I = Path.begin(), E = Path.end(); I != E; ) {
+ // Pattern match the current piece and its successor.
+ const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
+
+ if (!PieceI) {
+ ++I;
+ continue;
+ }
+
+ const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
+ const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
+
+ PathPieces::iterator NextI = I; ++NextI;
+ if (NextI == E)
+ break;
+
+ const auto *PieceNextI =
+ dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
+
+ if (!PieceNextI) {
+ if (isa<PathDiagnosticEventPiece>(NextI->get())) {
+ ++NextI;
+ if (NextI == E)
+ break;
+ PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
+ }
+
+ if (!PieceNextI) {
+ ++I;
+ continue;
+ }
+ }
+
+ const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
+ const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
+
+ if (s1Start && s2Start && s1Start == s2End && s2Start == s1End) {
+ const size_t MAX_SHORT_LINE_LENGTH = 80;
+ Optional<size_t> s1Length = getLengthOnSingleLine(SM, s1Start);
+ if (s1Length && *s1Length <= MAX_SHORT_LINE_LENGTH) {
+ Optional<size_t> s2Length = getLengthOnSingleLine(SM, s2Start);
+ if (s2Length && *s2Length <= MAX_SHORT_LINE_LENGTH) {
+ Path.erase(I);
+ I = Path.erase(NextI);
+ continue;
+ }
+ }
+ }
+
+ ++I;
+ }
+}
+
+/// Return true if X is contained by Y.
+static bool lexicalContains(ParentMap &PM, const Stmt *X, const Stmt *Y) {
+ while (X) {
+ if (X == Y)
+ return true;
+ X = PM.getParent(X);
+ }
+ return false;
+}
+
+// Remove short edges on the same line less than 3 columns in difference.
+static void removePunyEdges(PathPieces &path, SourceManager &SM,
+ ParentMap &PM) {
+ bool erased = false;
+
+ for (PathPieces::iterator I = path.begin(), E = path.end(); I != E;
+ erased ? I : ++I) {
+ erased = false;
+
+ const auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
+
+ if (!PieceI)
+ continue;
+
+ const Stmt *start = PieceI->getStartLocation().getStmtOrNull();
+ const Stmt *end = PieceI->getEndLocation().getStmtOrNull();
+
+ if (!start || !end)
+ continue;
+
+ const Stmt *endParent = PM.getParent(end);
+ if (!endParent)
+ continue;
+
+ if (isConditionForTerminator(end, endParent))
+ continue;
+
+ SourceLocation FirstLoc = start->getBeginLoc();
+ SourceLocation SecondLoc = end->getBeginLoc();
+
+ if (!SM.isWrittenInSameFile(FirstLoc, SecondLoc))
+ continue;
+ if (SM.isBeforeInTranslationUnit(SecondLoc, FirstLoc))
+ std::swap(SecondLoc, FirstLoc);
+
+ SourceRange EdgeRange(FirstLoc, SecondLoc);
+ Optional<size_t> ByteWidth = getLengthOnSingleLine(SM, EdgeRange);
+
+ // If the statements are on different lines, continue.
+ if (!ByteWidth)
+ continue;
+
+ const size_t MAX_PUNY_EDGE_LENGTH = 2;
+ if (*ByteWidth <= MAX_PUNY_EDGE_LENGTH) {
+ // FIXME: There are enough /bytes/ between the endpoints of the edge, but
+ // there might not be enough /columns/. A proper user-visible column count
+ // is probably too expensive, though.
+ I = path.erase(I);
+ erased = true;
+ continue;
+ }
+ }
+}
+
+static void removeIdenticalEvents(PathPieces &path) {
+ for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ++I) {
+ const auto *PieceI = dyn_cast<PathDiagnosticEventPiece>(I->get());
+
+ if (!PieceI)
+ continue;
+
+ PathPieces::iterator NextI = I; ++NextI;
+ if (NextI == E)
+ return;
+
+ const auto *PieceNextI = dyn_cast<PathDiagnosticEventPiece>(NextI->get());
+
+ if (!PieceNextI)
+ continue;
+
+ // Erase the second piece if it has the same exact message text.
+ if (PieceI->getString() == PieceNextI->getString()) {
+ path.erase(NextI);
+ }
+ }
+}
+
+static bool optimizeEdges(PathPieces &path, SourceManager &SM,
+ OptimizedCallsSet &OCS,
+ LocationContextMap &LCM) {
+ bool hasChanges = false;
+ const LocationContext *LC = LCM[&path];
+ assert(LC);
+ ParentMap &PM = LC->getParentMap();
+
+ for (PathPieces::iterator I = path.begin(), E = path.end(); I != E; ) {
+ // Optimize subpaths.
+ if (auto *CallI = dyn_cast<PathDiagnosticCallPiece>(I->get())) {
+ // Record the fact that a call has been optimized so we only do the
+ // effort once.
+ if (!OCS.count(CallI)) {
+ while (optimizeEdges(CallI->path, SM, OCS, LCM)) {}
+ OCS.insert(CallI);
+ }
+ ++I;
+ continue;
+ }
+
+ // Pattern match the current piece and its successor.
+ auto *PieceI = dyn_cast<PathDiagnosticControlFlowPiece>(I->get());
+
+ if (!PieceI) {
+ ++I;
+ continue;
+ }
+
+ const Stmt *s1Start = PieceI->getStartLocation().getStmtOrNull();
+ const Stmt *s1End = PieceI->getEndLocation().getStmtOrNull();
+ const Stmt *level1 = getStmtParent(s1Start, PM);
+ const Stmt *level2 = getStmtParent(s1End, PM);
+
+ PathPieces::iterator NextI = I; ++NextI;
+ if (NextI == E)
+ break;
+
+ const auto *PieceNextI = dyn_cast<PathDiagnosticControlFlowPiece>(NextI->get());
+
+ if (!PieceNextI) {
+ ++I;
+ continue;
+ }
+
+ const Stmt *s2Start = PieceNextI->getStartLocation().getStmtOrNull();
+ const Stmt *s2End = PieceNextI->getEndLocation().getStmtOrNull();
+ const Stmt *level3 = getStmtParent(s2Start, PM);
+ const Stmt *level4 = getStmtParent(s2End, PM);
+
+ // Rule I.
+ //
+ // If we have two consecutive control edges whose end/begin locations
+ // are at the same level (e.g. statements or top-level expressions within
+ // a compound statement, or siblings share a single ancestor expression),
+ // then merge them if they have no interesting intermediate event.
+ //
+ // For example:
+ //
+ // (1.1 -> 1.2) -> (1.2 -> 1.3) becomes (1.1 -> 1.3) because the common
+ // parent is '1'. Here 'x.y.z' represents the hierarchy of statements.
+ //
+ // NOTE: this will be limited later in cases where we add barriers
+ // to prevent this optimization.
+ if (level1 && level1 == level2 && level1 == level3 && level1 == level4) {
+ PieceI->setEndLocation(PieceNextI->getEndLocation());
+ path.erase(NextI);
+ hasChanges = true;
+ continue;
+ }
+
+ // Rule II.
+ //
+ // Eliminate edges between subexpressions and parent expressions
+ // when the subexpression is consumed.
+ //
+ // NOTE: this will be limited later in cases where we add barriers
+ // to prevent this optimization.
+ if (s1End && s1End == s2Start && level2) {
+ bool removeEdge = false;
+ // Remove edges into the increment or initialization of a
+ // loop that have no interleaving event. This means that
+ // they aren't interesting.
+ if (isIncrementOrInitInForLoop(s1End, level2))
+ removeEdge = true;
+ // Next only consider edges that are not anchored on
+ // the condition of a terminator. This are intermediate edges
+ // that we might want to trim.
+ else if (!isConditionForTerminator(level2, s1End)) {
+ // Trim edges on expressions that are consumed by
+ // the parent expression.
+ if (isa<Expr>(s1End) && PM.isConsumedExpr(cast<Expr>(s1End))) {
+ removeEdge = true;
+ }
+ // Trim edges where a lexical containment doesn't exist.
+ // For example:
+ //
+ // X -> Y -> Z
+ //
+ // If 'Z' lexically contains Y (it is an ancestor) and
+ // 'X' does not lexically contain Y (it is a descendant OR
+ // it has no lexical relationship at all) then trim.
+ //
+ // This can eliminate edges where we dive into a subexpression
+ // and then pop back out, etc.
+ else if (s1Start && s2End &&
+ lexicalContains(PM, s2Start, s2End) &&
+ !lexicalContains(PM, s1End, s1Start)) {
+ removeEdge = true;
+ }
+ // Trim edges from a subexpression back to the top level if the
+ // subexpression is on a different line.
+ //
+ // A.1 -> A -> B
+ // becomes
+ // A.1 -> B
+ //
+ // These edges just look ugly and don't usually add anything.
+ else if (s1Start && s2End &&
+ lexicalContains(PM, s1Start, s1End)) {
+ SourceRange EdgeRange(PieceI->getEndLocation().asLocation(),
+ PieceI->getStartLocation().asLocation());
+ if (!getLengthOnSingleLine(SM, EdgeRange).hasValue())
+ removeEdge = true;
+ }
+ }
+
+ if (removeEdge) {
+ PieceI->setEndLocation(PieceNextI->getEndLocation());
+ path.erase(NextI);
+ hasChanges = true;
+ continue;
+ }
+ }
+
+ // Optimize edges for ObjC fast-enumeration loops.
+ //
+ // (X -> collection) -> (collection -> element)
+ //
+ // becomes:
+ //
+ // (X -> element)
+ if (s1End == s2Start) {
+ const auto *FS = dyn_cast_or_null<ObjCForCollectionStmt>(level3);
+ if (FS && FS->getCollection()->IgnoreParens() == s2Start &&
+ s2End == FS->getElement()) {
+ PieceI->setEndLocation(PieceNextI->getEndLocation());
+ path.erase(NextI);
+ hasChanges = true;
+ continue;
+ }
+ }
+
+ // No changes at this index? Move to the next one.
+ ++I;
+ }
+
+ if (!hasChanges) {
+ // Adjust edges into subexpressions to make them more uniform
+ // and aesthetically pleasing.
+ addContextEdges(path, SM, PM, LC);
+ // Remove "cyclical" edges that include one or more context edges.
+ removeContextCycles(path, SM);
+ // Hoist edges originating from branch conditions to branches
+ // for simple branches.
+ simplifySimpleBranches(path);
+ // Remove any puny edges left over after primary optimization pass.
+ removePunyEdges(path, SM, PM);
+ // Remove identical events.
+ removeIdenticalEvents(path);
+ }
+
+ return hasChanges;
+}
+
+/// Drop the very first edge in a path, which should be a function entry edge.
+///
+/// If the first edge is not a function entry edge (say, because the first
+/// statement had an invalid source location), this function does nothing.
+// FIXME: We should just generate invalid edges anyway and have the optimizer
+// deal with them.
+static void dropFunctionEntryEdge(PathPieces &Path, LocationContextMap &LCM,
+ SourceManager &SM) {
+ const auto *FirstEdge =
+ dyn_cast<PathDiagnosticControlFlowPiece>(Path.front().get());
+ if (!FirstEdge)
+ return;
+
+ const Decl *D = LCM[&Path]->getDecl();
+ PathDiagnosticLocation EntryLoc = PathDiagnosticLocation::createBegin(D, SM);
+ if (FirstEdge->getStartLocation() != EntryLoc)
+ return;
+
+ Path.pop_front();
+}
+
+using VisitorsDiagnosticsTy = llvm::DenseMap<const ExplodedNode *,
+ std::vector<std::shared_ptr<PathDiagnosticPiece>>>;
+
+/// Populate executes lines with lines containing at least one diagnostics.
+static void updateExecutedLinesWithDiagnosticPieces(
+ PathDiagnostic &PD) {
+
+ PathPieces path = PD.path.flatten(/*ShouldFlattenMacros=*/true);
+ FilesToLineNumsMap &ExecutedLines = PD.getExecutedLines();
+
+ for (const auto &P : path) {
+ FullSourceLoc Loc = P->getLocation().asLocation().getExpansionLoc();
+ FileID FID = Loc.getFileID();
+ unsigned LineNo = Loc.getLineNumber();
+ assert(FID.isValid());
+ ExecutedLines[FID].insert(LineNo);
+ }
+}
+
+/// This function is responsible for generating diagnostic pieces that are
+/// *not* provided by bug report visitors.
+/// These diagnostics may differ depending on the consumer's settings,
+/// and are therefore constructed separately for each consumer.
+///
+/// There are two path diagnostics generation modes: with adding edges (used
+/// for plists) and without (used for HTML and text).
+/// When edges are added (\p ActiveScheme is Extensive),
+/// the path is modified to insert artificially generated
+/// edges.
+/// Otherwise, more detailed diagnostics is emitted for block edges, explaining
+/// the transitions in words.
+static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer(
+ PathDiagnosticConsumer::PathGenerationScheme ActiveScheme,
+ PathDiagnosticBuilder &PDB,
+ const ExplodedNode *ErrorNode,
+ const VisitorsDiagnosticsTy &VisitorsDiagnostics) {
+
+ bool GenerateDiagnostics = (ActiveScheme != PathDiagnosticConsumer::None);
+ bool AddPathEdges = (ActiveScheme == PathDiagnosticConsumer::Extensive);
+ SourceManager &SM = PDB.getSourceManager();
+ BugReport *R = PDB.getBugReport();
+ AnalyzerOptions &Opts = PDB.getBugReporter().getAnalyzerOptions();
+ StackDiagVector CallStack;
+ InterestingExprs IE;
+ LocationContextMap LCM;
+ std::unique_ptr<PathDiagnostic> PD = generateEmptyDiagnosticForReport(R, SM);
+
+ if (GenerateDiagnostics) {
+ auto EndNotes = VisitorsDiagnostics.find(ErrorNode);
+ std::shared_ptr<PathDiagnosticPiece> LastPiece;
+ if (EndNotes != VisitorsDiagnostics.end()) {
+ assert(!EndNotes->second.empty());
+ LastPiece = EndNotes->second[0];
+ } else {
+ LastPiece = BugReporterVisitor::getDefaultEndPath(PDB, ErrorNode, *R);
+ }
+ PD->setEndOfPath(LastPiece);
+ }
+
+ PathDiagnosticLocation PrevLoc = PD->getLocation();
+ const ExplodedNode *NextNode = ErrorNode->getFirstPred();
+ while (NextNode) {
+ if (GenerateDiagnostics)
+ generatePathDiagnosticsForNode(
+ NextNode, *PD, PrevLoc, PDB, LCM, CallStack, IE, AddPathEdges);
+
+ auto VisitorNotes = VisitorsDiagnostics.find(NextNode);
+ NextNode = NextNode->getFirstPred();
+ if (!GenerateDiagnostics || VisitorNotes == VisitorsDiagnostics.end())
+ continue;
+
+ // This is a workaround due to inability to put shared PathDiagnosticPiece
+ // into a FoldingSet.
+ std::set<llvm::FoldingSetNodeID> DeduplicationSet;
+
+ // Add pieces from custom visitors.
+ for (const auto &Note : VisitorNotes->second) {
+ llvm::FoldingSetNodeID ID;
+ Note->Profile(ID);
+ auto P = DeduplicationSet.insert(ID);
+ if (!P.second)
+ continue;
+
+ if (AddPathEdges)
+ addEdgeToPath(PD->getActivePath(), PrevLoc, Note->getLocation());
+ updateStackPiecesWithMessage(*Note, CallStack);
+ PD->getActivePath().push_front(Note);
+ }
+ }
+
+ if (AddPathEdges) {
+ // Add an edge to the start of the function.
+ // We'll prune it out later, but it helps make diagnostics more uniform.
+ const StackFrameContext *CalleeLC = PDB.LC->getStackFrame();
+ const Decl *D = CalleeLC->getDecl();
+ addEdgeToPath(PD->getActivePath(), PrevLoc,
+ PathDiagnosticLocation::createBegin(D, SM));
+ }
+
+
+ // Finally, prune the diagnostic path of uninteresting stuff.
+ if (!PD->path.empty()) {
+ if (R->shouldPrunePath() && Opts.ShouldPrunePaths) {
+ bool stillHasNotes =
+ removeUnneededCalls(PD->getMutablePieces(), R, LCM);
+ assert(stillHasNotes);
+ (void)stillHasNotes;
+ }
+
+ // Redirect all call pieces to have valid locations.
+ adjustCallLocations(PD->getMutablePieces());
+ removePiecesWithInvalidLocations(PD->getMutablePieces());
+
+ if (AddPathEdges) {
+
+ // Reduce the number of edges from a very conservative set
+ // to an aesthetically pleasing subset that conveys the
+ // necessary information.
+ OptimizedCallsSet OCS;
+ while (optimizeEdges(PD->getMutablePieces(), SM, OCS, LCM)) {}
+
+ // Drop the very first function-entry edge. It's not really necessary
+ // for top-level functions.
+ dropFunctionEntryEdge(PD->getMutablePieces(), LCM, SM);
+ }
+
+ // Remove messages that are basically the same, and edges that may not
+ // make sense.
+ // We have to do this after edge optimization in the Extensive mode.
+ removeRedundantMsgs(PD->getMutablePieces());
+ removeEdgesToDefaultInitializers(PD->getMutablePieces());
+ }
+
+ if (GenerateDiagnostics && Opts.ShouldDisplayMacroExpansions)
+ CompactMacroExpandedPieces(PD->getMutablePieces(), SM);
+
+ return PD;
+}
+
+
+//===----------------------------------------------------------------------===//
+// Methods for BugType and subclasses.
+//===----------------------------------------------------------------------===//
+
+void BugType::anchor() {}
+
+void BuiltinBug::anchor() {}
+
+//===----------------------------------------------------------------------===//
+// Methods for BugReport and subclasses.
+//===----------------------------------------------------------------------===//
+
+void BugReport::NodeResolver::anchor() {}
+
+void BugReport::addVisitor(std::unique_ptr<BugReporterVisitor> visitor) {
+ if (!visitor)
+ return;
+
+ llvm::FoldingSetNodeID ID;
+ visitor->Profile(ID);
+
+ void *InsertPos = nullptr;
+ if (CallbacksSet.FindNodeOrInsertPos(ID, InsertPos)) {
+ return;
+ }
+
+ Callbacks.push_back(std::move(visitor));
+}
+
+void BugReport::clearVisitors() {
+ Callbacks.clear();
+}
+
+BugReport::~BugReport() {
+ while (!interestingSymbols.empty()) {
+ popInterestingSymbolsAndRegions();
+ }
+}
+
+const Decl *BugReport::getDeclWithIssue() const {
+ if (DeclWithIssue)
+ return DeclWithIssue;
+
+ const ExplodedNode *N = getErrorNode();
+ if (!N)
+ return nullptr;
+
+ const LocationContext *LC = N->getLocationContext();
+ return LC->getStackFrame()->getDecl();
+}
+
+void BugReport::Profile(llvm::FoldingSetNodeID& hash) const {
+ hash.AddPointer(&BT);
+ hash.AddString(Description);
+ PathDiagnosticLocation UL = getUniqueingLocation();
+ if (UL.isValid()) {
+ UL.Profile(hash);
+ } else if (Location.isValid()) {
+ Location.Profile(hash);
+ } else {
+ assert(ErrorNode);
+ hash.AddPointer(GetCurrentOrPreviousStmt(ErrorNode));
+ }
+
+ for (SourceRange range : Ranges) {
+ if (!range.isValid())
+ continue;
+ hash.AddInteger(range.getBegin().getRawEncoding());
+ hash.AddInteger(range.getEnd().getRawEncoding());
+ }
+}
+
+void BugReport::markInteresting(SymbolRef sym) {
+ if (!sym)
+ return;
+
+ getInterestingSymbols().insert(sym);
+
+ if (const auto *meta = dyn_cast<SymbolMetadata>(sym))
+ getInterestingRegions().insert(meta->getRegion());
+}
+
+void BugReport::markInteresting(const MemRegion *R) {
+ if (!R)
+ return;
+
+ R = R->getBaseRegion();
+ getInterestingRegions().insert(R);
+
+ if (const auto *SR = dyn_cast<SymbolicRegion>(R))
+ getInterestingSymbols().insert(SR->getSymbol());
+}
+
+void BugReport::markInteresting(SVal V) {
+ markInteresting(V.getAsRegion());
+ markInteresting(V.getAsSymbol());
+}
+
+void BugReport::markInteresting(const LocationContext *LC) {
+ if (!LC)
+ return;
+ InterestingLocationContexts.insert(LC);
+}
+
+bool BugReport::isInteresting(SVal V) {
+ return isInteresting(V.getAsRegion()) || isInteresting(V.getAsSymbol());
+}
+
+bool BugReport::isInteresting(SymbolRef sym) {
+ if (!sym)
+ return false;
+ // We don't currently consider metadata symbols to be interesting
+ // even if we know their region is interesting. Is that correct behavior?
+ return getInterestingSymbols().count(sym);
+}
+
+bool BugReport::isInteresting(const MemRegion *R) {
+ if (!R)
+ return false;
+ R = R->getBaseRegion();
+ bool b = getInterestingRegions().count(R);
+ if (b)
+ return true;
+ if (const auto *SR = dyn_cast<SymbolicRegion>(R))
+ return getInterestingSymbols().count(SR->getSymbol());
+ return false;
+}
+
+bool BugReport::isInteresting(const LocationContext *LC) {
+ if (!LC)
+ return false;
+ return InterestingLocationContexts.count(LC);
+}
+
+void BugReport::lazyInitializeInterestingSets() {
+ if (interestingSymbols.empty()) {
+ interestingSymbols.push_back(new Symbols());
+ interestingRegions.push_back(new Regions());
+ }
+}
+
+BugReport::Symbols &BugReport::getInterestingSymbols() {
+ lazyInitializeInterestingSets();
+ return *interestingSymbols.back();
+}
+
+BugReport::Regions &BugReport::getInterestingRegions() {
+ lazyInitializeInterestingSets();
+ return *interestingRegions.back();
+}
+
+void BugReport::pushInterestingSymbolsAndRegions() {
+ interestingSymbols.push_back(new Symbols(getInterestingSymbols()));
+ interestingRegions.push_back(new Regions(getInterestingRegions()));
+}
+
+void BugReport::popInterestingSymbolsAndRegions() {
+ delete interestingSymbols.pop_back_val();
+ delete interestingRegions.pop_back_val();
+}
+
+const Stmt *BugReport::getStmt() const {
+ if (!ErrorNode)
+ return nullptr;
+
+ ProgramPoint ProgP = ErrorNode->getLocation();
+ const Stmt *S = nullptr;
+
+ if (Optional<BlockEntrance> BE = ProgP.getAs<BlockEntrance>()) {
+ CFGBlock &Exit = ProgP.getLocationContext()->getCFG()->getExit();
+ if (BE->getBlock() == &Exit)
+ S = GetPreviousStmt(ErrorNode);
+ }
+ if (!S)
+ S = PathDiagnosticLocation::getStmt(ErrorNode);
+
+ return S;
+}
+
+llvm::iterator_range<BugReport::ranges_iterator> BugReport::getRanges() {
+ // If no custom ranges, add the range of the statement corresponding to
+ // the error node.
+ if (Ranges.empty()) {
+ if (const auto *E = dyn_cast_or_null<Expr>(getStmt()))
+ addRange(E->getSourceRange());
+ else
+ return llvm::make_range(ranges_iterator(), ranges_iterator());
+ }
+
+ // User-specified absence of range info.
+ if (Ranges.size() == 1 && !Ranges.begin()->isValid())
+ return llvm::make_range(ranges_iterator(), ranges_iterator());
+
+ return llvm::make_range(Ranges.begin(), Ranges.end());
+}
+
+PathDiagnosticLocation BugReport::getLocation(const SourceManager &SM) const {
+ if (ErrorNode) {
+ assert(!Location.isValid() &&
+ "Either Location or ErrorNode should be specified but not both.");
+ return PathDiagnosticLocation::createEndOfPath(ErrorNode, SM);
+ }
+
+ assert(Location.isValid());
+ return Location;
+}
+
+//===----------------------------------------------------------------------===//
+// Methods for BugReporter and subclasses.
+//===----------------------------------------------------------------------===//
+
+BugReportEquivClass::~BugReportEquivClass() = default;
+
+GRBugReporter::~GRBugReporter() = default;
+
+BugReporterData::~BugReporterData() = default;
+
+ExplodedGraph &GRBugReporter::getGraph() { return Eng.getGraph(); }
+
+ProgramStateManager&
+GRBugReporter::getStateManager() { return Eng.getStateManager(); }
+
+BugReporter::~BugReporter() {
+ FlushReports();
+
+ // Free the bug reports we are tracking.
+ for (const auto I : EQClassesVector)
+ delete I;
+}
+
+void BugReporter::FlushReports() {
+ if (BugTypes.isEmpty())
+ return;
+
+ // We need to flush reports in deterministic order to ensure the order
+ // of the reports is consistent between runs.
+ for (const auto EQ : EQClassesVector)
+ FlushReport(*EQ);
+
+ // BugReporter owns and deletes only BugTypes created implicitly through
+ // EmitBasicReport.
+ // FIXME: There are leaks from checkers that assume that the BugTypes they
+ // create will be destroyed by the BugReporter.
+ llvm::DeleteContainerSeconds(StrBugTypes);
+
+ // Remove all references to the BugType objects.
+ BugTypes = F.getEmptySet();
+}
+
+//===----------------------------------------------------------------------===//
+// PathDiagnostics generation.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+/// A wrapper around a report graph, which contains only a single path, and its
+/// node maps.
+class ReportGraph {
+public:
+ InterExplodedGraphMap BackMap;
+ std::unique_ptr<ExplodedGraph> Graph;
+ const ExplodedNode *ErrorNode;
+ size_t Index;
+};
+
+/// A wrapper around a trimmed graph and its node maps.
+class TrimmedGraph {
+ InterExplodedGraphMap InverseMap;
+
+ using PriorityMapTy = llvm::DenseMap<const ExplodedNode *, unsigned>;
+
+ PriorityMapTy PriorityMap;
+
+ using NodeIndexPair = std::pair<const ExplodedNode *, size_t>;
+
+ SmallVector<NodeIndexPair, 32> ReportNodes;
+
+ std::unique_ptr<ExplodedGraph> G;
+
+ /// A helper class for sorting ExplodedNodes by priority.
+ template <bool Descending>
+ class PriorityCompare {
+ const PriorityMapTy &PriorityMap;
+
+ public:
+ PriorityCompare(const PriorityMapTy &M) : PriorityMap(M) {}
+
+ bool operator()(const ExplodedNode *LHS, const ExplodedNode *RHS) const {
+ PriorityMapTy::const_iterator LI = PriorityMap.find(LHS);
+ PriorityMapTy::const_iterator RI = PriorityMap.find(RHS);
+ PriorityMapTy::const_iterator E = PriorityMap.end();
+
+ if (LI == E)
+ return Descending;
+ if (RI == E)
+ return !Descending;
+
+ return Descending ? LI->second > RI->second
+ : LI->second < RI->second;
+ }
+
+ bool operator()(const NodeIndexPair &LHS, const NodeIndexPair &RHS) const {
+ return (*this)(LHS.first, RHS.first);
+ }
+ };
+
+public:
+ TrimmedGraph(const ExplodedGraph *OriginalGraph,
+ ArrayRef<const ExplodedNode *> Nodes);
+
+ bool popNextReportGraph(ReportGraph &GraphWrapper);
+};
+
+} // namespace
+
+TrimmedGraph::TrimmedGraph(const ExplodedGraph *OriginalGraph,
+ ArrayRef<const ExplodedNode *> Nodes) {
+ // The trimmed graph is created in the body of the constructor to ensure
+ // that the DenseMaps have been initialized already.
+ InterExplodedGraphMap ForwardMap;
+ G = OriginalGraph->trim(Nodes, &ForwardMap, &InverseMap);
+
+ // Find the (first) error node in the trimmed graph. We just need to consult
+ // the node map which maps from nodes in the original graph to nodes
+ // in the new graph.
+ llvm::SmallPtrSet<const ExplodedNode *, 32> RemainingNodes;
+
+ for (unsigned i = 0, count = Nodes.size(); i < count; ++i) {
+ if (const ExplodedNode *NewNode = ForwardMap.lookup(Nodes[i])) {
+ ReportNodes.push_back(std::make_pair(NewNode, i));
+ RemainingNodes.insert(NewNode);
+ }
+ }
+
+ assert(!RemainingNodes.empty() && "No error node found in the trimmed graph");
+
+ // Perform a forward BFS to find all the shortest paths.
+ std::queue<const ExplodedNode *> WS;
+
+ assert(G->num_roots() == 1);
+ WS.push(*G->roots_begin());
+ unsigned Priority = 0;
+
+ while (!WS.empty()) {
+ const ExplodedNode *Node = WS.front();
+ WS.pop();
+
+ PriorityMapTy::iterator PriorityEntry;
+ bool IsNew;
+ std::tie(PriorityEntry, IsNew) =
+ PriorityMap.insert(std::make_pair(Node, Priority));
+ ++Priority;
+
+ if (!IsNew) {
+ assert(PriorityEntry->second <= Priority);
+ continue;
+ }
+
+ if (RemainingNodes.erase(Node))
+ if (RemainingNodes.empty())
+ break;
+
+ for (ExplodedNode::const_pred_iterator I = Node->succ_begin(),
+ E = Node->succ_end();
+ I != E; ++I)
+ WS.push(*I);
+ }
+
+ // Sort the error paths from longest to shortest.
+ llvm::sort(ReportNodes, PriorityCompare<true>(PriorityMap));
+}
+
+bool TrimmedGraph::popNextReportGraph(ReportGraph &GraphWrapper) {
+ if (ReportNodes.empty())
+ return false;
+
+ const ExplodedNode *OrigN;
+ std::tie(OrigN, GraphWrapper.Index) = ReportNodes.pop_back_val();
+ assert(PriorityMap.find(OrigN) != PriorityMap.end() &&
+ "error node not accessible from root");
+
+ // Create a new graph with a single path. This is the graph
+ // that will be returned to the caller.
+ auto GNew = llvm::make_unique<ExplodedGraph>();
+ GraphWrapper.BackMap.clear();
+
+ // Now walk from the error node up the BFS path, always taking the
+ // predeccessor with the lowest number.
+ ExplodedNode *Succ = nullptr;
+ while (true) {
+ // Create the equivalent node in the new graph with the same state
+ // and location.
+ ExplodedNode *NewN = GNew->createUncachedNode(OrigN->getLocation(), OrigN->getState(),
+ OrigN->isSink());
+
+ // Store the mapping to the original node.
+ InterExplodedGraphMap::const_iterator IMitr = InverseMap.find(OrigN);
+ assert(IMitr != InverseMap.end() && "No mapping to original node.");
+ GraphWrapper.BackMap[NewN] = IMitr->second;
+
+ // Link up the new node with the previous node.
+ if (Succ)
+ Succ->addPredecessor(NewN, *GNew);
+ else
+ GraphWrapper.ErrorNode = NewN;
+
+ Succ = NewN;
+
+ // Are we at the final node?
+ if (OrigN->pred_empty()) {
+ GNew->addRoot(NewN);
+ break;
+ }
+
+ // Find the next predeccessor node. We choose the node that is marked
+ // with the lowest BFS number.
+ OrigN = *std::min_element(OrigN->pred_begin(), OrigN->pred_end(),
+ PriorityCompare<false>(PriorityMap));
+ }
+
+ GraphWrapper.Graph = std::move(GNew);
+
+ return true;
+}
+
+/// CompactMacroExpandedPieces - This function postprocesses a PathDiagnostic
+/// object and collapses PathDiagosticPieces that are expanded by macros.
+static void CompactMacroExpandedPieces(PathPieces &path,
+ const SourceManager& SM) {
+ using MacroStackTy =
+ std::vector<
+ std::pair<std::shared_ptr<PathDiagnosticMacroPiece>, SourceLocation>>;
+
+ using PiecesTy = std::vector<std::shared_ptr<PathDiagnosticPiece>>;
+
+ MacroStackTy MacroStack;
+ PiecesTy Pieces;
+
+ for (PathPieces::const_iterator I = path.begin(), E = path.end();
+ I != E; ++I) {
+ const auto &piece = *I;
+
+ // Recursively compact calls.
+ if (auto *call = dyn_cast<PathDiagnosticCallPiece>(&*piece)) {
+ CompactMacroExpandedPieces(call->path, SM);
+ }
+
+ // Get the location of the PathDiagnosticPiece.
+ const FullSourceLoc Loc = piece->getLocation().asLocation();
+
+ // Determine the instantiation location, which is the location we group
+ // related PathDiagnosticPieces.
+ SourceLocation InstantiationLoc = Loc.isMacroID() ?
+ SM.getExpansionLoc(Loc) :
+ SourceLocation();
+
+ if (Loc.isFileID()) {
+ MacroStack.clear();
+ Pieces.push_back(piece);
+ continue;
+ }
+
+ assert(Loc.isMacroID());
+
+ // Is the PathDiagnosticPiece within the same macro group?
+ if (!MacroStack.empty() && InstantiationLoc == MacroStack.back().second) {
+ MacroStack.back().first->subPieces.push_back(piece);
+ continue;
+ }
+
+ // We aren't in the same group. Are we descending into a new macro
+ // or are part of an old one?
+ std::shared_ptr<PathDiagnosticMacroPiece> MacroGroup;
+
+ SourceLocation ParentInstantiationLoc = InstantiationLoc.isMacroID() ?
+ SM.getExpansionLoc(Loc) :
+ SourceLocation();
+
+ // Walk the entire macro stack.
+ while (!MacroStack.empty()) {
+ if (InstantiationLoc == MacroStack.back().second) {
+ MacroGroup = MacroStack.back().first;
+ break;
+ }
+
+ if (ParentInstantiationLoc == MacroStack.back().second) {
+ MacroGroup = MacroStack.back().first;
+ break;
+ }
+
+ MacroStack.pop_back();
+ }
+
+ if (!MacroGroup || ParentInstantiationLoc == MacroStack.back().second) {
+ // Create a new macro group and add it to the stack.
+ auto NewGroup = std::make_shared<PathDiagnosticMacroPiece>(
+ PathDiagnosticLocation::createSingleLocation(piece->getLocation()));
+
+ if (MacroGroup)
+ MacroGroup->subPieces.push_back(NewGroup);
+ else {
+ assert(InstantiationLoc.isFileID());
+ Pieces.push_back(NewGroup);
+ }
+
+ MacroGroup = NewGroup;
+ MacroStack.push_back(std::make_pair(MacroGroup, InstantiationLoc));
+ }
+
+ // Finally, add the PathDiagnosticPiece to the group.
+ MacroGroup->subPieces.push_back(piece);
+ }
+
+ // Now take the pieces and construct a new PathDiagnostic.
+ path.clear();
+
+ path.insert(path.end(), Pieces.begin(), Pieces.end());
+}
+
+/// Generate notes from all visitors.
+/// Notes associated with {@code ErrorNode} are generated using
+/// {@code getEndPath}, and the rest are generated with {@code VisitNode}.
+static std::unique_ptr<VisitorsDiagnosticsTy>
+generateVisitorsDiagnostics(BugReport *R, const ExplodedNode *ErrorNode,
+ BugReporterContext &BRC) {
+ auto Notes = llvm::make_unique<VisitorsDiagnosticsTy>();
+ BugReport::VisitorList visitors;
+
+ // Run visitors on all nodes starting from the node *before* the last one.
+ // The last node is reserved for notes generated with {@code getEndPath}.
+ const ExplodedNode *NextNode = ErrorNode->getFirstPred();
+ while (NextNode) {
+
+ // At each iteration, move all visitors from report to visitor list.
+ for (BugReport::visitor_iterator I = R->visitor_begin(),
+ E = R->visitor_end();
+ I != E; ++I) {
+ visitors.push_back(std::move(*I));
+ }
+ R->clearVisitors();
+
+ const ExplodedNode *Pred = NextNode->getFirstPred();
+ if (!Pred) {
+ std::shared_ptr<PathDiagnosticPiece> LastPiece;
+ for (auto &V : visitors) {
+ V->finalizeVisitor(BRC, ErrorNode, *R);
+
+ if (auto Piece = V->getEndPath(BRC, ErrorNode, *R)) {
+ assert(!LastPiece &&
+ "There can only be one final piece in a diagnostic.");
+ LastPiece = std::move(Piece);
+ (*Notes)[ErrorNode].push_back(LastPiece);
+ }
+ }
+ break;
+ }
+
+ for (auto &V : visitors) {
+ auto P = V->VisitNode(NextNode, BRC, *R);
+ if (P)
+ (*Notes)[NextNode].push_back(std::move(P));
+ }
+
+ if (!R->isValid())
+ break;
+
+ NextNode = Pred;
+ }
+
+ return Notes;
+}
+
+/// Find a non-invalidated report for a given equivalence class,
+/// and return together with a cache of visitors notes.
+/// If none found, return a nullptr paired with an empty cache.
+static
+std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport(
+ TrimmedGraph &TrimG,
+ ReportGraph &ErrorGraph,
+ ArrayRef<BugReport *> &bugReports,
+ AnalyzerOptions &Opts,
+ GRBugReporter &Reporter) {
+
+ while (TrimG.popNextReportGraph(ErrorGraph)) {
+ // Find the BugReport with the original location.
+ assert(ErrorGraph.Index < bugReports.size());
+ BugReport *R = bugReports[ErrorGraph.Index];
+ assert(R && "No original report found for sliced graph.");
+ assert(R->isValid() && "Report selected by trimmed graph marked invalid.");
+ const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode;
+
+ // Register refutation visitors first, if they mark the bug invalid no
+ // further analysis is required
+ R->addVisitor(llvm::make_unique<LikelyFalsePositiveSuppressionBRVisitor>());
+
+ // Register additional node visitors.
+ R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>());
+ R->addVisitor(llvm::make_unique<ConditionBRVisitor>());
+ R->addVisitor(llvm::make_unique<CXXSelfAssignmentBRVisitor>());
+
+ BugReporterContext BRC(Reporter, ErrorGraph.BackMap);
+
+ // Run all visitors on a given graph, once.
+ std::unique_ptr<VisitorsDiagnosticsTy> visitorNotes =
+ generateVisitorsDiagnostics(R, ErrorNode, BRC);
+
+ if (R->isValid()) {
+ if (Opts.ShouldCrosscheckWithZ3) {
+ // If crosscheck is enabled, remove all visitors, add the refutation
+ // visitor and check again
+ R->clearVisitors();
+ R->addVisitor(llvm::make_unique<FalsePositiveRefutationBRVisitor>());
+
+ // We don't overrite the notes inserted by other visitors because the
+ // refutation manager does not add any new note to the path
+ generateVisitorsDiagnostics(R, ErrorGraph.ErrorNode, BRC);
+ }
+
+ // Check if the bug is still valid
+ if (R->isValid())
+ return std::make_pair(R, std::move(visitorNotes));
+ }
+ }
+
+ return std::make_pair(nullptr, llvm::make_unique<VisitorsDiagnosticsTy>());
+}
+
+std::unique_ptr<DiagnosticForConsumerMapTy>
+GRBugReporter::generatePathDiagnostics(
+ ArrayRef<PathDiagnosticConsumer *> consumers,
+ ArrayRef<BugReport *> &bugReports) {
+ assert(!bugReports.empty());
+
+ auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>();
+ bool HasValid = false;
+ SmallVector<const ExplodedNode *, 32> errorNodes;
+ for (const auto I : bugReports) {
+ if (I->isValid()) {
+ HasValid = true;
+ errorNodes.push_back(I->getErrorNode());
+ } else {
+ // Keep the errorNodes list in sync with the bugReports list.
+ errorNodes.push_back(nullptr);
+ }
+ }
+
+ // If all the reports have been marked invalid by a previous path generation,
+ // we're done.
+ if (!HasValid)
+ return Out;
+
+ TrimmedGraph TrimG(&getGraph(), errorNodes);
+ ReportGraph ErrorGraph;
+ auto ReportInfo = findValidReport(TrimG, ErrorGraph, bugReports,
+ getAnalyzerOptions(), *this);
+ BugReport *R = ReportInfo.first;
+
+ if (R && R->isValid()) {
+ const ExplodedNode *ErrorNode = ErrorGraph.ErrorNode;
+ for (PathDiagnosticConsumer *PC : consumers) {
+ PathDiagnosticBuilder PDB(*this, R, ErrorGraph.BackMap, PC);
+ std::unique_ptr<PathDiagnostic> PD = generatePathDiagnosticForConsumer(
+ PC->getGenerationScheme(), PDB, ErrorNode, *ReportInfo.second);
+ (*Out)[PC] = std::move(PD);
+ }
+ }
+
+ return Out;
+}
+
+void BugReporter::Register(BugType *BT) {
+ BugTypes = F.add(BugTypes, BT);
+}
+
+void BugReporter::emitReport(std::unique_ptr<BugReport> R) {
+ if (const ExplodedNode *E = R->getErrorNode()) {
+ // An error node must either be a sink or have a tag, otherwise
+ // it could get reclaimed before the path diagnostic is created.
+ assert((E->isSink() || E->getLocation().getTag()) &&
+ "Error node must either be a sink or have a tag");
+
+ const AnalysisDeclContext *DeclCtx =
+ E->getLocationContext()->getAnalysisDeclContext();
+ // The source of autosynthesized body can be handcrafted AST or a model
+ // file. The locations from handcrafted ASTs have no valid source locations
+ // and have to be discarded. Locations from model files should be preserved
+ // for processing and reporting.
+ if (DeclCtx->isBodyAutosynthesized() &&
+ !DeclCtx->isBodyAutosynthesizedFromModelFile())
+ return;
+ }
+
+ bool ValidSourceLoc = R->getLocation(getSourceManager()).isValid();
+ assert(ValidSourceLoc);
+ // If we mess up in a release build, we'd still prefer to just drop the bug
+ // instead of trying to go on.
+ if (!ValidSourceLoc)
+ return;
+
+ // Compute the bug report's hash to determine its equivalence class.
+ llvm::FoldingSetNodeID ID;
+ R->Profile(ID);
+
+ // Lookup the equivance class. If there isn't one, create it.
+ BugType& BT = R->getBugType();
+ Register(&BT);
+ void *InsertPos;
+ BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos);
+
+ if (!EQ) {
+ EQ = new BugReportEquivClass(std::move(R));
+ EQClasses.InsertNode(EQ, InsertPos);
+ EQClassesVector.push_back(EQ);
+ } else
+ EQ->AddReport(std::move(R));
+}
+
+//===----------------------------------------------------------------------===//
+// Emitting reports in equivalence classes.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+struct FRIEC_WLItem {
+ const ExplodedNode *N;
+ ExplodedNode::const_succ_iterator I, E;
+
+ FRIEC_WLItem(const ExplodedNode *n)
+ : N(n), I(N->succ_begin()), E(N->succ_end()) {}
+};
+
+} // namespace
+
+static const CFGBlock *findBlockForNode(const ExplodedNode *N) {
+ ProgramPoint P = N->getLocation();
+ if (auto BEP = P.getAs<BlockEntrance>())
+ return BEP->getBlock();
+
+ // Find the node's current statement in the CFG.
+ if (const Stmt *S = PathDiagnosticLocation::getStmt(N))
+ return N->getLocationContext()->getAnalysisDeclContext()
+ ->getCFGStmtMap()->getBlock(S);
+
+ return nullptr;
+}
+
+// Returns true if by simply looking at the block, we can be sure that it
+// results in a sink during analysis. This is useful to know when the analysis
+// was interrupted, and we try to figure out if it would sink eventually.
+// There may be many more reasons why a sink would appear during analysis
+// (eg. checkers may generate sinks arbitrarily), but here we only consider
+// sinks that would be obvious by looking at the CFG.
+static bool isImmediateSinkBlock(const CFGBlock *Blk) {
+ if (Blk->hasNoReturnElement())
+ return true;
+
+ // FIXME: Throw-expressions are currently generating sinks during analysis:
+ // they're not supported yet, and also often used for actually terminating
+ // the program. So we should treat them as sinks in this analysis as well,
+ // at least for now, but once we have better support for exceptions,
+ // we'd need to carefully handle the case when the throw is being
+ // immediately caught.
+ if (std::any_of(Blk->begin(), Blk->end(), [](const CFGElement &Elm) {
+ if (Optional<CFGStmt> StmtElm = Elm.getAs<CFGStmt>())
+ if (isa<CXXThrowExpr>(StmtElm->getStmt()))
+ return true;
+ return false;
+ }))
+ return true;
+
+ return false;
+}
+
+// Returns true if by looking at the CFG surrounding the node's program
+// point, we can be sure that any analysis starting from this point would
+// eventually end with a sink. We scan the child CFG blocks in a depth-first
+// manner and see if all paths eventually end up in an immediate sink block.
+static bool isInevitablySinking(const ExplodedNode *N) {
+ const CFG &Cfg = N->getCFG();
+
+ const CFGBlock *StartBlk = findBlockForNode(N);
+ if (!StartBlk)
+ return false;
+ if (isImmediateSinkBlock(StartBlk))
+ return true;
+
+ llvm::SmallVector<const CFGBlock *, 32> DFSWorkList;
+ llvm::SmallPtrSet<const CFGBlock *, 32> Visited;
+
+ DFSWorkList.push_back(StartBlk);
+ while (!DFSWorkList.empty()) {
+ const CFGBlock *Blk = DFSWorkList.back();
+ DFSWorkList.pop_back();
+ Visited.insert(Blk);
+
+ // If at least one path reaches the CFG exit, it means that control is
+ // returned to the caller. For now, say that we are not sure what
+ // happens next. If necessary, this can be improved to analyze
+ // the parent StackFrameContext's call site in a similar manner.
+ if (Blk == &Cfg.getExit())
+ return false;
+
+ for (const auto &Succ : Blk->succs()) {
+ if (const CFGBlock *SuccBlk = Succ.getReachableBlock()) {
+ if (!isImmediateSinkBlock(SuccBlk) && !Visited.count(SuccBlk)) {
+ // If the block has reachable child blocks that aren't no-return,
+ // add them to the worklist.
+ DFSWorkList.push_back(SuccBlk);
+ }
+ }
+ }
+ }
+
+ // Nothing reached the exit. It can only mean one thing: there's no return.
+ return true;
+}
+
+static BugReport *
+FindReportInEquivalenceClass(BugReportEquivClass& EQ,
+ SmallVectorImpl<BugReport*> &bugReports) {
+ BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end();
+ assert(I != E);
+ BugType& BT = I->getBugType();
+
+ // If we don't need to suppress any of the nodes because they are
+ // post-dominated by a sink, simply add all the nodes in the equivalence class
+ // to 'Nodes'. Any of the reports will serve as a "representative" report.
+ if (!BT.isSuppressOnSink()) {
+ BugReport *R = &*I;
+ for (auto &I : EQ) {
+ const ExplodedNode *N = I.getErrorNode();
+ if (N) {
+ R = &I;
+ bugReports.push_back(R);
+ }
+ }
+ return R;
+ }
+
+ // For bug reports that should be suppressed when all paths are post-dominated
+ // by a sink node, iterate through the reports in the equivalence class
+ // until we find one that isn't post-dominated (if one exists). We use a
+ // DFS traversal of the ExplodedGraph to find a non-sink node. We could write
+ // this as a recursive function, but we don't want to risk blowing out the
+ // stack for very long paths.
+ BugReport *exampleReport = nullptr;
+
+ for (; I != E; ++I) {
+ const ExplodedNode *errorNode = I->getErrorNode();
+
+ if (!errorNode)
+ continue;
+ if (errorNode->isSink()) {
+ llvm_unreachable(
+ "BugType::isSuppressSink() should not be 'true' for sink end nodes");
+ }
+ // No successors? By definition this nodes isn't post-dominated by a sink.
+ if (errorNode->succ_empty()) {
+ bugReports.push_back(&*I);
+ if (!exampleReport)
+ exampleReport = &*I;
+ continue;
+ }
+
+ // See if we are in a no-return CFG block. If so, treat this similarly
+ // to being post-dominated by a sink. This works better when the analysis
+ // is incomplete and we have never reached the no-return function call(s)
+ // that we'd inevitably bump into on this path.
+ if (isInevitablySinking(errorNode))
+ continue;
+
+ // At this point we know that 'N' is not a sink and it has at least one
+ // successor. Use a DFS worklist to find a non-sink end-of-path node.
+ using WLItem = FRIEC_WLItem;
+ using DFSWorkList = SmallVector<WLItem, 10>;
+
+ llvm::DenseMap<const ExplodedNode *, unsigned> Visited;
+
+ DFSWorkList WL;
+ WL.push_back(errorNode);
+ Visited[errorNode] = 1;
+
+ while (!WL.empty()) {
+ WLItem &WI = WL.back();
+ assert(!WI.N->succ_empty());
+
+ for (; WI.I != WI.E; ++WI.I) {
+ const ExplodedNode *Succ = *WI.I;
+ // End-of-path node?
+ if (Succ->succ_empty()) {
+ // If we found an end-of-path node that is not a sink.
+ if (!Succ->isSink()) {
+ bugReports.push_back(&*I);
+ if (!exampleReport)
+ exampleReport = &*I;
+ WL.clear();
+ break;
+ }
+ // Found a sink? Continue on to the next successor.
+ continue;
+ }
+ // Mark the successor as visited. If it hasn't been explored,
+ // enqueue it to the DFS worklist.
+ unsigned &mark = Visited[Succ];
+ if (!mark) {
+ mark = 1;
+ WL.push_back(Succ);
+ break;
+ }
+ }
+
+ // The worklist may have been cleared at this point. First
+ // check if it is empty before checking the last item.
+ if (!WL.empty() && &WL.back() == &WI)
+ WL.pop_back();
+ }
+ }
+
+ // ExampleReport will be NULL if all the nodes in the equivalence class
+ // were post-dominated by sinks.
+ return exampleReport;
+}
+
+void BugReporter::FlushReport(BugReportEquivClass& EQ) {
+ SmallVector<BugReport*, 10> bugReports;
+ BugReport *report = FindReportInEquivalenceClass(EQ, bugReports);
+ if (!report)
+ return;
+
+ ArrayRef<PathDiagnosticConsumer*> Consumers = getPathDiagnosticConsumers();
+ std::unique_ptr<DiagnosticForConsumerMapTy> Diagnostics =
+ generateDiagnosticForConsumerMap(report, Consumers, bugReports);
+
+ for (auto &P : *Diagnostics) {
+ PathDiagnosticConsumer *Consumer = P.first;
+ std::unique_ptr<PathDiagnostic> &PD = P.second;
+
+ // If the path is empty, generate a single step path with the location
+ // of the issue.
+ if (PD->path.empty()) {
+ PathDiagnosticLocation L = report->getLocation(getSourceManager());
+ auto piece = llvm::make_unique<PathDiagnosticEventPiece>(
+ L, report->getDescription());
+ for (SourceRange Range : report->getRanges())
+ piece->addRange(Range);
+ PD->setEndOfPath(std::move(piece));
+ }
+
+ PathPieces &Pieces = PD->getMutablePieces();
+ if (getAnalyzerOptions().ShouldDisplayNotesAsEvents) {
+ // For path diagnostic consumers that don't support extra notes,
+ // we may optionally convert those to path notes.
+ for (auto I = report->getNotes().rbegin(),
+ E = report->getNotes().rend(); I != E; ++I) {
+ PathDiagnosticNotePiece *Piece = I->get();
+ auto ConvertedPiece = std::make_shared<PathDiagnosticEventPiece>(
+ Piece->getLocation(), Piece->getString());
+ for (const auto &R: Piece->getRanges())
+ ConvertedPiece->addRange(R);
+
+ Pieces.push_front(std::move(ConvertedPiece));
+ }
+ } else {
+ for (auto I = report->getNotes().rbegin(),
+ E = report->getNotes().rend(); I != E; ++I)
+ Pieces.push_front(*I);
+ }
+
+ // Get the meta data.
+ const BugReport::ExtraTextList &Meta = report->getExtraText();
+ for (const auto &i : Meta)
+ PD->addMeta(i);
+
+ updateExecutedLinesWithDiagnosticPieces(*PD);
+ Consumer->HandlePathDiagnostic(std::move(PD));
+ }
+}
+
+/// Insert all lines participating in the function signature \p Signature
+/// into \p ExecutedLines.
+static void populateExecutedLinesWithFunctionSignature(
+ const Decl *Signature, SourceManager &SM,
+ FilesToLineNumsMap &ExecutedLines) {
+ SourceRange SignatureSourceRange;
+ const Stmt* Body = Signature->getBody();
+ if (const auto FD = dyn_cast<FunctionDecl>(Signature)) {
+ SignatureSourceRange = FD->getSourceRange();
+ } else if (const auto OD = dyn_cast<ObjCMethodDecl>(Signature)) {
+ SignatureSourceRange = OD->getSourceRange();
+ } else {
+ return;
+ }
+ SourceLocation Start = SignatureSourceRange.getBegin();
+ SourceLocation End = Body ? Body->getSourceRange().getBegin()
+ : SignatureSourceRange.getEnd();
+ if (!Start.isValid() || !End.isValid())
+ return;
+ unsigned StartLine = SM.getExpansionLineNumber(Start);
+ unsigned EndLine = SM.getExpansionLineNumber(End);
+
+ FileID FID = SM.getFileID(SM.getExpansionLoc(Start));
+ for (unsigned Line = StartLine; Line <= EndLine; Line++)
+ ExecutedLines[FID].insert(Line);
+}
+
+static void populateExecutedLinesWithStmt(
+ const Stmt *S, SourceManager &SM,
+ FilesToLineNumsMap &ExecutedLines) {
+ SourceLocation Loc = S->getSourceRange().getBegin();
+ if (!Loc.isValid())
+ return;
+ SourceLocation ExpansionLoc = SM.getExpansionLoc(Loc);
+ FileID FID = SM.getFileID(ExpansionLoc);
+ unsigned LineNo = SM.getExpansionLineNumber(ExpansionLoc);
+ ExecutedLines[FID].insert(LineNo);
+}
+
+/// \return all executed lines including function signatures on the path
+/// starting from \p N.
+static std::unique_ptr<FilesToLineNumsMap>
+findExecutedLines(SourceManager &SM, const ExplodedNode *N) {
+ auto ExecutedLines = llvm::make_unique<FilesToLineNumsMap>();
+
+ while (N) {
+ if (N->getFirstPred() == nullptr) {
+ // First node: show signature of the entrance point.
+ const Decl *D = N->getLocationContext()->getDecl();
+ populateExecutedLinesWithFunctionSignature(D, SM, *ExecutedLines);
+ } else if (auto CE = N->getLocationAs<CallEnter>()) {
+ // Inlined function: show signature.
+ const Decl* D = CE->getCalleeContext()->getDecl();
+ populateExecutedLinesWithFunctionSignature(D, SM, *ExecutedLines);
+ } else if (const Stmt *S = PathDiagnosticLocation::getStmt(N)) {
+ populateExecutedLinesWithStmt(S, SM, *ExecutedLines);
+
+ // Show extra context for some parent kinds.
+ const Stmt *P = N->getParentMap().getParent(S);
+
+ // The path exploration can die before the node with the associated
+ // return statement is generated, but we do want to show the whole
+ // return.
+ if (const auto *RS = dyn_cast_or_null<ReturnStmt>(P)) {
+ populateExecutedLinesWithStmt(RS, SM, *ExecutedLines);
+ P = N->getParentMap().getParent(RS);
+ }
+
+ if (P && (isa<SwitchCase>(P) || isa<LabelStmt>(P)))
+ populateExecutedLinesWithStmt(P, SM, *ExecutedLines);
+ }
+
+ N = N->getFirstPred();
+ }
+ return ExecutedLines;
+}
+
+std::unique_ptr<DiagnosticForConsumerMapTy>
+BugReporter::generateDiagnosticForConsumerMap(
+ BugReport *report, ArrayRef<PathDiagnosticConsumer *> consumers,
+ ArrayRef<BugReport *> bugReports) {
+
+ if (!report->isPathSensitive()) {
+ auto Out = llvm::make_unique<DiagnosticForConsumerMapTy>();
+ for (auto *Consumer : consumers)
+ (*Out)[Consumer] = generateEmptyDiagnosticForReport(report,
+ getSourceManager());
+ return Out;
+ }
+
+ // Generate the full path sensitive diagnostic, using the generation scheme
+ // specified by the PathDiagnosticConsumer. Note that we have to generate
+ // path diagnostics even for consumers which do not support paths, because
+ // the BugReporterVisitors may mark this bug as a false positive.
+ assert(!bugReports.empty());
+ MaxBugClassSize.updateMax(bugReports.size());
+ std::unique_ptr<DiagnosticForConsumerMapTy> Out =
+ generatePathDiagnostics(consumers, bugReports);
+
+ if (Out->empty())
+ return Out;
+
+ MaxValidBugClassSize.updateMax(bugReports.size());
+
+ // Examine the report and see if the last piece is in a header. Reset the
+ // report location to the last piece in the main source file.
+ AnalyzerOptions &Opts = getAnalyzerOptions();
+ for (auto const &P : *Out)
+ if (Opts.ShouldReportIssuesInMainSourceFile && !Opts.AnalyzeAll)
+ P.second->resetDiagnosticLocationToMainFile();
+
+ return Out;
+}
+
+void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
+ const CheckerBase *Checker,
+ StringRef Name, StringRef Category,
+ StringRef Str, PathDiagnosticLocation Loc,
+ ArrayRef<SourceRange> Ranges) {
+ EmitBasicReport(DeclWithIssue, Checker->getCheckName(), Name, Category, Str,
+ Loc, Ranges);
+}
+
+void BugReporter::EmitBasicReport(const Decl *DeclWithIssue,
+ CheckName CheckName,
+ StringRef name, StringRef category,
+ StringRef str, PathDiagnosticLocation Loc,
+ ArrayRef<SourceRange> Ranges) {
+ // 'BT' is owned by BugReporter.
+ BugType *BT = getBugTypeForName(CheckName, name, category);
+ auto R = llvm::make_unique<BugReport>(*BT, str, Loc);
+ R->setDeclWithIssue(DeclWithIssue);
+ for (ArrayRef<SourceRange>::iterator I = Ranges.begin(), E = Ranges.end();
+ I != E; ++I)
+ R->addRange(*I);
+ emitReport(std::move(R));
+}
+
+BugType *BugReporter::getBugTypeForName(CheckName CheckName, StringRef name,
+ StringRef category) {
+ SmallString<136> fullDesc;
+ llvm::raw_svector_ostream(fullDesc) << CheckName.getName() << ":" << name
+ << ":" << category;
+ BugType *&BT = StrBugTypes[fullDesc];
+ if (!BT)
+ BT = new BugType(CheckName, name, category);
+ return BT;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
new file mode 100644
index 000000000000..da94b6eb21e9
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -0,0 +1,2479 @@
+//===- BugReporterVisitors.cpp - Helpers for reporting bugs ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a set of BugReporter "visitors" which can be used to
+// enhance the diagnostics reported for a bug.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <deque>
+#include <memory>
+#include <string>
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+//===----------------------------------------------------------------------===//
+// Utility functions.
+//===----------------------------------------------------------------------===//
+
+static const Expr *peelOffPointerArithmetic(const BinaryOperator *B) {
+ if (B->isAdditiveOp() && B->getType()->isPointerType()) {
+ if (B->getLHS()->getType()->isPointerType()) {
+ return B->getLHS();
+ } else if (B->getRHS()->getType()->isPointerType()) {
+ return B->getRHS();
+ }
+ }
+ return nullptr;
+}
+
+/// Given that expression S represents a pointer that would be dereferenced,
+/// try to find a sub-expression from which the pointer came from.
+/// This is used for tracking down origins of a null or undefined value:
+/// "this is null because that is null because that is null" etc.
+/// We wipe away field and element offsets because they merely add offsets.
+/// We also wipe away all casts except lvalue-to-rvalue casts, because the
+/// latter represent an actual pointer dereference; however, we remove
+/// the final lvalue-to-rvalue cast before returning from this function
+/// because it demonstrates more clearly from where the pointer rvalue was
+/// loaded. Examples:
+/// x->y.z ==> x (lvalue)
+/// foo()->y.z ==> foo() (rvalue)
+const Expr *bugreporter::getDerefExpr(const Stmt *S) {
+ const auto *E = dyn_cast<Expr>(S);
+ if (!E)
+ return nullptr;
+
+ while (true) {
+ if (const auto *CE = dyn_cast<CastExpr>(E)) {
+ if (CE->getCastKind() == CK_LValueToRValue) {
+ // This cast represents the load we're looking for.
+ break;
+ }
+ E = CE->getSubExpr();
+ } else if (const auto *B = dyn_cast<BinaryOperator>(E)) {
+ // Pointer arithmetic: '*(x + 2)' -> 'x') etc.
+ if (const Expr *Inner = peelOffPointerArithmetic(B)) {
+ E = Inner;
+ } else {
+ // Probably more arithmetic can be pattern-matched here,
+ // but for now give up.
+ break;
+ }
+ } else if (const auto *U = dyn_cast<UnaryOperator>(E)) {
+ if (U->getOpcode() == UO_Deref || U->getOpcode() == UO_AddrOf ||
+ (U->isIncrementDecrementOp() && U->getType()->isPointerType())) {
+ // Operators '*' and '&' don't actually mean anything.
+ // We look at casts instead.
+ E = U->getSubExpr();
+ } else {
+ // Probably more arithmetic can be pattern-matched here,
+ // but for now give up.
+ break;
+ }
+ }
+ // Pattern match for a few useful cases: a[0], p->f, *p etc.
+ else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
+ E = ME->getBase();
+ } else if (const auto *IvarRef = dyn_cast<ObjCIvarRefExpr>(E)) {
+ E = IvarRef->getBase();
+ } else if (const auto *AE = dyn_cast<ArraySubscriptExpr>(E)) {
+ E = AE->getBase();
+ } else if (const auto *PE = dyn_cast<ParenExpr>(E)) {
+ E = PE->getSubExpr();
+ } else if (const auto *FE = dyn_cast<FullExpr>(E)) {
+ E = FE->getSubExpr();
+ } else {
+ // Other arbitrary stuff.
+ break;
+ }
+ }
+
+ // Special case: remove the final lvalue-to-rvalue cast, but do not recurse
+ // deeper into the sub-expression. This way we return the lvalue from which
+ // our pointer rvalue was loaded.
+ if (const auto *CE = dyn_cast<ImplicitCastExpr>(E))
+ if (CE->getCastKind() == CK_LValueToRValue)
+ E = CE->getSubExpr();
+
+ return E;
+}
+
+//===----------------------------------------------------------------------===//
+// Definitions for bug reporter visitors.
+//===----------------------------------------------------------------------===//
+
+std::shared_ptr<PathDiagnosticPiece>
+BugReporterVisitor::getEndPath(BugReporterContext &,
+ const ExplodedNode *, BugReport &) {
+ return nullptr;
+}
+
+void
+BugReporterVisitor::finalizeVisitor(BugReporterContext &,
+ const ExplodedNode *, BugReport &) {}
+
+std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath(
+ BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) {
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager());
+
+ const auto &Ranges = BR.getRanges();
+
+ // Only add the statement itself as a range if we didn't specify any
+ // special ranges for this report.
+ auto P = std::make_shared<PathDiagnosticEventPiece>(
+ L, BR.getDescription(), Ranges.begin() == Ranges.end());
+ for (SourceRange Range : Ranges)
+ P->addRange(Range);
+
+ return P;
+}
+
+/// \return name of the macro inside the location \p Loc.
+static StringRef getMacroName(SourceLocation Loc,
+ BugReporterContext &BRC) {
+ return Lexer::getImmediateMacroName(
+ Loc,
+ BRC.getSourceManager(),
+ BRC.getASTContext().getLangOpts());
+}
+
+/// \return Whether given spelling location corresponds to an expansion
+/// of a function-like macro.
+static bool isFunctionMacroExpansion(SourceLocation Loc,
+ const SourceManager &SM) {
+ if (!Loc.isMacroID())
+ return false;
+ while (SM.isMacroArgExpansion(Loc))
+ Loc = SM.getImmediateExpansionRange(Loc).getBegin();
+ std::pair<FileID, unsigned> TLInfo = SM.getDecomposedLoc(Loc);
+ SrcMgr::SLocEntry SE = SM.getSLocEntry(TLInfo.first);
+ const SrcMgr::ExpansionInfo &EInfo = SE.getExpansion();
+ return EInfo.isFunctionMacroExpansion();
+}
+
+/// \return Whether \c RegionOfInterest was modified at \p N,
+/// where \p ReturnState is a state associated with the return
+/// from the current frame.
+static bool wasRegionOfInterestModifiedAt(
+ const SubRegion *RegionOfInterest,
+ const ExplodedNode *N,
+ SVal ValueAfter) {
+ ProgramStateRef State = N->getState();
+ ProgramStateManager &Mgr = N->getState()->getStateManager();
+
+ if (!N->getLocationAs<PostStore>()
+ && !N->getLocationAs<PostInitializer>()
+ && !N->getLocationAs<PostStmt>())
+ return false;
+
+ // Writing into region of interest.
+ if (auto PS = N->getLocationAs<PostStmt>())
+ if (auto *BO = PS->getStmtAs<BinaryOperator>())
+ if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(
+ N->getSVal(BO->getLHS()).getAsRegion()))
+ return true;
+
+ // SVal after the state is possibly different.
+ SVal ValueAtN = N->getState()->getSVal(RegionOfInterest);
+ if (!Mgr.getSValBuilder().areEqual(State, ValueAtN, ValueAfter).isConstrainedTrue() &&
+ (!ValueAtN.isUndef() || !ValueAfter.isUndef()))
+ return true;
+
+ return false;
+}
+
+
+namespace {
+
+/// Put a diagnostic on return statement of all inlined functions
+/// for which the region of interest \p RegionOfInterest was passed into,
+/// but not written inside, and it has caused an undefined read or a null
+/// pointer dereference outside.
+class NoStoreFuncVisitor final : public BugReporterVisitor {
+ const SubRegion *RegionOfInterest;
+ MemRegionManager &MmrMgr;
+ const SourceManager &SM;
+ const PrintingPolicy &PP;
+
+ /// Recursion limit for dereferencing fields when looking for the
+ /// region of interest.
+ /// The limit of two indicates that we will dereference fields only once.
+ static const unsigned DEREFERENCE_LIMIT = 2;
+
+ /// Frames writing into \c RegionOfInterest.
+ /// This visitor generates a note only if a function does not write into
+ /// a region of interest. This information is not immediately available
+ /// by looking at the node associated with the exit from the function
+ /// (usually the return statement). To avoid recomputing the same information
+ /// many times (going up the path for each node and checking whether the
+ /// region was written into) we instead lazily compute the
+ /// stack frames along the path which write into the region of interest.
+ llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingRegion;
+ llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated;
+
+ using RegionVector = SmallVector<const MemRegion *, 5>;
+public:
+ NoStoreFuncVisitor(const SubRegion *R)
+ : RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()),
+ SM(MmrMgr.getContext().getSourceManager()),
+ PP(MmrMgr.getContext().getPrintingPolicy()) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int Tag = 0;
+ ID.AddPointer(&Tag);
+ ID.AddPointer(RegionOfInterest);
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BR,
+ BugReport &) override {
+
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *SCtx = Ctx->getStackFrame();
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+
+ // No diagnostic if region was modified inside the frame.
+ if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N))
+ return nullptr;
+
+ CallEventRef<> Call =
+ BR.getStateManager().getCallEventManager().getCaller(SCtx, State);
+
+ if (SM.isInSystemHeader(Call->getDecl()->getSourceRange().getBegin()))
+ return nullptr;
+
+ // Region of interest corresponds to an IVar, exiting a method
+ // which could have written into that IVar, but did not.
+ if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) {
+ if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) {
+ const MemRegion *SelfRegion = MC->getReceiverSVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(SelfRegion) &&
+ potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(),
+ IvarR->getDecl()))
+ return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, SelfRegion,
+ "self", /*FirstIsReferenceType=*/false,
+ 1);
+ }
+ }
+
+ if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
+ const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(ThisR)
+ && !CCall->getDecl()->isImplicit())
+ return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, ThisR,
+ "this",
+ /*FirstIsReferenceType=*/false, 1);
+
+ // Do not generate diagnostics for not modified parameters in
+ // constructors.
+ return nullptr;
+ }
+
+ ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call);
+ for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) {
+ const ParmVarDecl *PVD = parameters[I];
+ SVal S = Call->getArgSVal(I);
+ bool ParamIsReferenceType = PVD->getType()->isReferenceType();
+ std::string ParamName = PVD->getNameAsString();
+
+ int IndirectionLevel = 1;
+ QualType T = PVD->getType();
+ while (const MemRegion *R = S.getAsRegion()) {
+ if (RegionOfInterest->isSubRegionOf(R) && !isPointerToConst(T))
+ return notModifiedDiagnostics(Ctx, *CallExitLoc, Call, {}, R,
+ ParamName, ParamIsReferenceType,
+ IndirectionLevel);
+
+ QualType PT = T->getPointeeType();
+ if (PT.isNull() || PT->isVoidType()) break;
+
+ if (const RecordDecl *RD = PT->getAsRecordDecl())
+ if (auto P = findRegionOfInterestInRecord(RD, State, R))
+ return notModifiedDiagnostics(
+ Ctx, *CallExitLoc, Call, *P, RegionOfInterest, ParamName,
+ ParamIsReferenceType, IndirectionLevel);
+
+ S = State->getSVal(R, PT);
+ T = PT;
+ IndirectionLevel++;
+ }
+ }
+
+ return nullptr;
+ }
+
+private:
+ /// Attempts to find the region of interest in a given CXX decl,
+ /// by either following the base classes or fields.
+ /// Dereferences fields up to a given recursion limit.
+ /// Note that \p Vec is passed by value, leading to quadratic copying cost,
+ /// but it's OK in practice since its length is limited to DEREFERENCE_LIMIT.
+ /// \return A chain fields leading to the region of interest or None.
+ const Optional<RegionVector>
+ findRegionOfInterestInRecord(const RecordDecl *RD, ProgramStateRef State,
+ const MemRegion *R,
+ const RegionVector &Vec = {},
+ int depth = 0) {
+
+ if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth.
+ return None;
+
+ if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))
+ if (!RDX->hasDefinition())
+ return None;
+
+ // Recursively examine the base classes.
+ // Note that following base classes does not increase the recursion depth.
+ if (const auto *RDX = dyn_cast<CXXRecordDecl>(RD))
+ for (const auto II : RDX->bases())
+ if (const RecordDecl *RRD = II.getType()->getAsRecordDecl())
+ if (auto Out = findRegionOfInterestInRecord(RRD, State, R, Vec, depth))
+ return Out;
+
+ for (const FieldDecl *I : RD->fields()) {
+ QualType FT = I->getType();
+ const FieldRegion *FR = MmrMgr.getFieldRegion(I, cast<SubRegion>(R));
+ const SVal V = State->getSVal(FR);
+ const MemRegion *VR = V.getAsRegion();
+
+ RegionVector VecF = Vec;
+ VecF.push_back(FR);
+
+ if (RegionOfInterest == VR)
+ return VecF;
+
+ if (const RecordDecl *RRD = FT->getAsRecordDecl())
+ if (auto Out =
+ findRegionOfInterestInRecord(RRD, State, FR, VecF, depth + 1))
+ return Out;
+
+ QualType PT = FT->getPointeeType();
+ if (PT.isNull() || PT->isVoidType() || !VR) continue;
+
+ if (const RecordDecl *RRD = PT->getAsRecordDecl())
+ if (auto Out =
+ findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1))
+ return Out;
+
+ }
+
+ return None;
+ }
+
+ /// \return Whether the method declaration \p Parent
+ /// syntactically has a binary operation writing into the ivar \p Ivar.
+ bool potentiallyWritesIntoIvar(const Decl *Parent,
+ const ObjCIvarDecl *Ivar) {
+ using namespace ast_matchers;
+ const char * IvarBind = "Ivar";
+ if (!Parent || !Parent->hasBody())
+ return false;
+ StatementMatcher WriteIntoIvarM = binaryOperator(
+ hasOperatorName("="),
+ hasLHS(ignoringParenImpCasts(
+ objcIvarRefExpr(hasDeclaration(equalsNode(Ivar))).bind(IvarBind))));
+ StatementMatcher ParentM = stmt(hasDescendant(WriteIntoIvarM));
+ auto Matches = match(ParentM, *Parent->getBody(), Parent->getASTContext());
+ for (BoundNodes &Match : Matches) {
+ auto IvarRef = Match.getNodeAs<ObjCIvarRefExpr>(IvarBind);
+ if (IvarRef->isFreeIvar())
+ return true;
+
+ const Expr *Base = IvarRef->getBase();
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base))
+ Base = ICE->getSubExpr();
+
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(Base))
+ if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl()))
+ if (ID->getParameterKind() == ImplicitParamDecl::ObjCSelf)
+ return true;
+
+ return false;
+ }
+ return false;
+ }
+
+ /// Check and lazily calculate whether the region of interest is
+ /// modified in the stack frame to which \p N belongs.
+ /// The calculation is cached in FramesModifyingRegion.
+ bool isRegionOfInterestModifiedInFrame(const ExplodedNode *N) {
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *SCtx = Ctx->getStackFrame();
+ if (!FramesModifyingCalculated.count(SCtx))
+ findModifyingFrames(N);
+ return FramesModifyingRegion.count(SCtx);
+ }
+
+
+ /// Write to \c FramesModifyingRegion all stack frames along
+ /// the path in the current stack frame which modify \c RegionOfInterest.
+ void findModifyingFrames(const ExplodedNode *N) {
+ assert(N->getLocationAs<CallExitBegin>());
+ ProgramStateRef LastReturnState = N->getState();
+ SVal ValueAtReturn = LastReturnState->getSVal(RegionOfInterest);
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *OriginalSCtx = Ctx->getStackFrame();
+
+ do {
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+ if (CallExitLoc) {
+ LastReturnState = State;
+ ValueAtReturn = LastReturnState->getSVal(RegionOfInterest);
+ }
+
+ FramesModifyingCalculated.insert(
+ N->getLocationContext()->getStackFrame());
+
+ if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtReturn)) {
+ const StackFrameContext *SCtx = N->getStackFrame();
+ while (!SCtx->inTopFrame()) {
+ auto p = FramesModifyingRegion.insert(SCtx);
+ if (!p.second)
+ break; // Frame and all its parents already inserted.
+ SCtx = SCtx->getParent()->getStackFrame();
+ }
+ }
+
+ // Stop calculation at the call to the current function.
+ if (auto CE = N->getLocationAs<CallEnter>())
+ if (CE->getCalleeContext() == OriginalSCtx)
+ break;
+
+ N = N->getFirstPred();
+ } while (N);
+ }
+
+ /// Get parameters associated with runtime definition in order
+ /// to get the correct parameter name.
+ ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) {
+ // Use runtime definition, if available.
+ RuntimeDefinition RD = Call->getRuntimeDefinition();
+ if (const auto *FD = dyn_cast_or_null<FunctionDecl>(RD.getDecl()))
+ return FD->parameters();
+ if (const auto *MD = dyn_cast_or_null<ObjCMethodDecl>(RD.getDecl()))
+ return MD->parameters();
+
+ return Call->parameters();
+ }
+
+ /// \return whether \p Ty points to a const type, or is a const reference.
+ bool isPointerToConst(QualType Ty) {
+ return !Ty->getPointeeType().isNull() &&
+ Ty->getPointeeType().getCanonicalType().isConstQualified();
+ }
+
+ /// \return Diagnostics piece for region not modified in the current function.
+ std::shared_ptr<PathDiagnosticPiece>
+ notModifiedDiagnostics(const LocationContext *Ctx, CallExitBegin &CallExitLoc,
+ CallEventRef<> Call, const RegionVector &FieldChain,
+ const MemRegion *MatchedRegion, StringRef FirstElement,
+ bool FirstIsReferenceType, unsigned IndirectionLevel) {
+
+ PathDiagnosticLocation L;
+ if (const ReturnStmt *RS = CallExitLoc.getReturnStmt()) {
+ L = PathDiagnosticLocation::createBegin(RS, SM, Ctx);
+ } else {
+ L = PathDiagnosticLocation(
+ Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(),
+ SM);
+ }
+
+ SmallString<256> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ os << "Returning without writing to '";
+
+ // Do not generate the note if failed to pretty-print.
+ if (!prettyPrintRegionName(FirstElement, FirstIsReferenceType,
+ MatchedRegion, FieldChain, IndirectionLevel, os))
+ return nullptr;
+
+ os << "'";
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+ }
+
+ /// Pretty-print region \p MatchedRegion to \p os.
+ /// \return Whether printing succeeded.
+ bool prettyPrintRegionName(StringRef FirstElement, bool FirstIsReferenceType,
+ const MemRegion *MatchedRegion,
+ const RegionVector &FieldChain,
+ int IndirectionLevel,
+ llvm::raw_svector_ostream &os) {
+
+ if (FirstIsReferenceType)
+ IndirectionLevel--;
+
+ RegionVector RegionSequence;
+
+ // Add the regions in the reverse order, then reverse the resulting array.
+ assert(RegionOfInterest->isSubRegionOf(MatchedRegion));
+ const MemRegion *R = RegionOfInterest;
+ while (R != MatchedRegion) {
+ RegionSequence.push_back(R);
+ R = cast<SubRegion>(R)->getSuperRegion();
+ }
+ std::reverse(RegionSequence.begin(), RegionSequence.end());
+ RegionSequence.append(FieldChain.begin(), FieldChain.end());
+
+ StringRef Sep;
+ for (const MemRegion *R : RegionSequence) {
+
+ // Just keep going up to the base region.
+ // Element regions may appear due to casts.
+ if (isa<CXXBaseObjectRegion>(R) || isa<CXXTempObjectRegion>(R))
+ continue;
+
+ if (Sep.empty())
+ Sep = prettyPrintFirstElement(FirstElement,
+ /*MoreItemsExpected=*/true,
+ IndirectionLevel, os);
+
+ os << Sep;
+
+ // Can only reasonably pretty-print DeclRegions.
+ if (!isa<DeclRegion>(R))
+ return false;
+
+ const auto *DR = cast<DeclRegion>(R);
+ Sep = DR->getValueType()->isAnyPointerType() ? "->" : ".";
+ DR->getDecl()->getDeclName().print(os, PP);
+ }
+
+ if (Sep.empty())
+ prettyPrintFirstElement(FirstElement,
+ /*MoreItemsExpected=*/false, IndirectionLevel,
+ os);
+ return true;
+ }
+
+ /// Print first item in the chain, return new separator.
+ StringRef prettyPrintFirstElement(StringRef FirstElement,
+ bool MoreItemsExpected,
+ int IndirectionLevel,
+ llvm::raw_svector_ostream &os) {
+ StringRef Out = ".";
+
+ if (IndirectionLevel > 0 && MoreItemsExpected) {
+ IndirectionLevel--;
+ Out = "->";
+ }
+
+ if (IndirectionLevel > 0 && MoreItemsExpected)
+ os << "(";
+
+ for (int i=0; i<IndirectionLevel; i++)
+ os << "*";
+ os << FirstElement;
+
+ if (IndirectionLevel > 0 && MoreItemsExpected)
+ os << ")";
+
+ return Out;
+ }
+};
+
+/// Suppress null-pointer-dereference bugs where dereferenced null was returned
+/// the macro.
+class MacroNullReturnSuppressionVisitor final : public BugReporterVisitor {
+ const SubRegion *RegionOfInterest;
+ const SVal ValueAtDereference;
+
+ // Do not invalidate the reports where the value was modified
+ // after it got assigned to from the macro.
+ bool WasModified = false;
+
+public:
+ MacroNullReturnSuppressionVisitor(const SubRegion *R,
+ const SVal V) : RegionOfInterest(R),
+ ValueAtDereference(V) {}
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override {
+ if (WasModified)
+ return nullptr;
+
+ auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>();
+ if (!BugPoint)
+ return nullptr;
+
+ const SourceManager &SMgr = BRC.getSourceManager();
+ if (auto Loc = matchAssignment(N)) {
+ if (isFunctionMacroExpansion(*Loc, SMgr)) {
+ std::string MacroName = getMacroName(*Loc, BRC);
+ SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc();
+ if (!BugLoc.isMacroID() || getMacroName(BugLoc, BRC) != MacroName)
+ BR.markInvalid(getTag(), MacroName.c_str());
+ }
+ }
+
+ if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtDereference))
+ WasModified = true;
+
+ return nullptr;
+ }
+
+ static void addMacroVisitorIfNecessary(
+ const ExplodedNode *N, const MemRegion *R,
+ bool EnableNullFPSuppression, BugReport &BR,
+ const SVal V) {
+ AnalyzerOptions &Options = N->getState()->getAnalysisManager().options;
+ if (EnableNullFPSuppression &&
+ Options.ShouldSuppressNullReturnPaths && V.getAs<Loc>())
+ BR.addVisitor(llvm::make_unique<MacroNullReturnSuppressionVisitor>(
+ R->getAs<SubRegion>(), V));
+ }
+
+ void* getTag() const {
+ static int Tag = 0;
+ return static_cast<void *>(&Tag);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddPointer(getTag());
+ }
+
+private:
+ /// \return Source location of right hand side of an assignment
+ /// into \c RegionOfInterest, empty optional if none found.
+ Optional<SourceLocation> matchAssignment(const ExplodedNode *N) {
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ ProgramStateRef State = N->getState();
+ auto *LCtx = N->getLocationContext();
+ if (!S)
+ return None;
+
+ if (const auto *DS = dyn_cast<DeclStmt>(S)) {
+ if (const auto *VD = dyn_cast<VarDecl>(DS->getSingleDecl()))
+ if (const Expr *RHS = VD->getInit())
+ if (RegionOfInterest->isSubRegionOf(
+ State->getLValue(VD, LCtx).getAsRegion()))
+ return RHS->getBeginLoc();
+ } else if (const auto *BO = dyn_cast<BinaryOperator>(S)) {
+ const MemRegion *R = N->getSVal(BO->getLHS()).getAsRegion();
+ const Expr *RHS = BO->getRHS();
+ if (BO->isAssignmentOp() && RegionOfInterest->isSubRegionOf(R)) {
+ return RHS->getBeginLoc();
+ }
+ }
+ return None;
+ }
+};
+
+/// Emits an extra note at the return statement of an interesting stack frame.
+///
+/// The returned value is marked as an interesting value, and if it's null,
+/// adds a visitor to track where it became null.
+///
+/// This visitor is intended to be used when another visitor discovers that an
+/// interesting value comes from an inlined function call.
+class ReturnVisitor : public BugReporterVisitor {
+ const StackFrameContext *StackFrame;
+ enum {
+ Initial,
+ MaybeUnsuppress,
+ Satisfied
+ } Mode = Initial;
+
+ bool EnableNullFPSuppression;
+ bool ShouldInvalidate = true;
+ AnalyzerOptions& Options;
+
+public:
+ ReturnVisitor(const StackFrameContext *Frame,
+ bool Suppressed,
+ AnalyzerOptions &Options)
+ : StackFrame(Frame), EnableNullFPSuppression(Suppressed),
+ Options(Options) {}
+
+ static void *getTag() {
+ static int Tag = 0;
+ return static_cast<void *>(&Tag);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddPointer(ReturnVisitor::getTag());
+ ID.AddPointer(StackFrame);
+ ID.AddBoolean(EnableNullFPSuppression);
+ }
+
+ /// Adds a ReturnVisitor if the given statement represents a call that was
+ /// inlined.
+ ///
+ /// This will search back through the ExplodedGraph, starting from the given
+ /// node, looking for when the given statement was processed. If it turns out
+ /// the statement is a call that was inlined, we add the visitor to the
+ /// bug report, so it can print a note later.
+ static void addVisitorIfNecessary(const ExplodedNode *Node, const Stmt *S,
+ BugReport &BR,
+ bool InEnableNullFPSuppression) {
+ if (!CallEvent::isCallStmt(S))
+ return;
+
+ // First, find when we processed the statement.
+ do {
+ if (auto CEE = Node->getLocationAs<CallExitEnd>())
+ if (CEE->getCalleeContext()->getCallSite() == S)
+ break;
+ if (auto SP = Node->getLocationAs<StmtPoint>())
+ if (SP->getStmt() == S)
+ break;
+
+ Node = Node->getFirstPred();
+ } while (Node);
+
+ // Next, step over any post-statement checks.
+ while (Node && Node->getLocation().getAs<PostStmt>())
+ Node = Node->getFirstPred();
+ if (!Node)
+ return;
+
+ // Finally, see if we inlined the call.
+ Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>();
+ if (!CEE)
+ return;
+
+ const StackFrameContext *CalleeContext = CEE->getCalleeContext();
+ if (CalleeContext->getCallSite() != S)
+ return;
+
+ // Check the return value.
+ ProgramStateRef State = Node->getState();
+ SVal RetVal = Node->getSVal(S);
+
+ // Handle cases where a reference is returned and then immediately used.
+ if (cast<Expr>(S)->isGLValue())
+ if (Optional<Loc> LValue = RetVal.getAs<Loc>())
+ RetVal = State->getSVal(*LValue);
+
+ // See if the return value is NULL. If so, suppress the report.
+ AnalyzerOptions &Options = State->getAnalysisManager().options;
+
+ bool EnableNullFPSuppression = false;
+ if (InEnableNullFPSuppression &&
+ Options.ShouldSuppressNullReturnPaths)
+ if (Optional<Loc> RetLoc = RetVal.getAs<Loc>())
+ EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue();
+
+ BR.markInteresting(CalleeContext);
+ BR.addVisitor(llvm::make_unique<ReturnVisitor>(CalleeContext,
+ EnableNullFPSuppression,
+ Options));
+ }
+
+ std::shared_ptr<PathDiagnosticPiece>
+ visitNodeInitial(const ExplodedNode *N,
+ BugReporterContext &BRC, BugReport &BR) {
+ // Only print a message at the interesting return statement.
+ if (N->getLocationContext() != StackFrame)
+ return nullptr;
+
+ Optional<StmtPoint> SP = N->getLocationAs<StmtPoint>();
+ if (!SP)
+ return nullptr;
+
+ const auto *Ret = dyn_cast<ReturnStmt>(SP->getStmt());
+ if (!Ret)
+ return nullptr;
+
+ // Okay, we're at the right return statement, but do we have the return
+ // value available?
+ ProgramStateRef State = N->getState();
+ SVal V = State->getSVal(Ret, StackFrame);
+ if (V.isUnknownOrUndef())
+ return nullptr;
+
+ // Don't print any more notes after this one.
+ Mode = Satisfied;
+
+ const Expr *RetE = Ret->getRetValue();
+ assert(RetE && "Tracking a return value for a void function");
+
+ // Handle cases where a reference is returned and then immediately used.
+ Optional<Loc> LValue;
+ if (RetE->isGLValue()) {
+ if ((LValue = V.getAs<Loc>())) {
+ SVal RValue = State->getRawSVal(*LValue, RetE->getType());
+ if (RValue.getAs<DefinedSVal>())
+ V = RValue;
+ }
+ }
+
+ // Ignore aggregate rvalues.
+ if (V.getAs<nonloc::LazyCompoundVal>() ||
+ V.getAs<nonloc::CompoundVal>())
+ return nullptr;
+
+ RetE = RetE->IgnoreParenCasts();
+
+ // If we're returning 0, we should track where that 0 came from.
+ bugreporter::trackExpressionValue(N, RetE, BR, EnableNullFPSuppression);
+
+ // Build an appropriate message based on the return value.
+ SmallString<64> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+
+ if (State->isNull(V).isConstrainedTrue()) {
+ if (V.getAs<Loc>()) {
+
+ // If we have counter-suppression enabled, make sure we keep visiting
+ // future nodes. We want to emit a path note as well, in case
+ // the report is resurrected as valid later on.
+ if (EnableNullFPSuppression &&
+ Options.ShouldAvoidSuppressingNullArgumentPaths)
+ Mode = MaybeUnsuppress;
+
+ if (RetE->getType()->isObjCObjectPointerType()) {
+ Out << "Returning nil";
+ } else {
+ Out << "Returning null pointer";
+ }
+ } else {
+ Out << "Returning zero";
+ }
+
+ } else {
+ if (auto CI = V.getAs<nonloc::ConcreteInt>()) {
+ Out << "Returning the value " << CI->getValue();
+ } else if (V.getAs<Loc>()) {
+ Out << "Returning pointer";
+ } else {
+ Out << "Returning value";
+ }
+ }
+
+ if (LValue) {
+ if (const MemRegion *MR = LValue->getAsRegion()) {
+ if (MR->canPrintPretty()) {
+ Out << " (reference to ";
+ MR->printPretty(Out);
+ Out << ")";
+ }
+ }
+ } else {
+ // FIXME: We should have a more generalized location printing mechanism.
+ if (const auto *DR = dyn_cast<DeclRefExpr>(RetE))
+ if (const auto *DD = dyn_cast<DeclaratorDecl>(DR->getDecl()))
+ Out << " (loaded from '" << *DD << "')";
+ }
+
+ PathDiagnosticLocation L(Ret, BRC.getSourceManager(), StackFrame);
+ if (!L.isValid() || !L.asLocation().isValid())
+ return nullptr;
+
+ return std::make_shared<PathDiagnosticEventPiece>(L, Out.str());
+ }
+
+ std::shared_ptr<PathDiagnosticPiece>
+ visitNodeMaybeUnsuppress(const ExplodedNode *N,
+ BugReporterContext &BRC, BugReport &BR) {
+#ifndef NDEBUG
+ assert(Options.ShouldAvoidSuppressingNullArgumentPaths);
+#endif
+
+ // Are we at the entry node for this call?
+ Optional<CallEnter> CE = N->getLocationAs<CallEnter>();
+ if (!CE)
+ return nullptr;
+
+ if (CE->getCalleeContext() != StackFrame)
+ return nullptr;
+
+ Mode = Satisfied;
+
+ // Don't automatically suppress a report if one of the arguments is
+ // known to be a null pointer. Instead, start tracking /that/ null
+ // value back to its origin.
+ ProgramStateManager &StateMgr = BRC.getStateManager();
+ CallEventManager &CallMgr = StateMgr.getCallEventManager();
+
+ ProgramStateRef State = N->getState();
+ CallEventRef<> Call = CallMgr.getCaller(StackFrame, State);
+ for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) {
+ Optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>();
+ if (!ArgV)
+ continue;
+
+ const Expr *ArgE = Call->getArgExpr(I);
+ if (!ArgE)
+ continue;
+
+ // Is it possible for this argument to be non-null?
+ if (!State->isNull(*ArgV).isConstrainedTrue())
+ continue;
+
+ if (bugreporter::trackExpressionValue(N, ArgE, BR, EnableNullFPSuppression))
+ ShouldInvalidate = false;
+
+ // If we /can't/ track the null pointer, we should err on the side of
+ // false negatives, and continue towards marking this report invalid.
+ // (We will still look at the other arguments, though.)
+ }
+
+ return nullptr;
+ }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override {
+ switch (Mode) {
+ case Initial:
+ return visitNodeInitial(N, BRC, BR);
+ case MaybeUnsuppress:
+ return visitNodeMaybeUnsuppress(N, BRC, BR);
+ case Satisfied:
+ return nullptr;
+ }
+
+ llvm_unreachable("Invalid visit mode!");
+ }
+
+ void finalizeVisitor(BugReporterContext &, const ExplodedNode *,
+ BugReport &BR) override {
+ if (EnableNullFPSuppression && ShouldInvalidate)
+ BR.markInvalid(ReturnVisitor::getTag(), StackFrame);
+ }
+};
+
+} // namespace
+
+void FindLastStoreBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
+ static int tag = 0;
+ ID.AddPointer(&tag);
+ ID.AddPointer(R);
+ ID.Add(V);
+ ID.AddBoolean(EnableNullFPSuppression);
+}
+
+/// Returns true if \p N represents the DeclStmt declaring and initializing
+/// \p VR.
+static bool isInitializationOfVar(const ExplodedNode *N, const VarRegion *VR) {
+ Optional<PostStmt> P = N->getLocationAs<PostStmt>();
+ if (!P)
+ return false;
+
+ const DeclStmt *DS = P->getStmtAs<DeclStmt>();
+ if (!DS)
+ return false;
+
+ if (DS->getSingleDecl() != VR->getDecl())
+ return false;
+
+ const MemSpaceRegion *VarSpace = VR->getMemorySpace();
+ const auto *FrameSpace = dyn_cast<StackSpaceRegion>(VarSpace);
+ if (!FrameSpace) {
+ // If we ever directly evaluate global DeclStmts, this assertion will be
+ // invalid, but this still seems preferable to silently accepting an
+ // initialization that may be for a path-sensitive variable.
+ assert(VR->getDecl()->isStaticLocal() && "non-static stackless VarRegion");
+ return true;
+ }
+
+ assert(VR->getDecl()->hasLocalStorage());
+ const LocationContext *LCtx = N->getLocationContext();
+ return FrameSpace->getStackFrame() == LCtx->getStackFrame();
+}
+
+/// Show diagnostics for initializing or declaring a region \p R with a bad value.
+static void showBRDiagnostics(const char *action, llvm::raw_svector_ostream &os,
+ const MemRegion *R, SVal V, const DeclStmt *DS) {
+ if (R->canPrintPretty()) {
+ R->printPretty(os);
+ os << " ";
+ }
+
+ if (V.getAs<loc::ConcreteInt>()) {
+ bool b = false;
+ if (R->isBoundable()) {
+ if (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
+ if (TR->getValueType()->isObjCObjectPointerType()) {
+ os << action << "nil";
+ b = true;
+ }
+ }
+ }
+ if (!b)
+ os << action << "a null pointer value";
+
+ } else if (auto CVal = V.getAs<nonloc::ConcreteInt>()) {
+ os << action << CVal->getValue();
+ } else if (DS) {
+ if (V.isUndef()) {
+ if (isa<VarRegion>(R)) {
+ const auto *VD = cast<VarDecl>(DS->getSingleDecl());
+ if (VD->getInit()) {
+ os << (R->canPrintPretty() ? "initialized" : "Initializing")
+ << " to a garbage value";
+ } else {
+ os << (R->canPrintPretty() ? "declared" : "Declaring")
+ << " without an initial value";
+ }
+ }
+ } else {
+ os << (R->canPrintPretty() ? "initialized" : "Initialized")
+ << " here";
+ }
+ }
+}
+
+/// Display diagnostics for passing bad region as a parameter.
+static void showBRParamDiagnostics(llvm::raw_svector_ostream& os,
+ const VarRegion *VR,
+ SVal V) {
+ const auto *Param = cast<ParmVarDecl>(VR->getDecl());
+
+ os << "Passing ";
+
+ if (V.getAs<loc::ConcreteInt>()) {
+ if (Param->getType()->isObjCObjectPointerType())
+ os << "nil object reference";
+ else
+ os << "null pointer value";
+ } else if (V.isUndef()) {
+ os << "uninitialized value";
+ } else if (auto CI = V.getAs<nonloc::ConcreteInt>()) {
+ os << "the value " << CI->getValue();
+ } else {
+ os << "value";
+ }
+
+ // Printed parameter indexes are 1-based, not 0-based.
+ unsigned Idx = Param->getFunctionScopeIndex() + 1;
+ os << " via " << Idx << llvm::getOrdinalSuffix(Idx) << " parameter";
+ if (VR->canPrintPretty()) {
+ os << " ";
+ VR->printPretty(os);
+ }
+}
+
+/// Show default diagnostics for storing bad region.
+static void showBRDefaultDiagnostics(llvm::raw_svector_ostream& os,
+ const MemRegion *R,
+ SVal V) {
+ if (V.getAs<loc::ConcreteInt>()) {
+ bool b = false;
+ if (R->isBoundable()) {
+ if (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
+ if (TR->getValueType()->isObjCObjectPointerType()) {
+ os << "nil object reference stored";
+ b = true;
+ }
+ }
+ }
+ if (!b) {
+ if (R->canPrintPretty())
+ os << "Null pointer value stored";
+ else
+ os << "Storing null pointer value";
+ }
+
+ } else if (V.isUndef()) {
+ if (R->canPrintPretty())
+ os << "Uninitialized value stored";
+ else
+ os << "Storing uninitialized value";
+
+ } else if (auto CV = V.getAs<nonloc::ConcreteInt>()) {
+ if (R->canPrintPretty())
+ os << "The value " << CV->getValue() << " is assigned";
+ else
+ os << "Assigning " << CV->getValue();
+
+ } else {
+ if (R->canPrintPretty())
+ os << "Value assigned";
+ else
+ os << "Assigning value";
+ }
+
+ if (R->canPrintPretty()) {
+ os << " to ";
+ R->printPretty(os);
+ }
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC, BugReport &BR) {
+ if (Satisfied)
+ return nullptr;
+
+ const ExplodedNode *StoreSite = nullptr;
+ const ExplodedNode *Pred = Succ->getFirstPred();
+ const Expr *InitE = nullptr;
+ bool IsParam = false;
+
+ // First see if we reached the declaration of the region.
+ if (const auto *VR = dyn_cast<VarRegion>(R)) {
+ if (isInitializationOfVar(Pred, VR)) {
+ StoreSite = Pred;
+ InitE = VR->getDecl()->getInit();
+ }
+ }
+
+ // If this is a post initializer expression, initializing the region, we
+ // should track the initializer expression.
+ if (Optional<PostInitializer> PIP = Pred->getLocationAs<PostInitializer>()) {
+ const MemRegion *FieldReg = (const MemRegion *)PIP->getLocationValue();
+ if (FieldReg && FieldReg == R) {
+ StoreSite = Pred;
+ InitE = PIP->getInitializer()->getInit();
+ }
+ }
+
+ // Otherwise, see if this is the store site:
+ // (1) Succ has this binding and Pred does not, i.e. this is
+ // where the binding first occurred.
+ // (2) Succ has this binding and is a PostStore node for this region, i.e.
+ // the same binding was re-assigned here.
+ if (!StoreSite) {
+ if (Succ->getState()->getSVal(R) != V)
+ return nullptr;
+
+ if (Pred->getState()->getSVal(R) == V) {
+ Optional<PostStore> PS = Succ->getLocationAs<PostStore>();
+ if (!PS || PS->getLocationValue() != R)
+ return nullptr;
+ }
+
+ StoreSite = Succ;
+
+ // If this is an assignment expression, we can track the value
+ // being assigned.
+ if (Optional<PostStmt> P = Succ->getLocationAs<PostStmt>())
+ if (const BinaryOperator *BO = P->getStmtAs<BinaryOperator>())
+ if (BO->isAssignmentOp())
+ InitE = BO->getRHS();
+
+ // If this is a call entry, the variable should be a parameter.
+ // FIXME: Handle CXXThisRegion as well. (This is not a priority because
+ // 'this' should never be NULL, but this visitor isn't just for NULL and
+ // UndefinedVal.)
+ if (Optional<CallEnter> CE = Succ->getLocationAs<CallEnter>()) {
+ if (const auto *VR = dyn_cast<VarRegion>(R)) {
+ const auto *Param = cast<ParmVarDecl>(VR->getDecl());
+
+ ProgramStateManager &StateMgr = BRC.getStateManager();
+ CallEventManager &CallMgr = StateMgr.getCallEventManager();
+
+ CallEventRef<> Call = CallMgr.getCaller(CE->getCalleeContext(),
+ Succ->getState());
+ InitE = Call->getArgExpr(Param->getFunctionScopeIndex());
+ IsParam = true;
+ }
+ }
+
+ // If this is a CXXTempObjectRegion, the Expr responsible for its creation
+ // is wrapped inside of it.
+ if (const auto *TmpR = dyn_cast<CXXTempObjectRegion>(R))
+ InitE = TmpR->getExpr();
+ }
+
+ if (!StoreSite)
+ return nullptr;
+ Satisfied = true;
+
+ // If we have an expression that provided the value, try to track where it
+ // came from.
+ if (InitE) {
+ if (V.isUndef() ||
+ V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) {
+ if (!IsParam)
+ InitE = InitE->IgnoreParenCasts();
+ bugreporter::trackExpressionValue(StoreSite, InitE, BR,
+ EnableNullFPSuppression);
+ }
+ ReturnVisitor::addVisitorIfNecessary(StoreSite, InitE->IgnoreParenCasts(),
+ BR, EnableNullFPSuppression);
+ }
+
+ // Okay, we've found the binding. Emit an appropriate message.
+ SmallString<256> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+
+ if (Optional<PostStmt> PS = StoreSite->getLocationAs<PostStmt>()) {
+ const Stmt *S = PS->getStmt();
+ const char *action = nullptr;
+ const auto *DS = dyn_cast<DeclStmt>(S);
+ const auto *VR = dyn_cast<VarRegion>(R);
+
+ if (DS) {
+ action = R->canPrintPretty() ? "initialized to " :
+ "Initializing to ";
+ } else if (isa<BlockExpr>(S)) {
+ action = R->canPrintPretty() ? "captured by block as " :
+ "Captured by block as ";
+ if (VR) {
+ // See if we can get the BlockVarRegion.
+ ProgramStateRef State = StoreSite->getState();
+ SVal V = StoreSite->getSVal(S);
+ if (const auto *BDR =
+ dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) {
+ if (const VarRegion *OriginalR = BDR->getOriginalRegion(VR)) {
+ if (auto KV = State->getSVal(OriginalR).getAs<KnownSVal>())
+ BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
+ *KV, OriginalR, EnableNullFPSuppression));
+ }
+ }
+ }
+ }
+ if (action)
+ showBRDiagnostics(action, os, R, V, DS);
+
+ } else if (StoreSite->getLocation().getAs<CallEnter>()) {
+ if (const auto *VR = dyn_cast<VarRegion>(R))
+ showBRParamDiagnostics(os, VR, V);
+ }
+
+ if (os.str().empty())
+ showBRDefaultDiagnostics(os, R, V);
+
+ // Construct a new PathDiagnosticPiece.
+ ProgramPoint P = StoreSite->getLocation();
+ PathDiagnosticLocation L;
+ if (P.getAs<CallEnter>() && InitE)
+ L = PathDiagnosticLocation(InitE, BRC.getSourceManager(),
+ P.getLocationContext());
+
+ if (!L.isValid() || !L.asLocation().isValid())
+ L = PathDiagnosticLocation::create(P, BRC.getSourceManager());
+
+ if (!L.isValid() || !L.asLocation().isValid())
+ return nullptr;
+
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+}
+
+void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
+ static int tag = 0;
+ ID.AddPointer(&tag);
+ ID.AddBoolean(Assumption);
+ ID.Add(Constraint);
+}
+
+/// Return the tag associated with this visitor. This tag will be used
+/// to make all PathDiagnosticPieces created by this visitor.
+const char *TrackConstraintBRVisitor::getTag() {
+ return "TrackConstraintBRVisitor";
+}
+
+bool TrackConstraintBRVisitor::isUnderconstrained(const ExplodedNode *N) const {
+ if (IsZeroCheck)
+ return N->getState()->isNull(Constraint).isUnderconstrained();
+ return (bool)N->getState()->assume(Constraint, !Assumption);
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC, BugReport &) {
+ const ExplodedNode *PrevN = N->getFirstPred();
+ if (IsSatisfied)
+ return nullptr;
+
+ // Start tracking after we see the first state in which the value is
+ // constrained.
+ if (!IsTrackingTurnedOn)
+ if (!isUnderconstrained(N))
+ IsTrackingTurnedOn = true;
+ if (!IsTrackingTurnedOn)
+ return nullptr;
+
+ // Check if in the previous state it was feasible for this constraint
+ // to *not* be true.
+ if (isUnderconstrained(PrevN)) {
+ IsSatisfied = true;
+
+ // As a sanity check, make sure that the negation of the constraint
+ // was infeasible in the current state. If it is feasible, we somehow
+ // missed the transition point.
+ assert(!isUnderconstrained(N));
+
+ // We found the transition point for the constraint. We now need to
+ // pretty-print the constraint. (work-in-progress)
+ SmallString<64> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+
+ if (Constraint.getAs<Loc>()) {
+ os << "Assuming pointer value is ";
+ os << (Assumption ? "non-null" : "null");
+ }
+
+ if (os.str().empty())
+ return nullptr;
+
+ // Construct a new PathDiagnosticPiece.
+ ProgramPoint P = N->getLocation();
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(P, BRC.getSourceManager());
+ if (!L.isValid())
+ return nullptr;
+
+ auto X = std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+ X->setTag(getTag());
+ return std::move(X);
+ }
+
+ return nullptr;
+}
+
+SuppressInlineDefensiveChecksVisitor::
+SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N)
+ : V(Value) {
+ // Check if the visitor is disabled.
+ AnalyzerOptions &Options = N->getState()->getAnalysisManager().options;
+ if (!Options.ShouldSuppressInlinedDefensiveChecks)
+ IsSatisfied = true;
+
+ assert(N->getState()->isNull(V).isConstrainedTrue() &&
+ "The visitor only tracks the cases where V is constrained to 0");
+}
+
+void SuppressInlineDefensiveChecksVisitor::Profile(
+ llvm::FoldingSetNodeID &ID) const {
+ static int id = 0;
+ ID.AddPointer(&id);
+ ID.Add(V);
+}
+
+const char *SuppressInlineDefensiveChecksVisitor::getTag() {
+ return "IDCVisitor";
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+ const ExplodedNode *Pred = Succ->getFirstPred();
+ if (IsSatisfied)
+ return nullptr;
+
+ // Start tracking after we see the first state in which the value is null.
+ if (!IsTrackingTurnedOn)
+ if (Succ->getState()->isNull(V).isConstrainedTrue())
+ IsTrackingTurnedOn = true;
+ if (!IsTrackingTurnedOn)
+ return nullptr;
+
+ // Check if in the previous state it was feasible for this value
+ // to *not* be null.
+ if (!Pred->getState()->isNull(V).isConstrainedTrue()) {
+ IsSatisfied = true;
+
+ assert(Succ->getState()->isNull(V).isConstrainedTrue());
+
+ // Check if this is inlined defensive checks.
+ const LocationContext *CurLC =Succ->getLocationContext();
+ const LocationContext *ReportLC = BR.getErrorNode()->getLocationContext();
+ if (CurLC != ReportLC && !CurLC->isParentOf(ReportLC)) {
+ BR.markInvalid("Suppress IDC", CurLC);
+ return nullptr;
+ }
+
+ // Treat defensive checks in function-like macros as if they were an inlined
+ // defensive check. If the bug location is not in a macro and the
+ // terminator for the current location is in a macro then suppress the
+ // warning.
+ auto BugPoint = BR.getErrorNode()->getLocation().getAs<StmtPoint>();
+
+ if (!BugPoint)
+ return nullptr;
+
+ ProgramPoint CurPoint = Succ->getLocation();
+ const Stmt *CurTerminatorStmt = nullptr;
+ if (auto BE = CurPoint.getAs<BlockEdge>()) {
+ CurTerminatorStmt = BE->getSrc()->getTerminator().getStmt();
+ } else if (auto SP = CurPoint.getAs<StmtPoint>()) {
+ const Stmt *CurStmt = SP->getStmt();
+ if (!CurStmt->getBeginLoc().isMacroID())
+ return nullptr;
+
+ CFGStmtMap *Map = CurLC->getAnalysisDeclContext()->getCFGStmtMap();
+ CurTerminatorStmt = Map->getBlock(CurStmt)->getTerminator();
+ } else {
+ return nullptr;
+ }
+
+ if (!CurTerminatorStmt)
+ return nullptr;
+
+ SourceLocation TerminatorLoc = CurTerminatorStmt->getBeginLoc();
+ if (TerminatorLoc.isMacroID()) {
+ SourceLocation BugLoc = BugPoint->getStmt()->getBeginLoc();
+
+ // Suppress reports unless we are in that same macro.
+ if (!BugLoc.isMacroID() ||
+ getMacroName(BugLoc, BRC) != getMacroName(TerminatorLoc, BRC)) {
+ BR.markInvalid("Suppress Macro IDC", CurLC);
+ }
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+static const MemRegion *getLocationRegionIfReference(const Expr *E,
+ const ExplodedNode *N) {
+ if (const auto *DR = dyn_cast<DeclRefExpr>(E)) {
+ if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ if (!VD->getType()->isReferenceType())
+ return nullptr;
+ ProgramStateManager &StateMgr = N->getState()->getStateManager();
+ MemRegionManager &MRMgr = StateMgr.getRegionManager();
+ return MRMgr.getVarRegion(VD, N->getLocationContext());
+ }
+ }
+
+ // FIXME: This does not handle other kinds of null references,
+ // for example, references from FieldRegions:
+ // struct Wrapper { int &ref; };
+ // Wrapper w = { *(int *)0 };
+ // w.ref = 1;
+
+ return nullptr;
+}
+
+/// \return A subexpression of {@code Ex} which represents the
+/// expression-of-interest.
+static const Expr *peelOffOuterExpr(const Expr *Ex,
+ const ExplodedNode *N) {
+ Ex = Ex->IgnoreParenCasts();
+ if (const auto *FE = dyn_cast<FullExpr>(Ex))
+ return peelOffOuterExpr(FE->getSubExpr(), N);
+ if (const auto *OVE = dyn_cast<OpaqueValueExpr>(Ex))
+ return peelOffOuterExpr(OVE->getSourceExpr(), N);
+ if (const auto *POE = dyn_cast<PseudoObjectExpr>(Ex)) {
+ const auto *PropRef = dyn_cast<ObjCPropertyRefExpr>(POE->getSyntacticForm());
+ if (PropRef && PropRef->isMessagingGetter()) {
+ const Expr *GetterMessageSend =
+ POE->getSemanticExpr(POE->getNumSemanticExprs() - 1);
+ assert(isa<ObjCMessageExpr>(GetterMessageSend->IgnoreParenCasts()));
+ return peelOffOuterExpr(GetterMessageSend, N);
+ }
+ }
+
+ // Peel off the ternary operator.
+ if (const auto *CO = dyn_cast<ConditionalOperator>(Ex)) {
+ // Find a node where the branching occurred and find out which branch
+ // we took (true/false) by looking at the ExplodedGraph.
+ const ExplodedNode *NI = N;
+ do {
+ ProgramPoint ProgPoint = NI->getLocation();
+ if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
+ const CFGBlock *srcBlk = BE->getSrc();
+ if (const Stmt *term = srcBlk->getTerminator()) {
+ if (term == CO) {
+ bool TookTrueBranch = (*(srcBlk->succ_begin()) == BE->getDst());
+ if (TookTrueBranch)
+ return peelOffOuterExpr(CO->getTrueExpr(), N);
+ else
+ return peelOffOuterExpr(CO->getFalseExpr(), N);
+ }
+ }
+ }
+ NI = NI->getFirstPred();
+ } while (NI);
+ }
+
+ if (auto *BO = dyn_cast<BinaryOperator>(Ex))
+ if (const Expr *SubEx = peelOffPointerArithmetic(BO))
+ return peelOffOuterExpr(SubEx, N);
+
+ if (auto *UO = dyn_cast<UnaryOperator>(Ex)) {
+ if (UO->getOpcode() == UO_LNot)
+ return peelOffOuterExpr(UO->getSubExpr(), N);
+
+ // FIXME: There's a hack in our Store implementation that always computes
+ // field offsets around null pointers as if they are always equal to 0.
+ // The idea here is to report accesses to fields as null dereferences
+ // even though the pointer value that's being dereferenced is actually
+ // the offset of the field rather than exactly 0.
+ // See the FIXME in StoreManager's getLValueFieldOrIvar() method.
+ // This code interacts heavily with this hack; otherwise the value
+ // would not be null at all for most fields, so we'd be unable to track it.
+ if (UO->getOpcode() == UO_AddrOf && UO->getSubExpr()->isLValue())
+ if (const Expr *DerefEx = bugreporter::getDerefExpr(UO->getSubExpr()))
+ return peelOffOuterExpr(DerefEx, N);
+ }
+
+ return Ex;
+}
+
+/// Find the ExplodedNode where the lvalue (the value of 'Ex')
+/// was computed.
+static const ExplodedNode* findNodeForExpression(const ExplodedNode *N,
+ const Expr *Inner) {
+ while (N) {
+ if (PathDiagnosticLocation::getStmt(N) == Inner)
+ return N;
+ N = N->getFirstPred();
+ }
+ return N;
+}
+
+bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
+ const Expr *E, BugReport &report,
+ bool EnableNullFPSuppression) {
+ if (!E || !InputNode)
+ return false;
+
+ const Expr *Inner = peelOffOuterExpr(E, InputNode);
+ const ExplodedNode *LVNode = findNodeForExpression(InputNode, Inner);
+ if (!LVNode)
+ return false;
+
+ ProgramStateRef LVState = LVNode->getState();
+
+ // The message send could be nil due to the receiver being nil.
+ // At this point in the path, the receiver should be live since we are at the
+ // message send expr. If it is nil, start tracking it.
+ if (const Expr *Receiver = NilReceiverBRVisitor::getNilReceiver(Inner, LVNode))
+ trackExpressionValue(LVNode, Receiver, report, EnableNullFPSuppression);
+
+ // See if the expression we're interested refers to a variable.
+ // If so, we can track both its contents and constraints on its value.
+ if (ExplodedGraph::isInterestingLValueExpr(Inner)) {
+ SVal LVal = LVNode->getSVal(Inner);
+
+ const MemRegion *RR = getLocationRegionIfReference(Inner, LVNode);
+ bool LVIsNull = LVState->isNull(LVal).isConstrainedTrue();
+
+ // If this is a C++ reference to a null pointer, we are tracking the
+ // pointer. In addition, we should find the store at which the reference
+ // got initialized.
+ if (RR && !LVIsNull)
+ if (auto KV = LVal.getAs<KnownSVal>())
+ report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
+ *KV, RR, EnableNullFPSuppression));
+
+ // In case of C++ references, we want to differentiate between a null
+ // reference and reference to null pointer.
+ // If the LVal is null, check if we are dealing with null reference.
+ // For those, we want to track the location of the reference.
+ const MemRegion *R = (RR && LVIsNull) ? RR :
+ LVNode->getSVal(Inner).getAsRegion();
+
+ if (R) {
+
+ // Mark both the variable region and its contents as interesting.
+ SVal V = LVState->getRawSVal(loc::MemRegionVal(R));
+ report.addVisitor(
+ llvm::make_unique<NoStoreFuncVisitor>(cast<SubRegion>(R)));
+
+ MacroNullReturnSuppressionVisitor::addMacroVisitorIfNecessary(
+ LVNode, R, EnableNullFPSuppression, report, V);
+
+ report.markInteresting(V);
+ report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(R));
+
+ // If the contents are symbolic, find out when they became null.
+ if (V.getAsLocSymbol(/*IncludeBaseRegions*/ true))
+ report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>(
+ V.castAs<DefinedSVal>(), false));
+
+ // Add visitor, which will suppress inline defensive checks.
+ if (auto DV = V.getAs<DefinedSVal>())
+ if (!DV->isZeroConstant() && LVState->isNull(*DV).isConstrainedTrue() &&
+ EnableNullFPSuppression)
+ report.addVisitor(
+ llvm::make_unique<SuppressInlineDefensiveChecksVisitor>(*DV,
+ LVNode));
+
+ if (auto KV = V.getAs<KnownSVal>())
+ report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
+ *KV, R, EnableNullFPSuppression));
+ return true;
+ }
+ }
+
+ // If the expression is not an "lvalue expression", we can still
+ // track the constraints on its contents.
+ SVal V = LVState->getSValAsScalarOrLoc(Inner, LVNode->getLocationContext());
+
+ ReturnVisitor::addVisitorIfNecessary(
+ LVNode, Inner, report, EnableNullFPSuppression);
+
+ // Is it a symbolic value?
+ if (auto L = V.getAs<loc::MemRegionVal>()) {
+ report.addVisitor(llvm::make_unique<UndefOrNullArgVisitor>(L->getRegion()));
+
+ // FIXME: this is a hack for fixing a later crash when attempting to
+ // dereference a void* pointer.
+ // We should not try to dereference pointers at all when we don't care
+ // what is written inside the pointer.
+ bool CanDereference = true;
+ if (const auto *SR = dyn_cast<SymbolicRegion>(L->getRegion()))
+ if (SR->getSymbol()->getType()->getPointeeType()->isVoidType())
+ CanDereference = false;
+
+ // At this point we are dealing with the region's LValue.
+ // However, if the rvalue is a symbolic region, we should track it as well.
+ // Try to use the correct type when looking up the value.
+ SVal RVal;
+ if (ExplodedGraph::isInterestingLValueExpr(Inner)) {
+ RVal = LVState->getRawSVal(L.getValue(), Inner->getType());
+ } else if (CanDereference) {
+ RVal = LVState->getSVal(L->getRegion());
+ }
+
+ if (CanDereference)
+ if (auto KV = RVal.getAs<KnownSVal>())
+ report.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
+ *KV, L->getRegion(), EnableNullFPSuppression));
+
+ const MemRegion *RegionRVal = RVal.getAsRegion();
+ if (RegionRVal && isa<SymbolicRegion>(RegionRVal)) {
+ report.markInteresting(RegionRVal);
+ report.addVisitor(llvm::make_unique<TrackConstraintBRVisitor>(
+ loc::MemRegionVal(RegionRVal), /*assumption=*/false));
+ }
+ }
+ return true;
+}
+
+const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S,
+ const ExplodedNode *N) {
+ const auto *ME = dyn_cast<ObjCMessageExpr>(S);
+ if (!ME)
+ return nullptr;
+ if (const Expr *Receiver = ME->getInstanceReceiver()) {
+ ProgramStateRef state = N->getState();
+ SVal V = N->getSVal(Receiver);
+ if (state->isNull(V).isConstrainedTrue())
+ return Receiver;
+ }
+ return nullptr;
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+NilReceiverBRVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC, BugReport &BR) {
+ Optional<PreStmt> P = N->getLocationAs<PreStmt>();
+ if (!P)
+ return nullptr;
+
+ const Stmt *S = P->getStmt();
+ const Expr *Receiver = getNilReceiver(S, N);
+ if (!Receiver)
+ return nullptr;
+
+ llvm::SmallString<256> Buf;
+ llvm::raw_svector_ostream OS(Buf);
+
+ if (const auto *ME = dyn_cast<ObjCMessageExpr>(S)) {
+ OS << "'";
+ ME->getSelector().print(OS);
+ OS << "' not called";
+ }
+ else {
+ OS << "No method is called";
+ }
+ OS << " because the receiver is nil";
+
+ // The receiver was nil, and hence the method was skipped.
+ // Register a BugReporterVisitor to issue a message telling us how
+ // the receiver was null.
+ bugreporter::trackExpressionValue(N, Receiver, BR,
+ /*EnableNullFPSuppression*/ false);
+ // Issue a message saying that the method was skipped.
+ PathDiagnosticLocation L(Receiver, BRC.getSourceManager(),
+ N->getLocationContext());
+ return std::make_shared<PathDiagnosticEventPiece>(L, OS.str());
+}
+
+// Registers every VarDecl inside a Stmt with a last store visitor.
+void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR,
+ const Stmt *S,
+ bool EnableNullFPSuppression) {
+ const ExplodedNode *N = BR.getErrorNode();
+ std::deque<const Stmt *> WorkList;
+ WorkList.push_back(S);
+
+ while (!WorkList.empty()) {
+ const Stmt *Head = WorkList.front();
+ WorkList.pop_front();
+
+ ProgramStateManager &StateMgr = N->getState()->getStateManager();
+
+ if (const auto *DR = dyn_cast<DeclRefExpr>(Head)) {
+ if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ const VarRegion *R =
+ StateMgr.getRegionManager().getVarRegion(VD, N->getLocationContext());
+
+ // What did we load?
+ SVal V = N->getSVal(S);
+
+ if (V.getAs<loc::ConcreteInt>() || V.getAs<nonloc::ConcreteInt>()) {
+ // Register a new visitor with the BugReport.
+ BR.addVisitor(llvm::make_unique<FindLastStoreBRVisitor>(
+ V.castAs<KnownSVal>(), R, EnableNullFPSuppression));
+ }
+ }
+ }
+
+ for (const Stmt *SubStmt : Head->children())
+ WorkList.push_back(SubStmt);
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Visitor that tries to report interesting diagnostics from conditions.
+//===----------------------------------------------------------------------===//
+
+/// Return the tag associated with this visitor. This tag will be used
+/// to make all PathDiagnosticPieces created by this visitor.
+const char *ConditionBRVisitor::getTag() {
+ return "ConditionBRVisitor";
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+ConditionBRVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC, BugReport &BR) {
+ auto piece = VisitNodeImpl(N, BRC, BR);
+ if (piece) {
+ piece->setTag(getTag());
+ if (auto *ev = dyn_cast<PathDiagnosticEventPiece>(piece.get()))
+ ev->setPrunable(true, /* override */ false);
+ }
+ return piece;
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N,
+ BugReporterContext &BRC, BugReport &BR) {
+ ProgramPoint progPoint = N->getLocation();
+ ProgramStateRef CurrentState = N->getState();
+ ProgramStateRef PrevState = N->getFirstPred()->getState();
+
+ // Compare the GDMs of the state, because that is where constraints
+ // are managed. Note that ensure that we only look at nodes that
+ // were generated by the analyzer engine proper, not checkers.
+ if (CurrentState->getGDM().getRoot() ==
+ PrevState->getGDM().getRoot())
+ return nullptr;
+
+ // If an assumption was made on a branch, it should be caught
+ // here by looking at the state transition.
+ if (Optional<BlockEdge> BE = progPoint.getAs<BlockEdge>()) {
+ const CFGBlock *srcBlk = BE->getSrc();
+ if (const Stmt *term = srcBlk->getTerminator())
+ return VisitTerminator(term, N, srcBlk, BE->getDst(), BR, BRC);
+ return nullptr;
+ }
+
+ if (Optional<PostStmt> PS = progPoint.getAs<PostStmt>()) {
+ const std::pair<const ProgramPointTag *, const ProgramPointTag *> &tags =
+ ExprEngine::geteagerlyAssumeBinOpBifurcationTags();
+
+ const ProgramPointTag *tag = PS->getTag();
+ if (tag == tags.first)
+ return VisitTrueTest(cast<Expr>(PS->getStmt()), true,
+ BRC, BR, N);
+ if (tag == tags.second)
+ return VisitTrueTest(cast<Expr>(PS->getStmt()), false,
+ BRC, BR, N);
+
+ return nullptr;
+ }
+
+ return nullptr;
+}
+
+std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator(
+ const Stmt *Term, const ExplodedNode *N, const CFGBlock *srcBlk,
+ const CFGBlock *dstBlk, BugReport &R, BugReporterContext &BRC) {
+ const Expr *Cond = nullptr;
+
+ // In the code below, Term is a CFG terminator and Cond is a branch condition
+ // expression upon which the decision is made on this terminator.
+ //
+ // For example, in "if (x == 0)", the "if (x == 0)" statement is a terminator,
+ // and "x == 0" is the respective condition.
+ //
+ // Another example: in "if (x && y)", we've got two terminators and two
+ // conditions due to short-circuit nature of operator "&&":
+ // 1. The "if (x && y)" statement is a terminator,
+ // and "y" is the respective condition.
+ // 2. Also "x && ..." is another terminator,
+ // and "x" is its condition.
+
+ switch (Term->getStmtClass()) {
+ // FIXME: Stmt::SwitchStmtClass is worth handling, however it is a bit
+ // more tricky because there are more than two branches to account for.
+ default:
+ return nullptr;
+ case Stmt::IfStmtClass:
+ Cond = cast<IfStmt>(Term)->getCond();
+ break;
+ case Stmt::ConditionalOperatorClass:
+ Cond = cast<ConditionalOperator>(Term)->getCond();
+ break;
+ case Stmt::BinaryOperatorClass:
+ // When we encounter a logical operator (&& or ||) as a CFG terminator,
+ // then the condition is actually its LHS; otherwise, we'd encounter
+ // the parent, such as if-statement, as a terminator.
+ const auto *BO = cast<BinaryOperator>(Term);
+ assert(BO->isLogicalOp() &&
+ "CFG terminator is not a short-circuit operator!");
+ Cond = BO->getLHS();
+ break;
+ }
+
+ // However, when we encounter a logical operator as a branch condition,
+ // then the condition is actually its RHS, because LHS would be
+ // the condition for the logical operator terminator.
+ while (const auto *InnerBO = dyn_cast<BinaryOperator>(Cond)) {
+ if (!InnerBO->isLogicalOp())
+ break;
+ Cond = InnerBO->getRHS()->IgnoreParens();
+ }
+
+ assert(Cond);
+ assert(srcBlk->succ_size() == 2);
+ const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk;
+ return VisitTrueTest(Cond, tookTrue, BRC, R, N);
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue,
+ BugReporterContext &BRC, BugReport &R,
+ const ExplodedNode *N) {
+ // These will be modified in code below, but we need to preserve the original
+ // values in case we want to throw the generic message.
+ const Expr *CondTmp = Cond;
+ bool tookTrueTmp = tookTrue;
+
+ while (true) {
+ CondTmp = CondTmp->IgnoreParenCasts();
+ switch (CondTmp->getStmtClass()) {
+ default:
+ break;
+ case Stmt::BinaryOperatorClass:
+ if (auto P = VisitTrueTest(Cond, cast<BinaryOperator>(CondTmp),
+ tookTrueTmp, BRC, R, N))
+ return P;
+ break;
+ case Stmt::DeclRefExprClass:
+ if (auto P = VisitTrueTest(Cond, cast<DeclRefExpr>(CondTmp),
+ tookTrueTmp, BRC, R, N))
+ return P;
+ break;
+ case Stmt::UnaryOperatorClass: {
+ const auto *UO = cast<UnaryOperator>(CondTmp);
+ if (UO->getOpcode() == UO_LNot) {
+ tookTrueTmp = !tookTrueTmp;
+ CondTmp = UO->getSubExpr();
+ continue;
+ }
+ break;
+ }
+ }
+ break;
+ }
+
+ // Condition too complex to explain? Just say something so that the user
+ // knew we've made some path decision at this point.
+ const LocationContext *LCtx = N->getLocationContext();
+ PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
+ if (!Loc.isValid() || !Loc.asLocation().isValid())
+ return nullptr;
+
+ return std::make_shared<PathDiagnosticEventPiece>(
+ Loc, tookTrue ? GenericTrueMessage : GenericFalseMessage);
+}
+
+bool ConditionBRVisitor::patternMatch(const Expr *Ex,
+ const Expr *ParentEx,
+ raw_ostream &Out,
+ BugReporterContext &BRC,
+ BugReport &report,
+ const ExplodedNode *N,
+ Optional<bool> &prunable) {
+ const Expr *OriginalExpr = Ex;
+ Ex = Ex->IgnoreParenCasts();
+
+ // Use heuristics to determine if Ex is a macro expending to a literal and
+ // if so, use the macro's name.
+ SourceLocation LocStart = Ex->getBeginLoc();
+ SourceLocation LocEnd = Ex->getEndLoc();
+ if (LocStart.isMacroID() && LocEnd.isMacroID() &&
+ (isa<GNUNullExpr>(Ex) ||
+ isa<ObjCBoolLiteralExpr>(Ex) ||
+ isa<CXXBoolLiteralExpr>(Ex) ||
+ isa<IntegerLiteral>(Ex) ||
+ isa<FloatingLiteral>(Ex))) {
+ StringRef StartName = Lexer::getImmediateMacroNameForDiagnostics(LocStart,
+ BRC.getSourceManager(), BRC.getASTContext().getLangOpts());
+ StringRef EndName = Lexer::getImmediateMacroNameForDiagnostics(LocEnd,
+ BRC.getSourceManager(), BRC.getASTContext().getLangOpts());
+ bool beginAndEndAreTheSameMacro = StartName.equals(EndName);
+
+ bool partOfParentMacro = false;
+ if (ParentEx->getBeginLoc().isMacroID()) {
+ StringRef PName = Lexer::getImmediateMacroNameForDiagnostics(
+ ParentEx->getBeginLoc(), BRC.getSourceManager(),
+ BRC.getASTContext().getLangOpts());
+ partOfParentMacro = PName.equals(StartName);
+ }
+
+ if (beginAndEndAreTheSameMacro && !partOfParentMacro ) {
+ // Get the location of the macro name as written by the caller.
+ SourceLocation Loc = LocStart;
+ while (LocStart.isMacroID()) {
+ Loc = LocStart;
+ LocStart = BRC.getSourceManager().getImmediateMacroCallerLoc(LocStart);
+ }
+ StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
+ Loc, BRC.getSourceManager(), BRC.getASTContext().getLangOpts());
+
+ // Return the macro name.
+ Out << MacroName;
+ return false;
+ }
+ }
+
+ if (const auto *DR = dyn_cast<DeclRefExpr>(Ex)) {
+ const bool quotes = isa<VarDecl>(DR->getDecl());
+ if (quotes) {
+ Out << '\'';
+ const LocationContext *LCtx = N->getLocationContext();
+ const ProgramState *state = N->getState().get();
+ if (const MemRegion *R = state->getLValue(cast<VarDecl>(DR->getDecl()),
+ LCtx).getAsRegion()) {
+ if (report.isInteresting(R))
+ prunable = false;
+ else {
+ const ProgramState *state = N->getState().get();
+ SVal V = state->getSVal(R);
+ if (report.isInteresting(V))
+ prunable = false;
+ }
+ }
+ }
+ Out << DR->getDecl()->getDeclName().getAsString();
+ if (quotes)
+ Out << '\'';
+ return quotes;
+ }
+
+ if (const auto *IL = dyn_cast<IntegerLiteral>(Ex)) {
+ QualType OriginalTy = OriginalExpr->getType();
+ if (OriginalTy->isPointerType()) {
+ if (IL->getValue() == 0) {
+ Out << "null";
+ return false;
+ }
+ }
+ else if (OriginalTy->isObjCObjectPointerType()) {
+ if (IL->getValue() == 0) {
+ Out << "nil";
+ return false;
+ }
+ }
+
+ Out << IL->getValue();
+ return false;
+ }
+
+ return false;
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr,
+ const bool tookTrue, BugReporterContext &BRC,
+ BugReport &R, const ExplodedNode *N) {
+ bool shouldInvert = false;
+ Optional<bool> shouldPrune;
+
+ SmallString<128> LhsString, RhsString;
+ {
+ llvm::raw_svector_ostream OutLHS(LhsString), OutRHS(RhsString);
+ const bool isVarLHS = patternMatch(BExpr->getLHS(), BExpr, OutLHS,
+ BRC, R, N, shouldPrune);
+ const bool isVarRHS = patternMatch(BExpr->getRHS(), BExpr, OutRHS,
+ BRC, R, N, shouldPrune);
+
+ shouldInvert = !isVarLHS && isVarRHS;
+ }
+
+ BinaryOperator::Opcode Op = BExpr->getOpcode();
+
+ if (BinaryOperator::isAssignmentOp(Op)) {
+ // For assignment operators, all that we care about is that the LHS
+ // evaluates to "true" or "false".
+ return VisitConditionVariable(LhsString, BExpr->getLHS(), tookTrue,
+ BRC, R, N);
+ }
+
+ // For non-assignment operations, we require that we can understand
+ // both the LHS and RHS.
+ if (LhsString.empty() || RhsString.empty() ||
+ !BinaryOperator::isComparisonOp(Op) || Op == BO_Cmp)
+ return nullptr;
+
+ // Should we invert the strings if the LHS is not a variable name?
+ SmallString<256> buf;
+ llvm::raw_svector_ostream Out(buf);
+ Out << "Assuming " << (shouldInvert ? RhsString : LhsString) << " is ";
+
+ // Do we need to invert the opcode?
+ if (shouldInvert)
+ switch (Op) {
+ default: break;
+ case BO_LT: Op = BO_GT; break;
+ case BO_GT: Op = BO_LT; break;
+ case BO_LE: Op = BO_GE; break;
+ case BO_GE: Op = BO_LE; break;
+ }
+
+ if (!tookTrue)
+ switch (Op) {
+ case BO_EQ: Op = BO_NE; break;
+ case BO_NE: Op = BO_EQ; break;
+ case BO_LT: Op = BO_GE; break;
+ case BO_GT: Op = BO_LE; break;
+ case BO_LE: Op = BO_GT; break;
+ case BO_GE: Op = BO_LT; break;
+ default:
+ return nullptr;
+ }
+
+ switch (Op) {
+ case BO_EQ:
+ Out << "equal to ";
+ break;
+ case BO_NE:
+ Out << "not equal to ";
+ break;
+ default:
+ Out << BinaryOperator::getOpcodeStr(Op) << ' ';
+ break;
+ }
+
+ Out << (shouldInvert ? LhsString : RhsString);
+ const LocationContext *LCtx = N->getLocationContext();
+ PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
+ auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
+ if (shouldPrune.hasValue())
+ event->setPrunable(shouldPrune.getValue());
+ return event;
+}
+
+std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable(
+ StringRef LhsString, const Expr *CondVarExpr, const bool tookTrue,
+ BugReporterContext &BRC, BugReport &report, const ExplodedNode *N) {
+ // FIXME: If there's already a constraint tracker for this variable,
+ // we shouldn't emit anything here (c.f. the double note in
+ // test/Analysis/inlining/path-notes.c)
+ SmallString<256> buf;
+ llvm::raw_svector_ostream Out(buf);
+ Out << "Assuming " << LhsString << " is ";
+
+ QualType Ty = CondVarExpr->getType();
+
+ if (Ty->isPointerType())
+ Out << (tookTrue ? "not null" : "null");
+ else if (Ty->isObjCObjectPointerType())
+ Out << (tookTrue ? "not nil" : "nil");
+ else if (Ty->isBooleanType())
+ Out << (tookTrue ? "true" : "false");
+ else if (Ty->isIntegralOrEnumerationType())
+ Out << (tookTrue ? "non-zero" : "zero");
+ else
+ return nullptr;
+
+ const LocationContext *LCtx = N->getLocationContext();
+ PathDiagnosticLocation Loc(CondVarExpr, BRC.getSourceManager(), LCtx);
+ auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
+
+ if (const auto *DR = dyn_cast<DeclRefExpr>(CondVarExpr)) {
+ if (const auto *VD = dyn_cast<VarDecl>(DR->getDecl())) {
+ const ProgramState *state = N->getState().get();
+ if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) {
+ if (report.isInteresting(R))
+ event->setPrunable(false);
+ }
+ }
+ }
+
+ return event;
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR,
+ const bool tookTrue, BugReporterContext &BRC,
+ BugReport &report, const ExplodedNode *N) {
+ const auto *VD = dyn_cast<VarDecl>(DR->getDecl());
+ if (!VD)
+ return nullptr;
+
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+
+ Out << "Assuming '" << VD->getDeclName() << "' is ";
+
+ QualType VDTy = VD->getType();
+
+ if (VDTy->isPointerType())
+ Out << (tookTrue ? "non-null" : "null");
+ else if (VDTy->isObjCObjectPointerType())
+ Out << (tookTrue ? "non-nil" : "nil");
+ else if (VDTy->isScalarType())
+ Out << (tookTrue ? "not equal to 0" : "0");
+ else
+ return nullptr;
+
+ const LocationContext *LCtx = N->getLocationContext();
+ PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
+ auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
+
+ const ProgramState *state = N->getState().get();
+ if (const MemRegion *R = state->getLValue(VD, LCtx).getAsRegion()) {
+ if (report.isInteresting(R))
+ event->setPrunable(false);
+ else {
+ SVal V = state->getSVal(R);
+ if (report.isInteresting(V))
+ event->setPrunable(false);
+ }
+ }
+ return std::move(event);
+}
+
+const char *const ConditionBRVisitor::GenericTrueMessage =
+ "Assuming the condition is true";
+const char *const ConditionBRVisitor::GenericFalseMessage =
+ "Assuming the condition is false";
+
+bool ConditionBRVisitor::isPieceMessageGeneric(
+ const PathDiagnosticPiece *Piece) {
+ return Piece->getString() == GenericTrueMessage ||
+ Piece->getString() == GenericFalseMessage;
+}
+
+void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor(
+ BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR) {
+ // Here we suppress false positives coming from system headers. This list is
+ // based on known issues.
+ AnalyzerOptions &Options = BRC.getAnalyzerOptions();
+ const Decl *D = N->getLocationContext()->getDecl();
+
+ if (AnalysisDeclContext::isInStdNamespace(D)) {
+ // Skip reports within the 'std' namespace. Although these can sometimes be
+ // the user's fault, we currently don't report them very well, and
+ // Note that this will not help for any other data structure libraries, like
+ // TR1, Boost, or llvm/ADT.
+ if (Options.ShouldSuppressFromCXXStandardLibrary) {
+ BR.markInvalid(getTag(), nullptr);
+ return;
+ } else {
+ // If the complete 'std' suppression is not enabled, suppress reports
+ // from the 'std' namespace that are known to produce false positives.
+
+ // The analyzer issues a false use-after-free when std::list::pop_front
+ // or std::list::pop_back are called multiple times because we cannot
+ // reason about the internal invariants of the data structure.
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
+ const CXXRecordDecl *CD = MD->getParent();
+ if (CD->getName() == "list") {
+ BR.markInvalid(getTag(), nullptr);
+ return;
+ }
+ }
+
+ // The analyzer issues a false positive when the constructor of
+ // std::__independent_bits_engine from algorithms is used.
+ if (const auto *MD = dyn_cast<CXXConstructorDecl>(D)) {
+ const CXXRecordDecl *CD = MD->getParent();
+ if (CD->getName() == "__independent_bits_engine") {
+ BR.markInvalid(getTag(), nullptr);
+ return;
+ }
+ }
+
+ for (const LocationContext *LCtx = N->getLocationContext(); LCtx;
+ LCtx = LCtx->getParent()) {
+ const auto *MD = dyn_cast<CXXMethodDecl>(LCtx->getDecl());
+ if (!MD)
+ continue;
+
+ const CXXRecordDecl *CD = MD->getParent();
+ // The analyzer issues a false positive on
+ // std::basic_string<uint8_t> v; v.push_back(1);
+ // and
+ // std::u16string s; s += u'a';
+ // because we cannot reason about the internal invariants of the
+ // data structure.
+ if (CD->getName() == "basic_string") {
+ BR.markInvalid(getTag(), nullptr);
+ return;
+ }
+
+ // The analyzer issues a false positive on
+ // std::shared_ptr<int> p(new int(1)); p = nullptr;
+ // because it does not reason properly about temporary destructors.
+ if (CD->getName() == "shared_ptr") {
+ BR.markInvalid(getTag(), nullptr);
+ return;
+ }
+ }
+ }
+ }
+
+ // Skip reports within the sys/queue.h macros as we do not have the ability to
+ // reason about data structure shapes.
+ SourceManager &SM = BRC.getSourceManager();
+ FullSourceLoc Loc = BR.getLocation(SM).asLocation();
+ while (Loc.isMacroID()) {
+ Loc = Loc.getSpellingLoc();
+ if (SM.getFilename(Loc).endswith("sys/queue.h")) {
+ BR.markInvalid(getTag(), nullptr);
+ return;
+ }
+ }
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC, BugReport &BR) {
+ ProgramStateRef State = N->getState();
+ ProgramPoint ProgLoc = N->getLocation();
+
+ // We are only interested in visiting CallEnter nodes.
+ Optional<CallEnter> CEnter = ProgLoc.getAs<CallEnter>();
+ if (!CEnter)
+ return nullptr;
+
+ // Check if one of the arguments is the region the visitor is tracking.
+ CallEventManager &CEMgr = BRC.getStateManager().getCallEventManager();
+ CallEventRef<> Call = CEMgr.getCaller(CEnter->getCalleeContext(), State);
+ unsigned Idx = 0;
+ ArrayRef<ParmVarDecl *> parms = Call->parameters();
+
+ for (const auto ParamDecl : parms) {
+ const MemRegion *ArgReg = Call->getArgSVal(Idx).getAsRegion();
+ ++Idx;
+
+ // Are we tracking the argument or its subregion?
+ if ( !ArgReg || !R->isSubRegionOf(ArgReg->StripCasts()))
+ continue;
+
+ // Check the function parameter type.
+ assert(ParamDecl && "Formal parameter has no decl?");
+ QualType T = ParamDecl->getType();
+
+ if (!(T->isAnyPointerType() || T->isReferenceType())) {
+ // Function can only change the value passed in by address.
+ continue;
+ }
+
+ // If it is a const pointer value, the function does not intend to
+ // change the value.
+ if (T->getPointeeType().isConstQualified())
+ continue;
+
+ // Mark the call site (LocationContext) as interesting if the value of the
+ // argument is undefined or '0'/'NULL'.
+ SVal BoundVal = State->getSVal(R);
+ if (BoundVal.isUndef() || BoundVal.isZeroConstant()) {
+ BR.markInteresting(CEnter->getCalleeContext());
+ return nullptr;
+ }
+ }
+ return nullptr;
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+CXXSelfAssignmentBRVisitor::VisitNode(const ExplodedNode *Succ,
+ BugReporterContext &BRC, BugReport &) {
+ if (Satisfied)
+ return nullptr;
+
+ const auto Edge = Succ->getLocation().getAs<BlockEdge>();
+ if (!Edge.hasValue())
+ return nullptr;
+
+ auto Tag = Edge->getTag();
+ if (!Tag)
+ return nullptr;
+
+ if (Tag->getTagDescription() != "cplusplus.SelfAssignment")
+ return nullptr;
+
+ Satisfied = true;
+
+ const auto *Met =
+ dyn_cast<CXXMethodDecl>(Succ->getCodeDecl().getAsFunction());
+ assert(Met && "Not a C++ method.");
+ assert((Met->isCopyAssignmentOperator() || Met->isMoveAssignmentOperator()) &&
+ "Not a copy/move assignment operator.");
+
+ const auto *LCtx = Edge->getLocationContext();
+
+ const auto &State = Succ->getState();
+ auto &SVB = State->getStateManager().getSValBuilder();
+
+ const auto Param =
+ State->getSVal(State->getRegion(Met->getParamDecl(0), LCtx));
+ const auto This =
+ State->getSVal(SVB.getCXXThis(Met, LCtx->getStackFrame()));
+
+ auto L = PathDiagnosticLocation::create(Met, BRC.getSourceManager());
+
+ if (!L.isValid() || !L.asLocation().isValid())
+ return nullptr;
+
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+
+ Out << "Assuming " << Met->getParamDecl(0)->getName() <<
+ ((Param == This) ? " == " : " != ") << "*this";
+
+ auto Piece = std::make_shared<PathDiagnosticEventPiece>(L, Out.str());
+ Piece->addRange(Met->getSourceRange());
+
+ return std::move(Piece);
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+TaintBugVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC, BugReport &) {
+
+ // Find the ExplodedNode where the taint was first introduced
+ if (!N->getState()->isTainted(V) || N->getFirstPred()->getState()->isTainted(V))
+ return nullptr;
+
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ if (!S)
+ return nullptr;
+
+ const LocationContext *NCtx = N->getLocationContext();
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::createBegin(S, BRC.getSourceManager(), NCtx);
+ if (!L.isValid() || !L.asLocation().isValid())
+ return nullptr;
+
+ return std::make_shared<PathDiagnosticEventPiece>(L, "Taint originated here");
+}
+
+FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor()
+ : Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {}
+
+void FalsePositiveRefutationBRVisitor::finalizeVisitor(
+ BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) {
+ // Collect new constraints
+ VisitNode(EndPathNode, BRC, BR);
+
+ // Create a refutation manager
+ SMTSolverRef RefutationSolver = CreateZ3Solver();
+ ASTContext &Ctx = BRC.getASTContext();
+
+ // Add constraints to the solver
+ for (const auto &I : Constraints) {
+ const SymbolRef Sym = I.first;
+ auto RangeIt = I.second.begin();
+
+ SMTExprRef Constraints = SMTConv::getRangeExpr(
+ RefutationSolver, Ctx, Sym, RangeIt->From(), RangeIt->To(),
+ /*InRange=*/true);
+ while ((++RangeIt) != I.second.end()) {
+ Constraints = RefutationSolver->mkOr(
+ Constraints, SMTConv::getRangeExpr(RefutationSolver, Ctx, Sym,
+ RangeIt->From(), RangeIt->To(),
+ /*InRange=*/true));
+ }
+
+ RefutationSolver->addConstraint(Constraints);
+ }
+
+ // And check for satisfiability
+ Optional<bool> isSat = RefutationSolver->check();
+ if (!isSat.hasValue())
+ return;
+
+ if (!isSat.getValue())
+ BR.markInvalid("Infeasible constraints", EndPathNode->getLocationContext());
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+FalsePositiveRefutationBRVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &,
+ BugReport &) {
+ // Collect new constraints
+ const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>();
+ ConstraintRangeTy::Factory &CF =
+ N->getState()->get_context<ConstraintRange>();
+
+ // Add constraints if we don't have them yet
+ for (auto const &C : NewCs) {
+ const SymbolRef &Sym = C.first;
+ if (!Constraints.contains(Sym)) {
+ Constraints = CF.add(Constraints, Sym, C.second);
+ }
+ }
+
+ return nullptr;
+}
+
+void FalsePositiveRefutationBRVisitor::Profile(
+ llvm::FoldingSetNodeID &ID) const {
+ static int Tag = 0;
+ ID.AddPointer(&Tag);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
new file mode 100644
index 000000000000..0e7f31502e81
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -0,0 +1,1424 @@
+//===- CallEvent.cpp - Wrapper for all function and method calls ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file This file defines CallEvent and its subclasses, which represent path-
+/// sensitive instances of different kinds of function and method calls
+/// (C, C++, and Objective-C).
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/CFGStmtMap.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/CrossTU/CrossTranslationUnit.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Specifiers.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerIntPair.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <utility>
+
+#define DEBUG_TYPE "static-analyzer-call-event"
+
+using namespace clang;
+using namespace ento;
+
+QualType CallEvent::getResultType() const {
+ ASTContext &Ctx = getState()->getStateManager().getContext();
+ const Expr *E = getOriginExpr();
+ if (!E)
+ return Ctx.VoidTy;
+ assert(E);
+
+ QualType ResultTy = E->getType();
+
+ // A function that returns a reference to 'int' will have a result type
+ // of simply 'int'. Check the origin expr's value kind to recover the
+ // proper type.
+ switch (E->getValueKind()) {
+ case VK_LValue:
+ ResultTy = Ctx.getLValueReferenceType(ResultTy);
+ break;
+ case VK_XValue:
+ ResultTy = Ctx.getRValueReferenceType(ResultTy);
+ break;
+ case VK_RValue:
+ // No adjustment is necessary.
+ break;
+ }
+
+ return ResultTy;
+}
+
+static bool isCallback(QualType T) {
+ // If a parameter is a block or a callback, assume it can modify pointer.
+ if (T->isBlockPointerType() ||
+ T->isFunctionPointerType() ||
+ T->isObjCSelType())
+ return true;
+
+ // Check if a callback is passed inside a struct (for both, struct passed by
+ // reference and by value). Dig just one level into the struct for now.
+
+ if (T->isAnyPointerType() || T->isReferenceType())
+ T = T->getPointeeType();
+
+ if (const RecordType *RT = T->getAsStructureType()) {
+ const RecordDecl *RD = RT->getDecl();
+ for (const auto *I : RD->fields()) {
+ QualType FieldT = I->getType();
+ if (FieldT->isBlockPointerType() || FieldT->isFunctionPointerType())
+ return true;
+ }
+ }
+ return false;
+}
+
+static bool isVoidPointerToNonConst(QualType T) {
+ if (const auto *PT = T->getAs<PointerType>()) {
+ QualType PointeeTy = PT->getPointeeType();
+ if (PointeeTy.isConstQualified())
+ return false;
+ return PointeeTy->isVoidType();
+ } else
+ return false;
+}
+
+bool CallEvent::hasNonNullArgumentsWithType(bool (*Condition)(QualType)) const {
+ unsigned NumOfArgs = getNumArgs();
+
+ // If calling using a function pointer, assume the function does not
+ // satisfy the callback.
+ // TODO: We could check the types of the arguments here.
+ if (!getDecl())
+ return false;
+
+ unsigned Idx = 0;
+ for (CallEvent::param_type_iterator I = param_type_begin(),
+ E = param_type_end();
+ I != E && Idx < NumOfArgs; ++I, ++Idx) {
+ // If the parameter is 0, it's harmless.
+ if (getArgSVal(Idx).isZeroConstant())
+ continue;
+
+ if (Condition(*I))
+ return true;
+ }
+ return false;
+}
+
+bool CallEvent::hasNonZeroCallbackArg() const {
+ return hasNonNullArgumentsWithType(isCallback);
+}
+
+bool CallEvent::hasVoidPointerToNonConstArg() const {
+ return hasNonNullArgumentsWithType(isVoidPointerToNonConst);
+}
+
+bool CallEvent::isGlobalCFunction(StringRef FunctionName) const {
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(getDecl());
+ if (!FD)
+ return false;
+
+ return CheckerContext::isCLibraryFunction(FD, FunctionName);
+}
+
+AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const {
+ const Decl *D = getDecl();
+ if (!D)
+ return nullptr;
+
+ // TODO: For now we skip functions without definitions, even if we have
+ // our own getDecl(), because it's hard to find out which re-declaration
+ // is going to be used, and usually clients don't really care about this
+ // situation because there's a loss of precision anyway because we cannot
+ // inline the call.
+ RuntimeDefinition RD = getRuntimeDefinition();
+ if (!RD.getDecl())
+ return nullptr;
+
+ AnalysisDeclContext *ADC =
+ LCtx->getAnalysisDeclContext()->getManager()->getContext(D);
+
+ // TODO: For now we skip virtual functions, because this also rises
+ // the problem of which decl to use, but now it's across different classes.
+ if (RD.mayHaveOtherDefinitions() || RD.getDecl() != ADC->getDecl())
+ return nullptr;
+
+ return ADC;
+}
+
+const StackFrameContext *CallEvent::getCalleeStackFrame() const {
+ AnalysisDeclContext *ADC = getCalleeAnalysisDeclContext();
+ if (!ADC)
+ return nullptr;
+
+ const Expr *E = getOriginExpr();
+ if (!E)
+ return nullptr;
+
+ // Recover CFG block via reverse lookup.
+ // TODO: If we were to keep CFG element information as part of the CallEvent
+ // instead of doing this reverse lookup, we would be able to build the stack
+ // frame for non-expression-based calls, and also we wouldn't need the reverse
+ // lookup.
+ CFGStmtMap *Map = LCtx->getAnalysisDeclContext()->getCFGStmtMap();
+ const CFGBlock *B = Map->getBlock(E);
+ assert(B);
+
+ // Also recover CFG index by scanning the CFG block.
+ unsigned Idx = 0, Sz = B->size();
+ for (; Idx < Sz; ++Idx)
+ if (auto StmtElem = (*B)[Idx].getAs<CFGStmt>())
+ if (StmtElem->getStmt() == E)
+ break;
+ assert(Idx < Sz);
+
+ return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, Idx);
+}
+
+const VarRegion *CallEvent::getParameterLocation(unsigned Index) const {
+ const StackFrameContext *SFC = getCalleeStackFrame();
+ // We cannot construct a VarRegion without a stack frame.
+ if (!SFC)
+ return nullptr;
+
+ // Retrieve parameters of the definition, which are different from
+ // CallEvent's parameters() because getDecl() isn't necessarily
+ // the definition. SFC contains the definition that would be used
+ // during analysis.
+ const Decl *D = SFC->getDecl();
+
+ // TODO: Refactor into a virtual method of CallEvent, like parameters().
+ const ParmVarDecl *PVD = nullptr;
+ if (const auto *FD = dyn_cast<FunctionDecl>(D))
+ PVD = FD->parameters()[Index];
+ else if (const auto *BD = dyn_cast<BlockDecl>(D))
+ PVD = BD->parameters()[Index];
+ else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
+ PVD = MD->parameters()[Index];
+ else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D))
+ PVD = CD->parameters()[Index];
+ assert(PVD && "Unexpected Decl kind!");
+
+ const VarRegion *VR =
+ State->getStateManager().getRegionManager().getVarRegion(PVD, SFC);
+
+ // This sanity check would fail if our parameter declaration doesn't
+ // correspond to the stack frame's function declaration.
+ assert(VR->getStackFrame() == SFC);
+
+ return VR;
+}
+
+/// Returns true if a type is a pointer-to-const or reference-to-const
+/// with no further indirection.
+static bool isPointerToConst(QualType Ty) {
+ QualType PointeeTy = Ty->getPointeeType();
+ if (PointeeTy == QualType())
+ return false;
+ if (!PointeeTy.isConstQualified())
+ return false;
+ if (PointeeTy->isAnyPointerType())
+ return false;
+ return true;
+}
+
+// Try to retrieve the function declaration and find the function parameter
+// types which are pointers/references to a non-pointer const.
+// We will not invalidate the corresponding argument regions.
+static void findPtrToConstParams(llvm::SmallSet<unsigned, 4> &PreserveArgs,
+ const CallEvent &Call) {
+ unsigned Idx = 0;
+ for (CallEvent::param_type_iterator I = Call.param_type_begin(),
+ E = Call.param_type_end();
+ I != E; ++I, ++Idx) {
+ if (isPointerToConst(*I))
+ PreserveArgs.insert(Idx);
+ }
+}
+
+ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
+ ProgramStateRef Orig) const {
+ ProgramStateRef Result = (Orig ? Orig : getState());
+
+ // Don't invalidate anything if the callee is marked pure/const.
+ if (const Decl *callee = getDecl())
+ if (callee->hasAttr<PureAttr>() || callee->hasAttr<ConstAttr>())
+ return Result;
+
+ SmallVector<SVal, 8> ValuesToInvalidate;
+ RegionAndSymbolInvalidationTraits ETraits;
+
+ getExtraInvalidatedValues(ValuesToInvalidate, &ETraits);
+
+ // Indexes of arguments whose values will be preserved by the call.
+ llvm::SmallSet<unsigned, 4> PreserveArgs;
+ if (!argumentsMayEscape())
+ findPtrToConstParams(PreserveArgs, *this);
+
+ for (unsigned Idx = 0, Count = getNumArgs(); Idx != Count; ++Idx) {
+ // Mark this region for invalidation. We batch invalidate regions
+ // below for efficiency.
+ if (PreserveArgs.count(Idx))
+ if (const MemRegion *MR = getArgSVal(Idx).getAsRegion())
+ ETraits.setTrait(MR->getBaseRegion(),
+ RegionAndSymbolInvalidationTraits::TK_PreserveContents);
+ // TODO: Factor this out + handle the lower level const pointers.
+
+ ValuesToInvalidate.push_back(getArgSVal(Idx));
+
+ // If a function accepts an object by argument (which would of course be a
+ // temporary that isn't lifetime-extended), invalidate the object itself,
+ // not only other objects reachable from it. This is necessary because the
+ // destructor has access to the temporary object after the call.
+ // TODO: Support placement arguments once we start
+ // constructing them directly.
+ // TODO: This is unnecessary when there's no destructor, but that's
+ // currently hard to figure out.
+ if (getKind() != CE_CXXAllocator)
+ if (isArgumentConstructedDirectly(Idx))
+ if (auto AdjIdx = getAdjustedParameterIndex(Idx))
+ if (const VarRegion *VR = getParameterLocation(*AdjIdx))
+ ValuesToInvalidate.push_back(loc::MemRegionVal(VR));
+ }
+
+ // Invalidate designated regions using the batch invalidation API.
+ // NOTE: Even if RegionsToInvalidate is empty, we may still invalidate
+ // global variables.
+ return Result->invalidateRegions(ValuesToInvalidate, getOriginExpr(),
+ BlockCount, getLocationContext(),
+ /*CausedByPointerEscape*/ true,
+ /*Symbols=*/nullptr, this, &ETraits);
+}
+
+ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
+ const ProgramPointTag *Tag) const {
+ if (const Expr *E = getOriginExpr()) {
+ if (IsPreVisit)
+ return PreStmt(E, getLocationContext(), Tag);
+ return PostStmt(E, getLocationContext(), Tag);
+ }
+
+ const Decl *D = getDecl();
+ assert(D && "Cannot get a program point without a statement or decl");
+
+ SourceLocation Loc = getSourceRange().getBegin();
+ if (IsPreVisit)
+ return PreImplicitCall(D, Loc, getLocationContext(), Tag);
+ return PostImplicitCall(D, Loc, getLocationContext(), Tag);
+}
+
+bool CallEvent::isCalled(const CallDescription &CD) const {
+ // FIXME: Add ObjC Message support.
+ if (getKind() == CE_ObjCMessage)
+ return false;
+ if (!CD.IsLookupDone) {
+ CD.IsLookupDone = true;
+ CD.II = &getState()->getStateManager().getContext().Idents.get(
+ CD.getFunctionName());
+ }
+ const IdentifierInfo *II = getCalleeIdentifier();
+ if (!II || II != CD.II)
+ return false;
+
+ const Decl *D = getDecl();
+ // If CallDescription provides prefix names, use them to improve matching
+ // accuracy.
+ if (CD.QualifiedName.size() > 1 && D) {
+ const DeclContext *Ctx = D->getDeclContext();
+ // See if we'll be able to match them all.
+ size_t NumUnmatched = CD.QualifiedName.size() - 1;
+ for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) {
+ if (NumUnmatched == 0)
+ break;
+
+ if (const auto *ND = dyn_cast<NamespaceDecl>(Ctx)) {
+ if (ND->getName() == CD.QualifiedName[NumUnmatched - 1])
+ --NumUnmatched;
+ continue;
+ }
+
+ if (const auto *RD = dyn_cast<RecordDecl>(Ctx)) {
+ if (RD->getName() == CD.QualifiedName[NumUnmatched - 1])
+ --NumUnmatched;
+ continue;
+ }
+ }
+
+ if (NumUnmatched > 0)
+ return false;
+ }
+
+ return (CD.RequiredArgs == CallDescription::NoArgRequirement ||
+ CD.RequiredArgs == getNumArgs());
+}
+
+SVal CallEvent::getArgSVal(unsigned Index) const {
+ const Expr *ArgE = getArgExpr(Index);
+ if (!ArgE)
+ return UnknownVal();
+ return getSVal(ArgE);
+}
+
+SourceRange CallEvent::getArgSourceRange(unsigned Index) const {
+ const Expr *ArgE = getArgExpr(Index);
+ if (!ArgE)
+ return {};
+ return ArgE->getSourceRange();
+}
+
+SVal CallEvent::getReturnValue() const {
+ const Expr *E = getOriginExpr();
+ if (!E)
+ return UndefinedVal();
+ return getSVal(E);
+}
+
+LLVM_DUMP_METHOD void CallEvent::dump() const { dump(llvm::errs()); }
+
+void CallEvent::dump(raw_ostream &Out) const {
+ ASTContext &Ctx = getState()->getStateManager().getContext();
+ if (const Expr *E = getOriginExpr()) {
+ E->printPretty(Out, nullptr, Ctx.getPrintingPolicy());
+ Out << "\n";
+ return;
+ }
+
+ if (const Decl *D = getDecl()) {
+ Out << "Call to ";
+ D->print(Out, Ctx.getPrintingPolicy());
+ return;
+ }
+
+ // FIXME: a string representation of the kind would be nice.
+ Out << "Unknown call (type " << getKind() << ")";
+}
+
+bool CallEvent::isCallStmt(const Stmt *S) {
+ return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S)
+ || isa<CXXConstructExpr>(S)
+ || isa<CXXNewExpr>(S);
+}
+
+QualType CallEvent::getDeclaredResultType(const Decl *D) {
+ assert(D);
+ if (const auto *FD = dyn_cast<FunctionDecl>(D))
+ return FD->getReturnType();
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
+ return MD->getReturnType();
+ if (const auto *BD = dyn_cast<BlockDecl>(D)) {
+ // Blocks are difficult because the return type may not be stored in the
+ // BlockDecl itself. The AST should probably be enhanced, but for now we
+ // just do what we can.
+ // If the block is declared without an explicit argument list, the
+ // signature-as-written just includes the return type, not the entire
+ // function type.
+ // FIXME: All blocks should have signatures-as-written, even if the return
+ // type is inferred. (That's signified with a dependent result type.)
+ if (const TypeSourceInfo *TSI = BD->getSignatureAsWritten()) {
+ QualType Ty = TSI->getType();
+ if (const FunctionType *FT = Ty->getAs<FunctionType>())
+ Ty = FT->getReturnType();
+ if (!Ty->isDependentType())
+ return Ty;
+ }
+
+ return {};
+ }
+
+ llvm_unreachable("unknown callable kind");
+}
+
+bool CallEvent::isVariadic(const Decl *D) {
+ assert(D);
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(D))
+ return FD->isVariadic();
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
+ return MD->isVariadic();
+ if (const auto *BD = dyn_cast<BlockDecl>(D))
+ return BD->isVariadic();
+
+ llvm_unreachable("unknown callable kind");
+}
+
+static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
+ CallEvent::BindingsTy &Bindings,
+ SValBuilder &SVB,
+ const CallEvent &Call,
+ ArrayRef<ParmVarDecl*> parameters) {
+ MemRegionManager &MRMgr = SVB.getRegionManager();
+
+ // If the function has fewer parameters than the call has arguments, we simply
+ // do not bind any values to them.
+ unsigned NumArgs = Call.getNumArgs();
+ unsigned Idx = 0;
+ ArrayRef<ParmVarDecl*>::iterator I = parameters.begin(), E = parameters.end();
+ for (; I != E && Idx < NumArgs; ++I, ++Idx) {
+ const ParmVarDecl *ParamDecl = *I;
+ assert(ParamDecl && "Formal parameter has no decl?");
+
+ // TODO: Support allocator calls.
+ if (Call.getKind() != CE_CXXAllocator)
+ if (Call.isArgumentConstructedDirectly(Idx))
+ continue;
+
+ // TODO: Allocators should receive the correct size and possibly alignment,
+ // determined in compile-time but not represented as arg-expressions,
+ // which makes getArgSVal() fail and return UnknownVal.
+ SVal ArgVal = Call.getArgSVal(Idx);
+ if (!ArgVal.isUnknown()) {
+ Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx));
+ Bindings.push_back(std::make_pair(ParamLoc, ArgVal));
+ }
+ }
+
+ // FIXME: Variadic arguments are not handled at all right now.
+}
+
+ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const {
+ const FunctionDecl *D = getDecl();
+ if (!D)
+ return None;
+ return D->parameters();
+}
+
+RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const {
+ const FunctionDecl *FD = getDecl();
+ if (!FD)
+ return {};
+
+ // Note that the AnalysisDeclContext will have the FunctionDecl with
+ // the definition (if one exists).
+ AnalysisDeclContext *AD =
+ getLocationContext()->getAnalysisDeclContext()->
+ getManager()->getContext(FD);
+ bool IsAutosynthesized;
+ Stmt* Body = AD->getBody(IsAutosynthesized);
+ LLVM_DEBUG({
+ if (IsAutosynthesized)
+ llvm::dbgs() << "Using autosynthesized body for " << FD->getName()
+ << "\n";
+ });
+ if (Body) {
+ const Decl* Decl = AD->getDecl();
+ return RuntimeDefinition(Decl);
+ }
+
+ SubEngine &Engine = getState()->getStateManager().getOwningEngine();
+ AnalyzerOptions &Opts = Engine.getAnalysisManager().options;
+
+ // Try to get CTU definition only if CTUDir is provided.
+ if (!Opts.IsNaiveCTUEnabled)
+ return {};
+
+ cross_tu::CrossTranslationUnitContext &CTUCtx =
+ *Engine.getCrossTranslationUnitContext();
+ llvm::Expected<const FunctionDecl *> CTUDeclOrError =
+ CTUCtx.getCrossTUDefinition(FD, Opts.CTUDir, Opts.CTUIndexName,
+ Opts.DisplayCTUProgress);
+
+ if (!CTUDeclOrError) {
+ handleAllErrors(CTUDeclOrError.takeError(),
+ [&](const cross_tu::IndexError &IE) {
+ CTUCtx.emitCrossTUDiagnostics(IE);
+ });
+ return {};
+ }
+
+ return RuntimeDefinition(*CTUDeclOrError);
+}
+
+void AnyFunctionCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ const auto *D = cast<FunctionDecl>(CalleeCtx->getDecl());
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
+ D->parameters());
+}
+
+bool AnyFunctionCall::argumentsMayEscape() const {
+ if (CallEvent::argumentsMayEscape() || hasVoidPointerToNonConstArg())
+ return true;
+
+ const FunctionDecl *D = getDecl();
+ if (!D)
+ return true;
+
+ const IdentifierInfo *II = D->getIdentifier();
+ if (!II)
+ return false;
+
+ // This set of "escaping" APIs is
+
+ // - 'int pthread_setspecific(ptheread_key k, const void *)' stores a
+ // value into thread local storage. The value can later be retrieved with
+ // 'void *ptheread_getspecific(pthread_key)'. So even thought the
+ // parameter is 'const void *', the region escapes through the call.
+ if (II->isStr("pthread_setspecific"))
+ return true;
+
+ // - xpc_connection_set_context stores a value which can be retrieved later
+ // with xpc_connection_get_context.
+ if (II->isStr("xpc_connection_set_context"))
+ return true;
+
+ // - funopen - sets a buffer for future IO calls.
+ if (II->isStr("funopen"))
+ return true;
+
+ // - __cxa_demangle - can reallocate memory and can return the pointer to
+ // the input buffer.
+ if (II->isStr("__cxa_demangle"))
+ return true;
+
+ StringRef FName = II->getName();
+
+ // - CoreFoundation functions that end with "NoCopy" can free a passed-in
+ // buffer even if it is const.
+ if (FName.endswith("NoCopy"))
+ return true;
+
+ // - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
+ // be deallocated by NSMapRemove.
+ if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
+ return true;
+
+ // - Many CF containers allow objects to escape through custom
+ // allocators/deallocators upon container construction. (PR12101)
+ if (FName.startswith("CF") || FName.startswith("CG")) {
+ return StrInStrNoCase(FName, "InsertValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "WithData") != StringRef::npos ||
+ StrInStrNoCase(FName, "AppendValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "SetAttribute") != StringRef::npos;
+ }
+
+ return false;
+}
+
+const FunctionDecl *SimpleFunctionCall::getDecl() const {
+ const FunctionDecl *D = getOriginExpr()->getDirectCallee();
+ if (D)
+ return D;
+
+ return getSVal(getOriginExpr()->getCallee()).getAsFunctionDecl();
+}
+
+const FunctionDecl *CXXInstanceCall::getDecl() const {
+ const auto *CE = cast_or_null<CallExpr>(getOriginExpr());
+ if (!CE)
+ return AnyFunctionCall::getDecl();
+
+ const FunctionDecl *D = CE->getDirectCallee();
+ if (D)
+ return D;
+
+ return getSVal(CE->getCallee()).getAsFunctionDecl();
+}
+
+void CXXInstanceCall::getExtraInvalidatedValues(
+ ValueList &Values, RegionAndSymbolInvalidationTraits *ETraits) const {
+ SVal ThisVal = getCXXThisVal();
+ Values.push_back(ThisVal);
+
+ // Don't invalidate if the method is const and there are no mutable fields.
+ if (const auto *D = cast_or_null<CXXMethodDecl>(getDecl())) {
+ if (!D->isConst())
+ return;
+ // Get the record decl for the class of 'This'. D->getParent() may return a
+ // base class decl, rather than the class of the instance which needs to be
+ // checked for mutable fields.
+ // TODO: We might as well look at the dynamic type of the object.
+ const Expr *Ex = getCXXThisExpr()->ignoreParenBaseCasts();
+ QualType T = Ex->getType();
+ if (T->isPointerType()) // Arrow or implicit-this syntax?
+ T = T->getPointeeType();
+ const CXXRecordDecl *ParentRecord = T->getAsCXXRecordDecl();
+ assert(ParentRecord);
+ if (ParentRecord->hasMutableFields())
+ return;
+ // Preserve CXXThis.
+ const MemRegion *ThisRegion = ThisVal.getAsRegion();
+ if (!ThisRegion)
+ return;
+
+ ETraits->setTrait(ThisRegion->getBaseRegion(),
+ RegionAndSymbolInvalidationTraits::TK_PreserveContents);
+ }
+}
+
+SVal CXXInstanceCall::getCXXThisVal() const {
+ const Expr *Base = getCXXThisExpr();
+ // FIXME: This doesn't handle an overloaded ->* operator.
+ if (!Base)
+ return UnknownVal();
+
+ SVal ThisVal = getSVal(Base);
+ assert(ThisVal.isUnknownOrUndef() || ThisVal.getAs<Loc>());
+ return ThisVal;
+}
+
+RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
+ // Do we have a decl at all?
+ const Decl *D = getDecl();
+ if (!D)
+ return {};
+
+ // If the method is non-virtual, we know we can inline it.
+ const auto *MD = cast<CXXMethodDecl>(D);
+ if (!MD->isVirtual())
+ return AnyFunctionCall::getRuntimeDefinition();
+
+ // Do we know the implicit 'this' object being called?
+ const MemRegion *R = getCXXThisVal().getAsRegion();
+ if (!R)
+ return {};
+
+ // Do we know anything about the type of 'this'?
+ DynamicTypeInfo DynType = getDynamicTypeInfo(getState(), R);
+ if (!DynType.isValid())
+ return {};
+
+ // Is the type a C++ class? (This is mostly a defensive check.)
+ QualType RegionType = DynType.getType()->getPointeeType();
+ assert(!RegionType.isNull() && "DynamicTypeInfo should always be a pointer.");
+
+ const CXXRecordDecl *RD = RegionType->getAsCXXRecordDecl();
+ if (!RD || !RD->hasDefinition())
+ return {};
+
+ // Find the decl for this method in that class.
+ const CXXMethodDecl *Result = MD->getCorrespondingMethodInClass(RD, true);
+ if (!Result) {
+ // We might not even get the original statically-resolved method due to
+ // some particularly nasty casting (e.g. casts to sister classes).
+ // However, we should at least be able to search up and down our own class
+ // hierarchy, and some real bugs have been caught by checking this.
+ assert(!RD->isDerivedFrom(MD->getParent()) && "Couldn't find known method");
+
+ // FIXME: This is checking that our DynamicTypeInfo is at least as good as
+ // the static type. However, because we currently don't update
+ // DynamicTypeInfo when an object is cast, we can't actually be sure the
+ // DynamicTypeInfo is up to date. This assert should be re-enabled once
+ // this is fixed. <rdar://problem/12287087>
+ //assert(!MD->getParent()->isDerivedFrom(RD) && "Bad DynamicTypeInfo");
+
+ return {};
+ }
+
+ // Does the decl that we found have an implementation?
+ const FunctionDecl *Definition;
+ if (!Result->hasBody(Definition))
+ return {};
+
+ // We found a definition. If we're not sure that this devirtualization is
+ // actually what will happen at runtime, make sure to provide the region so
+ // that ExprEngine can decide what to do with it.
+ if (DynType.canBeASubClass())
+ return RuntimeDefinition(Definition, R->StripCasts());
+ return RuntimeDefinition(Definition, /*DispatchRegion=*/nullptr);
+}
+
+void CXXInstanceCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings);
+
+ // Handle the binding of 'this' in the new stack frame.
+ SVal ThisVal = getCXXThisVal();
+ if (!ThisVal.isUnknown()) {
+ ProgramStateManager &StateMgr = getState()->getStateManager();
+ SValBuilder &SVB = StateMgr.getSValBuilder();
+
+ const auto *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx);
+
+ // If we devirtualized to a different member function, we need to make sure
+ // we have the proper layering of CXXBaseObjectRegions.
+ if (MD->getCanonicalDecl() != getDecl()->getCanonicalDecl()) {
+ ASTContext &Ctx = SVB.getContext();
+ const CXXRecordDecl *Class = MD->getParent();
+ QualType Ty = Ctx.getPointerType(Ctx.getRecordType(Class));
+
+ // FIXME: CallEvent maybe shouldn't be directly accessing StoreManager.
+ bool Failed;
+ ThisVal = StateMgr.getStoreManager().attemptDownCast(ThisVal, Ty, Failed);
+ if (Failed) {
+ // We might have suffered some sort of placement new earlier, so
+ // we're constructing in a completely unexpected storage.
+ // Fall back to a generic pointer cast for this-value.
+ const CXXMethodDecl *StaticMD = cast<CXXMethodDecl>(getDecl());
+ const CXXRecordDecl *StaticClass = StaticMD->getParent();
+ QualType StaticTy = Ctx.getPointerType(Ctx.getRecordType(StaticClass));
+ ThisVal = SVB.evalCast(ThisVal, Ty, StaticTy);
+ }
+ }
+
+ if (!ThisVal.isUnknown())
+ Bindings.push_back(std::make_pair(ThisLoc, ThisVal));
+ }
+}
+
+const Expr *CXXMemberCall::getCXXThisExpr() const {
+ return getOriginExpr()->getImplicitObjectArgument();
+}
+
+RuntimeDefinition CXXMemberCall::getRuntimeDefinition() const {
+ // C++11 [expr.call]p1: ...If the selected function is non-virtual, or if the
+ // id-expression in the class member access expression is a qualified-id,
+ // that function is called. Otherwise, its final overrider in the dynamic type
+ // of the object expression is called.
+ if (const auto *ME = dyn_cast<MemberExpr>(getOriginExpr()->getCallee()))
+ if (ME->hasQualifier())
+ return AnyFunctionCall::getRuntimeDefinition();
+
+ return CXXInstanceCall::getRuntimeDefinition();
+}
+
+const Expr *CXXMemberOperatorCall::getCXXThisExpr() const {
+ return getOriginExpr()->getArg(0);
+}
+
+const BlockDataRegion *BlockCall::getBlockRegion() const {
+ const Expr *Callee = getOriginExpr()->getCallee();
+ const MemRegion *DataReg = getSVal(Callee).getAsRegion();
+
+ return dyn_cast_or_null<BlockDataRegion>(DataReg);
+}
+
+ArrayRef<ParmVarDecl*> BlockCall::parameters() const {
+ const BlockDecl *D = getDecl();
+ if (!D)
+ return None;
+ return D->parameters();
+}
+
+void BlockCall::getExtraInvalidatedValues(ValueList &Values,
+ RegionAndSymbolInvalidationTraits *ETraits) const {
+ // FIXME: This also needs to invalidate captured globals.
+ if (const MemRegion *R = getBlockRegion())
+ Values.push_back(loc::MemRegionVal(R));
+}
+
+void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ ArrayRef<ParmVarDecl*> Params;
+ if (isConversionFromLambda()) {
+ auto *LambdaOperatorDecl = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ Params = LambdaOperatorDecl->parameters();
+
+ // For blocks converted from a C++ lambda, the callee declaration is the
+ // operator() method on the lambda so we bind "this" to
+ // the lambda captured by the block.
+ const VarRegion *CapturedLambdaRegion = getRegionStoringCapturedLambda();
+ SVal ThisVal = loc::MemRegionVal(CapturedLambdaRegion);
+ Loc ThisLoc = SVB.getCXXThis(LambdaOperatorDecl, CalleeCtx);
+ Bindings.push_back(std::make_pair(ThisLoc, ThisVal));
+ } else {
+ Params = cast<BlockDecl>(CalleeCtx->getDecl())->parameters();
+ }
+
+ addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
+ Params);
+}
+
+SVal CXXConstructorCall::getCXXThisVal() const {
+ if (Data)
+ return loc::MemRegionVal(static_cast<const MemRegion *>(Data));
+ return UnknownVal();
+}
+
+void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values,
+ RegionAndSymbolInvalidationTraits *ETraits) const {
+ if (Data) {
+ loc::MemRegionVal MV(static_cast<const MemRegion *>(Data));
+ if (SymbolRef Sym = MV.getAsSymbol(true))
+ ETraits->setTrait(Sym,
+ RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
+ Values.push_back(MV);
+ }
+}
+
+void CXXConstructorCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings);
+
+ SVal ThisVal = getCXXThisVal();
+ if (!ThisVal.isUnknown()) {
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ const auto *MD = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ Loc ThisLoc = SVB.getCXXThis(MD, CalleeCtx);
+ Bindings.push_back(std::make_pair(ThisLoc, ThisVal));
+ }
+}
+
+SVal CXXDestructorCall::getCXXThisVal() const {
+ if (Data)
+ return loc::MemRegionVal(DtorDataTy::getFromOpaqueValue(Data).getPointer());
+ return UnknownVal();
+}
+
+RuntimeDefinition CXXDestructorCall::getRuntimeDefinition() const {
+ // Base destructors are always called non-virtually.
+ // Skip CXXInstanceCall's devirtualization logic in this case.
+ if (isBaseDestructor())
+ return AnyFunctionCall::getRuntimeDefinition();
+
+ return CXXInstanceCall::getRuntimeDefinition();
+}
+
+ArrayRef<ParmVarDecl*> ObjCMethodCall::parameters() const {
+ const ObjCMethodDecl *D = getDecl();
+ if (!D)
+ return None;
+ return D->parameters();
+}
+
+void ObjCMethodCall::getExtraInvalidatedValues(
+ ValueList &Values, RegionAndSymbolInvalidationTraits *ETraits) const {
+
+ // If the method call is a setter for property known to be backed by
+ // an instance variable, don't invalidate the entire receiver, just
+ // the storage for that instance variable.
+ if (const ObjCPropertyDecl *PropDecl = getAccessedProperty()) {
+ if (const ObjCIvarDecl *PropIvar = PropDecl->getPropertyIvarDecl()) {
+ SVal IvarLVal = getState()->getLValue(PropIvar, getReceiverSVal());
+ if (const MemRegion *IvarRegion = IvarLVal.getAsRegion()) {
+ ETraits->setTrait(
+ IvarRegion,
+ RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
+ ETraits->setTrait(
+ IvarRegion,
+ RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
+ Values.push_back(IvarLVal);
+ }
+ return;
+ }
+ }
+
+ Values.push_back(getReceiverSVal());
+}
+
+SVal ObjCMethodCall::getSelfSVal() const {
+ const LocationContext *LCtx = getLocationContext();
+ const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
+ if (!SelfDecl)
+ return SVal();
+ return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx));
+}
+
+SVal ObjCMethodCall::getReceiverSVal() const {
+ // FIXME: Is this the best way to handle class receivers?
+ if (!isInstanceMessage())
+ return UnknownVal();
+
+ if (const Expr *RecE = getOriginExpr()->getInstanceReceiver())
+ return getSVal(RecE);
+
+ // An instance message with no expression means we are sending to super.
+ // In this case the object reference is the same as 'self'.
+ assert(getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperInstance);
+ SVal SelfVal = getSelfSVal();
+ assert(SelfVal.isValid() && "Calling super but not in ObjC method");
+ return SelfVal;
+}
+
+bool ObjCMethodCall::isReceiverSelfOrSuper() const {
+ if (getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperInstance ||
+ getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperClass)
+ return true;
+
+ if (!isInstanceMessage())
+ return false;
+
+ SVal RecVal = getSVal(getOriginExpr()->getInstanceReceiver());
+
+ return (RecVal == getSelfSVal());
+}
+
+SourceRange ObjCMethodCall::getSourceRange() const {
+ switch (getMessageKind()) {
+ case OCM_Message:
+ return getOriginExpr()->getSourceRange();
+ case OCM_PropertyAccess:
+ case OCM_Subscript:
+ return getContainingPseudoObjectExpr()->getSourceRange();
+ }
+ llvm_unreachable("unknown message kind");
+}
+
+using ObjCMessageDataTy = llvm::PointerIntPair<const PseudoObjectExpr *, 2>;
+
+const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const {
+ assert(Data && "Lazy lookup not yet performed.");
+ assert(getMessageKind() != OCM_Message && "Explicit message send.");
+ return ObjCMessageDataTy::getFromOpaqueValue(Data).getPointer();
+}
+
+static const Expr *
+getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) {
+ const Expr *Syntactic = POE->getSyntacticForm();
+
+ // This handles the funny case of assigning to the result of a getter.
+ // This can happen if the getter returns a non-const reference.
+ if (const auto *BO = dyn_cast<BinaryOperator>(Syntactic))
+ Syntactic = BO->getLHS();
+
+ return Syntactic;
+}
+
+ObjCMessageKind ObjCMethodCall::getMessageKind() const {
+ if (!Data) {
+ // Find the parent, ignoring implicit casts.
+ ParentMap &PM = getLocationContext()->getParentMap();
+ const Stmt *S = PM.getParentIgnoreParenCasts(getOriginExpr());
+
+ // Check if parent is a PseudoObjectExpr.
+ if (const auto *POE = dyn_cast_or_null<PseudoObjectExpr>(S)) {
+ const Expr *Syntactic = getSyntacticFromForPseudoObjectExpr(POE);
+
+ ObjCMessageKind K;
+ switch (Syntactic->getStmtClass()) {
+ case Stmt::ObjCPropertyRefExprClass:
+ K = OCM_PropertyAccess;
+ break;
+ case Stmt::ObjCSubscriptRefExprClass:
+ K = OCM_Subscript;
+ break;
+ default:
+ // FIXME: Can this ever happen?
+ K = OCM_Message;
+ break;
+ }
+
+ if (K != OCM_Message) {
+ const_cast<ObjCMethodCall *>(this)->Data
+ = ObjCMessageDataTy(POE, K).getOpaqueValue();
+ assert(getMessageKind() == K);
+ return K;
+ }
+ }
+
+ const_cast<ObjCMethodCall *>(this)->Data
+ = ObjCMessageDataTy(nullptr, 1).getOpaqueValue();
+ assert(getMessageKind() == OCM_Message);
+ return OCM_Message;
+ }
+
+ ObjCMessageDataTy Info = ObjCMessageDataTy::getFromOpaqueValue(Data);
+ if (!Info.getPointer())
+ return OCM_Message;
+ return static_cast<ObjCMessageKind>(Info.getInt());
+}
+
+const ObjCPropertyDecl *ObjCMethodCall::getAccessedProperty() const {
+ // Look for properties accessed with property syntax (foo.bar = ...)
+ if ( getMessageKind() == OCM_PropertyAccess) {
+ const PseudoObjectExpr *POE = getContainingPseudoObjectExpr();
+ assert(POE && "Property access without PseudoObjectExpr?");
+
+ const Expr *Syntactic = getSyntacticFromForPseudoObjectExpr(POE);
+ auto *RefExpr = cast<ObjCPropertyRefExpr>(Syntactic);
+
+ if (RefExpr->isExplicitProperty())
+ return RefExpr->getExplicitProperty();
+ }
+
+ // Look for properties accessed with method syntax ([foo setBar:...]).
+ const ObjCMethodDecl *MD = getDecl();
+ if (!MD || !MD->isPropertyAccessor())
+ return nullptr;
+
+ // Note: This is potentially quite slow.
+ return MD->findPropertyDecl();
+}
+
+bool ObjCMethodCall::canBeOverridenInSubclass(ObjCInterfaceDecl *IDecl,
+ Selector Sel) const {
+ assert(IDecl);
+ AnalysisManager &AMgr =
+ getState()->getStateManager().getOwningEngine().getAnalysisManager();
+ // If the class interface is declared inside the main file, assume it is not
+ // subcassed.
+ // TODO: It could actually be subclassed if the subclass is private as well.
+ // This is probably very rare.
+ SourceLocation InterfLoc = IDecl->getEndOfDefinitionLoc();
+ if (InterfLoc.isValid() && AMgr.isInCodeFile(InterfLoc))
+ return false;
+
+ // Assume that property accessors are not overridden.
+ if (getMessageKind() == OCM_PropertyAccess)
+ return false;
+
+ // We assume that if the method is public (declared outside of main file) or
+ // has a parent which publicly declares the method, the method could be
+ // overridden in a subclass.
+
+ // Find the first declaration in the class hierarchy that declares
+ // the selector.
+ ObjCMethodDecl *D = nullptr;
+ while (true) {
+ D = IDecl->lookupMethod(Sel, true);
+
+ // Cannot find a public definition.
+ if (!D)
+ return false;
+
+ // If outside the main file,
+ if (D->getLocation().isValid() && !AMgr.isInCodeFile(D->getLocation()))
+ return true;
+
+ if (D->isOverriding()) {
+ // Search in the superclass on the next iteration.
+ IDecl = D->getClassInterface();
+ if (!IDecl)
+ return false;
+
+ IDecl = IDecl->getSuperClass();
+ if (!IDecl)
+ return false;
+
+ continue;
+ }
+
+ return false;
+ };
+
+ llvm_unreachable("The while loop should always terminate.");
+}
+
+static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) {
+ if (!MD)
+ return MD;
+
+ // Find the redeclaration that defines the method.
+ if (!MD->hasBody()) {
+ for (auto I : MD->redecls())
+ if (I->hasBody())
+ MD = cast<ObjCMethodDecl>(I);
+ }
+ return MD;
+}
+
+static bool isCallToSelfClass(const ObjCMessageExpr *ME) {
+ const Expr* InstRec = ME->getInstanceReceiver();
+ if (!InstRec)
+ return false;
+ const auto *InstRecIg = dyn_cast<DeclRefExpr>(InstRec->IgnoreParenImpCasts());
+
+ // Check that receiver is called 'self'.
+ if (!InstRecIg || !InstRecIg->getFoundDecl() ||
+ !InstRecIg->getFoundDecl()->getName().equals("self"))
+ return false;
+
+ // Check that the method name is 'class'.
+ if (ME->getSelector().getNumArgs() != 0 ||
+ !ME->getSelector().getNameForSlot(0).equals("class"))
+ return false;
+
+ return true;
+}
+
+RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
+ const ObjCMessageExpr *E = getOriginExpr();
+ assert(E);
+ Selector Sel = E->getSelector();
+
+ if (E->isInstanceMessage()) {
+ // Find the receiver type.
+ const ObjCObjectPointerType *ReceiverT = nullptr;
+ bool CanBeSubClassed = false;
+ QualType SupersType = E->getSuperType();
+ const MemRegion *Receiver = nullptr;
+
+ if (!SupersType.isNull()) {
+ // The receiver is guaranteed to be 'super' in this case.
+ // Super always means the type of immediate predecessor to the method
+ // where the call occurs.
+ ReceiverT = cast<ObjCObjectPointerType>(SupersType);
+ } else {
+ Receiver = getReceiverSVal().getAsRegion();
+ if (!Receiver)
+ return {};
+
+ DynamicTypeInfo DTI = getDynamicTypeInfo(getState(), Receiver);
+ if (!DTI.isValid()) {
+ assert(isa<AllocaRegion>(Receiver) &&
+ "Unhandled untyped region class!");
+ return {};
+ }
+
+ QualType DynType = DTI.getType();
+ CanBeSubClassed = DTI.canBeASubClass();
+ ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
+
+ if (ReceiverT && CanBeSubClassed)
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
+ if (!canBeOverridenInSubclass(IDecl, Sel))
+ CanBeSubClassed = false;
+ }
+
+ // Handle special cases of '[self classMethod]' and
+ // '[[self class] classMethod]', which are treated by the compiler as
+ // instance (not class) messages. We will statically dispatch to those.
+ if (auto *PT = dyn_cast_or_null<ObjCObjectPointerType>(ReceiverT)) {
+ // For [self classMethod], return the compiler visible declaration.
+ if (PT->getObjectType()->isObjCClass() &&
+ Receiver == getSelfSVal().getAsRegion())
+ return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
+
+ // Similarly, handle [[self class] classMethod].
+ // TODO: We are currently doing a syntactic match for this pattern with is
+ // limiting as the test cases in Analysis/inlining/InlineObjCClassMethod.m
+ // shows. A better way would be to associate the meta type with the symbol
+ // using the dynamic type info tracking and use it here. We can add a new
+ // SVal for ObjC 'Class' values that know what interface declaration they
+ // come from. Then 'self' in a class method would be filled in with
+ // something meaningful in ObjCMethodCall::getReceiverSVal() and we could
+ // do proper dynamic dispatch for class methods just like we do for
+ // instance methods now.
+ if (E->getInstanceReceiver())
+ if (const auto *M = dyn_cast<ObjCMessageExpr>(E->getInstanceReceiver()))
+ if (isCallToSelfClass(M))
+ return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
+ }
+
+ // Lookup the instance method implementation.
+ if (ReceiverT)
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) {
+ // Repeatedly calling lookupPrivateMethod() is expensive, especially
+ // when in many cases it returns null. We cache the results so
+ // that repeated queries on the same ObjCIntefaceDecl and Selector
+ // don't incur the same cost. On some test cases, we can see the
+ // same query being issued thousands of times.
+ //
+ // NOTE: This cache is essentially a "global" variable, but it
+ // only gets lazily created when we get here. The value of the
+ // cache probably comes from it being global across ExprEngines,
+ // where the same queries may get issued. If we are worried about
+ // concurrency, or possibly loading/unloading ASTs, etc., we may
+ // need to revisit this someday. In terms of memory, this table
+ // stays around until clang quits, which also may be bad if we
+ // need to release memory.
+ using PrivateMethodKey = std::pair<const ObjCInterfaceDecl *, Selector>;
+ using PrivateMethodCache =
+ llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>;
+
+ static PrivateMethodCache PMC;
+ Optional<const ObjCMethodDecl *> &Val = PMC[std::make_pair(IDecl, Sel)];
+
+ // Query lookupPrivateMethod() if the cache does not hit.
+ if (!Val.hasValue()) {
+ Val = IDecl->lookupPrivateMethod(Sel);
+
+ // If the method is a property accessor, we should try to "inline" it
+ // even if we don't actually have an implementation.
+ if (!*Val)
+ if (const ObjCMethodDecl *CompileTimeMD = E->getMethodDecl())
+ if (CompileTimeMD->isPropertyAccessor()) {
+ if (!CompileTimeMD->getSelfDecl() &&
+ isa<ObjCCategoryDecl>(CompileTimeMD->getDeclContext())) {
+ // If the method is an accessor in a category, and it doesn't
+ // have a self declaration, first
+ // try to find the method in a class extension. This
+ // works around a bug in Sema where multiple accessors
+ // are synthesized for properties in class
+ // extensions that are redeclared in a category and the
+ // the implicit parameters are not filled in for
+ // the method on the category.
+ // This ensures we find the accessor in the extension, which
+ // has the implicit parameters filled in.
+ auto *ID = CompileTimeMD->getClassInterface();
+ for (auto *CatDecl : ID->visible_extensions()) {
+ Val = CatDecl->getMethod(Sel,
+ CompileTimeMD->isInstanceMethod());
+ if (*Val)
+ break;
+ }
+ }
+ if (!*Val)
+ Val = IDecl->lookupInstanceMethod(Sel);
+ }
+ }
+
+ const ObjCMethodDecl *MD = Val.getValue();
+ if (CanBeSubClassed)
+ return RuntimeDefinition(MD, Receiver);
+ else
+ return RuntimeDefinition(MD, nullptr);
+ }
+ } else {
+ // This is a class method.
+ // If we have type info for the receiver class, we are calling via
+ // class name.
+ if (ObjCInterfaceDecl *IDecl = E->getReceiverInterface()) {
+ // Find/Return the method implementation.
+ return RuntimeDefinition(IDecl->lookupPrivateClassMethod(Sel));
+ }
+ }
+
+ return {};
+}
+
+bool ObjCMethodCall::argumentsMayEscape() const {
+ if (isInSystemHeader() && !isInstanceMessage()) {
+ Selector Sel = getSelector();
+ if (Sel.getNumArgs() == 1 &&
+ Sel.getIdentifierInfoForSlot(0)->isStr("valueWithPointer"))
+ return true;
+ }
+
+ return CallEvent::argumentsMayEscape();
+}
+
+void ObjCMethodCall::getInitialStackFrameContents(
+ const StackFrameContext *CalleeCtx,
+ BindingsTy &Bindings) const {
+ const auto *D = cast<ObjCMethodDecl>(CalleeCtx->getDecl());
+ SValBuilder &SVB = getState()->getStateManager().getSValBuilder();
+ addParameterValuesToBindings(CalleeCtx, Bindings, SVB, *this,
+ D->parameters());
+
+ SVal SelfVal = getReceiverSVal();
+ if (!SelfVal.isUnknown()) {
+ const VarDecl *SelfD = CalleeCtx->getAnalysisDeclContext()->getSelfDecl();
+ MemRegionManager &MRMgr = SVB.getRegionManager();
+ Loc SelfLoc = SVB.makeLoc(MRMgr.getVarRegion(SelfD, CalleeCtx));
+ Bindings.push_back(std::make_pair(SelfLoc, SelfVal));
+ }
+}
+
+CallEventRef<>
+CallEventManager::getSimpleCall(const CallExpr *CE, ProgramStateRef State,
+ const LocationContext *LCtx) {
+ if (const auto *MCE = dyn_cast<CXXMemberCallExpr>(CE))
+ return create<CXXMemberCall>(MCE, State, LCtx);
+
+ if (const auto *OpCE = dyn_cast<CXXOperatorCallExpr>(CE)) {
+ const FunctionDecl *DirectCallee = OpCE->getDirectCallee();
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(DirectCallee))
+ if (MD->isInstance())
+ return create<CXXMemberOperatorCall>(OpCE, State, LCtx);
+
+ } else if (CE->getCallee()->getType()->isBlockPointerType()) {
+ return create<BlockCall>(CE, State, LCtx);
+ }
+
+ // Otherwise, it's a normal function call, static member function call, or
+ // something we can't reason about.
+ return create<SimpleFunctionCall>(CE, State, LCtx);
+}
+
+CallEventRef<>
+CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
+ ProgramStateRef State) {
+ const LocationContext *ParentCtx = CalleeCtx->getParent();
+ const LocationContext *CallerCtx = ParentCtx->getStackFrame();
+ assert(CallerCtx && "This should not be used for top-level stack frames");
+
+ const Stmt *CallSite = CalleeCtx->getCallSite();
+
+ if (CallSite) {
+ if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx))
+ return Out;
+
+ // All other cases are handled by getCall.
+ assert(isa<CXXConstructExpr>(CallSite) &&
+ "This is not an inlineable statement");
+
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ const auto *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl());
+ Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx);
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite),
+ ThisVal.getAsRegion(), State, CallerCtx);
+ }
+
+ // Fall back to the CFG. The only thing we haven't handled yet is
+ // destructors, though this could change in the future.
+ const CFGBlock *B = CalleeCtx->getCallSiteBlock();
+ CFGElement E = (*B)[CalleeCtx->getIndex()];
+ assert((E.getAs<CFGImplicitDtor>() || E.getAs<CFGTemporaryDtor>()) &&
+ "All other CFG elements should have exprs");
+
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ const auto *Dtor = cast<CXXDestructorDecl>(CalleeCtx->getDecl());
+ Loc ThisPtr = SVB.getCXXThis(Dtor, CalleeCtx);
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ const Stmt *Trigger;
+ if (Optional<CFGAutomaticObjDtor> AutoDtor = E.getAs<CFGAutomaticObjDtor>())
+ Trigger = AutoDtor->getTriggerStmt();
+ else if (Optional<CFGDeleteDtor> DeleteDtor = E.getAs<CFGDeleteDtor>())
+ Trigger = DeleteDtor->getDeleteExpr();
+ else
+ Trigger = Dtor->getBody();
+
+ return getCXXDestructorCall(Dtor, Trigger, ThisVal.getAsRegion(),
+ E.getAs<CFGBaseDtor>().hasValue(), State,
+ CallerCtx);
+}
+
+CallEventRef<> CallEventManager::getCall(const Stmt *S, ProgramStateRef State,
+ const LocationContext *LC) {
+ if (const auto *CE = dyn_cast<CallExpr>(S)) {
+ return getSimpleCall(CE, State, LC);
+ } else if (const auto *NE = dyn_cast<CXXNewExpr>(S)) {
+ return getCXXAllocatorCall(NE, State, LC);
+ } else if (const auto *ME = dyn_cast<ObjCMessageExpr>(S)) {
+ return getObjCMethodCall(ME, State, LC);
+ } else {
+ return nullptr;
+ }
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp
new file mode 100644
index 000000000000..72bfd84b40a3
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Checker.cpp
@@ -0,0 +1,40 @@
+//== Checker.cpp - Registration mechanism for checkers -----------*- C++ -*--=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines Checker, used to create and register checkers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+
+using namespace clang;
+using namespace ento;
+
+int ImplicitNullDerefEvent::Tag;
+
+StringRef CheckerBase::getTagDescription() const {
+ return getCheckName().getName();
+}
+
+CheckName CheckerBase::getCheckName() const { return Name; }
+
+CheckerProgramPointTag::CheckerProgramPointTag(StringRef CheckerName,
+ StringRef Msg)
+ : SimpleProgramPointTag(CheckerName, Msg) {}
+
+CheckerProgramPointTag::CheckerProgramPointTag(const CheckerBase *Checker,
+ StringRef Msg)
+ : SimpleProgramPointTag(Checker->getCheckName().getName(), Msg) {}
+
+raw_ostream& clang::ento::operator<<(raw_ostream &Out,
+ const CheckerBase &Checker) {
+ Out << Checker.getCheckName().getName();
+ return Out;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
new file mode 100644
index 000000000000..6cf931abbddd
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
@@ -0,0 +1,132 @@
+//== CheckerContext.cpp - Context info for path-sensitive checkers-----------=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines CheckerContext that provides contextual info for
+// path-sensitive checkers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/Basic/Builtins.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang;
+using namespace ento;
+
+const FunctionDecl *CheckerContext::getCalleeDecl(const CallExpr *CE) const {
+ const Expr *Callee = CE->getCallee();
+ SVal L = Pred->getSVal(Callee);
+ return L.getAsFunctionDecl();
+}
+
+StringRef CheckerContext::getCalleeName(const FunctionDecl *FunDecl) const {
+ if (!FunDecl)
+ return StringRef();
+ IdentifierInfo *funI = FunDecl->getIdentifier();
+ if (!funI)
+ return StringRef();
+ return funI->getName();
+}
+
+StringRef CheckerContext::getDeclDescription(const Decl *D) {
+ if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
+ return "method";
+ if (isa<BlockDecl>(D))
+ return "anonymous block";
+ return "function";
+}
+
+bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD,
+ StringRef Name) {
+ // To avoid false positives (Ex: finding user defined functions with
+ // similar names), only perform fuzzy name matching when it's a builtin.
+ // Using a string compare is slow, we might want to switch on BuiltinID here.
+ unsigned BId = FD->getBuiltinID();
+ if (BId != 0) {
+ if (Name.empty())
+ return true;
+ StringRef BName = FD->getASTContext().BuiltinInfo.getName(BId);
+ if (BName.find(Name) != StringRef::npos)
+ return true;
+ }
+
+ const IdentifierInfo *II = FD->getIdentifier();
+ // If this is a special C++ name without IdentifierInfo, it can't be a
+ // C library function.
+ if (!II)
+ return false;
+
+ // Look through 'extern "C"' and anything similar invented in the future.
+ // If this function is not in TU directly, it is not a C library function.
+ if (!FD->getDeclContext()->getRedeclContext()->isTranslationUnit())
+ return false;
+
+ // If this function is not externally visible, it is not a C library function.
+ // Note that we make an exception for inline functions, which may be
+ // declared in header files without external linkage.
+ if (!FD->isInlined() && !FD->isExternallyVisible())
+ return false;
+
+ if (Name.empty())
+ return true;
+
+ StringRef FName = II->getName();
+ if (FName.equals(Name))
+ return true;
+
+ if (FName.startswith("__inline") && (FName.find(Name) != StringRef::npos))
+ return true;
+
+ if (FName.startswith("__") && FName.endswith("_chk") &&
+ FName.find(Name) != StringRef::npos)
+ return true;
+
+ return false;
+}
+
+StringRef CheckerContext::getMacroNameOrSpelling(SourceLocation &Loc) {
+ if (Loc.isMacroID())
+ return Lexer::getImmediateMacroName(Loc, getSourceManager(),
+ getLangOpts());
+ SmallVector<char, 16> buf;
+ return Lexer::getSpelling(Loc, buf, getSourceManager(), getLangOpts());
+}
+
+/// Evaluate comparison and return true if it's known that condition is true
+static bool evalComparison(SVal LHSVal, BinaryOperatorKind ComparisonOp,
+ SVal RHSVal, ProgramStateRef State) {
+ if (LHSVal.isUnknownOrUndef())
+ return false;
+ ProgramStateManager &Mgr = State->getStateManager();
+ if (!LHSVal.getAs<NonLoc>()) {
+ LHSVal = Mgr.getStoreManager().getBinding(State->getStore(),
+ LHSVal.castAs<Loc>());
+ if (LHSVal.isUnknownOrUndef() || !LHSVal.getAs<NonLoc>())
+ return false;
+ }
+
+ SValBuilder &Bldr = Mgr.getSValBuilder();
+ SVal Eval = Bldr.evalBinOp(State, ComparisonOp, LHSVal, RHSVal,
+ Bldr.getConditionType());
+ if (Eval.isUnknownOrUndef())
+ return false;
+ ProgramStateRef StTrue, StFalse;
+ std::tie(StTrue, StFalse) = State->assume(Eval.castAs<DefinedSVal>());
+ return StTrue && !StFalse;
+}
+
+bool CheckerContext::isGreaterOrEqual(const Expr *E, unsigned long long Val) {
+ DefinedSVal V = getSValBuilder().makeIntVal(Val, getASTContext().LongLongTy);
+ return evalComparison(getSVal(E), BO_GE, V, getState());
+}
+
+bool CheckerContext::isNegative(const Expr *E) {
+ DefinedSVal V = getSValBuilder().makeIntVal(0, false);
+ return evalComparison(getSVal(E), BO_LT, V, getState());
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
new file mode 100644
index 000000000000..e73a22ae3981
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
@@ -0,0 +1,115 @@
+//===---- CheckerHelpers.cpp - Helper functions for checkers ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines several static functions for use in checkers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+
+namespace clang {
+
+namespace ento {
+
+// Recursively find any substatements containing macros
+bool containsMacro(const Stmt *S) {
+ if (S->getBeginLoc().isMacroID())
+ return true;
+
+ if (S->getEndLoc().isMacroID())
+ return true;
+
+ for (const Stmt *Child : S->children())
+ if (Child && containsMacro(Child))
+ return true;
+
+ return false;
+}
+
+// Recursively find any substatements containing enum constants
+bool containsEnum(const Stmt *S) {
+ const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
+
+ if (DR && isa<EnumConstantDecl>(DR->getDecl()))
+ return true;
+
+ for (const Stmt *Child : S->children())
+ if (Child && containsEnum(Child))
+ return true;
+
+ return false;
+}
+
+// Recursively find any substatements containing static vars
+bool containsStaticLocal(const Stmt *S) {
+ const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(S);
+
+ if (DR)
+ if (const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl()))
+ if (VD->isStaticLocal())
+ return true;
+
+ for (const Stmt *Child : S->children())
+ if (Child && containsStaticLocal(Child))
+ return true;
+
+ return false;
+}
+
+// Recursively find any substatements containing __builtin_offsetof
+bool containsBuiltinOffsetOf(const Stmt *S) {
+ if (isa<OffsetOfExpr>(S))
+ return true;
+
+ for (const Stmt *Child : S->children())
+ if (Child && containsBuiltinOffsetOf(Child))
+ return true;
+
+ return false;
+}
+
+// Extract lhs and rhs from assignment statement
+std::pair<const clang::VarDecl *, const clang::Expr *>
+parseAssignment(const Stmt *S) {
+ const VarDecl *VD = nullptr;
+ const Expr *RHS = nullptr;
+
+ if (auto Assign = dyn_cast_or_null<BinaryOperator>(S)) {
+ if (Assign->isAssignmentOp()) {
+ // Ordinary assignment
+ RHS = Assign->getRHS();
+ if (auto DE = dyn_cast_or_null<DeclRefExpr>(Assign->getLHS()))
+ VD = dyn_cast_or_null<VarDecl>(DE->getDecl());
+ }
+ } else if (auto PD = dyn_cast_or_null<DeclStmt>(S)) {
+ // Initialization
+ assert(PD->isSingleDecl() && "We process decls one by one");
+ VD = dyn_cast_or_null<VarDecl>(PD->getSingleDecl());
+ RHS = VD->getAnyInitializer();
+ }
+
+ return std::make_pair(VD, RHS);
+}
+
+Nullability getNullabilityAnnotation(QualType Type) {
+ const auto *AttrType = Type->getAs<AttributedType>();
+ if (!AttrType)
+ return Nullability::Unspecified;
+ if (AttrType->getAttrKind() == attr::TypeNullable)
+ return Nullability::Nullable;
+ else if (AttrType->getAttrKind() == attr::TypeNonNull)
+ return Nullability::Nonnull;
+ return Nullability::Unspecified;
+}
+
+
+} // end namespace ento
+} // end namespace clang
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
new file mode 100644
index 000000000000..688c47e984cc
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -0,0 +1,835 @@
+//===- CheckerManager.cpp - Static Analyzer Checker Manager ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the Static Analyzer Checker Manager.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <cassert>
+#include <vector>
+
+using namespace clang;
+using namespace ento;
+
+bool CheckerManager::hasPathSensitiveCheckers() const {
+ return !StmtCheckers.empty() ||
+ !PreObjCMessageCheckers.empty() ||
+ !PostObjCMessageCheckers.empty() ||
+ !PreCallCheckers.empty() ||
+ !PostCallCheckers.empty() ||
+ !LocationCheckers.empty() ||
+ !BindCheckers.empty() ||
+ !EndAnalysisCheckers.empty() ||
+ !EndFunctionCheckers.empty() ||
+ !BranchConditionCheckers.empty() ||
+ !LiveSymbolsCheckers.empty() ||
+ !DeadSymbolsCheckers.empty() ||
+ !RegionChangesCheckers.empty() ||
+ !EvalAssumeCheckers.empty() ||
+ !EvalCallCheckers.empty();
+}
+
+void CheckerManager::finishedCheckerRegistration() {
+#ifndef NDEBUG
+ // Make sure that for every event that has listeners, there is at least
+ // one dispatcher registered for it.
+ for (const auto &Event : Events)
+ assert(Event.second.HasDispatcher &&
+ "No dispatcher registered for an event");
+#endif
+}
+
+//===----------------------------------------------------------------------===//
+// Functions for running checkers for AST traversing..
+//===----------------------------------------------------------------------===//
+
+void CheckerManager::runCheckersOnASTDecl(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) {
+ assert(D);
+
+ unsigned DeclKind = D->getKind();
+ CachedDeclCheckers *checkers = nullptr;
+ CachedDeclCheckersMapTy::iterator CCI = CachedDeclCheckersMap.find(DeclKind);
+ if (CCI != CachedDeclCheckersMap.end()) {
+ checkers = &(CCI->second);
+ } else {
+ // Find the checkers that should run for this Decl and cache them.
+ checkers = &CachedDeclCheckersMap[DeclKind];
+ for (const auto &info : DeclCheckers)
+ if (info.IsForDeclFn(D))
+ checkers->push_back(info.CheckFn);
+ }
+
+ assert(checkers);
+ for (const auto checker : *checkers)
+ checker(D, mgr, BR);
+}
+
+void CheckerManager::runCheckersOnASTBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) {
+ assert(D && D->hasBody());
+
+ for (const auto BodyChecker : BodyCheckers)
+ BodyChecker(D, mgr, BR);
+}
+
+//===----------------------------------------------------------------------===//
+// Functions for running checkers for path-sensitive checking.
+//===----------------------------------------------------------------------===//
+
+template <typename CHECK_CTX>
+static void expandGraphWithCheckers(CHECK_CTX checkCtx,
+ ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src) {
+ const NodeBuilderContext &BldrCtx = checkCtx.Eng.getBuilderContext();
+ if (Src.empty())
+ return;
+
+ typename CHECK_CTX::CheckersTy::const_iterator
+ I = checkCtx.checkers_begin(), E = checkCtx.checkers_end();
+ if (I == E) {
+ Dst.insert(Src);
+ return;
+ }
+
+ ExplodedNodeSet Tmp1, Tmp2;
+ const ExplodedNodeSet *PrevSet = &Src;
+
+ for (; I != E; ++I) {
+ ExplodedNodeSet *CurrSet = nullptr;
+ if (I+1 == E)
+ CurrSet = &Dst;
+ else {
+ CurrSet = (PrevSet == &Tmp1) ? &Tmp2 : &Tmp1;
+ CurrSet->clear();
+ }
+
+ NodeBuilder B(*PrevSet, *CurrSet, BldrCtx);
+ for (const auto &NI : *PrevSet)
+ checkCtx.runChecker(*I, B, NI);
+
+ // If all the produced transitions are sinks, stop.
+ if (CurrSet->empty())
+ return;
+
+ // Update which NodeSet is the current one.
+ PrevSet = CurrSet;
+ }
+}
+
+namespace {
+
+ struct CheckStmtContext {
+ using CheckersTy = SmallVectorImpl<CheckerManager::CheckStmtFunc>;
+
+ bool IsPreVisit;
+ const CheckersTy &Checkers;
+ const Stmt *S;
+ ExprEngine &Eng;
+ bool WasInlined;
+
+ CheckStmtContext(bool isPreVisit, const CheckersTy &checkers,
+ const Stmt *s, ExprEngine &eng, bool wasInlined = false)
+ : IsPreVisit(isPreVisit), Checkers(checkers), S(s), Eng(eng),
+ WasInlined(wasInlined) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckStmtFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ // FIXME: Remove respondsToCallback from CheckerContext;
+ ProgramPoint::Kind K = IsPreVisit ? ProgramPoint::PreStmtKind :
+ ProgramPoint::PostStmtKind;
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, K,
+ Pred->getLocationContext(), checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+ checkFn(S, C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for visiting Stmts.
+void CheckerManager::runCheckersForStmt(bool isPreVisit,
+ ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ const Stmt *S,
+ ExprEngine &Eng,
+ bool WasInlined) {
+ CheckStmtContext C(isPreVisit, getCachedStmtCheckersFor(S, isPreVisit),
+ S, Eng, WasInlined);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+namespace {
+
+ struct CheckObjCMessageContext {
+ using CheckersTy = std::vector<CheckerManager::CheckObjCMessageFunc>;
+
+ ObjCMessageVisitKind Kind;
+ bool WasInlined;
+ const CheckersTy &Checkers;
+ const ObjCMethodCall &Msg;
+ ExprEngine &Eng;
+
+ CheckObjCMessageContext(ObjCMessageVisitKind visitKind,
+ const CheckersTy &checkers,
+ const ObjCMethodCall &msg, ExprEngine &eng,
+ bool wasInlined)
+ : Kind(visitKind), WasInlined(wasInlined), Checkers(checkers), Msg(msg),
+ Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckObjCMessageFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ bool IsPreVisit;
+
+ switch (Kind) {
+ case ObjCMessageVisitKind::Pre:
+ IsPreVisit = true;
+ break;
+ case ObjCMessageVisitKind::MessageNil:
+ case ObjCMessageVisitKind::Post:
+ IsPreVisit = false;
+ break;
+ }
+
+ const ProgramPoint &L = Msg.getProgramPoint(IsPreVisit,checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+
+ checkFn(*Msg.cloneWithState<ObjCMethodCall>(Pred->getState()), C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for visiting obj-c messages.
+void CheckerManager::runCheckersForObjCMessage(ObjCMessageVisitKind visitKind,
+ ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ const ObjCMethodCall &msg,
+ ExprEngine &Eng,
+ bool WasInlined) {
+ auto &checkers = getObjCMessageCheckers(visitKind);
+ CheckObjCMessageContext C(visitKind, checkers, msg, Eng, WasInlined);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+const std::vector<CheckerManager::CheckObjCMessageFunc> &
+CheckerManager::getObjCMessageCheckers(ObjCMessageVisitKind Kind) {
+ switch (Kind) {
+ case ObjCMessageVisitKind::Pre:
+ return PreObjCMessageCheckers;
+ break;
+ case ObjCMessageVisitKind::Post:
+ return PostObjCMessageCheckers;
+ case ObjCMessageVisitKind::MessageNil:
+ return ObjCMessageNilCheckers;
+ }
+ llvm_unreachable("Unknown Kind");
+}
+
+namespace {
+
+ // FIXME: This has all the same signatures as CheckObjCMessageContext.
+ // Is there a way we can merge the two?
+ struct CheckCallContext {
+ using CheckersTy = std::vector<CheckerManager::CheckCallFunc>;
+
+ bool IsPreVisit, WasInlined;
+ const CheckersTy &Checkers;
+ const CallEvent &Call;
+ ExprEngine &Eng;
+
+ CheckCallContext(bool isPreVisit, const CheckersTy &checkers,
+ const CallEvent &call, ExprEngine &eng,
+ bool wasInlined)
+ : IsPreVisit(isPreVisit), WasInlined(wasInlined), Checkers(checkers),
+ Call(call), Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckCallFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ const ProgramPoint &L = Call.getProgramPoint(IsPreVisit,checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+
+ checkFn(*Call.cloneWithState(Pred->getState()), C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for visiting an abstract call event.
+void CheckerManager::runCheckersForCallEvent(bool isPreVisit,
+ ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ const CallEvent &Call,
+ ExprEngine &Eng,
+ bool WasInlined) {
+ CheckCallContext C(isPreVisit,
+ isPreVisit ? PreCallCheckers
+ : PostCallCheckers,
+ Call, Eng, WasInlined);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+namespace {
+
+ struct CheckLocationContext {
+ using CheckersTy = std::vector<CheckerManager::CheckLocationFunc>;
+
+ const CheckersTy &Checkers;
+ SVal Loc;
+ bool IsLoad;
+ const Stmt *NodeEx; /* Will become a CFGStmt */
+ const Stmt *BoundEx;
+ ExprEngine &Eng;
+
+ CheckLocationContext(const CheckersTy &checkers,
+ SVal loc, bool isLoad, const Stmt *NodeEx,
+ const Stmt *BoundEx,
+ ExprEngine &eng)
+ : Checkers(checkers), Loc(loc), IsLoad(isLoad), NodeEx(NodeEx),
+ BoundEx(BoundEx), Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckLocationFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ ProgramPoint::Kind K = IsLoad ? ProgramPoint::PreLoadKind :
+ ProgramPoint::PreStoreKind;
+ const ProgramPoint &L =
+ ProgramPoint::getProgramPoint(NodeEx, K,
+ Pred->getLocationContext(),
+ checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+ checkFn(Loc, IsLoad, BoundEx, C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for load/store of a location.
+
+void CheckerManager::runCheckersForLocation(ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ SVal location, bool isLoad,
+ const Stmt *NodeEx,
+ const Stmt *BoundEx,
+ ExprEngine &Eng) {
+ CheckLocationContext C(LocationCheckers, location, isLoad, NodeEx,
+ BoundEx, Eng);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+namespace {
+
+ struct CheckBindContext {
+ using CheckersTy = std::vector<CheckerManager::CheckBindFunc>;
+
+ const CheckersTy &Checkers;
+ SVal Loc;
+ SVal Val;
+ const Stmt *S;
+ ExprEngine &Eng;
+ const ProgramPoint &PP;
+
+ CheckBindContext(const CheckersTy &checkers,
+ SVal loc, SVal val, const Stmt *s, ExprEngine &eng,
+ const ProgramPoint &pp)
+ : Checkers(checkers), Loc(loc), Val(val), S(s), Eng(eng), PP(pp) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckBindFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ const ProgramPoint &L = PP.withTag(checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+
+ checkFn(Loc, Val, S, C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for binding of a value to a location.
+void CheckerManager::runCheckersForBind(ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ SVal location, SVal val,
+ const Stmt *S, ExprEngine &Eng,
+ const ProgramPoint &PP) {
+ CheckBindContext C(BindCheckers, location, val, S, Eng, PP);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+void CheckerManager::runCheckersForEndAnalysis(ExplodedGraph &G,
+ BugReporter &BR,
+ ExprEngine &Eng) {
+ for (const auto EndAnalysisChecker : EndAnalysisCheckers)
+ EndAnalysisChecker(G, BR, Eng);
+}
+
+namespace {
+
+struct CheckBeginFunctionContext {
+ using CheckersTy = std::vector<CheckerManager::CheckBeginFunctionFunc>;
+
+ const CheckersTy &Checkers;
+ ExprEngine &Eng;
+ const ProgramPoint &PP;
+
+ CheckBeginFunctionContext(const CheckersTy &Checkers, ExprEngine &Eng,
+ const ProgramPoint &PP)
+ : Checkers(Checkers), Eng(Eng), PP(PP) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckBeginFunctionFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ const ProgramPoint &L = PP.withTag(checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+
+ checkFn(C);
+ }
+};
+
+} // namespace
+
+void CheckerManager::runCheckersForBeginFunction(ExplodedNodeSet &Dst,
+ const BlockEdge &L,
+ ExplodedNode *Pred,
+ ExprEngine &Eng) {
+ ExplodedNodeSet Src;
+ Src.insert(Pred);
+ CheckBeginFunctionContext C(BeginFunctionCheckers, Eng, L);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+/// Run checkers for end of path.
+// Note, We do not chain the checker output (like in expandGraphWithCheckers)
+// for this callback since end of path nodes are expected to be final.
+void CheckerManager::runCheckersForEndFunction(NodeBuilderContext &BC,
+ ExplodedNodeSet &Dst,
+ ExplodedNode *Pred,
+ ExprEngine &Eng,
+ const ReturnStmt *RS) {
+ // We define the builder outside of the loop because if at least one checker
+ // creates a successor for Pred, we do not need to generate an
+ // autotransition for it.
+ NodeBuilder Bldr(Pred, Dst, BC);
+ for (const auto checkFn : EndFunctionCheckers) {
+ const ProgramPoint &L =
+ FunctionExitPoint(RS, Pred->getLocationContext(), checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+ checkFn(RS, C);
+ }
+}
+
+namespace {
+
+ struct CheckBranchConditionContext {
+ using CheckersTy = std::vector<CheckerManager::CheckBranchConditionFunc>;
+
+ const CheckersTy &Checkers;
+ const Stmt *Condition;
+ ExprEngine &Eng;
+
+ CheckBranchConditionContext(const CheckersTy &checkers,
+ const Stmt *Cond, ExprEngine &eng)
+ : Checkers(checkers), Condition(Cond), Eng(eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckBranchConditionFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ ProgramPoint L = PostCondition(Condition, Pred->getLocationContext(),
+ checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+ checkFn(Condition, C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for branch condition.
+void CheckerManager::runCheckersForBranchCondition(const Stmt *Condition,
+ ExplodedNodeSet &Dst,
+ ExplodedNode *Pred,
+ ExprEngine &Eng) {
+ ExplodedNodeSet Src;
+ Src.insert(Pred);
+ CheckBranchConditionContext C(BranchConditionCheckers, Condition, Eng);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+namespace {
+
+ struct CheckNewAllocatorContext {
+ using CheckersTy = std::vector<CheckerManager::CheckNewAllocatorFunc>;
+
+ const CheckersTy &Checkers;
+ const CXXNewExpr *NE;
+ SVal Target;
+ bool WasInlined;
+ ExprEngine &Eng;
+
+ CheckNewAllocatorContext(const CheckersTy &Checkers, const CXXNewExpr *NE,
+ SVal Target, bool WasInlined, ExprEngine &Eng)
+ : Checkers(Checkers), NE(NE), Target(Target), WasInlined(WasInlined),
+ Eng(Eng) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckNewAllocatorFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ ProgramPoint L = PostAllocatorCall(NE, Pred->getLocationContext());
+ CheckerContext C(Bldr, Eng, Pred, L, WasInlined);
+ checkFn(NE, Target, C);
+ }
+ };
+
+} // namespace
+
+void CheckerManager::runCheckersForNewAllocator(
+ const CXXNewExpr *NE, SVal Target, ExplodedNodeSet &Dst, ExplodedNode *Pred,
+ ExprEngine &Eng, bool WasInlined) {
+ ExplodedNodeSet Src;
+ Src.insert(Pred);
+ CheckNewAllocatorContext C(NewAllocatorCheckers, NE, Target, WasInlined, Eng);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+/// Run checkers for live symbols.
+void CheckerManager::runCheckersForLiveSymbols(ProgramStateRef state,
+ SymbolReaper &SymReaper) {
+ for (const auto LiveSymbolsChecker : LiveSymbolsCheckers)
+ LiveSymbolsChecker(state, SymReaper);
+}
+
+namespace {
+
+ struct CheckDeadSymbolsContext {
+ using CheckersTy = std::vector<CheckerManager::CheckDeadSymbolsFunc>;
+
+ const CheckersTy &Checkers;
+ SymbolReaper &SR;
+ const Stmt *S;
+ ExprEngine &Eng;
+ ProgramPoint::Kind ProgarmPointKind;
+
+ CheckDeadSymbolsContext(const CheckersTy &checkers, SymbolReaper &sr,
+ const Stmt *s, ExprEngine &eng,
+ ProgramPoint::Kind K)
+ : Checkers(checkers), SR(sr), S(s), Eng(eng), ProgarmPointKind(K) {}
+
+ CheckersTy::const_iterator checkers_begin() { return Checkers.begin(); }
+ CheckersTy::const_iterator checkers_end() { return Checkers.end(); }
+
+ void runChecker(CheckerManager::CheckDeadSymbolsFunc checkFn,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ const ProgramPoint &L = ProgramPoint::getProgramPoint(S, ProgarmPointKind,
+ Pred->getLocationContext(), checkFn.Checker);
+ CheckerContext C(Bldr, Eng, Pred, L);
+
+ // Note, do not pass the statement to the checkers without letting them
+ // differentiate if we ran remove dead bindings before or after the
+ // statement.
+ checkFn(SR, C);
+ }
+ };
+
+} // namespace
+
+/// Run checkers for dead symbols.
+void CheckerManager::runCheckersForDeadSymbols(ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ SymbolReaper &SymReaper,
+ const Stmt *S,
+ ExprEngine &Eng,
+ ProgramPoint::Kind K) {
+ CheckDeadSymbolsContext C(DeadSymbolsCheckers, SymReaper, S, Eng, K);
+ expandGraphWithCheckers(C, Dst, Src);
+}
+
+/// Run checkers for region changes.
+ProgramStateRef
+CheckerManager::runCheckersForRegionChanges(ProgramStateRef state,
+ const InvalidatedSymbols *invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call) {
+ for (const auto RegionChangesChecker : RegionChangesCheckers) {
+ // If any checker declares the state infeasible (or if it starts that way),
+ // bail out.
+ if (!state)
+ return nullptr;
+ state = RegionChangesChecker(state, invalidated, ExplicitRegions, Regions,
+ LCtx, Call);
+ }
+ return state;
+}
+
+/// Run checkers to process symbol escape event.
+ProgramStateRef
+CheckerManager::runCheckersForPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols &Escaped,
+ const CallEvent *Call,
+ PointerEscapeKind Kind,
+ RegionAndSymbolInvalidationTraits *ETraits) {
+ assert((Call != nullptr ||
+ (Kind != PSK_DirectEscapeOnCall &&
+ Kind != PSK_IndirectEscapeOnCall)) &&
+ "Call must not be NULL when escaping on call");
+ for (const auto PointerEscapeChecker : PointerEscapeCheckers) {
+ // If any checker declares the state infeasible (or if it starts that
+ // way), bail out.
+ if (!State)
+ return nullptr;
+ State = PointerEscapeChecker(State, Escaped, Call, Kind, ETraits);
+ }
+ return State;
+}
+
+/// Run checkers for handling assumptions on symbolic values.
+ProgramStateRef
+CheckerManager::runCheckersForEvalAssume(ProgramStateRef state,
+ SVal Cond, bool Assumption) {
+ for (const auto EvalAssumeChecker : EvalAssumeCheckers) {
+ // If any checker declares the state infeasible (or if it starts that way),
+ // bail out.
+ if (!state)
+ return nullptr;
+ state = EvalAssumeChecker(state, Cond, Assumption);
+ }
+ return state;
+}
+
+/// Run checkers for evaluating a call.
+/// Only one checker will evaluate the call.
+void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
+ const ExplodedNodeSet &Src,
+ const CallEvent &Call,
+ ExprEngine &Eng) {
+ const CallExpr *CE = cast<CallExpr>(Call.getOriginExpr());
+ for (const auto Pred : Src) {
+ bool anyEvaluated = false;
+
+ ExplodedNodeSet checkDst;
+ NodeBuilder B(Pred, checkDst, Eng.getBuilderContext());
+
+ // Check if any of the EvalCall callbacks can evaluate the call.
+ for (const auto EvalCallChecker : EvalCallCheckers) {
+ ProgramPoint::Kind K = ProgramPoint::PostStmtKind;
+ const ProgramPoint &L =
+ ProgramPoint::getProgramPoint(CE, K, Pred->getLocationContext(),
+ EvalCallChecker.Checker);
+ bool evaluated = false;
+ { // CheckerContext generates transitions(populates checkDest) on
+ // destruction, so introduce the scope to make sure it gets properly
+ // populated.
+ CheckerContext C(B, Eng, Pred, L);
+ evaluated = EvalCallChecker(CE, C);
+ }
+ assert(!(evaluated && anyEvaluated)
+ && "There are more than one checkers evaluating the call");
+ if (evaluated) {
+ anyEvaluated = true;
+ Dst.insert(checkDst);
+#ifdef NDEBUG
+ break; // on release don't check that no other checker also evals.
+#endif
+ }
+ }
+
+ // If none of the checkers evaluated the call, ask ExprEngine to handle it.
+ if (!anyEvaluated) {
+ NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
+ Eng.defaultEvalCall(B, Pred, Call);
+ }
+ }
+}
+
+/// Run checkers for the entire Translation Unit.
+void CheckerManager::runCheckersOnEndOfTranslationUnit(
+ const TranslationUnitDecl *TU,
+ AnalysisManager &mgr,
+ BugReporter &BR) {
+ for (const auto EndOfTranslationUnitChecker : EndOfTranslationUnitCheckers)
+ EndOfTranslationUnitChecker(TU, mgr, BR);
+}
+
+void CheckerManager::runCheckersForPrintState(raw_ostream &Out,
+ ProgramStateRef State,
+ const char *NL, const char *Sep) {
+ for (const auto &CheckerTag : CheckerTags)
+ CheckerTag.second->printState(Out, State, NL, Sep);
+}
+
+//===----------------------------------------------------------------------===//
+// Internal registration functions for AST traversing.
+//===----------------------------------------------------------------------===//
+
+void CheckerManager::_registerForDecl(CheckDeclFunc checkfn,
+ HandlesDeclFunc isForDeclFn) {
+ DeclCheckerInfo info = { checkfn, isForDeclFn };
+ DeclCheckers.push_back(info);
+}
+
+void CheckerManager::_registerForBody(CheckDeclFunc checkfn) {
+ BodyCheckers.push_back(checkfn);
+}
+
+//===----------------------------------------------------------------------===//
+// Internal registration functions for path-sensitive checking.
+//===----------------------------------------------------------------------===//
+
+void CheckerManager::_registerForPreStmt(CheckStmtFunc checkfn,
+ HandlesStmtFunc isForStmtFn) {
+ StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/true };
+ StmtCheckers.push_back(info);
+}
+
+void CheckerManager::_registerForPostStmt(CheckStmtFunc checkfn,
+ HandlesStmtFunc isForStmtFn) {
+ StmtCheckerInfo info = { checkfn, isForStmtFn, /*IsPreVisit*/false };
+ StmtCheckers.push_back(info);
+}
+
+void CheckerManager::_registerForPreObjCMessage(CheckObjCMessageFunc checkfn) {
+ PreObjCMessageCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForObjCMessageNil(CheckObjCMessageFunc checkfn) {
+ ObjCMessageNilCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForPostObjCMessage(CheckObjCMessageFunc checkfn) {
+ PostObjCMessageCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForPreCall(CheckCallFunc checkfn) {
+ PreCallCheckers.push_back(checkfn);
+}
+void CheckerManager::_registerForPostCall(CheckCallFunc checkfn) {
+ PostCallCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForLocation(CheckLocationFunc checkfn) {
+ LocationCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForBind(CheckBindFunc checkfn) {
+ BindCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForEndAnalysis(CheckEndAnalysisFunc checkfn) {
+ EndAnalysisCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForBeginFunction(CheckBeginFunctionFunc checkfn) {
+ BeginFunctionCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForEndFunction(CheckEndFunctionFunc checkfn) {
+ EndFunctionCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForBranchCondition(
+ CheckBranchConditionFunc checkfn) {
+ BranchConditionCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForNewAllocator(CheckNewAllocatorFunc checkfn) {
+ NewAllocatorCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForLiveSymbols(CheckLiveSymbolsFunc checkfn) {
+ LiveSymbolsCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForDeadSymbols(CheckDeadSymbolsFunc checkfn) {
+ DeadSymbolsCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForRegionChanges(CheckRegionChangesFunc checkfn) {
+ RegionChangesCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForPointerEscape(CheckPointerEscapeFunc checkfn){
+ PointerEscapeCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForConstPointerEscape(
+ CheckPointerEscapeFunc checkfn) {
+ PointerEscapeCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForEvalAssume(EvalAssumeFunc checkfn) {
+ EvalAssumeCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForEvalCall(EvalCallFunc checkfn) {
+ EvalCallCheckers.push_back(checkfn);
+}
+
+void CheckerManager::_registerForEndOfTranslationUnit(
+ CheckEndOfTranslationUnit checkfn) {
+ EndOfTranslationUnitCheckers.push_back(checkfn);
+}
+
+//===----------------------------------------------------------------------===//
+// Implementation details.
+//===----------------------------------------------------------------------===//
+
+const CheckerManager::CachedStmtCheckers &
+CheckerManager::getCachedStmtCheckersFor(const Stmt *S, bool isPreVisit) {
+ assert(S);
+
+ unsigned Key = (S->getStmtClass() << 1) | unsigned(isPreVisit);
+ CachedStmtCheckersMapTy::iterator CCI = CachedStmtCheckersMap.find(Key);
+ if (CCI != CachedStmtCheckersMap.end())
+ return CCI->second;
+
+ // Find the checkers that should run for this Stmt and cache them.
+ CachedStmtCheckers &Checkers = CachedStmtCheckersMap[Key];
+ for (const auto &Info : StmtCheckers)
+ if (Info.IsPreVisit == isPreVisit && Info.IsForStmtFn(S))
+ Checkers.push_back(Info.CheckFn);
+ return Checkers;
+}
+
+CheckerManager::~CheckerManager() {
+ for (const auto CheckerDtor : CheckerDtors)
+ CheckerDtor();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
new file mode 100644
index 000000000000..cdae3ef0116a
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
@@ -0,0 +1,21 @@
+//=--- CommonBugCategories.cpp - Provides common issue categories -*- C++ -*-=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
+
+// Common strings used for the "category" of many static analyzer issues.
+namespace clang { namespace ento { namespace categories {
+
+const char * const CoreFoundationObjectiveC = "Core Foundation/Objective-C";
+const char * const LogicError = "Logic error";
+const char * const MemoryRefCount =
+ "Memory (Core Foundation/Objective-C/OSObject)";
+const char * const MemoryError = "Memory error";
+const char * const UnixAPI = "Unix API";
+}}}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp
new file mode 100644
index 000000000000..ef9c44c51be4
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ConstraintManager.cpp
@@ -0,0 +1,44 @@
+//===- ConstraintManager.cpp - Constraints on symbolic values. ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defined the interface to manage constraints on symbolic values.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
+#include "clang/AST/Type.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+
+using namespace clang;
+using namespace ento;
+
+ConstraintManager::~ConstraintManager() = default;
+
+static DefinedSVal getLocFromSymbol(const ProgramStateRef &State,
+ SymbolRef Sym) {
+ const MemRegion *R =
+ State->getStateManager().getRegionManager().getSymbolicRegion(Sym);
+ return loc::MemRegionVal(R);
+}
+
+ConditionTruthVal ConstraintManager::checkNull(ProgramStateRef State,
+ SymbolRef Sym) {
+ QualType Ty = Sym->getType();
+ DefinedSVal V = Loc::isLocType(Ty) ? getLocFromSymbol(State, Sym)
+ : nonloc::SymbolVal(Sym);
+ const ProgramStatePair &P = assumeDual(State, V);
+ if (P.first && !P.second)
+ return ConditionTruthVal(false);
+ if (!P.first && P.second)
+ return ConditionTruthVal(true);
+ return {};
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
new file mode 100644
index 000000000000..196854cb09da
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -0,0 +1,655 @@
+//===- CoreEngine.cpp - Path-Sensitive Dataflow Engine --------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a generic engine for intraprocedural, path-sensitive,
+// dataflow analysis via graph reachability engine.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/BlockCounter.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <algorithm>
+#include <cassert>
+#include <memory>
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+#define DEBUG_TYPE "CoreEngine"
+
+STATISTIC(NumSteps,
+ "The # of steps executed.");
+STATISTIC(NumReachedMaxSteps,
+ "The # of times we reached the max number of steps.");
+STATISTIC(NumPathsExplored,
+ "The # of paths explored by the analyzer.");
+
+//===----------------------------------------------------------------------===//
+// Core analysis engine.
+//===----------------------------------------------------------------------===//
+
+static std::unique_ptr<WorkList> generateWorkList(AnalyzerOptions &Opts,
+ SubEngine &subengine) {
+ switch (Opts.getExplorationStrategy()) {
+ case ExplorationStrategyKind::DFS:
+ return WorkList::makeDFS();
+ case ExplorationStrategyKind::BFS:
+ return WorkList::makeBFS();
+ case ExplorationStrategyKind::BFSBlockDFSContents:
+ return WorkList::makeBFSBlockDFSContents();
+ case ExplorationStrategyKind::UnexploredFirst:
+ return WorkList::makeUnexploredFirst();
+ case ExplorationStrategyKind::UnexploredFirstQueue:
+ return WorkList::makeUnexploredFirstPriorityQueue();
+ case ExplorationStrategyKind::UnexploredFirstLocationQueue:
+ return WorkList::makeUnexploredFirstPriorityLocationQueue();
+ }
+ llvm_unreachable("Unknown AnalyzerOptions::ExplorationStrategyKind");
+}
+
+CoreEngine::CoreEngine(SubEngine &subengine, FunctionSummariesTy *FS,
+ AnalyzerOptions &Opts)
+ : SubEng(subengine), WList(generateWorkList(Opts, subengine)),
+ BCounterFactory(G.getAllocator()), FunctionSummaries(FS) {}
+
+/// ExecuteWorkList - Run the worklist algorithm for a maximum number of steps.
+bool CoreEngine::ExecuteWorkList(const LocationContext *L, unsigned Steps,
+ ProgramStateRef InitState) {
+ if (G.num_roots() == 0) { // Initialize the analysis by constructing
+ // the root if none exists.
+
+ const CFGBlock *Entry = &(L->getCFG()->getEntry());
+
+ assert(Entry->empty() && "Entry block must be empty.");
+
+ assert(Entry->succ_size() == 1 && "Entry block must have 1 successor.");
+
+ // Mark the entry block as visited.
+ FunctionSummaries->markVisitedBasicBlock(Entry->getBlockID(),
+ L->getDecl(),
+ L->getCFG()->getNumBlockIDs());
+
+ // Get the solitary successor.
+ const CFGBlock *Succ = *(Entry->succ_begin());
+
+ // Construct an edge representing the
+ // starting location in the function.
+ BlockEdge StartLoc(Entry, Succ, L);
+
+ // Set the current block counter to being empty.
+ WList->setBlockCounter(BCounterFactory.GetEmptyCounter());
+
+ if (!InitState)
+ InitState = SubEng.getInitialState(L);
+
+ bool IsNew;
+ ExplodedNode *Node = G.getNode(StartLoc, InitState, false, &IsNew);
+ assert(IsNew);
+ G.addRoot(Node);
+
+ NodeBuilderContext BuilderCtx(*this, StartLoc.getDst(), Node);
+ ExplodedNodeSet DstBegin;
+ SubEng.processBeginOfFunction(BuilderCtx, Node, DstBegin, StartLoc);
+
+ enqueue(DstBegin);
+ }
+
+ // Check if we have a steps limit
+ bool UnlimitedSteps = Steps == 0;
+ // Cap our pre-reservation in the event that the user specifies
+ // a very large number of maximum steps.
+ const unsigned PreReservationCap = 4000000;
+ if(!UnlimitedSteps)
+ G.reserve(std::min(Steps,PreReservationCap));
+
+ while (WList->hasWork()) {
+ if (!UnlimitedSteps) {
+ if (Steps == 0) {
+ NumReachedMaxSteps++;
+ break;
+ }
+ --Steps;
+ }
+
+ NumSteps++;
+
+ const WorkListUnit& WU = WList->dequeue();
+
+ // Set the current block counter.
+ WList->setBlockCounter(WU.getBlockCounter());
+
+ // Retrieve the node.
+ ExplodedNode *Node = WU.getNode();
+
+ dispatchWorkItem(Node, Node->getLocation(), WU);
+ }
+ SubEng.processEndWorklist();
+ return WList->hasWork();
+}
+
+void CoreEngine::dispatchWorkItem(ExplodedNode* Pred, ProgramPoint Loc,
+ const WorkListUnit& WU) {
+ // Dispatch on the location type.
+ switch (Loc.getKind()) {
+ case ProgramPoint::BlockEdgeKind:
+ HandleBlockEdge(Loc.castAs<BlockEdge>(), Pred);
+ break;
+
+ case ProgramPoint::BlockEntranceKind:
+ HandleBlockEntrance(Loc.castAs<BlockEntrance>(), Pred);
+ break;
+
+ case ProgramPoint::BlockExitKind:
+ assert(false && "BlockExit location never occur in forward analysis.");
+ break;
+
+ case ProgramPoint::CallEnterKind:
+ HandleCallEnter(Loc.castAs<CallEnter>(), Pred);
+ break;
+
+ case ProgramPoint::CallExitBeginKind:
+ SubEng.processCallExit(Pred);
+ break;
+
+ case ProgramPoint::EpsilonKind: {
+ assert(Pred->hasSinglePred() &&
+ "Assume epsilon has exactly one predecessor by construction");
+ ExplodedNode *PNode = Pred->getFirstPred();
+ dispatchWorkItem(Pred, PNode->getLocation(), WU);
+ break;
+ }
+ default:
+ assert(Loc.getAs<PostStmt>() ||
+ Loc.getAs<PostInitializer>() ||
+ Loc.getAs<PostImplicitCall>() ||
+ Loc.getAs<CallExitEnd>() ||
+ Loc.getAs<LoopExit>() ||
+ Loc.getAs<PostAllocatorCall>());
+ HandlePostStmt(WU.getBlock(), WU.getIndex(), Pred);
+ break;
+ }
+}
+
+bool CoreEngine::ExecuteWorkListWithInitialState(const LocationContext *L,
+ unsigned Steps,
+ ProgramStateRef InitState,
+ ExplodedNodeSet &Dst) {
+ bool DidNotFinish = ExecuteWorkList(L, Steps, InitState);
+ for (ExplodedGraph::eop_iterator I = G.eop_begin(), E = G.eop_end(); I != E;
+ ++I) {
+ Dst.Add(*I);
+ }
+ return DidNotFinish;
+}
+
+void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
+ const CFGBlock *Blk = L.getDst();
+ NodeBuilderContext BuilderCtx(*this, Blk, Pred);
+
+ // Mark this block as visited.
+ const LocationContext *LC = Pred->getLocationContext();
+ FunctionSummaries->markVisitedBasicBlock(Blk->getBlockID(),
+ LC->getDecl(),
+ LC->getCFG()->getNumBlockIDs());
+
+ // Check if we are entering the EXIT block.
+ if (Blk == &(L.getLocationContext()->getCFG()->getExit())) {
+ assert(L.getLocationContext()->getCFG()->getExit().empty() &&
+ "EXIT block cannot contain Stmts.");
+
+ // Get return statement..
+ const ReturnStmt *RS = nullptr;
+ if (!L.getSrc()->empty()) {
+ CFGElement LastElement = L.getSrc()->back();
+ if (Optional<CFGStmt> LastStmt = LastElement.getAs<CFGStmt>()) {
+ RS = dyn_cast<ReturnStmt>(LastStmt->getStmt());
+ } else if (Optional<CFGAutomaticObjDtor> AutoDtor =
+ LastElement.getAs<CFGAutomaticObjDtor>()) {
+ RS = dyn_cast<ReturnStmt>(AutoDtor->getTriggerStmt());
+ }
+ }
+
+ // Process the final state transition.
+ SubEng.processEndOfFunction(BuilderCtx, Pred, RS);
+
+ // This path is done. Don't enqueue any more nodes.
+ return;
+ }
+
+ // Call into the SubEngine to process entering the CFGBlock.
+ ExplodedNodeSet dstNodes;
+ BlockEntrance BE(Blk, Pred->getLocationContext());
+ NodeBuilderWithSinks nodeBuilder(Pred, dstNodes, BuilderCtx, BE);
+ SubEng.processCFGBlockEntrance(L, nodeBuilder, Pred);
+
+ // Auto-generate a node.
+ if (!nodeBuilder.hasGeneratedNodes()) {
+ nodeBuilder.generateNode(Pred->State, Pred);
+ }
+
+ // Enqueue nodes onto the worklist.
+ enqueue(dstNodes);
+}
+
+void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
+ ExplodedNode *Pred) {
+ // Increment the block counter.
+ const LocationContext *LC = Pred->getLocationContext();
+ unsigned BlockId = L.getBlock()->getBlockID();
+ BlockCounter Counter = WList->getBlockCounter();
+ Counter = BCounterFactory.IncrementCount(Counter, LC->getStackFrame(),
+ BlockId);
+ WList->setBlockCounter(Counter);
+
+ // Process the entrance of the block.
+ if (Optional<CFGElement> E = L.getFirstElement()) {
+ NodeBuilderContext Ctx(*this, L.getBlock(), Pred);
+ SubEng.processCFGElement(*E, Pred, 0, &Ctx);
+ }
+ else
+ HandleBlockExit(L.getBlock(), Pred);
+}
+
+void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
+ if (const Stmt *Term = B->getTerminator()) {
+ switch (Term->getStmtClass()) {
+ default:
+ llvm_unreachable("Analysis for this terminator not implemented.");
+
+ case Stmt::CXXBindTemporaryExprClass:
+ HandleCleanupTemporaryBranch(
+ cast<CXXBindTemporaryExpr>(B->getTerminator().getStmt()), B, Pred);
+ return;
+
+ // Model static initializers.
+ case Stmt::DeclStmtClass:
+ HandleStaticInit(cast<DeclStmt>(Term), B, Pred);
+ return;
+
+ case Stmt::BinaryOperatorClass: // '&&' and '||'
+ HandleBranch(cast<BinaryOperator>(Term)->getLHS(), Term, B, Pred);
+ return;
+
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass:
+ HandleBranch(cast<AbstractConditionalOperator>(Term)->getCond(),
+ Term, B, Pred);
+ return;
+
+ // FIXME: Use constant-folding in CFG construction to simplify this
+ // case.
+
+ case Stmt::ChooseExprClass:
+ HandleBranch(cast<ChooseExpr>(Term)->getCond(), Term, B, Pred);
+ return;
+
+ case Stmt::CXXTryStmtClass:
+ // Generate a node for each of the successors.
+ // Our logic for EH analysis can certainly be improved.
+ for (CFGBlock::const_succ_iterator it = B->succ_begin(),
+ et = B->succ_end(); it != et; ++it) {
+ if (const CFGBlock *succ = *it) {
+ generateNode(BlockEdge(B, succ, Pred->getLocationContext()),
+ Pred->State, Pred);
+ }
+ }
+ return;
+
+ case Stmt::DoStmtClass:
+ HandleBranch(cast<DoStmt>(Term)->getCond(), Term, B, Pred);
+ return;
+
+ case Stmt::CXXForRangeStmtClass:
+ HandleBranch(cast<CXXForRangeStmt>(Term)->getCond(), Term, B, Pred);
+ return;
+
+ case Stmt::ForStmtClass:
+ HandleBranch(cast<ForStmt>(Term)->getCond(), Term, B, Pred);
+ return;
+
+ case Stmt::ContinueStmtClass:
+ case Stmt::BreakStmtClass:
+ case Stmt::GotoStmtClass:
+ break;
+
+ case Stmt::IfStmtClass:
+ HandleBranch(cast<IfStmt>(Term)->getCond(), Term, B, Pred);
+ return;
+
+ case Stmt::IndirectGotoStmtClass: {
+ // Only 1 successor: the indirect goto dispatch block.
+ assert(B->succ_size() == 1);
+
+ IndirectGotoNodeBuilder
+ builder(Pred, B, cast<IndirectGotoStmt>(Term)->getTarget(),
+ *(B->succ_begin()), this);
+
+ SubEng.processIndirectGoto(builder);
+ return;
+ }
+
+ case Stmt::ObjCForCollectionStmtClass:
+ // In the case of ObjCForCollectionStmt, it appears twice in a CFG:
+ //
+ // (1) inside a basic block, which represents the binding of the
+ // 'element' variable to a value.
+ // (2) in a terminator, which represents the branch.
+ //
+ // For (1), subengines will bind a value (i.e., 0 or 1) indicating
+ // whether or not collection contains any more elements. We cannot
+ // just test to see if the element is nil because a container can
+ // contain nil elements.
+ HandleBranch(Term, Term, B, Pred);
+ return;
+
+ case Stmt::SwitchStmtClass: {
+ SwitchNodeBuilder builder(Pred, B, cast<SwitchStmt>(Term)->getCond(),
+ this);
+
+ SubEng.processSwitch(builder);
+ return;
+ }
+
+ case Stmt::WhileStmtClass:
+ HandleBranch(cast<WhileStmt>(Term)->getCond(), Term, B, Pred);
+ return;
+ }
+ }
+
+ assert(B->succ_size() == 1 &&
+ "Blocks with no terminator should have at most 1 successor.");
+
+ generateNode(BlockEdge(B, *(B->succ_begin()), Pred->getLocationContext()),
+ Pred->State, Pred);
+}
+
+void CoreEngine::HandleCallEnter(const CallEnter &CE, ExplodedNode *Pred) {
+ NodeBuilderContext BuilderCtx(*this, CE.getEntry(), Pred);
+ SubEng.processCallEnter(BuilderCtx, CE, Pred);
+}
+
+void CoreEngine::HandleBranch(const Stmt *Cond, const Stmt *Term,
+ const CFGBlock * B, ExplodedNode *Pred) {
+ assert(B->succ_size() == 2);
+ NodeBuilderContext Ctx(*this, B, Pred);
+ ExplodedNodeSet Dst;
+ SubEng.processBranch(Cond, Ctx, Pred, Dst, *(B->succ_begin()),
+ *(B->succ_begin() + 1));
+ // Enqueue the new frontier onto the worklist.
+ enqueue(Dst);
+}
+
+void CoreEngine::HandleCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
+ const CFGBlock *B,
+ ExplodedNode *Pred) {
+ assert(B->succ_size() == 2);
+ NodeBuilderContext Ctx(*this, B, Pred);
+ ExplodedNodeSet Dst;
+ SubEng.processCleanupTemporaryBranch(BTE, Ctx, Pred, Dst, *(B->succ_begin()),
+ *(B->succ_begin() + 1));
+ // Enqueue the new frontier onto the worklist.
+ enqueue(Dst);
+}
+
+void CoreEngine::HandleStaticInit(const DeclStmt *DS, const CFGBlock *B,
+ ExplodedNode *Pred) {
+ assert(B->succ_size() == 2);
+ NodeBuilderContext Ctx(*this, B, Pred);
+ ExplodedNodeSet Dst;
+ SubEng.processStaticInitializer(DS, Ctx, Pred, Dst,
+ *(B->succ_begin()), *(B->succ_begin()+1));
+ // Enqueue the new frontier onto the worklist.
+ enqueue(Dst);
+}
+
+void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
+ ExplodedNode *Pred) {
+ assert(B);
+ assert(!B->empty());
+
+ if (StmtIdx == B->size())
+ HandleBlockExit(B, Pred);
+ else {
+ NodeBuilderContext Ctx(*this, B, Pred);
+ SubEng.processCFGElement((*B)[StmtIdx], Pred, StmtIdx, &Ctx);
+ }
+}
+
+/// generateNode - Utility method to generate nodes, hook up successors,
+/// and add nodes to the worklist.
+void CoreEngine::generateNode(const ProgramPoint &Loc,
+ ProgramStateRef State,
+ ExplodedNode *Pred) {
+ bool IsNew;
+ ExplodedNode *Node = G.getNode(Loc, State, false, &IsNew);
+
+ if (Pred)
+ Node->addPredecessor(Pred, G); // Link 'Node' with its predecessor.
+ else {
+ assert(IsNew);
+ G.addRoot(Node); // 'Node' has no predecessor. Make it a root.
+ }
+
+ // Only add 'Node' to the worklist if it was freshly generated.
+ if (IsNew) WList->enqueue(Node);
+}
+
+void CoreEngine::enqueueStmtNode(ExplodedNode *N,
+ const CFGBlock *Block, unsigned Idx) {
+ assert(Block);
+ assert(!N->isSink());
+
+ // Check if this node entered a callee.
+ if (N->getLocation().getAs<CallEnter>()) {
+ // Still use the index of the CallExpr. It's needed to create the callee
+ // StackFrameContext.
+ WList->enqueue(N, Block, Idx);
+ return;
+ }
+
+ // Do not create extra nodes. Move to the next CFG element.
+ if (N->getLocation().getAs<PostInitializer>() ||
+ N->getLocation().getAs<PostImplicitCall>()||
+ N->getLocation().getAs<LoopExit>()) {
+ WList->enqueue(N, Block, Idx+1);
+ return;
+ }
+
+ if (N->getLocation().getAs<EpsilonPoint>()) {
+ WList->enqueue(N, Block, Idx);
+ return;
+ }
+
+ if ((*Block)[Idx].getKind() == CFGElement::NewAllocator) {
+ WList->enqueue(N, Block, Idx+1);
+ return;
+ }
+
+ // At this point, we know we're processing a normal statement.
+ CFGStmt CS = (*Block)[Idx].castAs<CFGStmt>();
+ PostStmt Loc(CS.getStmt(), N->getLocationContext());
+
+ if (Loc == N->getLocation().withTag(nullptr)) {
+ // Note: 'N' should be a fresh node because otherwise it shouldn't be
+ // a member of Deferred.
+ WList->enqueue(N, Block, Idx+1);
+ return;
+ }
+
+ bool IsNew;
+ ExplodedNode *Succ = G.getNode(Loc, N->getState(), false, &IsNew);
+ Succ->addPredecessor(N, G);
+
+ if (IsNew)
+ WList->enqueue(Succ, Block, Idx+1);
+}
+
+ExplodedNode *CoreEngine::generateCallExitBeginNode(ExplodedNode *N,
+ const ReturnStmt *RS) {
+ // Create a CallExitBegin node and enqueue it.
+ const auto *LocCtx = cast<StackFrameContext>(N->getLocationContext());
+
+ // Use the callee location context.
+ CallExitBegin Loc(LocCtx, RS);
+
+ bool isNew;
+ ExplodedNode *Node = G.getNode(Loc, N->getState(), false, &isNew);
+ Node->addPredecessor(N, G);
+ return isNew ? Node : nullptr;
+}
+
+void CoreEngine::enqueue(ExplodedNodeSet &Set) {
+ for (const auto I : Set)
+ WList->enqueue(I);
+}
+
+void CoreEngine::enqueue(ExplodedNodeSet &Set,
+ const CFGBlock *Block, unsigned Idx) {
+ for (const auto I : Set)
+ enqueueStmtNode(I, Block, Idx);
+}
+
+void CoreEngine::enqueueEndOfFunction(ExplodedNodeSet &Set, const ReturnStmt *RS) {
+ for (auto I : Set) {
+ // If we are in an inlined call, generate CallExitBegin node.
+ if (I->getLocationContext()->getParent()) {
+ I = generateCallExitBeginNode(I, RS);
+ if (I)
+ WList->enqueue(I);
+ } else {
+ // TODO: We should run remove dead bindings here.
+ G.addEndOfPath(I);
+ NumPathsExplored++;
+ }
+ }
+}
+
+void NodeBuilder::anchor() {}
+
+ExplodedNode* NodeBuilder::generateNodeImpl(const ProgramPoint &Loc,
+ ProgramStateRef State,
+ ExplodedNode *FromN,
+ bool MarkAsSink) {
+ HasGeneratedNodes = true;
+ bool IsNew;
+ ExplodedNode *N = C.Eng.G.getNode(Loc, State, MarkAsSink, &IsNew);
+ N->addPredecessor(FromN, C.Eng.G);
+ Frontier.erase(FromN);
+
+ if (!IsNew)
+ return nullptr;
+
+ if (!MarkAsSink)
+ Frontier.Add(N);
+
+ return N;
+}
+
+void NodeBuilderWithSinks::anchor() {}
+
+StmtNodeBuilder::~StmtNodeBuilder() {
+ if (EnclosingBldr)
+ for (const auto I : Frontier)
+ EnclosingBldr->addNodes(I);
+}
+
+void BranchNodeBuilder::anchor() {}
+
+ExplodedNode *BranchNodeBuilder::generateNode(ProgramStateRef State,
+ bool branch,
+ ExplodedNode *NodePred) {
+ // If the branch has been marked infeasible we should not generate a node.
+ if (!isFeasible(branch))
+ return nullptr;
+
+ ProgramPoint Loc = BlockEdge(C.Block, branch ? DstT:DstF,
+ NodePred->getLocationContext());
+ ExplodedNode *Succ = generateNodeImpl(Loc, State, NodePred);
+ return Succ;
+}
+
+ExplodedNode*
+IndirectGotoNodeBuilder::generateNode(const iterator &I,
+ ProgramStateRef St,
+ bool IsSink) {
+ bool IsNew;
+ ExplodedNode *Succ =
+ Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()),
+ St, IsSink, &IsNew);
+ Succ->addPredecessor(Pred, Eng.G);
+
+ if (!IsNew)
+ return nullptr;
+
+ if (!IsSink)
+ Eng.WList->enqueue(Succ);
+
+ return Succ;
+}
+
+ExplodedNode*
+SwitchNodeBuilder::generateCaseStmtNode(const iterator &I,
+ ProgramStateRef St) {
+ bool IsNew;
+ ExplodedNode *Succ =
+ Eng.G.getNode(BlockEdge(Src, I.getBlock(), Pred->getLocationContext()),
+ St, false, &IsNew);
+ Succ->addPredecessor(Pred, Eng.G);
+ if (!IsNew)
+ return nullptr;
+
+ Eng.WList->enqueue(Succ);
+ return Succ;
+}
+
+ExplodedNode*
+SwitchNodeBuilder::generateDefaultCaseNode(ProgramStateRef St,
+ bool IsSink) {
+ // Get the block for the default case.
+ assert(Src->succ_rbegin() != Src->succ_rend());
+ CFGBlock *DefaultBlock = *Src->succ_rbegin();
+
+ // Sanity check for default blocks that are unreachable and not caught
+ // by earlier stages.
+ if (!DefaultBlock)
+ return nullptr;
+
+ bool IsNew;
+ ExplodedNode *Succ =
+ Eng.G.getNode(BlockEdge(Src, DefaultBlock, Pred->getLocationContext()),
+ St, IsSink, &IsNew);
+ Succ->addPredecessor(Pred, Eng.G);
+
+ if (!IsNew)
+ return nullptr;
+
+ if (!IsSink)
+ Eng.WList->enqueue(Succ);
+
+ return Succ;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
new file mode 100644
index 000000000000..da7854df1def
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
@@ -0,0 +1,86 @@
+//===- DynamicTypeMap.cpp - Dynamic Type Info related APIs ----------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines APIs that track and query dynamic type information. This
+// information can be used to devirtualize calls during the symbolic execution
+// or do type checking.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+
+namespace clang {
+namespace ento {
+
+DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State,
+ const MemRegion *Reg) {
+ Reg = Reg->StripCasts();
+
+ // Look up the dynamic type in the GDM.
+ const DynamicTypeInfo *GDMType = State->get<DynamicTypeMap>(Reg);
+ if (GDMType)
+ return *GDMType;
+
+ // Otherwise, fall back to what we know about the region.
+ if (const auto *TR = dyn_cast<TypedRegion>(Reg))
+ return DynamicTypeInfo(TR->getLocationType(), /*CanBeSubclass=*/false);
+
+ if (const auto *SR = dyn_cast<SymbolicRegion>(Reg)) {
+ SymbolRef Sym = SR->getSymbol();
+ return DynamicTypeInfo(Sym->getType());
+ }
+
+ return {};
+}
+
+ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg,
+ DynamicTypeInfo NewTy) {
+ Reg = Reg->StripCasts();
+ ProgramStateRef NewState = State->set<DynamicTypeMap>(Reg, NewTy);
+ assert(NewState);
+ return NewState;
+}
+
+void printDynamicTypeInfo(ProgramStateRef State, raw_ostream &Out,
+ const char *NL, const char *Sep) {
+ bool First = true;
+ for (const auto &I : State->get<DynamicTypeMap>()) {
+ if (First) {
+ Out << NL << "Dynamic types of regions:" << NL;
+ First = false;
+ }
+ const MemRegion *MR = I.first;
+ const DynamicTypeInfo &DTI = I.second;
+ Out << MR << " : ";
+ if (DTI.isValid()) {
+ Out << DTI.getType()->getPointeeType().getAsString();
+ if (DTI.canBeASubClass()) {
+ Out << " (or its subclass)";
+ }
+ } else {
+ Out << "Invalid type info";
+ }
+ Out << NL;
+ }
+}
+
+void *ProgramStateTrait<DynamicTypeMap>::GDMIndex() {
+ static int index = 0;
+ return &index;
+}
+
+} // namespace ento
+} // namespace clang
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp
new file mode 100644
index 000000000000..b45f93b6dde8
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Environment.cpp
@@ -0,0 +1,242 @@
+//===- Environment.cpp - Map from Stmt* to Locations/Values ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defined the Environment and EnvironmentManager classes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/Environment.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/SmallPtrSet.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+
+using namespace clang;
+using namespace ento;
+
+static const Expr *ignoreTransparentExprs(const Expr *E) {
+ E = E->IgnoreParens();
+
+ switch (E->getStmtClass()) {
+ case Stmt::OpaqueValueExprClass:
+ E = cast<OpaqueValueExpr>(E)->getSourceExpr();
+ break;
+ case Stmt::ExprWithCleanupsClass:
+ E = cast<ExprWithCleanups>(E)->getSubExpr();
+ break;
+ case Stmt::ConstantExprClass:
+ E = cast<ConstantExpr>(E)->getSubExpr();
+ break;
+ case Stmt::CXXBindTemporaryExprClass:
+ E = cast<CXXBindTemporaryExpr>(E)->getSubExpr();
+ break;
+ case Stmt::SubstNonTypeTemplateParmExprClass:
+ E = cast<SubstNonTypeTemplateParmExpr>(E)->getReplacement();
+ break;
+ default:
+ // This is the base case: we can't look through more than we already have.
+ return E;
+ }
+
+ return ignoreTransparentExprs(E);
+}
+
+static const Stmt *ignoreTransparentExprs(const Stmt *S) {
+ if (const auto *E = dyn_cast<Expr>(S))
+ return ignoreTransparentExprs(E);
+ return S;
+}
+
+EnvironmentEntry::EnvironmentEntry(const Stmt *S, const LocationContext *L)
+ : std::pair<const Stmt *,
+ const StackFrameContext *>(ignoreTransparentExprs(S),
+ L ? L->getStackFrame()
+ : nullptr) {}
+
+SVal Environment::lookupExpr(const EnvironmentEntry &E) const {
+ const SVal* X = ExprBindings.lookup(E);
+ if (X) {
+ SVal V = *X;
+ return V;
+ }
+ return UnknownVal();
+}
+
+SVal Environment::getSVal(const EnvironmentEntry &Entry,
+ SValBuilder& svalBuilder) const {
+ const Stmt *S = Entry.getStmt();
+ const LocationContext *LCtx = Entry.getLocationContext();
+
+ switch (S->getStmtClass()) {
+ case Stmt::CXXBindTemporaryExprClass:
+ case Stmt::ExprWithCleanupsClass:
+ case Stmt::GenericSelectionExprClass:
+ case Stmt::OpaqueValueExprClass:
+ case Stmt::ConstantExprClass:
+ case Stmt::ParenExprClass:
+ case Stmt::SubstNonTypeTemplateParmExprClass:
+ llvm_unreachable("Should have been handled by ignoreTransparentExprs");
+
+ case Stmt::AddrLabelExprClass:
+ case Stmt::CharacterLiteralClass:
+ case Stmt::CXXBoolLiteralExprClass:
+ case Stmt::CXXScalarValueInitExprClass:
+ case Stmt::ImplicitValueInitExprClass:
+ case Stmt::IntegerLiteralClass:
+ case Stmt::ObjCBoolLiteralExprClass:
+ case Stmt::CXXNullPtrLiteralExprClass:
+ case Stmt::ObjCStringLiteralClass:
+ case Stmt::StringLiteralClass:
+ case Stmt::TypeTraitExprClass:
+ // Known constants; defer to SValBuilder.
+ return svalBuilder.getConstantVal(cast<Expr>(S)).getValue();
+
+ case Stmt::ReturnStmtClass: {
+ const auto *RS = cast<ReturnStmt>(S);
+ if (const Expr *RE = RS->getRetValue())
+ return getSVal(EnvironmentEntry(RE, LCtx), svalBuilder);
+ return UndefinedVal();
+ }
+
+ // Handle all other Stmt* using a lookup.
+ default:
+ return lookupExpr(EnvironmentEntry(S, LCtx));
+ }
+}
+
+Environment EnvironmentManager::bindExpr(Environment Env,
+ const EnvironmentEntry &E,
+ SVal V,
+ bool Invalidate) {
+ if (V.isUnknown()) {
+ if (Invalidate)
+ return Environment(F.remove(Env.ExprBindings, E));
+ else
+ return Env;
+ }
+ return Environment(F.add(Env.ExprBindings, E, V));
+}
+
+namespace {
+
+class MarkLiveCallback final : public SymbolVisitor {
+ SymbolReaper &SymReaper;
+
+public:
+ MarkLiveCallback(SymbolReaper &symreaper) : SymReaper(symreaper) {}
+
+ bool VisitSymbol(SymbolRef sym) override {
+ SymReaper.markLive(sym);
+ return true;
+ }
+
+ bool VisitMemRegion(const MemRegion *R) override {
+ SymReaper.markLive(R);
+ return true;
+ }
+};
+
+} // namespace
+
+// removeDeadBindings:
+// - Remove subexpression bindings.
+// - Remove dead block expression bindings.
+// - Keep live block expression bindings:
+// - Mark their reachable symbols live in SymbolReaper,
+// see ScanReachableSymbols.
+// - Mark the region in DRoots if the binding is a loc::MemRegionVal.
+Environment
+EnvironmentManager::removeDeadBindings(Environment Env,
+ SymbolReaper &SymReaper,
+ ProgramStateRef ST) {
+ // We construct a new Environment object entirely, as this is cheaper than
+ // individually removing all the subexpression bindings (which will greatly
+ // outnumber block-level expression bindings).
+ Environment NewEnv = getInitialEnvironment();
+
+ MarkLiveCallback CB(SymReaper);
+ ScanReachableSymbols RSScaner(ST, CB);
+
+ llvm::ImmutableMapRef<EnvironmentEntry, SVal>
+ EBMapRef(NewEnv.ExprBindings.getRootWithoutRetain(),
+ F.getTreeFactory());
+
+ // Iterate over the block-expr bindings.
+ for (Environment::iterator I = Env.begin(), E = Env.end();
+ I != E; ++I) {
+ const EnvironmentEntry &BlkExpr = I.getKey();
+ const SVal &X = I.getData();
+
+ if (SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext())) {
+ // Copy the binding to the new map.
+ EBMapRef = EBMapRef.add(BlkExpr, X);
+
+ // Mark all symbols in the block expr's value live.
+ RSScaner.scan(X);
+ }
+ }
+
+ NewEnv.ExprBindings = EBMapRef.asImmutableMap();
+ return NewEnv;
+}
+
+void Environment::print(raw_ostream &Out, const char *NL,
+ const char *Sep,
+ const ASTContext &Context,
+ const LocationContext *WithLC) const {
+ if (ExprBindings.isEmpty())
+ return;
+
+ if (!WithLC) {
+ // Find the freshest location context.
+ llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts;
+ for (auto I : *this) {
+ const LocationContext *LC = I.first.getLocationContext();
+ if (FoundContexts.count(LC) == 0) {
+ // This context is fresher than all other contexts so far.
+ WithLC = LC;
+ for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent())
+ FoundContexts.insert(LCI);
+ }
+ }
+ }
+
+ assert(WithLC);
+
+ PrintingPolicy PP = Context.getPrintingPolicy();
+
+ Out << NL << "Expressions by stack frame:" << NL;
+ WithLC->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) {
+ for (auto I : ExprBindings) {
+ if (I.first.getLocationContext() != LC)
+ continue;
+
+ const Stmt *S = I.first.getStmt();
+ assert(S != nullptr && "Expected non-null Stmt");
+
+ Out << "(LC" << LC->getID() << ", S" << S->getID(Context) << ") ";
+ S->printPretty(Out, /*Helper=*/nullptr, PP);
+ Out << " : " << I.second << NL;
+ }
+ });
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
new file mode 100644
index 000000000000..d6bcbb96b55f
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -0,0 +1,441 @@
+//===- ExplodedGraph.cpp - Local, Path-Sens. "Exploded Graph" -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the template classes ExplodedNode and ExplodedGraph,
+// which represent a path-sensitive, intra-procedural "exploded graph."
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Analysis/Support/BumpVector.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include <cassert>
+#include <memory>
+
+using namespace clang;
+using namespace ento;
+
+//===----------------------------------------------------------------------===//
+// Cleanup.
+//===----------------------------------------------------------------------===//
+
+ExplodedGraph::ExplodedGraph() = default;
+
+ExplodedGraph::~ExplodedGraph() = default;
+
+//===----------------------------------------------------------------------===//
+// Node reclamation.
+//===----------------------------------------------------------------------===//
+
+bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) {
+ if (!Ex->isLValue())
+ return false;
+ return isa<DeclRefExpr>(Ex) ||
+ isa<MemberExpr>(Ex) ||
+ isa<ObjCIvarRefExpr>(Ex);
+}
+
+bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
+ // First, we only consider nodes for reclamation of the following
+ // conditions apply:
+ //
+ // (1) 1 predecessor (that has one successor)
+ // (2) 1 successor (that has one predecessor)
+ //
+ // If a node has no successor it is on the "frontier", while a node
+ // with no predecessor is a root.
+ //
+ // After these prerequisites, we discard all "filler" nodes that
+ // are used only for intermediate processing, and are not essential
+ // for analyzer history:
+ //
+ // (a) PreStmtPurgeDeadSymbols
+ //
+ // We then discard all other nodes where *all* of the following conditions
+ // apply:
+ //
+ // (3) The ProgramPoint is for a PostStmt, but not a PostStore.
+ // (4) There is no 'tag' for the ProgramPoint.
+ // (5) The 'store' is the same as the predecessor.
+ // (6) The 'GDM' is the same as the predecessor.
+ // (7) The LocationContext is the same as the predecessor.
+ // (8) Expressions that are *not* lvalue expressions.
+ // (9) The PostStmt isn't for a non-consumed Stmt or Expr.
+ // (10) The successor is neither a CallExpr StmtPoint nor a CallEnter or
+ // PreImplicitCall (so that we would be able to find it when retrying a
+ // call with no inlining).
+ // FIXME: It may be safe to reclaim PreCall and PostCall nodes as well.
+
+ // Conditions 1 and 2.
+ if (node->pred_size() != 1 || node->succ_size() != 1)
+ return false;
+
+ const ExplodedNode *pred = *(node->pred_begin());
+ if (pred->succ_size() != 1)
+ return false;
+
+ const ExplodedNode *succ = *(node->succ_begin());
+ if (succ->pred_size() != 1)
+ return false;
+
+ // Now reclaim any nodes that are (by definition) not essential to
+ // analysis history and are not consulted by any client code.
+ ProgramPoint progPoint = node->getLocation();
+ if (progPoint.getAs<PreStmtPurgeDeadSymbols>())
+ return !progPoint.getTag();
+
+ // Condition 3.
+ if (!progPoint.getAs<PostStmt>() || progPoint.getAs<PostStore>())
+ return false;
+
+ // Condition 4.
+ if (progPoint.getTag())
+ return false;
+
+ // Conditions 5, 6, and 7.
+ ProgramStateRef state = node->getState();
+ ProgramStateRef pred_state = pred->getState();
+ if (state->store != pred_state->store || state->GDM != pred_state->GDM ||
+ progPoint.getLocationContext() != pred->getLocationContext())
+ return false;
+
+ // All further checks require expressions. As per #3, we know that we have
+ // a PostStmt.
+ const Expr *Ex = dyn_cast<Expr>(progPoint.castAs<PostStmt>().getStmt());
+ if (!Ex)
+ return false;
+
+ // Condition 8.
+ // Do not collect nodes for "interesting" lvalue expressions since they are
+ // used extensively for generating path diagnostics.
+ if (isInterestingLValueExpr(Ex))
+ return false;
+
+ // Condition 9.
+ // Do not collect nodes for non-consumed Stmt or Expr to ensure precise
+ // diagnostic generation; specifically, so that we could anchor arrows
+ // pointing to the beginning of statements (as written in code).
+ ParentMap &PM = progPoint.getLocationContext()->getParentMap();
+ if (!PM.isConsumedExpr(Ex))
+ return false;
+
+ // Condition 10.
+ const ProgramPoint SuccLoc = succ->getLocation();
+ if (Optional<StmtPoint> SP = SuccLoc.getAs<StmtPoint>())
+ if (CallEvent::isCallStmt(SP->getStmt()))
+ return false;
+
+ // Condition 10, continuation.
+ if (SuccLoc.getAs<CallEnter>() || SuccLoc.getAs<PreImplicitCall>())
+ return false;
+
+ return true;
+}
+
+void ExplodedGraph::collectNode(ExplodedNode *node) {
+ // Removing a node means:
+ // (a) changing the predecessors successor to the successor of this node
+ // (b) changing the successors predecessor to the predecessor of this node
+ // (c) Putting 'node' onto freeNodes.
+ assert(node->pred_size() == 1 || node->succ_size() == 1);
+ ExplodedNode *pred = *(node->pred_begin());
+ ExplodedNode *succ = *(node->succ_begin());
+ pred->replaceSuccessor(succ);
+ succ->replacePredecessor(pred);
+ FreeNodes.push_back(node);
+ Nodes.RemoveNode(node);
+ --NumNodes;
+ node->~ExplodedNode();
+}
+
+void ExplodedGraph::reclaimRecentlyAllocatedNodes() {
+ if (ChangedNodes.empty())
+ return;
+
+ // Only periodically reclaim nodes so that we can build up a set of
+ // nodes that meet the reclamation criteria. Freshly created nodes
+ // by definition have no successor, and thus cannot be reclaimed (see below).
+ assert(ReclaimCounter > 0);
+ if (--ReclaimCounter != 0)
+ return;
+ ReclaimCounter = ReclaimNodeInterval;
+
+ for (const auto node : ChangedNodes)
+ if (shouldCollect(node))
+ collectNode(node);
+ ChangedNodes.clear();
+}
+
+//===----------------------------------------------------------------------===//
+// ExplodedNode.
+//===----------------------------------------------------------------------===//
+
+// An NodeGroup's storage type is actually very much like a TinyPtrVector:
+// it can be either a pointer to a single ExplodedNode, or a pointer to a
+// BumpVector allocated with the ExplodedGraph's allocator. This allows the
+// common case of single-node NodeGroups to be implemented with no extra memory.
+//
+// Consequently, each of the NodeGroup methods have up to four cases to handle:
+// 1. The flag is set and this group does not actually contain any nodes.
+// 2. The group is empty, in which case the storage value is null.
+// 3. The group contains a single node.
+// 4. The group contains more than one node.
+using ExplodedNodeVector = BumpVector<ExplodedNode *>;
+using GroupStorage = llvm::PointerUnion<ExplodedNode *, ExplodedNodeVector *>;
+
+void ExplodedNode::addPredecessor(ExplodedNode *V, ExplodedGraph &G) {
+ assert(!V->isSink());
+ Preds.addNode(V, G);
+ V->Succs.addNode(this, G);
+}
+
+void ExplodedNode::NodeGroup::replaceNode(ExplodedNode *node) {
+ assert(!getFlag());
+
+ GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
+ assert(Storage.is<ExplodedNode *>());
+ Storage = node;
+ assert(Storage.is<ExplodedNode *>());
+}
+
+void ExplodedNode::NodeGroup::addNode(ExplodedNode *N, ExplodedGraph &G) {
+ assert(!getFlag());
+
+ GroupStorage &Storage = reinterpret_cast<GroupStorage&>(P);
+ if (Storage.isNull()) {
+ Storage = N;
+ assert(Storage.is<ExplodedNode *>());
+ return;
+ }
+
+ ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>();
+
+ if (!V) {
+ // Switch from single-node to multi-node representation.
+ ExplodedNode *Old = Storage.get<ExplodedNode *>();
+
+ BumpVectorContext &Ctx = G.getNodeAllocator();
+ V = G.getAllocator().Allocate<ExplodedNodeVector>();
+ new (V) ExplodedNodeVector(Ctx, 4);
+ V->push_back(Old, Ctx);
+
+ Storage = V;
+ assert(!getFlag());
+ assert(Storage.is<ExplodedNodeVector *>());
+ }
+
+ V->push_back(N, G.getNodeAllocator());
+}
+
+unsigned ExplodedNode::NodeGroup::size() const {
+ if (getFlag())
+ return 0;
+
+ const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
+ if (Storage.isNull())
+ return 0;
+ if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
+ return V->size();
+ return 1;
+}
+
+ExplodedNode * const *ExplodedNode::NodeGroup::begin() const {
+ if (getFlag())
+ return nullptr;
+
+ const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
+ if (Storage.isNull())
+ return nullptr;
+ if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
+ return V->begin();
+ return Storage.getAddrOfPtr1();
+}
+
+ExplodedNode * const *ExplodedNode::NodeGroup::end() const {
+ if (getFlag())
+ return nullptr;
+
+ const GroupStorage &Storage = reinterpret_cast<const GroupStorage &>(P);
+ if (Storage.isNull())
+ return nullptr;
+ if (ExplodedNodeVector *V = Storage.dyn_cast<ExplodedNodeVector *>())
+ return V->end();
+ return Storage.getAddrOfPtr1() + 1;
+}
+
+int64_t ExplodedNode::getID(ExplodedGraph *G) const {
+ return G->getAllocator().identifyKnownAlignedObject<ExplodedNode>(this);
+}
+
+bool ExplodedNode::isTrivial() const {
+ return pred_size() == 1 && succ_size() == 1 &&
+ getFirstPred()->getState()->getID() == getState()->getID() &&
+ getFirstPred()->succ_size() == 1;
+}
+
+ExplodedNode *ExplodedGraph::getNode(const ProgramPoint &L,
+ ProgramStateRef State,
+ bool IsSink,
+ bool* IsNew) {
+ // Profile 'State' to determine if we already have an existing node.
+ llvm::FoldingSetNodeID profile;
+ void *InsertPos = nullptr;
+
+ NodeTy::Profile(profile, L, State, IsSink);
+ NodeTy* V = Nodes.FindNodeOrInsertPos(profile, InsertPos);
+
+ if (!V) {
+ if (!FreeNodes.empty()) {
+ V = FreeNodes.back();
+ FreeNodes.pop_back();
+ }
+ else {
+ // Allocate a new node.
+ V = (NodeTy*) getAllocator().Allocate<NodeTy>();
+ }
+
+ new (V) NodeTy(L, State, IsSink);
+
+ if (ReclaimNodeInterval)
+ ChangedNodes.push_back(V);
+
+ // Insert the node into the node set and return it.
+ Nodes.InsertNode(V, InsertPos);
+ ++NumNodes;
+
+ if (IsNew) *IsNew = true;
+ }
+ else
+ if (IsNew) *IsNew = false;
+
+ return V;
+}
+
+ExplodedNode *ExplodedGraph::createUncachedNode(const ProgramPoint &L,
+ ProgramStateRef State,
+ bool IsSink) {
+ NodeTy *V = (NodeTy *) getAllocator().Allocate<NodeTy>();
+ new (V) NodeTy(L, State, IsSink);
+ return V;
+}
+
+std::unique_ptr<ExplodedGraph>
+ExplodedGraph::trim(ArrayRef<const NodeTy *> Sinks,
+ InterExplodedGraphMap *ForwardMap,
+ InterExplodedGraphMap *InverseMap) const {
+ if (Nodes.empty())
+ return nullptr;
+
+ using Pass1Ty = llvm::DenseSet<const ExplodedNode *>;
+ Pass1Ty Pass1;
+
+ using Pass2Ty = InterExplodedGraphMap;
+ InterExplodedGraphMap Pass2Scratch;
+ Pass2Ty &Pass2 = ForwardMap ? *ForwardMap : Pass2Scratch;
+
+ SmallVector<const ExplodedNode*, 10> WL1, WL2;
+
+ // ===- Pass 1 (reverse DFS) -===
+ for (const auto Sink : Sinks)
+ if (Sink)
+ WL1.push_back(Sink);
+
+ // Process the first worklist until it is empty.
+ while (!WL1.empty()) {
+ const ExplodedNode *N = WL1.pop_back_val();
+
+ // Have we already visited this node? If so, continue to the next one.
+ if (!Pass1.insert(N).second)
+ continue;
+
+ // If this is a root enqueue it to the second worklist.
+ if (N->Preds.empty()) {
+ WL2.push_back(N);
+ continue;
+ }
+
+ // Visit our predecessors and enqueue them.
+ WL1.append(N->Preds.begin(), N->Preds.end());
+ }
+
+ // We didn't hit a root? Return with a null pointer for the new graph.
+ if (WL2.empty())
+ return nullptr;
+
+ // Create an empty graph.
+ std::unique_ptr<ExplodedGraph> G = MakeEmptyGraph();
+
+ // ===- Pass 2 (forward DFS to construct the new graph) -===
+ while (!WL2.empty()) {
+ const ExplodedNode *N = WL2.pop_back_val();
+
+ // Skip this node if we have already processed it.
+ if (Pass2.find(N) != Pass2.end())
+ continue;
+
+ // Create the corresponding node in the new graph and record the mapping
+ // from the old node to the new node.
+ ExplodedNode *NewN = G->createUncachedNode(N->getLocation(), N->State, N->isSink());
+ Pass2[N] = NewN;
+
+ // Also record the reverse mapping from the new node to the old node.
+ if (InverseMap) (*InverseMap)[NewN] = N;
+
+ // If this node is a root, designate it as such in the graph.
+ if (N->Preds.empty())
+ G->addRoot(NewN);
+
+ // In the case that some of the intended predecessors of NewN have already
+ // been created, we should hook them up as predecessors.
+
+ // Walk through the predecessors of 'N' and hook up their corresponding
+ // nodes in the new graph (if any) to the freshly created node.
+ for (ExplodedNode::pred_iterator I = N->Preds.begin(), E = N->Preds.end();
+ I != E; ++I) {
+ Pass2Ty::iterator PI = Pass2.find(*I);
+ if (PI == Pass2.end())
+ continue;
+
+ NewN->addPredecessor(const_cast<ExplodedNode *>(PI->second), *G);
+ }
+
+ // In the case that some of the intended successors of NewN have already
+ // been created, we should hook them up as successors. Otherwise, enqueue
+ // the new nodes from the original graph that should have nodes created
+ // in the new graph.
+ for (ExplodedNode::succ_iterator I = N->Succs.begin(), E = N->Succs.end();
+ I != E; ++I) {
+ Pass2Ty::iterator PI = Pass2.find(*I);
+ if (PI != Pass2.end()) {
+ const_cast<ExplodedNode *>(PI->second)->addPredecessor(NewN, *G);
+ continue;
+ }
+
+ // Enqueue nodes to the worklist that were marked during pass 1.
+ if (Pass1.count(*I))
+ WL2.push_back(*I);
+ }
+ }
+
+ return G;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
new file mode 100644
index 000000000000..151eef56fece
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -0,0 +1,3115 @@
+//===- ExprEngine.cpp - Path-Sensitive Expression-Level Dataflow ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a meta-engine for path-sensitive dataflow analysis that
+// is built on GREngine, but provides the boilerplate to execute transfer
+// functions and build the ExplodedGraph at the expression level.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "PrettyStackTraceLocationContext.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/StmtObjC.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/ConstructionContext.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/PrettyStackTrace.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Specifiers.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/DOTGraphTraits.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/GraphWriter.h"
+#include "llvm/Support/SaveAndRestore.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstdint>
+#include <memory>
+#include <string>
+#include <tuple>
+#include <utility>
+#include <vector>
+
+using namespace clang;
+using namespace ento;
+
+#define DEBUG_TYPE "ExprEngine"
+
+STATISTIC(NumRemoveDeadBindings,
+ "The # of times RemoveDeadBindings is called");
+STATISTIC(NumMaxBlockCountReached,
+ "The # of aborted paths due to reaching the maximum block count in "
+ "a top level function");
+STATISTIC(NumMaxBlockCountReachedInInlined,
+ "The # of aborted paths due to reaching the maximum block count in "
+ "an inlined function");
+STATISTIC(NumTimesRetriedWithoutInlining,
+ "The # of times we re-evaluated a call without inlining");
+
+//===----------------------------------------------------------------------===//
+// Internal program state traits.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+// When modeling a C++ constructor, for a variety of reasons we need to track
+// the location of the object for the duration of its ConstructionContext.
+// ObjectsUnderConstruction maps statements within the construction context
+// to the object's location, so that on every such statement the location
+// could have been retrieved.
+
+/// ConstructedObjectKey is used for being able to find the path-sensitive
+/// memory region of a freshly constructed object while modeling the AST node
+/// that syntactically represents the object that is being constructed.
+/// Semantics of such nodes may sometimes require access to the region that's
+/// not otherwise present in the program state, or to the very fact that
+/// the construction context was present and contained references to these
+/// AST nodes.
+class ConstructedObjectKey {
+ typedef std::pair<ConstructionContextItem, const LocationContext *>
+ ConstructedObjectKeyImpl;
+
+ const ConstructedObjectKeyImpl Impl;
+
+ const void *getAnyASTNodePtr() const {
+ if (const Stmt *S = getItem().getStmtOrNull())
+ return S;
+ else
+ return getItem().getCXXCtorInitializer();
+ }
+
+public:
+ explicit ConstructedObjectKey(const ConstructionContextItem &Item,
+ const LocationContext *LC)
+ : Impl(Item, LC) {}
+
+ const ConstructionContextItem &getItem() const { return Impl.first; }
+ const LocationContext *getLocationContext() const { return Impl.second; }
+
+ ASTContext &getASTContext() const {
+ return getLocationContext()->getDecl()->getASTContext();
+ }
+
+ void print(llvm::raw_ostream &OS, PrinterHelper *Helper, PrintingPolicy &PP) {
+ OS << "(LC" << getLocationContext()->getID() << ',';
+ if (const Stmt *S = getItem().getStmtOrNull())
+ OS << 'S' << S->getID(getASTContext());
+ else
+ OS << 'I' << getItem().getCXXCtorInitializer()->getID(getASTContext());
+ OS << ',' << getItem().getKindAsString();
+ if (getItem().getKind() == ConstructionContextItem::ArgumentKind)
+ OS << " #" << getItem().getIndex();
+ OS << ") ";
+ if (const Stmt *S = getItem().getStmtOrNull()) {
+ S->printPretty(OS, Helper, PP);
+ } else {
+ const CXXCtorInitializer *I = getItem().getCXXCtorInitializer();
+ OS << I->getAnyMember()->getNameAsString();
+ }
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.Add(Impl.first);
+ ID.AddPointer(Impl.second);
+ }
+
+ bool operator==(const ConstructedObjectKey &RHS) const {
+ return Impl == RHS.Impl;
+ }
+
+ bool operator<(const ConstructedObjectKey &RHS) const {
+ return Impl < RHS.Impl;
+ }
+};
+} // namespace
+
+typedef llvm::ImmutableMap<ConstructedObjectKey, SVal>
+ ObjectsUnderConstructionMap;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ObjectsUnderConstruction,
+ ObjectsUnderConstructionMap)
+
+//===----------------------------------------------------------------------===//
+// Engine construction and deletion.
+//===----------------------------------------------------------------------===//
+
+static const char* TagProviderName = "ExprEngine";
+
+ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU,
+ AnalysisManager &mgr,
+ SetOfConstDecls *VisitedCalleesIn,
+ FunctionSummariesTy *FS,
+ InliningModes HowToInlineIn)
+ : CTU(CTU), AMgr(mgr),
+ AnalysisDeclContexts(mgr.getAnalysisDeclContextManager()),
+ Engine(*this, FS, mgr.getAnalyzerOptions()), G(Engine.getGraph()),
+ StateMgr(getContext(), mgr.getStoreManagerCreator(),
+ mgr.getConstraintManagerCreator(), G.getAllocator(),
+ this),
+ SymMgr(StateMgr.getSymbolManager()),
+ svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()),
+ BR(mgr, *this),
+ VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) {
+ unsigned TrimInterval = mgr.options.GraphTrimInterval;
+ if (TrimInterval != 0) {
+ // Enable eager node reclamation when constructing the ExplodedGraph.
+ G.enableNodeReclamation(TrimInterval);
+ }
+}
+
+ExprEngine::~ExprEngine() {
+ BR.FlushReports();
+}
+
+//===----------------------------------------------------------------------===//
+// Utility methods.
+//===----------------------------------------------------------------------===//
+
+ProgramStateRef ExprEngine::getInitialState(const LocationContext *InitLoc) {
+ ProgramStateRef state = StateMgr.getInitialState(InitLoc);
+ const Decl *D = InitLoc->getDecl();
+
+ // Preconditions.
+ // FIXME: It would be nice if we had a more general mechanism to add
+ // such preconditions. Some day.
+ do {
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ // Precondition: the first argument of 'main' is an integer guaranteed
+ // to be > 0.
+ const IdentifierInfo *II = FD->getIdentifier();
+ if (!II || !(II->getName() == "main" && FD->getNumParams() > 0))
+ break;
+
+ const ParmVarDecl *PD = FD->getParamDecl(0);
+ QualType T = PD->getType();
+ const auto *BT = dyn_cast<BuiltinType>(T);
+ if (!BT || !BT->isInteger())
+ break;
+
+ const MemRegion *R = state->getRegion(PD, InitLoc);
+ if (!R)
+ break;
+
+ SVal V = state->getSVal(loc::MemRegionVal(R));
+ SVal Constraint_untested = evalBinOp(state, BO_GT, V,
+ svalBuilder.makeZeroVal(T),
+ svalBuilder.getConditionType());
+
+ Optional<DefinedOrUnknownSVal> Constraint =
+ Constraint_untested.getAs<DefinedOrUnknownSVal>();
+
+ if (!Constraint)
+ break;
+
+ if (ProgramStateRef newState = state->assume(*Constraint, true))
+ state = newState;
+ }
+ break;
+ }
+ while (false);
+
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ // Precondition: 'self' is always non-null upon entry to an Objective-C
+ // method.
+ const ImplicitParamDecl *SelfD = MD->getSelfDecl();
+ const MemRegion *R = state->getRegion(SelfD, InitLoc);
+ SVal V = state->getSVal(loc::MemRegionVal(R));
+
+ if (Optional<Loc> LV = V.getAs<Loc>()) {
+ // Assume that the pointer value in 'self' is non-null.
+ state = state->assume(*LV, true);
+ assert(state && "'self' cannot be null");
+ }
+ }
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
+ if (!MD->isStatic()) {
+ // Precondition: 'this' is always non-null upon entry to the
+ // top-level function. This is our starting assumption for
+ // analyzing an "open" program.
+ const StackFrameContext *SFC = InitLoc->getStackFrame();
+ if (SFC->getParent() == nullptr) {
+ loc::MemRegionVal L = svalBuilder.getCXXThis(MD, SFC);
+ SVal V = state->getSVal(L);
+ if (Optional<Loc> LV = V.getAs<Loc>()) {
+ state = state->assume(*LV, true);
+ assert(state && "'this' cannot be null");
+ }
+ }
+ }
+ }
+
+ return state;
+}
+
+ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
+ ProgramStateRef State, const LocationContext *LC,
+ const Expr *InitWithAdjustments, const Expr *Result,
+ const SubRegion **OutRegionWithAdjustments) {
+ // FIXME: This function is a hack that works around the quirky AST
+ // we're often having with respect to C++ temporaries. If only we modelled
+ // the actual execution order of statements properly in the CFG,
+ // all the hassle with adjustments would not be necessary,
+ // and perhaps the whole function would be removed.
+ SVal InitValWithAdjustments = State->getSVal(InitWithAdjustments, LC);
+ if (!Result) {
+ // If we don't have an explicit result expression, we're in "if needed"
+ // mode. Only create a region if the current value is a NonLoc.
+ if (!InitValWithAdjustments.getAs<NonLoc>()) {
+ if (OutRegionWithAdjustments)
+ *OutRegionWithAdjustments = nullptr;
+ return State;
+ }
+ Result = InitWithAdjustments;
+ } else {
+ // We need to create a region no matter what. For sanity, make sure we don't
+ // try to stuff a Loc into a non-pointer temporary region.
+ assert(!InitValWithAdjustments.getAs<Loc>() ||
+ Loc::isLocType(Result->getType()) ||
+ Result->getType()->isMemberPointerType());
+ }
+
+ ProgramStateManager &StateMgr = State->getStateManager();
+ MemRegionManager &MRMgr = StateMgr.getRegionManager();
+ StoreManager &StoreMgr = StateMgr.getStoreManager();
+
+ // MaterializeTemporaryExpr may appear out of place, after a few field and
+ // base-class accesses have been made to the object, even though semantically
+ // it is the whole object that gets materialized and lifetime-extended.
+ //
+ // For example:
+ //
+ // `-MaterializeTemporaryExpr
+ // `-MemberExpr
+ // `-CXXTemporaryObjectExpr
+ //
+ // instead of the more natural
+ //
+ // `-MemberExpr
+ // `-MaterializeTemporaryExpr
+ // `-CXXTemporaryObjectExpr
+ //
+ // Use the usual methods for obtaining the expression of the base object,
+ // and record the adjustments that we need to make to obtain the sub-object
+ // that the whole expression 'Ex' refers to. This trick is usual,
+ // in the sense that CodeGen takes a similar route.
+
+ SmallVector<const Expr *, 2> CommaLHSs;
+ SmallVector<SubobjectAdjustment, 2> Adjustments;
+
+ const Expr *Init = InitWithAdjustments->skipRValueSubobjectAdjustments(
+ CommaLHSs, Adjustments);
+
+ // Take the region for Init, i.e. for the whole object. If we do not remember
+ // the region in which the object originally was constructed, come up with
+ // a new temporary region out of thin air and copy the contents of the object
+ // (which are currently present in the Environment, because Init is an rvalue)
+ // into that region. This is not correct, but it is better than nothing.
+ const TypedValueRegion *TR = nullptr;
+ if (const auto *MT = dyn_cast<MaterializeTemporaryExpr>(Result)) {
+ if (Optional<SVal> V = getObjectUnderConstruction(State, MT, LC)) {
+ State = finishObjectConstruction(State, MT, LC);
+ State = State->BindExpr(Result, LC, *V);
+ return State;
+ } else {
+ StorageDuration SD = MT->getStorageDuration();
+ // If this object is bound to a reference with static storage duration, we
+ // put it in a different region to prevent "address leakage" warnings.
+ if (SD == SD_Static || SD == SD_Thread) {
+ TR = MRMgr.getCXXStaticTempObjectRegion(Init);
+ } else {
+ TR = MRMgr.getCXXTempObjectRegion(Init, LC);
+ }
+ }
+ } else {
+ TR = MRMgr.getCXXTempObjectRegion(Init, LC);
+ }
+
+ SVal Reg = loc::MemRegionVal(TR);
+ SVal BaseReg = Reg;
+
+ // Make the necessary adjustments to obtain the sub-object.
+ for (auto I = Adjustments.rbegin(), E = Adjustments.rend(); I != E; ++I) {
+ const SubobjectAdjustment &Adj = *I;
+ switch (Adj.Kind) {
+ case SubobjectAdjustment::DerivedToBaseAdjustment:
+ Reg = StoreMgr.evalDerivedToBase(Reg, Adj.DerivedToBase.BasePath);
+ break;
+ case SubobjectAdjustment::FieldAdjustment:
+ Reg = StoreMgr.getLValueField(Adj.Field, Reg);
+ break;
+ case SubobjectAdjustment::MemberPointerAdjustment:
+ // FIXME: Unimplemented.
+ State = State->invalidateRegions(Reg, InitWithAdjustments,
+ currBldrCtx->blockCount(), LC, true,
+ nullptr, nullptr, nullptr);
+ return State;
+ }
+ }
+
+ // What remains is to copy the value of the object to the new region.
+ // FIXME: In other words, what we should always do is copy value of the
+ // Init expression (which corresponds to the bigger object) to the whole
+ // temporary region TR. However, this value is often no longer present
+ // in the Environment. If it has disappeared, we instead invalidate TR.
+ // Still, what we can do is assign the value of expression Ex (which
+ // corresponds to the sub-object) to the TR's sub-region Reg. At least,
+ // values inside Reg would be correct.
+ SVal InitVal = State->getSVal(Init, LC);
+ if (InitVal.isUnknown()) {
+ InitVal = getSValBuilder().conjureSymbolVal(Result, LC, Init->getType(),
+ currBldrCtx->blockCount());
+ State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);
+
+ // Then we'd need to take the value that certainly exists and bind it
+ // over.
+ if (InitValWithAdjustments.isUnknown()) {
+ // Try to recover some path sensitivity in case we couldn't
+ // compute the value.
+ InitValWithAdjustments = getSValBuilder().conjureSymbolVal(
+ Result, LC, InitWithAdjustments->getType(),
+ currBldrCtx->blockCount());
+ }
+ State =
+ State->bindLoc(Reg.castAs<Loc>(), InitValWithAdjustments, LC, false);
+ } else {
+ State = State->bindLoc(BaseReg.castAs<Loc>(), InitVal, LC, false);
+ }
+
+ // The result expression would now point to the correct sub-region of the
+ // newly created temporary region. Do this last in order to getSVal of Init
+ // correctly in case (Result == Init).
+ if (Result->isGLValue()) {
+ State = State->BindExpr(Result, LC, Reg);
+ } else {
+ State = State->BindExpr(Result, LC, InitValWithAdjustments);
+ }
+
+ // Notify checkers once for two bindLoc()s.
+ State = processRegionChange(State, TR, LC);
+
+ if (OutRegionWithAdjustments)
+ *OutRegionWithAdjustments = cast<SubRegion>(Reg.getAsRegion());
+ return State;
+}
+
+ProgramStateRef
+ExprEngine::addObjectUnderConstruction(ProgramStateRef State,
+ const ConstructionContextItem &Item,
+ const LocationContext *LC, SVal V) {
+ ConstructedObjectKey Key(Item, LC->getStackFrame());
+ // FIXME: Currently the state might already contain the marker due to
+ // incorrect handling of temporaries bound to default parameters.
+ assert(!State->get<ObjectsUnderConstruction>(Key) ||
+ Key.getItem().getKind() ==
+ ConstructionContextItem::TemporaryDestructorKind);
+ return State->set<ObjectsUnderConstruction>(Key, V);
+}
+
+Optional<SVal>
+ExprEngine::getObjectUnderConstruction(ProgramStateRef State,
+ const ConstructionContextItem &Item,
+ const LocationContext *LC) {
+ ConstructedObjectKey Key(Item, LC->getStackFrame());
+ return Optional<SVal>::create(State->get<ObjectsUnderConstruction>(Key));
+}
+
+ProgramStateRef
+ExprEngine::finishObjectConstruction(ProgramStateRef State,
+ const ConstructionContextItem &Item,
+ const LocationContext *LC) {
+ ConstructedObjectKey Key(Item, LC->getStackFrame());
+ assert(State->contains<ObjectsUnderConstruction>(Key));
+ return State->remove<ObjectsUnderConstruction>(Key);
+}
+
+ProgramStateRef ExprEngine::elideDestructor(ProgramStateRef State,
+ const CXXBindTemporaryExpr *BTE,
+ const LocationContext *LC) {
+ ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC);
+ // FIXME: Currently the state might already contain the marker due to
+ // incorrect handling of temporaries bound to default parameters.
+ return State->set<ObjectsUnderConstruction>(Key, UnknownVal());
+}
+
+ProgramStateRef
+ExprEngine::cleanupElidedDestructor(ProgramStateRef State,
+ const CXXBindTemporaryExpr *BTE,
+ const LocationContext *LC) {
+ ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC);
+ assert(State->contains<ObjectsUnderConstruction>(Key));
+ return State->remove<ObjectsUnderConstruction>(Key);
+}
+
+bool ExprEngine::isDestructorElided(ProgramStateRef State,
+ const CXXBindTemporaryExpr *BTE,
+ const LocationContext *LC) {
+ ConstructedObjectKey Key({BTE, /*IsElided=*/true}, LC);
+ return State->contains<ObjectsUnderConstruction>(Key);
+}
+
+bool ExprEngine::areAllObjectsFullyConstructed(ProgramStateRef State,
+ const LocationContext *FromLC,
+ const LocationContext *ToLC) {
+ const LocationContext *LC = FromLC;
+ while (LC != ToLC) {
+ assert(LC && "ToLC must be a parent of FromLC!");
+ for (auto I : State->get<ObjectsUnderConstruction>())
+ if (I.first.getLocationContext() == LC)
+ return false;
+
+ LC = LC->getParent();
+ }
+ return true;
+}
+
+
+//===----------------------------------------------------------------------===//
+// Top-level transfer function logic (Dispatcher).
+//===----------------------------------------------------------------------===//
+
+/// evalAssume - Called by ConstraintManager. Used to call checker-specific
+/// logic for handling assumptions on symbolic values.
+ProgramStateRef ExprEngine::processAssume(ProgramStateRef state,
+ SVal cond, bool assumption) {
+ return getCheckerManager().runCheckersForEvalAssume(state, cond, assumption);
+}
+
+ProgramStateRef
+ExprEngine::processRegionChanges(ProgramStateRef state,
+ const InvalidatedSymbols *invalidated,
+ ArrayRef<const MemRegion *> Explicits,
+ ArrayRef<const MemRegion *> Regions,
+ const LocationContext *LCtx,
+ const CallEvent *Call) {
+ return getCheckerManager().runCheckersForRegionChanges(state, invalidated,
+ Explicits, Regions,
+ LCtx, Call);
+}
+
+static void printObjectsUnderConstructionForContext(raw_ostream &Out,
+ ProgramStateRef State,
+ const char *NL,
+ const LocationContext *LC) {
+ PrintingPolicy PP =
+ LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy();
+ for (auto I : State->get<ObjectsUnderConstruction>()) {
+ ConstructedObjectKey Key = I.first;
+ SVal Value = I.second;
+ if (Key.getLocationContext() != LC)
+ continue;
+ Key.print(Out, nullptr, PP);
+ Out << " : " << Value << NL;
+ }
+}
+
+void ExprEngine::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep,
+ const LocationContext *LCtx) {
+ if (LCtx) {
+ if (!State->get<ObjectsUnderConstruction>().isEmpty()) {
+ Out << Sep << "Objects under construction:" << NL;
+
+ LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) {
+ printObjectsUnderConstructionForContext(Out, State, NL, LC);
+ });
+ }
+ }
+
+ getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep);
+}
+
+void ExprEngine::processEndWorklist() {
+ getCheckerManager().runCheckersForEndAnalysis(G, BR, *this);
+}
+
+void ExprEngine::processCFGElement(const CFGElement E, ExplodedNode *Pred,
+ unsigned StmtIdx, NodeBuilderContext *Ctx) {
+ PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
+ currStmtIdx = StmtIdx;
+ currBldrCtx = Ctx;
+
+ switch (E.getKind()) {
+ case CFGElement::Statement:
+ case CFGElement::Constructor:
+ case CFGElement::CXXRecordTypedCall:
+ ProcessStmt(E.castAs<CFGStmt>().getStmt(), Pred);
+ return;
+ case CFGElement::Initializer:
+ ProcessInitializer(E.castAs<CFGInitializer>(), Pred);
+ return;
+ case CFGElement::NewAllocator:
+ ProcessNewAllocator(E.castAs<CFGNewAllocator>().getAllocatorExpr(),
+ Pred);
+ return;
+ case CFGElement::AutomaticObjectDtor:
+ case CFGElement::DeleteDtor:
+ case CFGElement::BaseDtor:
+ case CFGElement::MemberDtor:
+ case CFGElement::TemporaryDtor:
+ ProcessImplicitDtor(E.castAs<CFGImplicitDtor>(), Pred);
+ return;
+ case CFGElement::LoopExit:
+ ProcessLoopExit(E.castAs<CFGLoopExit>().getLoopStmt(), Pred);
+ return;
+ case CFGElement::LifetimeEnds:
+ case CFGElement::ScopeBegin:
+ case CFGElement::ScopeEnd:
+ return;
+ }
+}
+
+static bool shouldRemoveDeadBindings(AnalysisManager &AMgr,
+ const Stmt *S,
+ const ExplodedNode *Pred,
+ const LocationContext *LC) {
+ // Are we never purging state values?
+ if (AMgr.options.AnalysisPurgeOpt == PurgeNone)
+ return false;
+
+ // Is this the beginning of a basic block?
+ if (Pred->getLocation().getAs<BlockEntrance>())
+ return true;
+
+ // Is this on a non-expression?
+ if (!isa<Expr>(S))
+ return true;
+
+ // Run before processing a call.
+ if (CallEvent::isCallStmt(S))
+ return true;
+
+ // Is this an expression that is consumed by another expression? If so,
+ // postpone cleaning out the state.
+ ParentMap &PM = LC->getAnalysisDeclContext()->getParentMap();
+ return !PM.isConsumedExpr(cast<Expr>(S));
+}
+
+void ExprEngine::removeDead(ExplodedNode *Pred, ExplodedNodeSet &Out,
+ const Stmt *ReferenceStmt,
+ const LocationContext *LC,
+ const Stmt *DiagnosticStmt,
+ ProgramPoint::Kind K) {
+ assert((K == ProgramPoint::PreStmtPurgeDeadSymbolsKind ||
+ ReferenceStmt == nullptr || isa<ReturnStmt>(ReferenceStmt))
+ && "PostStmt is not generally supported by the SymbolReaper yet");
+ assert(LC && "Must pass the current (or expiring) LocationContext");
+
+ if (!DiagnosticStmt) {
+ DiagnosticStmt = ReferenceStmt;
+ assert(DiagnosticStmt && "Required for clearing a LocationContext");
+ }
+
+ NumRemoveDeadBindings++;
+ ProgramStateRef CleanedState = Pred->getState();
+
+ // LC is the location context being destroyed, but SymbolReaper wants a
+ // location context that is still live. (If this is the top-level stack
+ // frame, this will be null.)
+ if (!ReferenceStmt) {
+ assert(K == ProgramPoint::PostStmtPurgeDeadSymbolsKind &&
+ "Use PostStmtPurgeDeadSymbolsKind for clearing a LocationContext");
+ LC = LC->getParent();
+ }
+
+ const StackFrameContext *SFC = LC ? LC->getStackFrame() : nullptr;
+ SymbolReaper SymReaper(SFC, ReferenceStmt, SymMgr, getStoreManager());
+
+ for (auto I : CleanedState->get<ObjectsUnderConstruction>()) {
+ if (SymbolRef Sym = I.second.getAsSymbol())
+ SymReaper.markLive(Sym);
+ if (const MemRegion *MR = I.second.getAsRegion())
+ SymReaper.markLive(MR);
+ }
+
+ getCheckerManager().runCheckersForLiveSymbols(CleanedState, SymReaper);
+
+ // Create a state in which dead bindings are removed from the environment
+ // and the store. TODO: The function should just return new env and store,
+ // not a new state.
+ CleanedState = StateMgr.removeDeadBindings(CleanedState, SFC, SymReaper);
+
+ // Process any special transfer function for dead symbols.
+ // A tag to track convenience transitions, which can be removed at cleanup.
+ static SimpleProgramPointTag cleanupTag(TagProviderName, "Clean Node");
+ // Call checkers with the non-cleaned state so that they could query the
+ // values of the soon to be dead symbols.
+ ExplodedNodeSet CheckedSet;
+ getCheckerManager().runCheckersForDeadSymbols(CheckedSet, Pred, SymReaper,
+ DiagnosticStmt, *this, K);
+
+ // For each node in CheckedSet, generate CleanedNodes that have the
+ // environment, the store, and the constraints cleaned up but have the
+ // user-supplied states as the predecessors.
+ StmtNodeBuilder Bldr(CheckedSet, Out, *currBldrCtx);
+ for (const auto I : CheckedSet) {
+ ProgramStateRef CheckerState = I->getState();
+
+ // The constraint manager has not been cleaned up yet, so clean up now.
+ CheckerState =
+ getConstraintManager().removeDeadBindings(CheckerState, SymReaper);
+
+ assert(StateMgr.haveEqualEnvironments(CheckerState, Pred->getState()) &&
+ "Checkers are not allowed to modify the Environment as a part of "
+ "checkDeadSymbols processing.");
+ assert(StateMgr.haveEqualStores(CheckerState, Pred->getState()) &&
+ "Checkers are not allowed to modify the Store as a part of "
+ "checkDeadSymbols processing.");
+
+ // Create a state based on CleanedState with CheckerState GDM and
+ // generate a transition to that state.
+ ProgramStateRef CleanedCheckerSt =
+ StateMgr.getPersistentStateWithGDM(CleanedState, CheckerState);
+ Bldr.generateNode(DiagnosticStmt, I, CleanedCheckerSt, &cleanupTag, K);
+ }
+}
+
+void ExprEngine::ProcessStmt(const Stmt *currStmt, ExplodedNode *Pred) {
+ // Reclaim any unnecessary nodes in the ExplodedGraph.
+ G.reclaimRecentlyAllocatedNodes();
+
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ currStmt->getBeginLoc(),
+ "Error evaluating statement");
+
+ // Remove dead bindings and symbols.
+ ExplodedNodeSet CleanedStates;
+ if (shouldRemoveDeadBindings(AMgr, currStmt, Pred,
+ Pred->getLocationContext())) {
+ removeDead(Pred, CleanedStates, currStmt,
+ Pred->getLocationContext());
+ } else
+ CleanedStates.Add(Pred);
+
+ // Visit the statement.
+ ExplodedNodeSet Dst;
+ for (const auto I : CleanedStates) {
+ ExplodedNodeSet DstI;
+ // Visit the statement.
+ Visit(currStmt, I, DstI);
+ Dst.insert(DstI);
+ }
+
+ // Enqueue the new nodes onto the work list.
+ Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
+}
+
+void ExprEngine::ProcessLoopExit(const Stmt* S, ExplodedNode *Pred) {
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ S->getBeginLoc(),
+ "Error evaluating end of the loop");
+ ExplodedNodeSet Dst;
+ Dst.Add(Pred);
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ ProgramStateRef NewState = Pred->getState();
+
+ if(AMgr.options.ShouldUnrollLoops)
+ NewState = processLoopEnd(S, NewState);
+
+ LoopExit PP(S, Pred->getLocationContext());
+ Bldr.generateNode(PP, NewState, Pred);
+ // Enqueue the new nodes onto the work list.
+ Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
+}
+
+void ExprEngine::ProcessInitializer(const CFGInitializer CFGInit,
+ ExplodedNode *Pred) {
+ const CXXCtorInitializer *BMI = CFGInit.getInitializer();
+ const Expr *Init = BMI->getInit()->IgnoreImplicit();
+ const LocationContext *LC = Pred->getLocationContext();
+
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ BMI->getSourceLocation(),
+ "Error evaluating initializer");
+
+ // We don't clean up dead bindings here.
+ const auto *stackFrame = cast<StackFrameContext>(Pred->getLocationContext());
+ const auto *decl = cast<CXXConstructorDecl>(stackFrame->getDecl());
+
+ ProgramStateRef State = Pred->getState();
+ SVal thisVal = State->getSVal(svalBuilder.getCXXThis(decl, stackFrame));
+
+ ExplodedNodeSet Tmp;
+ SVal FieldLoc;
+
+ // Evaluate the initializer, if necessary
+ if (BMI->isAnyMemberInitializer()) {
+ // Constructors build the object directly in the field,
+ // but non-objects must be copied in from the initializer.
+ if (getObjectUnderConstruction(State, BMI, LC)) {
+ // The field was directly constructed, so there is no need to bind.
+ // But we still need to stop tracking the object under construction.
+ State = finishObjectConstruction(State, BMI, LC);
+ NodeBuilder Bldr(Pred, Tmp, *currBldrCtx);
+ PostStore PS(Init, LC, /*Loc*/ nullptr, /*tag*/ nullptr);
+ Bldr.generateNode(PS, State, Pred);
+ } else {
+ const ValueDecl *Field;
+ if (BMI->isIndirectMemberInitializer()) {
+ Field = BMI->getIndirectMember();
+ FieldLoc = State->getLValue(BMI->getIndirectMember(), thisVal);
+ } else {
+ Field = BMI->getMember();
+ FieldLoc = State->getLValue(BMI->getMember(), thisVal);
+ }
+
+ SVal InitVal;
+ if (Init->getType()->isArrayType()) {
+ // Handle arrays of trivial type. We can represent this with a
+ // primitive load/copy from the base array region.
+ const ArraySubscriptExpr *ASE;
+ while ((ASE = dyn_cast<ArraySubscriptExpr>(Init)))
+ Init = ASE->getBase()->IgnoreImplicit();
+
+ SVal LValue = State->getSVal(Init, stackFrame);
+ if (!Field->getType()->isReferenceType())
+ if (Optional<Loc> LValueLoc = LValue.getAs<Loc>())
+ InitVal = State->getSVal(*LValueLoc);
+
+ // If we fail to get the value for some reason, use a symbolic value.
+ if (InitVal.isUnknownOrUndef()) {
+ SValBuilder &SVB = getSValBuilder();
+ InitVal = SVB.conjureSymbolVal(BMI->getInit(), stackFrame,
+ Field->getType(),
+ currBldrCtx->blockCount());
+ }
+ } else {
+ InitVal = State->getSVal(BMI->getInit(), stackFrame);
+ }
+
+ PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame);
+ evalBind(Tmp, Init, Pred, FieldLoc, InitVal, /*isInit=*/true, &PP);
+ }
+ } else {
+ assert(BMI->isBaseInitializer() || BMI->isDelegatingInitializer());
+ Tmp.insert(Pred);
+ // We already did all the work when visiting the CXXConstructExpr.
+ }
+
+ // Construct PostInitializer nodes whether the state changed or not,
+ // so that the diagnostics don't get confused.
+ PostInitializer PP(BMI, FieldLoc.getAsRegion(), stackFrame);
+ ExplodedNodeSet Dst;
+ NodeBuilder Bldr(Tmp, Dst, *currBldrCtx);
+ for (const auto I : Tmp) {
+ ProgramStateRef State = I->getState();
+ Bldr.generateNode(PP, State, I);
+ }
+
+ // Enqueue the new nodes onto the work list.
+ Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
+}
+
+void ExprEngine::ProcessImplicitDtor(const CFGImplicitDtor D,
+ ExplodedNode *Pred) {
+ ExplodedNodeSet Dst;
+ switch (D.getKind()) {
+ case CFGElement::AutomaticObjectDtor:
+ ProcessAutomaticObjDtor(D.castAs<CFGAutomaticObjDtor>(), Pred, Dst);
+ break;
+ case CFGElement::BaseDtor:
+ ProcessBaseDtor(D.castAs<CFGBaseDtor>(), Pred, Dst);
+ break;
+ case CFGElement::MemberDtor:
+ ProcessMemberDtor(D.castAs<CFGMemberDtor>(), Pred, Dst);
+ break;
+ case CFGElement::TemporaryDtor:
+ ProcessTemporaryDtor(D.castAs<CFGTemporaryDtor>(), Pred, Dst);
+ break;
+ case CFGElement::DeleteDtor:
+ ProcessDeleteDtor(D.castAs<CFGDeleteDtor>(), Pred, Dst);
+ break;
+ default:
+ llvm_unreachable("Unexpected dtor kind.");
+ }
+
+ // Enqueue the new nodes onto the work list.
+ Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
+}
+
+void ExprEngine::ProcessNewAllocator(const CXXNewExpr *NE,
+ ExplodedNode *Pred) {
+ ExplodedNodeSet Dst;
+ AnalysisManager &AMgr = getAnalysisManager();
+ AnalyzerOptions &Opts = AMgr.options;
+ // TODO: We're not evaluating allocators for all cases just yet as
+ // we're not handling the return value correctly, which causes false
+ // positives when the alpha.cplusplus.NewDeleteLeaks check is on.
+ if (Opts.MayInlineCXXAllocator)
+ VisitCXXNewAllocatorCall(NE, Pred, Dst);
+ else {
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ const LocationContext *LCtx = Pred->getLocationContext();
+ PostImplicitCall PP(NE->getOperatorNew(), NE->getBeginLoc(), LCtx);
+ Bldr.generateNode(PP, Pred->getState(), Pred);
+ }
+ Engine.enqueue(Dst, currBldrCtx->getBlock(), currStmtIdx);
+}
+
+void ExprEngine::ProcessAutomaticObjDtor(const CFGAutomaticObjDtor Dtor,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ const VarDecl *varDecl = Dtor.getVarDecl();
+ QualType varType = varDecl->getType();
+
+ ProgramStateRef state = Pred->getState();
+ SVal dest = state->getLValue(varDecl, Pred->getLocationContext());
+ const MemRegion *Region = dest.castAs<loc::MemRegionVal>().getRegion();
+
+ if (varType->isReferenceType()) {
+ const MemRegion *ValueRegion = state->getSVal(Region).getAsRegion();
+ if (!ValueRegion) {
+ // FIXME: This should not happen. The language guarantees a presence
+ // of a valid initializer here, so the reference shall not be undefined.
+ // It seems that we're calling destructors over variables that
+ // were not initialized yet.
+ return;
+ }
+ Region = ValueRegion->getBaseRegion();
+ varType = cast<TypedValueRegion>(Region)->getValueType();
+ }
+
+ // FIXME: We need to run the same destructor on every element of the array.
+ // This workaround will just run the first destructor (which will still
+ // invalidate the entire array).
+ EvalCallOptions CallOpts;
+ Region = makeZeroElementRegion(state, loc::MemRegionVal(Region), varType,
+ CallOpts.IsArrayCtorOrDtor).getAsRegion();
+
+ VisitCXXDestructor(varType, Region, Dtor.getTriggerStmt(), /*IsBase=*/ false,
+ Pred, Dst, CallOpts);
+}
+
+void ExprEngine::ProcessDeleteDtor(const CFGDeleteDtor Dtor,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ ProgramStateRef State = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ const CXXDeleteExpr *DE = Dtor.getDeleteExpr();
+ const Stmt *Arg = DE->getArgument();
+ QualType DTy = DE->getDestroyedType();
+ SVal ArgVal = State->getSVal(Arg, LCtx);
+
+ // If the argument to delete is known to be a null value,
+ // don't run destructor.
+ if (State->isNull(ArgVal).isConstrainedTrue()) {
+ QualType BTy = getContext().getBaseElementType(DTy);
+ const CXXRecordDecl *RD = BTy->getAsCXXRecordDecl();
+ const CXXDestructorDecl *Dtor = RD->getDestructor();
+
+ PostImplicitCall PP(Dtor, DE->getBeginLoc(), LCtx);
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ Bldr.generateNode(PP, Pred->getState(), Pred);
+ return;
+ }
+
+ EvalCallOptions CallOpts;
+ const MemRegion *ArgR = ArgVal.getAsRegion();
+ if (DE->isArrayForm()) {
+ // FIXME: We need to run the same destructor on every element of the array.
+ // This workaround will just run the first destructor (which will still
+ // invalidate the entire array).
+ CallOpts.IsArrayCtorOrDtor = true;
+ // Yes, it may even be a multi-dimensional array.
+ while (const auto *AT = getContext().getAsArrayType(DTy))
+ DTy = AT->getElementType();
+ if (ArgR)
+ ArgR = getStoreManager().GetElementZeroRegion(cast<SubRegion>(ArgR), DTy);
+ }
+
+ VisitCXXDestructor(DTy, ArgR, DE, /*IsBase=*/false, Pred, Dst, CallOpts);
+}
+
+void ExprEngine::ProcessBaseDtor(const CFGBaseDtor D,
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
+ Loc ThisPtr = getSValBuilder().getCXXThis(CurDtor,
+ LCtx->getStackFrame());
+ SVal ThisVal = Pred->getState()->getSVal(ThisPtr);
+
+ // Create the base object region.
+ const CXXBaseSpecifier *Base = D.getBaseSpecifier();
+ QualType BaseTy = Base->getType();
+ SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, BaseTy,
+ Base->isVirtual());
+
+ VisitCXXDestructor(BaseTy, BaseVal.castAs<loc::MemRegionVal>().getRegion(),
+ CurDtor->getBody(), /*IsBase=*/ true, Pred, Dst, {});
+}
+
+void ExprEngine::ProcessMemberDtor(const CFGMemberDtor D,
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ const FieldDecl *Member = D.getFieldDecl();
+ QualType T = Member->getType();
+ ProgramStateRef State = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ const auto *CurDtor = cast<CXXDestructorDecl>(LCtx->getDecl());
+ Loc ThisVal = getSValBuilder().getCXXThis(CurDtor,
+ LCtx->getStackFrame());
+ SVal FieldVal =
+ State->getLValue(Member, State->getSVal(ThisVal).castAs<Loc>());
+
+ // FIXME: We need to run the same destructor on every element of the array.
+ // This workaround will just run the first destructor (which will still
+ // invalidate the entire array).
+ EvalCallOptions CallOpts;
+ FieldVal = makeZeroElementRegion(State, FieldVal, T,
+ CallOpts.IsArrayCtorOrDtor);
+
+ VisitCXXDestructor(T, FieldVal.castAs<loc::MemRegionVal>().getRegion(),
+ CurDtor->getBody(), /*IsBase=*/false, Pred, Dst, CallOpts);
+}
+
+void ExprEngine::ProcessTemporaryDtor(const CFGTemporaryDtor D,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ const CXXBindTemporaryExpr *BTE = D.getBindTemporaryExpr();
+ ProgramStateRef State = Pred->getState();
+ const LocationContext *LC = Pred->getLocationContext();
+ const MemRegion *MR = nullptr;
+
+ if (Optional<SVal> V =
+ getObjectUnderConstruction(State, D.getBindTemporaryExpr(),
+ Pred->getLocationContext())) {
+ // FIXME: Currently we insert temporary destructors for default parameters,
+ // but we don't insert the constructors, so the entry in
+ // ObjectsUnderConstruction may be missing.
+ State = finishObjectConstruction(State, D.getBindTemporaryExpr(),
+ Pred->getLocationContext());
+ MR = V->getAsRegion();
+ }
+
+ // If copy elision has occurred, and the constructor corresponding to the
+ // destructor was elided, we need to skip the destructor as well.
+ if (isDestructorElided(State, BTE, LC)) {
+ State = cleanupElidedDestructor(State, BTE, LC);
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ PostImplicitCall PP(D.getDestructorDecl(getContext()),
+ D.getBindTemporaryExpr()->getBeginLoc(),
+ Pred->getLocationContext());
+ Bldr.generateNode(PP, State, Pred);
+ return;
+ }
+
+ ExplodedNodeSet CleanDtorState;
+ StmtNodeBuilder StmtBldr(Pred, CleanDtorState, *currBldrCtx);
+ StmtBldr.generateNode(D.getBindTemporaryExpr(), Pred, State);
+
+ QualType T = D.getBindTemporaryExpr()->getSubExpr()->getType();
+ // FIXME: Currently CleanDtorState can be empty here due to temporaries being
+ // bound to default parameters.
+ assert(CleanDtorState.size() <= 1);
+ ExplodedNode *CleanPred =
+ CleanDtorState.empty() ? Pred : *CleanDtorState.begin();
+
+ EvalCallOptions CallOpts;
+ CallOpts.IsTemporaryCtorOrDtor = true;
+ if (!MR) {
+ CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
+
+ // If we have no MR, we still need to unwrap the array to avoid destroying
+ // the whole array at once. Regardless, we'd eventually need to model array
+ // destructors properly, element-by-element.
+ while (const ArrayType *AT = getContext().getAsArrayType(T)) {
+ T = AT->getElementType();
+ CallOpts.IsArrayCtorOrDtor = true;
+ }
+ } else {
+ // We'd eventually need to makeZeroElementRegion() trick here,
+ // but for now we don't have the respective construction contexts,
+ // so MR would always be null in this case. Do nothing for now.
+ }
+ VisitCXXDestructor(T, MR, D.getBindTemporaryExpr(),
+ /*IsBase=*/false, CleanPred, Dst, CallOpts);
+}
+
+void ExprEngine::processCleanupTemporaryBranch(const CXXBindTemporaryExpr *BTE,
+ NodeBuilderContext &BldCtx,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst,
+ const CFGBlock *DstT,
+ const CFGBlock *DstF) {
+ BranchNodeBuilder TempDtorBuilder(Pred, Dst, BldCtx, DstT, DstF);
+ ProgramStateRef State = Pred->getState();
+ const LocationContext *LC = Pred->getLocationContext();
+ if (getObjectUnderConstruction(State, BTE, LC)) {
+ TempDtorBuilder.markInfeasible(false);
+ TempDtorBuilder.generateNode(State, true, Pred);
+ } else {
+ TempDtorBuilder.markInfeasible(true);
+ TempDtorBuilder.generateNode(State, false, Pred);
+ }
+}
+
+void ExprEngine::VisitCXXBindTemporaryExpr(const CXXBindTemporaryExpr *BTE,
+ ExplodedNodeSet &PreVisit,
+ ExplodedNodeSet &Dst) {
+ // This is a fallback solution in case we didn't have a construction
+ // context when we were constructing the temporary. Otherwise the map should
+ // have been populated there.
+ if (!getAnalysisManager().options.ShouldIncludeTemporaryDtorsInCFG) {
+ // In case we don't have temporary destructors in the CFG, do not mark
+ // the initialization - we would otherwise never clean it up.
+ Dst = PreVisit;
+ return;
+ }
+ StmtNodeBuilder StmtBldr(PreVisit, Dst, *currBldrCtx);
+ for (ExplodedNode *Node : PreVisit) {
+ ProgramStateRef State = Node->getState();
+ const LocationContext *LC = Node->getLocationContext();
+ if (!getObjectUnderConstruction(State, BTE, LC)) {
+ // FIXME: Currently the state might also already contain the marker due to
+ // incorrect handling of temporaries bound to default parameters; for
+ // those, we currently skip the CXXBindTemporaryExpr but rely on adding
+ // temporary destructor nodes.
+ State = addObjectUnderConstruction(State, BTE, LC, UnknownVal());
+ }
+ StmtBldr.generateNode(BTE, Node, State);
+ }
+}
+
+ProgramStateRef ExprEngine::escapeValue(ProgramStateRef State, SVal V,
+ PointerEscapeKind K) const {
+ class CollectReachableSymbolsCallback final : public SymbolVisitor {
+ InvalidatedSymbols Symbols;
+
+ public:
+ explicit CollectReachableSymbolsCallback(ProgramStateRef) {}
+
+ const InvalidatedSymbols &getSymbols() const { return Symbols; }
+
+ bool VisitSymbol(SymbolRef Sym) override {
+ Symbols.insert(Sym);
+ return true;
+ }
+ };
+
+ const CollectReachableSymbolsCallback &Scanner =
+ State->scanReachableSymbols<CollectReachableSymbolsCallback>(V);
+ return getCheckerManager().runCheckersForPointerEscape(
+ State, Scanner.getSymbols(), /*CallEvent*/ nullptr, K, nullptr);
+}
+
+void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
+ ExplodedNodeSet &DstTop) {
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ S->getBeginLoc(), "Error evaluating statement");
+ ExplodedNodeSet Dst;
+ StmtNodeBuilder Bldr(Pred, DstTop, *currBldrCtx);
+
+ assert(!isa<Expr>(S) || S == cast<Expr>(S)->IgnoreParens());
+
+ switch (S->getStmtClass()) {
+ // C++, OpenMP and ARC stuff we don't support yet.
+ case Expr::ObjCIndirectCopyRestoreExprClass:
+ case Stmt::CXXDependentScopeMemberExprClass:
+ case Stmt::CXXInheritedCtorInitExprClass:
+ case Stmt::CXXTryStmtClass:
+ case Stmt::CXXTypeidExprClass:
+ case Stmt::CXXUuidofExprClass:
+ case Stmt::CXXFoldExprClass:
+ case Stmt::MSPropertyRefExprClass:
+ case Stmt::MSPropertySubscriptExprClass:
+ case Stmt::CXXUnresolvedConstructExprClass:
+ case Stmt::DependentScopeDeclRefExprClass:
+ case Stmt::ArrayTypeTraitExprClass:
+ case Stmt::ExpressionTraitExprClass:
+ case Stmt::UnresolvedLookupExprClass:
+ case Stmt::UnresolvedMemberExprClass:
+ case Stmt::TypoExprClass:
+ case Stmt::CXXNoexceptExprClass:
+ case Stmt::PackExpansionExprClass:
+ case Stmt::SubstNonTypeTemplateParmPackExprClass:
+ case Stmt::FunctionParmPackExprClass:
+ case Stmt::CoroutineBodyStmtClass:
+ case Stmt::CoawaitExprClass:
+ case Stmt::DependentCoawaitExprClass:
+ case Stmt::CoreturnStmtClass:
+ case Stmt::CoyieldExprClass:
+ case Stmt::SEHTryStmtClass:
+ case Stmt::SEHExceptStmtClass:
+ case Stmt::SEHLeaveStmtClass:
+ case Stmt::SEHFinallyStmtClass:
+ case Stmt::OMPParallelDirectiveClass:
+ case Stmt::OMPSimdDirectiveClass:
+ case Stmt::OMPForDirectiveClass:
+ case Stmt::OMPForSimdDirectiveClass:
+ case Stmt::OMPSectionsDirectiveClass:
+ case Stmt::OMPSectionDirectiveClass:
+ case Stmt::OMPSingleDirectiveClass:
+ case Stmt::OMPMasterDirectiveClass:
+ case Stmt::OMPCriticalDirectiveClass:
+ case Stmt::OMPParallelForDirectiveClass:
+ case Stmt::OMPParallelForSimdDirectiveClass:
+ case Stmt::OMPParallelSectionsDirectiveClass:
+ case Stmt::OMPTaskDirectiveClass:
+ case Stmt::OMPTaskyieldDirectiveClass:
+ case Stmt::OMPBarrierDirectiveClass:
+ case Stmt::OMPTaskwaitDirectiveClass:
+ case Stmt::OMPTaskgroupDirectiveClass:
+ case Stmt::OMPFlushDirectiveClass:
+ case Stmt::OMPOrderedDirectiveClass:
+ case Stmt::OMPAtomicDirectiveClass:
+ case Stmt::OMPTargetDirectiveClass:
+ case Stmt::OMPTargetDataDirectiveClass:
+ case Stmt::OMPTargetEnterDataDirectiveClass:
+ case Stmt::OMPTargetExitDataDirectiveClass:
+ case Stmt::OMPTargetParallelDirectiveClass:
+ case Stmt::OMPTargetParallelForDirectiveClass:
+ case Stmt::OMPTargetUpdateDirectiveClass:
+ case Stmt::OMPTeamsDirectiveClass:
+ case Stmt::OMPCancellationPointDirectiveClass:
+ case Stmt::OMPCancelDirectiveClass:
+ case Stmt::OMPTaskLoopDirectiveClass:
+ case Stmt::OMPTaskLoopSimdDirectiveClass:
+ case Stmt::OMPDistributeDirectiveClass:
+ case Stmt::OMPDistributeParallelForDirectiveClass:
+ case Stmt::OMPDistributeParallelForSimdDirectiveClass:
+ case Stmt::OMPDistributeSimdDirectiveClass:
+ case Stmt::OMPTargetParallelForSimdDirectiveClass:
+ case Stmt::OMPTargetSimdDirectiveClass:
+ case Stmt::OMPTeamsDistributeDirectiveClass:
+ case Stmt::OMPTeamsDistributeSimdDirectiveClass:
+ case Stmt::OMPTeamsDistributeParallelForSimdDirectiveClass:
+ case Stmt::OMPTeamsDistributeParallelForDirectiveClass:
+ case Stmt::OMPTargetTeamsDirectiveClass:
+ case Stmt::OMPTargetTeamsDistributeDirectiveClass:
+ case Stmt::OMPTargetTeamsDistributeParallelForDirectiveClass:
+ case Stmt::OMPTargetTeamsDistributeParallelForSimdDirectiveClass:
+ case Stmt::OMPTargetTeamsDistributeSimdDirectiveClass:
+ case Stmt::CapturedStmtClass: {
+ const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
+ Engine.addAbortedBlock(node, currBldrCtx->getBlock());
+ break;
+ }
+
+ case Stmt::ParenExprClass:
+ llvm_unreachable("ParenExprs already handled.");
+ case Stmt::GenericSelectionExprClass:
+ llvm_unreachable("GenericSelectionExprs already handled.");
+ // Cases that should never be evaluated simply because they shouldn't
+ // appear in the CFG.
+ case Stmt::BreakStmtClass:
+ case Stmt::CaseStmtClass:
+ case Stmt::CompoundStmtClass:
+ case Stmt::ContinueStmtClass:
+ case Stmt::CXXForRangeStmtClass:
+ case Stmt::DefaultStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::GotoStmtClass:
+ case Stmt::IfStmtClass:
+ case Stmt::IndirectGotoStmtClass:
+ case Stmt::LabelStmtClass:
+ case Stmt::NoStmtClass:
+ case Stmt::NullStmtClass:
+ case Stmt::SwitchStmtClass:
+ case Stmt::WhileStmtClass:
+ case Expr::MSDependentExistsStmtClass:
+ llvm_unreachable("Stmt should not be in analyzer evaluation loop");
+
+ case Stmt::ObjCSubscriptRefExprClass:
+ case Stmt::ObjCPropertyRefExprClass:
+ llvm_unreachable("These are handled by PseudoObjectExpr");
+
+ case Stmt::GNUNullExprClass: {
+ // GNU __null is a pointer-width integer, not an actual pointer.
+ ProgramStateRef state = Pred->getState();
+ state = state->BindExpr(S, Pred->getLocationContext(),
+ svalBuilder.makeIntValWithPtrWidth(0, false));
+ Bldr.generateNode(S, Pred, state);
+ break;
+ }
+
+ case Stmt::ObjCAtSynchronizedStmtClass:
+ Bldr.takeNodes(Pred);
+ VisitObjCAtSynchronizedStmt(cast<ObjCAtSynchronizedStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Expr::ConstantExprClass:
+ case Stmt::ExprWithCleanupsClass:
+ // Handled due to fully linearised CFG.
+ break;
+
+ case Stmt::CXXBindTemporaryExprClass: {
+ Bldr.takeNodes(Pred);
+ ExplodedNodeSet PreVisit;
+ getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
+ ExplodedNodeSet Next;
+ VisitCXXBindTemporaryExpr(cast<CXXBindTemporaryExpr>(S), PreVisit, Next);
+ getCheckerManager().runCheckersForPostStmt(Dst, Next, S, *this);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ // Cases not handled yet; but will handle some day.
+ case Stmt::DesignatedInitExprClass:
+ case Stmt::DesignatedInitUpdateExprClass:
+ case Stmt::ArrayInitLoopExprClass:
+ case Stmt::ArrayInitIndexExprClass:
+ case Stmt::ExtVectorElementExprClass:
+ case Stmt::ImaginaryLiteralClass:
+ case Stmt::ObjCAtCatchStmtClass:
+ case Stmt::ObjCAtFinallyStmtClass:
+ case Stmt::ObjCAtTryStmtClass:
+ case Stmt::ObjCAutoreleasePoolStmtClass:
+ case Stmt::ObjCEncodeExprClass:
+ case Stmt::ObjCIsaExprClass:
+ case Stmt::ObjCProtocolExprClass:
+ case Stmt::ObjCSelectorExprClass:
+ case Stmt::ParenListExprClass:
+ case Stmt::ShuffleVectorExprClass:
+ case Stmt::ConvertVectorExprClass:
+ case Stmt::VAArgExprClass:
+ case Stmt::CUDAKernelCallExprClass:
+ case Stmt::OpaqueValueExprClass:
+ case Stmt::AsTypeExprClass:
+ // Fall through.
+
+ // Cases we intentionally don't evaluate, since they don't need
+ // to be explicitly evaluated.
+ case Stmt::PredefinedExprClass:
+ case Stmt::AddrLabelExprClass:
+ case Stmt::AttributedStmtClass:
+ case Stmt::IntegerLiteralClass:
+ case Stmt::FixedPointLiteralClass:
+ case Stmt::CharacterLiteralClass:
+ case Stmt::ImplicitValueInitExprClass:
+ case Stmt::CXXScalarValueInitExprClass:
+ case Stmt::CXXBoolLiteralExprClass:
+ case Stmt::ObjCBoolLiteralExprClass:
+ case Stmt::ObjCAvailabilityCheckExprClass:
+ case Stmt::FloatingLiteralClass:
+ case Stmt::NoInitExprClass:
+ case Stmt::SizeOfPackExprClass:
+ case Stmt::StringLiteralClass:
+ case Stmt::ObjCStringLiteralClass:
+ case Stmt::CXXPseudoDestructorExprClass:
+ case Stmt::SubstNonTypeTemplateParmExprClass:
+ case Stmt::CXXNullPtrLiteralExprClass:
+ case Stmt::OMPArraySectionExprClass:
+ case Stmt::TypeTraitExprClass: {
+ Bldr.takeNodes(Pred);
+ ExplodedNodeSet preVisit;
+ getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this);
+ getCheckerManager().runCheckersForPostStmt(Dst, preVisit, S, *this);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::CXXDefaultArgExprClass:
+ case Stmt::CXXDefaultInitExprClass: {
+ Bldr.takeNodes(Pred);
+ ExplodedNodeSet PreVisit;
+ getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
+
+ ExplodedNodeSet Tmp;
+ StmtNodeBuilder Bldr2(PreVisit, Tmp, *currBldrCtx);
+
+ const Expr *ArgE;
+ if (const auto *DefE = dyn_cast<CXXDefaultArgExpr>(S))
+ ArgE = DefE->getExpr();
+ else if (const auto *DefE = dyn_cast<CXXDefaultInitExpr>(S))
+ ArgE = DefE->getExpr();
+ else
+ llvm_unreachable("unknown constant wrapper kind");
+
+ bool IsTemporary = false;
+ if (const auto *MTE = dyn_cast<MaterializeTemporaryExpr>(ArgE)) {
+ ArgE = MTE->GetTemporaryExpr();
+ IsTemporary = true;
+ }
+
+ Optional<SVal> ConstantVal = svalBuilder.getConstantVal(ArgE);
+ if (!ConstantVal)
+ ConstantVal = UnknownVal();
+
+ const LocationContext *LCtx = Pred->getLocationContext();
+ for (const auto I : PreVisit) {
+ ProgramStateRef State = I->getState();
+ State = State->BindExpr(S, LCtx, *ConstantVal);
+ if (IsTemporary)
+ State = createTemporaryRegionIfNeeded(State, LCtx,
+ cast<Expr>(S),
+ cast<Expr>(S));
+ Bldr2.generateNode(S, I, State);
+ }
+
+ getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ // Cases we evaluate as opaque expressions, conjuring a symbol.
+ case Stmt::CXXStdInitializerListExprClass:
+ case Expr::ObjCArrayLiteralClass:
+ case Expr::ObjCDictionaryLiteralClass:
+ case Expr::ObjCBoxedExprClass: {
+ Bldr.takeNodes(Pred);
+
+ ExplodedNodeSet preVisit;
+ getCheckerManager().runCheckersForPreStmt(preVisit, Pred, S, *this);
+
+ ExplodedNodeSet Tmp;
+ StmtNodeBuilder Bldr2(preVisit, Tmp, *currBldrCtx);
+
+ const auto *Ex = cast<Expr>(S);
+ QualType resultType = Ex->getType();
+
+ for (const auto N : preVisit) {
+ const LocationContext *LCtx = N->getLocationContext();
+ SVal result = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx,
+ resultType,
+ currBldrCtx->blockCount());
+ ProgramStateRef State = N->getState()->BindExpr(Ex, LCtx, result);
+
+ // Escape pointers passed into the list, unless it's an ObjC boxed
+ // expression which is not a boxable C structure.
+ if (!(isa<ObjCBoxedExpr>(Ex) &&
+ !cast<ObjCBoxedExpr>(Ex)->getSubExpr()
+ ->getType()->isRecordType()))
+ for (auto Child : Ex->children()) {
+ assert(Child);
+ SVal Val = State->getSVal(Child, LCtx);
+ State = escapeValue(State, Val, PSK_EscapeOther);
+ }
+
+ Bldr2.generateNode(S, N, State);
+ }
+
+ getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::ArraySubscriptExprClass:
+ Bldr.takeNodes(Pred);
+ VisitArraySubscriptExpr(cast<ArraySubscriptExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::GCCAsmStmtClass:
+ Bldr.takeNodes(Pred);
+ VisitGCCAsmStmt(cast<GCCAsmStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::MSAsmStmtClass:
+ Bldr.takeNodes(Pred);
+ VisitMSAsmStmt(cast<MSAsmStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::BlockExprClass:
+ Bldr.takeNodes(Pred);
+ VisitBlockExpr(cast<BlockExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::LambdaExprClass:
+ if (AMgr.options.ShouldInlineLambdas) {
+ Bldr.takeNodes(Pred);
+ VisitLambdaExpr(cast<LambdaExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ } else {
+ const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
+ Engine.addAbortedBlock(node, currBldrCtx->getBlock());
+ }
+ break;
+
+ case Stmt::BinaryOperatorClass: {
+ const auto *B = cast<BinaryOperator>(S);
+ if (B->isLogicalOp()) {
+ Bldr.takeNodes(Pred);
+ VisitLogicalExpr(B, Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+ }
+ else if (B->getOpcode() == BO_Comma) {
+ ProgramStateRef state = Pred->getState();
+ Bldr.generateNode(B, Pred,
+ state->BindExpr(B, Pred->getLocationContext(),
+ state->getSVal(B->getRHS(),
+ Pred->getLocationContext())));
+ break;
+ }
+
+ Bldr.takeNodes(Pred);
+
+ if (AMgr.options.ShouldEagerlyAssume &&
+ (B->isRelationalOp() || B->isEqualityOp())) {
+ ExplodedNodeSet Tmp;
+ VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Tmp);
+ evalEagerlyAssumeBinOpBifurcation(Dst, Tmp, cast<Expr>(S));
+ }
+ else
+ VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst);
+
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::CXXOperatorCallExprClass: {
+ const auto *OCE = cast<CXXOperatorCallExpr>(S);
+
+ // For instance method operators, make sure the 'this' argument has a
+ // valid region.
+ const Decl *Callee = OCE->getCalleeDecl();
+ if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(Callee)) {
+ if (MD->isInstance()) {
+ ProgramStateRef State = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef NewState =
+ createTemporaryRegionIfNeeded(State, LCtx, OCE->getArg(0));
+ if (NewState != State) {
+ Pred = Bldr.generateNode(OCE, Pred, NewState, /*Tag=*/nullptr,
+ ProgramPoint::PreStmtKind);
+ // Did we cache out?
+ if (!Pred)
+ break;
+ }
+ }
+ }
+ // FALLTHROUGH
+ LLVM_FALLTHROUGH;
+ }
+
+ case Stmt::CallExprClass:
+ case Stmt::CXXMemberCallExprClass:
+ case Stmt::UserDefinedLiteralClass:
+ Bldr.takeNodes(Pred);
+ VisitCallExpr(cast<CallExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::CXXCatchStmtClass:
+ Bldr.takeNodes(Pred);
+ VisitCXXCatchStmt(cast<CXXCatchStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::CXXTemporaryObjectExprClass:
+ case Stmt::CXXConstructExprClass:
+ Bldr.takeNodes(Pred);
+ VisitCXXConstructExpr(cast<CXXConstructExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::CXXNewExprClass: {
+ Bldr.takeNodes(Pred);
+
+ ExplodedNodeSet PreVisit;
+ getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
+
+ ExplodedNodeSet PostVisit;
+ for (const auto i : PreVisit)
+ VisitCXXNewExpr(cast<CXXNewExpr>(S), i, PostVisit);
+
+ getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::CXXDeleteExprClass: {
+ Bldr.takeNodes(Pred);
+ ExplodedNodeSet PreVisit;
+ const auto *CDE = cast<CXXDeleteExpr>(S);
+ getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
+
+ for (const auto i : PreVisit)
+ VisitCXXDeleteExpr(CDE, i, Dst);
+
+ Bldr.addNodes(Dst);
+ break;
+ }
+ // FIXME: ChooseExpr is really a constant. We need to fix
+ // the CFG do not model them as explicit control-flow.
+
+ case Stmt::ChooseExprClass: { // __builtin_choose_expr
+ Bldr.takeNodes(Pred);
+ const auto *C = cast<ChooseExpr>(S);
+ VisitGuardedExpr(C, C->getLHS(), C->getRHS(), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::CompoundAssignOperatorClass:
+ Bldr.takeNodes(Pred);
+ VisitBinaryOperator(cast<BinaryOperator>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::CompoundLiteralExprClass:
+ Bldr.takeNodes(Pred);
+ VisitCompoundLiteralExpr(cast<CompoundLiteralExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass: { // '?' operator
+ Bldr.takeNodes(Pred);
+ const auto *C = cast<AbstractConditionalOperator>(S);
+ VisitGuardedExpr(C, C->getTrueExpr(), C->getFalseExpr(), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::CXXThisExprClass:
+ Bldr.takeNodes(Pred);
+ VisitCXXThisExpr(cast<CXXThisExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::DeclRefExprClass: {
+ Bldr.takeNodes(Pred);
+ const auto *DE = cast<DeclRefExpr>(S);
+ VisitCommonDeclRefExpr(DE, DE->getDecl(), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::DeclStmtClass:
+ Bldr.takeNodes(Pred);
+ VisitDeclStmt(cast<DeclStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::ImplicitCastExprClass:
+ case Stmt::CStyleCastExprClass:
+ case Stmt::CXXStaticCastExprClass:
+ case Stmt::CXXDynamicCastExprClass:
+ case Stmt::CXXReinterpretCastExprClass:
+ case Stmt::CXXConstCastExprClass:
+ case Stmt::CXXFunctionalCastExprClass:
+ case Stmt::ObjCBridgedCastExprClass: {
+ Bldr.takeNodes(Pred);
+ const auto *C = cast<CastExpr>(S);
+ ExplodedNodeSet dstExpr;
+ VisitCast(C, C->getSubExpr(), Pred, dstExpr);
+
+ // Handle the postvisit checks.
+ getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, C, *this);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Expr::MaterializeTemporaryExprClass: {
+ Bldr.takeNodes(Pred);
+ const auto *MTE = cast<MaterializeTemporaryExpr>(S);
+ ExplodedNodeSet dstPrevisit;
+ getCheckerManager().runCheckersForPreStmt(dstPrevisit, Pred, MTE, *this);
+ ExplodedNodeSet dstExpr;
+ for (const auto i : dstPrevisit)
+ CreateCXXTemporaryObject(MTE, i, dstExpr);
+ getCheckerManager().runCheckersForPostStmt(Dst, dstExpr, MTE, *this);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::InitListExprClass:
+ Bldr.takeNodes(Pred);
+ VisitInitListExpr(cast<InitListExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::MemberExprClass:
+ Bldr.takeNodes(Pred);
+ VisitMemberExpr(cast<MemberExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::AtomicExprClass:
+ Bldr.takeNodes(Pred);
+ VisitAtomicExpr(cast<AtomicExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::ObjCIvarRefExprClass:
+ Bldr.takeNodes(Pred);
+ VisitLvalObjCIvarRefExpr(cast<ObjCIvarRefExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::ObjCForCollectionStmtClass:
+ Bldr.takeNodes(Pred);
+ VisitObjCForCollectionStmt(cast<ObjCForCollectionStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::ObjCMessageExprClass:
+ Bldr.takeNodes(Pred);
+ VisitObjCMessage(cast<ObjCMessageExpr>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::ObjCAtThrowStmtClass:
+ case Stmt::CXXThrowExprClass:
+ // FIXME: This is not complete. We basically treat @throw as
+ // an abort.
+ Bldr.generateSink(S, Pred, Pred->getState());
+ break;
+
+ case Stmt::ReturnStmtClass:
+ Bldr.takeNodes(Pred);
+ VisitReturnStmt(cast<ReturnStmt>(S), Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::OffsetOfExprClass: {
+ Bldr.takeNodes(Pred);
+ ExplodedNodeSet PreVisit;
+ getCheckerManager().runCheckersForPreStmt(PreVisit, Pred, S, *this);
+
+ ExplodedNodeSet PostVisit;
+ for (const auto Node : PreVisit)
+ VisitOffsetOfExpr(cast<OffsetOfExpr>(S), Node, PostVisit);
+
+ getCheckerManager().runCheckersForPostStmt(Dst, PostVisit, S, *this);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::UnaryExprOrTypeTraitExprClass:
+ Bldr.takeNodes(Pred);
+ VisitUnaryExprOrTypeTraitExpr(cast<UnaryExprOrTypeTraitExpr>(S),
+ Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+
+ case Stmt::StmtExprClass: {
+ const auto *SE = cast<StmtExpr>(S);
+
+ if (SE->getSubStmt()->body_empty()) {
+ // Empty statement expression.
+ assert(SE->getType() == getContext().VoidTy
+ && "Empty statement expression must have void type.");
+ break;
+ }
+
+ if (const auto *LastExpr =
+ dyn_cast<Expr>(*SE->getSubStmt()->body_rbegin())) {
+ ProgramStateRef state = Pred->getState();
+ Bldr.generateNode(SE, Pred,
+ state->BindExpr(SE, Pred->getLocationContext(),
+ state->getSVal(LastExpr,
+ Pred->getLocationContext())));
+ }
+ break;
+ }
+
+ case Stmt::UnaryOperatorClass: {
+ Bldr.takeNodes(Pred);
+ const auto *U = cast<UnaryOperator>(S);
+ if (AMgr.options.ShouldEagerlyAssume && (U->getOpcode() == UO_LNot)) {
+ ExplodedNodeSet Tmp;
+ VisitUnaryOperator(U, Pred, Tmp);
+ evalEagerlyAssumeBinOpBifurcation(Dst, Tmp, U);
+ }
+ else
+ VisitUnaryOperator(U, Pred, Dst);
+ Bldr.addNodes(Dst);
+ break;
+ }
+
+ case Stmt::PseudoObjectExprClass: {
+ Bldr.takeNodes(Pred);
+ ProgramStateRef state = Pred->getState();
+ const auto *PE = cast<PseudoObjectExpr>(S);
+ if (const Expr *Result = PE->getResultExpr()) {
+ SVal V = state->getSVal(Result, Pred->getLocationContext());
+ Bldr.generateNode(S, Pred,
+ state->BindExpr(S, Pred->getLocationContext(), V));
+ }
+ else
+ Bldr.generateNode(S, Pred,
+ state->BindExpr(S, Pred->getLocationContext(),
+ UnknownVal()));
+
+ Bldr.addNodes(Dst);
+ break;
+ }
+ }
+}
+
+bool ExprEngine::replayWithoutInlining(ExplodedNode *N,
+ const LocationContext *CalleeLC) {
+ const StackFrameContext *CalleeSF = CalleeLC->getStackFrame();
+ const StackFrameContext *CallerSF = CalleeSF->getParent()->getStackFrame();
+ assert(CalleeSF && CallerSF);
+ ExplodedNode *BeforeProcessingCall = nullptr;
+ const Stmt *CE = CalleeSF->getCallSite();
+
+ // Find the first node before we started processing the call expression.
+ while (N) {
+ ProgramPoint L = N->getLocation();
+ BeforeProcessingCall = N;
+ N = N->pred_empty() ? nullptr : *(N->pred_begin());
+
+ // Skip the nodes corresponding to the inlined code.
+ if (L.getStackFrame() != CallerSF)
+ continue;
+ // We reached the caller. Find the node right before we started
+ // processing the call.
+ if (L.isPurgeKind())
+ continue;
+ if (L.getAs<PreImplicitCall>())
+ continue;
+ if (L.getAs<CallEnter>())
+ continue;
+ if (Optional<StmtPoint> SP = L.getAs<StmtPoint>())
+ if (SP->getStmt() == CE)
+ continue;
+ break;
+ }
+
+ if (!BeforeProcessingCall)
+ return false;
+
+ // TODO: Clean up the unneeded nodes.
+
+ // Build an Epsilon node from which we will restart the analyzes.
+ // Note that CE is permitted to be NULL!
+ ProgramPoint NewNodeLoc =
+ EpsilonPoint(BeforeProcessingCall->getLocationContext(), CE);
+ // Add the special flag to GDM to signal retrying with no inlining.
+ // Note, changing the state ensures that we are not going to cache out.
+ ProgramStateRef NewNodeState = BeforeProcessingCall->getState();
+ NewNodeState =
+ NewNodeState->set<ReplayWithoutInlining>(const_cast<Stmt *>(CE));
+
+ // Make the new node a successor of BeforeProcessingCall.
+ bool IsNew = false;
+ ExplodedNode *NewNode = G.getNode(NewNodeLoc, NewNodeState, false, &IsNew);
+ // We cached out at this point. Caching out is common due to us backtracking
+ // from the inlined function, which might spawn several paths.
+ if (!IsNew)
+ return true;
+
+ NewNode->addPredecessor(BeforeProcessingCall, G);
+
+ // Add the new node to the work list.
+ Engine.enqueueStmtNode(NewNode, CalleeSF->getCallSiteBlock(),
+ CalleeSF->getIndex());
+ NumTimesRetriedWithoutInlining++;
+ return true;
+}
+
+/// Block entrance. (Update counters).
+void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
+ NodeBuilderWithSinks &nodeBuilder,
+ ExplodedNode *Pred) {
+ PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
+ // If we reach a loop which has a known bound (and meets
+ // other constraints) then consider completely unrolling it.
+ if(AMgr.options.ShouldUnrollLoops) {
+ unsigned maxBlockVisitOnPath = AMgr.options.maxBlockVisitOnPath;
+ const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator();
+ if (Term) {
+ ProgramStateRef NewState = updateLoopStack(Term, AMgr.getASTContext(),
+ Pred, maxBlockVisitOnPath);
+ if (NewState != Pred->getState()) {
+ ExplodedNode *UpdatedNode = nodeBuilder.generateNode(NewState, Pred);
+ if (!UpdatedNode)
+ return;
+ Pred = UpdatedNode;
+ }
+ }
+ // Is we are inside an unrolled loop then no need the check the counters.
+ if(isUnrolledState(Pred->getState()))
+ return;
+ }
+
+ // If this block is terminated by a loop and it has already been visited the
+ // maximum number of times, widen the loop.
+ unsigned int BlockCount = nodeBuilder.getContext().blockCount();
+ if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 &&
+ AMgr.options.ShouldWidenLoops) {
+ const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator();
+ if (!(Term &&
+ (isa<ForStmt>(Term) || isa<WhileStmt>(Term) || isa<DoStmt>(Term))))
+ return;
+ // Widen.
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef WidenedState =
+ getWidenedLoopState(Pred->getState(), LCtx, BlockCount, Term);
+ nodeBuilder.generateNode(WidenedState, Pred);
+ return;
+ }
+
+ // FIXME: Refactor this into a checker.
+ if (BlockCount >= AMgr.options.maxBlockVisitOnPath) {
+ static SimpleProgramPointTag tag(TagProviderName, "Block count exceeded");
+ const ExplodedNode *Sink =
+ nodeBuilder.generateSink(Pred->getState(), Pred, &tag);
+
+ // Check if we stopped at the top level function or not.
+ // Root node should have the location context of the top most function.
+ const LocationContext *CalleeLC = Pred->getLocation().getLocationContext();
+ const LocationContext *CalleeSF = CalleeLC->getStackFrame();
+ const LocationContext *RootLC =
+ (*G.roots_begin())->getLocation().getLocationContext();
+ if (RootLC->getStackFrame() != CalleeSF) {
+ Engine.FunctionSummaries->markReachedMaxBlockCount(CalleeSF->getDecl());
+
+ // Re-run the call evaluation without inlining it, by storing the
+ // no-inlining policy in the state and enqueuing the new work item on
+ // the list. Replay should almost never fail. Use the stats to catch it
+ // if it does.
+ if ((!AMgr.options.NoRetryExhausted &&
+ replayWithoutInlining(Pred, CalleeLC)))
+ return;
+ NumMaxBlockCountReachedInInlined++;
+ } else
+ NumMaxBlockCountReached++;
+
+ // Make sink nodes as exhausted(for stats) only if retry failed.
+ Engine.blocksExhausted.push_back(std::make_pair(L, Sink));
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Branch processing.
+//===----------------------------------------------------------------------===//
+
+/// RecoverCastedSymbol - A helper function for ProcessBranch that is used
+/// to try to recover some path-sensitivity for casts of symbolic
+/// integers that promote their values (which are currently not tracked well).
+/// This function returns the SVal bound to Condition->IgnoreCasts if all the
+// cast(s) did was sign-extend the original value.
+static SVal RecoverCastedSymbol(ProgramStateRef state,
+ const Stmt *Condition,
+ const LocationContext *LCtx,
+ ASTContext &Ctx) {
+
+ const auto *Ex = dyn_cast<Expr>(Condition);
+ if (!Ex)
+ return UnknownVal();
+
+ uint64_t bits = 0;
+ bool bitsInit = false;
+
+ while (const auto *CE = dyn_cast<CastExpr>(Ex)) {
+ QualType T = CE->getType();
+
+ if (!T->isIntegralOrEnumerationType())
+ return UnknownVal();
+
+ uint64_t newBits = Ctx.getTypeSize(T);
+ if (!bitsInit || newBits < bits) {
+ bitsInit = true;
+ bits = newBits;
+ }
+
+ Ex = CE->getSubExpr();
+ }
+
+ // We reached a non-cast. Is it a symbolic value?
+ QualType T = Ex->getType();
+
+ if (!bitsInit || !T->isIntegralOrEnumerationType() ||
+ Ctx.getTypeSize(T) > bits)
+ return UnknownVal();
+
+ return state->getSVal(Ex, LCtx);
+}
+
+#ifndef NDEBUG
+static const Stmt *getRightmostLeaf(const Stmt *Condition) {
+ while (Condition) {
+ const auto *BO = dyn_cast<BinaryOperator>(Condition);
+ if (!BO || !BO->isLogicalOp()) {
+ return Condition;
+ }
+ Condition = BO->getRHS()->IgnoreParens();
+ }
+ return nullptr;
+}
+#endif
+
+// Returns the condition the branch at the end of 'B' depends on and whose value
+// has been evaluated within 'B'.
+// In most cases, the terminator condition of 'B' will be evaluated fully in
+// the last statement of 'B'; in those cases, the resolved condition is the
+// given 'Condition'.
+// If the condition of the branch is a logical binary operator tree, the CFG is
+// optimized: in that case, we know that the expression formed by all but the
+// rightmost leaf of the logical binary operator tree must be true, and thus
+// the branch condition is at this point equivalent to the truth value of that
+// rightmost leaf; the CFG block thus only evaluates this rightmost leaf
+// expression in its final statement. As the full condition in that case was
+// not evaluated, and is thus not in the SVal cache, we need to use that leaf
+// expression to evaluate the truth value of the condition in the current state
+// space.
+static const Stmt *ResolveCondition(const Stmt *Condition,
+ const CFGBlock *B) {
+ if (const auto *Ex = dyn_cast<Expr>(Condition))
+ Condition = Ex->IgnoreParens();
+
+ const auto *BO = dyn_cast<BinaryOperator>(Condition);
+ if (!BO || !BO->isLogicalOp())
+ return Condition;
+
+ assert(!B->getTerminator().isTemporaryDtorsBranch() &&
+ "Temporary destructor branches handled by processBindTemporary.");
+
+ // For logical operations, we still have the case where some branches
+ // use the traditional "merge" approach and others sink the branch
+ // directly into the basic blocks representing the logical operation.
+ // We need to distinguish between those two cases here.
+
+ // The invariants are still shifting, but it is possible that the
+ // last element in a CFGBlock is not a CFGStmt. Look for the last
+ // CFGStmt as the value of the condition.
+ CFGBlock::const_reverse_iterator I = B->rbegin(), E = B->rend();
+ for (; I != E; ++I) {
+ CFGElement Elem = *I;
+ Optional<CFGStmt> CS = Elem.getAs<CFGStmt>();
+ if (!CS)
+ continue;
+ const Stmt *LastStmt = CS->getStmt();
+ assert(LastStmt == Condition || LastStmt == getRightmostLeaf(Condition));
+ return LastStmt;
+ }
+ llvm_unreachable("could not resolve condition");
+}
+
+void ExprEngine::processBranch(const Stmt *Condition,
+ NodeBuilderContext& BldCtx,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst,
+ const CFGBlock *DstT,
+ const CFGBlock *DstF) {
+ assert((!Condition || !isa<CXXBindTemporaryExpr>(Condition)) &&
+ "CXXBindTemporaryExprs are handled by processBindTemporary.");
+ const LocationContext *LCtx = Pred->getLocationContext();
+ PrettyStackTraceLocationContext StackCrashInfo(LCtx);
+ currBldrCtx = &BldCtx;
+
+ // Check for NULL conditions; e.g. "for(;;)"
+ if (!Condition) {
+ BranchNodeBuilder NullCondBldr(Pred, Dst, BldCtx, DstT, DstF);
+ NullCondBldr.markInfeasible(false);
+ NullCondBldr.generateNode(Pred->getState(), true, Pred);
+ return;
+ }
+
+ if (const auto *Ex = dyn_cast<Expr>(Condition))
+ Condition = Ex->IgnoreParens();
+
+ Condition = ResolveCondition(Condition, BldCtx.getBlock());
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ Condition->getBeginLoc(),
+ "Error evaluating branch");
+
+ ExplodedNodeSet CheckersOutSet;
+ getCheckerManager().runCheckersForBranchCondition(Condition, CheckersOutSet,
+ Pred, *this);
+ // We generated only sinks.
+ if (CheckersOutSet.empty())
+ return;
+
+ BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF);
+ for (const auto PredI : CheckersOutSet) {
+ if (PredI->isSink())
+ continue;
+
+ ProgramStateRef PrevState = PredI->getState();
+ SVal X = PrevState->getSVal(Condition, PredI->getLocationContext());
+
+ if (X.isUnknownOrUndef()) {
+ // Give it a chance to recover from unknown.
+ if (const auto *Ex = dyn_cast<Expr>(Condition)) {
+ if (Ex->getType()->isIntegralOrEnumerationType()) {
+ // Try to recover some path-sensitivity. Right now casts of symbolic
+ // integers that promote their values are currently not tracked well.
+ // If 'Condition' is such an expression, try and recover the
+ // underlying value and use that instead.
+ SVal recovered = RecoverCastedSymbol(PrevState, Condition,
+ PredI->getLocationContext(),
+ getContext());
+
+ if (!recovered.isUnknown()) {
+ X = recovered;
+ }
+ }
+ }
+ }
+
+ // If the condition is still unknown, give up.
+ if (X.isUnknownOrUndef()) {
+ builder.generateNode(PrevState, true, PredI);
+ builder.generateNode(PrevState, false, PredI);
+ continue;
+ }
+
+ DefinedSVal V = X.castAs<DefinedSVal>();
+
+ ProgramStateRef StTrue, StFalse;
+ std::tie(StTrue, StFalse) = PrevState->assume(V);
+
+ // Process the true branch.
+ if (builder.isFeasible(true)) {
+ if (StTrue)
+ builder.generateNode(StTrue, true, PredI);
+ else
+ builder.markInfeasible(true);
+ }
+
+ // Process the false branch.
+ if (builder.isFeasible(false)) {
+ if (StFalse)
+ builder.generateNode(StFalse, false, PredI);
+ else
+ builder.markInfeasible(false);
+ }
+ }
+ currBldrCtx = nullptr;
+}
+
+/// The GDM component containing the set of global variables which have been
+/// previously initialized with explicit initializers.
+REGISTER_TRAIT_WITH_PROGRAMSTATE(InitializedGlobalsSet,
+ llvm::ImmutableSet<const VarDecl *>)
+
+void ExprEngine::processStaticInitializer(const DeclStmt *DS,
+ NodeBuilderContext &BuilderCtx,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst,
+ const CFGBlock *DstT,
+ const CFGBlock *DstF) {
+ PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
+ currBldrCtx = &BuilderCtx;
+
+ const auto *VD = cast<VarDecl>(DS->getSingleDecl());
+ ProgramStateRef state = Pred->getState();
+ bool initHasRun = state->contains<InitializedGlobalsSet>(VD);
+ BranchNodeBuilder builder(Pred, Dst, BuilderCtx, DstT, DstF);
+
+ if (!initHasRun) {
+ state = state->add<InitializedGlobalsSet>(VD);
+ }
+
+ builder.generateNode(state, initHasRun, Pred);
+ builder.markInfeasible(!initHasRun);
+
+ currBldrCtx = nullptr;
+}
+
+/// processIndirectGoto - Called by CoreEngine. Used to generate successor
+/// nodes by processing the 'effects' of a computed goto jump.
+void ExprEngine::processIndirectGoto(IndirectGotoNodeBuilder &builder) {
+ ProgramStateRef state = builder.getState();
+ SVal V = state->getSVal(builder.getTarget(), builder.getLocationContext());
+
+ // Three possibilities:
+ //
+ // (1) We know the computed label.
+ // (2) The label is NULL (or some other constant), or Undefined.
+ // (3) We have no clue about the label. Dispatch to all targets.
+ //
+
+ using iterator = IndirectGotoNodeBuilder::iterator;
+
+ if (Optional<loc::GotoLabel> LV = V.getAs<loc::GotoLabel>()) {
+ const LabelDecl *L = LV->getLabel();
+
+ for (iterator I = builder.begin(), E = builder.end(); I != E; ++I) {
+ if (I.getLabel() == L) {
+ builder.generateNode(I, state);
+ return;
+ }
+ }
+
+ llvm_unreachable("No block with label.");
+ }
+
+ if (V.getAs<loc::ConcreteInt>() || V.getAs<UndefinedVal>()) {
+ // Dispatch to the first target and mark it as a sink.
+ //ExplodedNode* N = builder.generateNode(builder.begin(), state, true);
+ // FIXME: add checker visit.
+ // UndefBranches.insert(N);
+ return;
+ }
+
+ // This is really a catch-all. We don't support symbolics yet.
+ // FIXME: Implement dispatch for symbolic pointers.
+
+ for (iterator I = builder.begin(), E = builder.end(); I != E; ++I)
+ builder.generateNode(I, state);
+}
+
+void ExprEngine::processBeginOfFunction(NodeBuilderContext &BC,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst,
+ const BlockEdge &L) {
+ SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC);
+ getCheckerManager().runCheckersForBeginFunction(Dst, L, Pred, *this);
+}
+
+/// ProcessEndPath - Called by CoreEngine. Used to generate end-of-path
+/// nodes when the control reaches the end of a function.
+void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
+ ExplodedNode *Pred,
+ const ReturnStmt *RS) {
+ ProgramStateRef State = Pred->getState();
+
+ if (!Pred->getStackFrame()->inTopFrame())
+ State = finishArgumentConstruction(
+ State, *getStateManager().getCallEventManager().getCaller(
+ Pred->getStackFrame(), Pred->getState()));
+
+ // FIXME: We currently cannot assert that temporaries are clear, because
+ // lifetime extended temporaries are not always modelled correctly. In some
+ // cases when we materialize the temporary, we do
+ // createTemporaryRegionIfNeeded(), and the region changes, and also the
+ // respective destructor becomes automatic from temporary. So for now clean up
+ // the state manually before asserting. Ideally, this braced block of code
+ // should go away.
+ {
+ const LocationContext *FromLC = Pred->getLocationContext();
+ const LocationContext *ToLC = FromLC->getStackFrame()->getParent();
+ const LocationContext *LC = FromLC;
+ while (LC != ToLC) {
+ assert(LC && "ToLC must be a parent of FromLC!");
+ for (auto I : State->get<ObjectsUnderConstruction>())
+ if (I.first.getLocationContext() == LC) {
+ // The comment above only pardons us for not cleaning up a
+ // temporary destructor. If any other statements are found here,
+ // it must be a separate problem.
+ assert(I.first.getItem().getKind() ==
+ ConstructionContextItem::TemporaryDestructorKind ||
+ I.first.getItem().getKind() ==
+ ConstructionContextItem::ElidedDestructorKind);
+ State = State->remove<ObjectsUnderConstruction>(I.first);
+ }
+ LC = LC->getParent();
+ }
+ }
+
+ // Perform the transition with cleanups.
+ if (State != Pred->getState()) {
+ ExplodedNodeSet PostCleanup;
+ NodeBuilder Bldr(Pred, PostCleanup, BC);
+ Pred = Bldr.generateNode(Pred->getLocation(), State, Pred);
+ if (!Pred) {
+ // The node with clean temporaries already exists. We might have reached
+ // it on a path on which we initialize different temporaries.
+ return;
+ }
+ }
+
+ assert(areAllObjectsFullyConstructed(Pred->getState(),
+ Pred->getLocationContext(),
+ Pred->getStackFrame()->getParent()));
+
+ PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
+ StateMgr.EndPath(Pred->getState());
+
+ ExplodedNodeSet Dst;
+ if (Pred->getLocationContext()->inTopFrame()) {
+ // Remove dead symbols.
+ ExplodedNodeSet AfterRemovedDead;
+ removeDeadOnEndOfFunction(BC, Pred, AfterRemovedDead);
+
+ // Notify checkers.
+ for (const auto I : AfterRemovedDead)
+ getCheckerManager().runCheckersForEndFunction(BC, Dst, I, *this, RS);
+ } else {
+ getCheckerManager().runCheckersForEndFunction(BC, Dst, Pred, *this, RS);
+ }
+
+ Engine.enqueueEndOfFunction(Dst, RS);
+}
+
+/// ProcessSwitch - Called by CoreEngine. Used to generate successor
+/// nodes by processing the 'effects' of a switch statement.
+void ExprEngine::processSwitch(SwitchNodeBuilder& builder) {
+ using iterator = SwitchNodeBuilder::iterator;
+
+ ProgramStateRef state = builder.getState();
+ const Expr *CondE = builder.getCondition();
+ SVal CondV_untested = state->getSVal(CondE, builder.getLocationContext());
+
+ if (CondV_untested.isUndef()) {
+ //ExplodedNode* N = builder.generateDefaultCaseNode(state, true);
+ // FIXME: add checker
+ //UndefBranches.insert(N);
+
+ return;
+ }
+ DefinedOrUnknownSVal CondV = CondV_untested.castAs<DefinedOrUnknownSVal>();
+
+ ProgramStateRef DefaultSt = state;
+
+ iterator I = builder.begin(), EI = builder.end();
+ bool defaultIsFeasible = I == EI;
+
+ for ( ; I != EI; ++I) {
+ // Successor may be pruned out during CFG construction.
+ if (!I.getBlock())
+ continue;
+
+ const CaseStmt *Case = I.getCase();
+
+ // Evaluate the LHS of the case value.
+ llvm::APSInt V1 = Case->getLHS()->EvaluateKnownConstInt(getContext());
+ assert(V1.getBitWidth() == getContext().getIntWidth(CondE->getType()));
+
+ // Get the RHS of the case, if it exists.
+ llvm::APSInt V2;
+ if (const Expr *E = Case->getRHS())
+ V2 = E->EvaluateKnownConstInt(getContext());
+ else
+ V2 = V1;
+
+ ProgramStateRef StateCase;
+ if (Optional<NonLoc> NL = CondV.getAs<NonLoc>())
+ std::tie(StateCase, DefaultSt) =
+ DefaultSt->assumeInclusiveRange(*NL, V1, V2);
+ else // UnknownVal
+ StateCase = DefaultSt;
+
+ if (StateCase)
+ builder.generateCaseStmtNode(I, StateCase);
+
+ // Now "assume" that the case doesn't match. Add this state
+ // to the default state (if it is feasible).
+ if (DefaultSt)
+ defaultIsFeasible = true;
+ else {
+ defaultIsFeasible = false;
+ break;
+ }
+ }
+
+ if (!defaultIsFeasible)
+ return;
+
+ // If we have switch(enum value), the default branch is not
+ // feasible if all of the enum constants not covered by 'case:' statements
+ // are not feasible values for the switch condition.
+ //
+ // Note that this isn't as accurate as it could be. Even if there isn't
+ // a case for a particular enum value as long as that enum value isn't
+ // feasible then it shouldn't be considered for making 'default:' reachable.
+ const SwitchStmt *SS = builder.getSwitch();
+ const Expr *CondExpr = SS->getCond()->IgnoreParenImpCasts();
+ if (CondExpr->getType()->getAs<EnumType>()) {
+ if (SS->isAllEnumCasesCovered())
+ return;
+ }
+
+ builder.generateDefaultCaseNode(DefaultSt);
+}
+
+//===----------------------------------------------------------------------===//
+// Transfer functions: Loads and stores.
+//===----------------------------------------------------------------------===//
+
+void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ if (const auto *VD = dyn_cast<VarDecl>(D)) {
+ // C permits "extern void v", and if you cast the address to a valid type,
+ // you can even do things with it. We simply pretend
+ assert(Ex->isGLValue() || VD->getType()->isVoidType());
+ const LocationContext *LocCtxt = Pred->getLocationContext();
+ const Decl *D = LocCtxt->getDecl();
+ const auto *MD = dyn_cast_or_null<CXXMethodDecl>(D);
+ const auto *DeclRefEx = dyn_cast<DeclRefExpr>(Ex);
+ Optional<std::pair<SVal, QualType>> VInfo;
+
+ if (AMgr.options.ShouldInlineLambdas && DeclRefEx &&
+ DeclRefEx->refersToEnclosingVariableOrCapture() && MD &&
+ MD->getParent()->isLambda()) {
+ // Lookup the field of the lambda.
+ const CXXRecordDecl *CXXRec = MD->getParent();
+ llvm::DenseMap<const VarDecl *, FieldDecl *> LambdaCaptureFields;
+ FieldDecl *LambdaThisCaptureField;
+ CXXRec->getCaptureFields(LambdaCaptureFields, LambdaThisCaptureField);
+
+ // Sema follows a sequence of complex rules to determine whether the
+ // variable should be captured.
+ if (const FieldDecl *FD = LambdaCaptureFields[VD]) {
+ Loc CXXThis =
+ svalBuilder.getCXXThis(MD, LocCtxt->getStackFrame());
+ SVal CXXThisVal = state->getSVal(CXXThis);
+ VInfo = std::make_pair(state->getLValue(FD, CXXThisVal), FD->getType());
+ }
+ }
+
+ if (!VInfo)
+ VInfo = std::make_pair(state->getLValue(VD, LocCtxt), VD->getType());
+
+ SVal V = VInfo->first;
+ bool IsReference = VInfo->second->isReferenceType();
+
+ // For references, the 'lvalue' is the pointer address stored in the
+ // reference region.
+ if (IsReference) {
+ if (const MemRegion *R = V.getAsRegion())
+ V = state->getSVal(R);
+ else
+ V = UnknownVal();
+ }
+
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
+ ProgramPoint::PostLValueKind);
+ return;
+ }
+ if (const auto *ED = dyn_cast<EnumConstantDecl>(D)) {
+ assert(!Ex->isGLValue());
+ SVal V = svalBuilder.makeIntVal(ED->getInitVal());
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V));
+ return;
+ }
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ SVal V = svalBuilder.getFunctionPointer(FD);
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
+ ProgramPoint::PostLValueKind);
+ return;
+ }
+ if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) {
+ // FIXME: Compute lvalue of field pointers-to-member.
+ // Right now we just use a non-null void pointer, so that it gives proper
+ // results in boolean contexts.
+ // FIXME: Maybe delegate this to the surrounding operator&.
+ // Note how this expression is lvalue, however pointer-to-member is NonLoc.
+ SVal V = svalBuilder.conjureSymbolVal(Ex, LCtx, getContext().VoidPtrTy,
+ currBldrCtx->blockCount());
+ state = state->assume(V.castAs<DefinedOrUnknownSVal>(), true);
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
+ ProgramPoint::PostLValueKind);
+ return;
+ }
+ if (isa<BindingDecl>(D)) {
+ // FIXME: proper support for bound declarations.
+ // For now, let's just prevent crashing.
+ return;
+ }
+
+ llvm_unreachable("Support for this Decl not implemented.");
+}
+
+/// VisitArraySubscriptExpr - Transfer function for array accesses
+void ExprEngine::VisitArraySubscriptExpr(const ArraySubscriptExpr *A,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst){
+ const Expr *Base = A->getBase()->IgnoreParens();
+ const Expr *Idx = A->getIdx()->IgnoreParens();
+
+ ExplodedNodeSet CheckerPreStmt;
+ getCheckerManager().runCheckersForPreStmt(CheckerPreStmt, Pred, A, *this);
+
+ ExplodedNodeSet EvalSet;
+ StmtNodeBuilder Bldr(CheckerPreStmt, EvalSet, *currBldrCtx);
+
+ bool IsVectorType = A->getBase()->getType()->isVectorType();
+
+ // The "like" case is for situations where C standard prohibits the type to
+ // be an lvalue, e.g. taking the address of a subscript of an expression of
+ // type "void *".
+ bool IsGLValueLike = A->isGLValue() ||
+ (A->getType().isCForbiddenLValueType() && !AMgr.getLangOpts().CPlusPlus);
+
+ for (auto *Node : CheckerPreStmt) {
+ const LocationContext *LCtx = Node->getLocationContext();
+ ProgramStateRef state = Node->getState();
+
+ if (IsGLValueLike) {
+ QualType T = A->getType();
+
+ // One of the forbidden LValue types! We still need to have sensible
+ // symbolic locations to represent this stuff. Note that arithmetic on
+ // void pointers is a GCC extension.
+ if (T->isVoidType())
+ T = getContext().CharTy;
+
+ SVal V = state->getLValue(T,
+ state->getSVal(Idx, LCtx),
+ state->getSVal(Base, LCtx));
+ Bldr.generateNode(A, Node, state->BindExpr(A, LCtx, V), nullptr,
+ ProgramPoint::PostLValueKind);
+ } else if (IsVectorType) {
+ // FIXME: non-glvalue vector reads are not modelled.
+ Bldr.generateNode(A, Node, state, nullptr);
+ } else {
+ llvm_unreachable("Array subscript should be an lValue when not \
+a vector and not a forbidden lvalue type");
+ }
+ }
+
+ getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, A, *this);
+}
+
+/// VisitMemberExpr - Transfer function for member expressions.
+void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ // FIXME: Prechecks eventually go in ::Visit().
+ ExplodedNodeSet CheckedSet;
+ getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, M, *this);
+
+ ExplodedNodeSet EvalSet;
+ ValueDecl *Member = M->getMemberDecl();
+
+ // Handle static member variables and enum constants accessed via
+ // member syntax.
+ if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) {
+ for (const auto I : CheckedSet)
+ VisitCommonDeclRefExpr(M, Member, I, EvalSet);
+ } else {
+ StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx);
+ ExplodedNodeSet Tmp;
+
+ for (const auto I : CheckedSet) {
+ ProgramStateRef state = I->getState();
+ const LocationContext *LCtx = I->getLocationContext();
+ Expr *BaseExpr = M->getBase();
+
+ // Handle C++ method calls.
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(Member)) {
+ if (MD->isInstance())
+ state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr);
+
+ SVal MDVal = svalBuilder.getFunctionPointer(MD);
+ state = state->BindExpr(M, LCtx, MDVal);
+
+ Bldr.generateNode(M, I, state);
+ continue;
+ }
+
+ // Handle regular struct fields / member variables.
+ const SubRegion *MR = nullptr;
+ state = createTemporaryRegionIfNeeded(state, LCtx, BaseExpr,
+ /*Result=*/nullptr,
+ /*OutRegionWithAdjustments=*/&MR);
+ SVal baseExprVal =
+ MR ? loc::MemRegionVal(MR) : state->getSVal(BaseExpr, LCtx);
+
+ const auto *field = cast<FieldDecl>(Member);
+ SVal L = state->getLValue(field, baseExprVal);
+
+ if (M->isGLValue() || M->getType()->isArrayType()) {
+ // We special-case rvalues of array type because the analyzer cannot
+ // reason about them, since we expect all regions to be wrapped in Locs.
+ // We instead treat these as lvalues and assume that they will decay to
+ // pointers as soon as they are used.
+ if (!M->isGLValue()) {
+ assert(M->getType()->isArrayType());
+ const auto *PE =
+ dyn_cast<ImplicitCastExpr>(I->getParentMap().getParentIgnoreParens(M));
+ if (!PE || PE->getCastKind() != CK_ArrayToPointerDecay) {
+ llvm_unreachable("should always be wrapped in ArrayToPointerDecay");
+ }
+ }
+
+ if (field->getType()->isReferenceType()) {
+ if (const MemRegion *R = L.getAsRegion())
+ L = state->getSVal(R);
+ else
+ L = UnknownVal();
+ }
+
+ Bldr.generateNode(M, I, state->BindExpr(M, LCtx, L), nullptr,
+ ProgramPoint::PostLValueKind);
+ } else {
+ Bldr.takeNodes(I);
+ evalLoad(Tmp, M, M, I, state, L);
+ Bldr.addNodes(Tmp);
+ }
+ }
+ }
+
+ getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, M, *this);
+}
+
+void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ ExplodedNodeSet AfterPreSet;
+ getCheckerManager().runCheckersForPreStmt(AfterPreSet, Pred, AE, *this);
+
+ // For now, treat all the arguments to C11 atomics as escaping.
+ // FIXME: Ideally we should model the behavior of the atomics precisely here.
+
+ ExplodedNodeSet AfterInvalidateSet;
+ StmtNodeBuilder Bldr(AfterPreSet, AfterInvalidateSet, *currBldrCtx);
+
+ for (const auto I : AfterPreSet) {
+ ProgramStateRef State = I->getState();
+ const LocationContext *LCtx = I->getLocationContext();
+
+ SmallVector<SVal, 8> ValuesToInvalidate;
+ for (unsigned SI = 0, Count = AE->getNumSubExprs(); SI != Count; SI++) {
+ const Expr *SubExpr = AE->getSubExprs()[SI];
+ SVal SubExprVal = State->getSVal(SubExpr, LCtx);
+ ValuesToInvalidate.push_back(SubExprVal);
+ }
+
+ State = State->invalidateRegions(ValuesToInvalidate, AE,
+ currBldrCtx->blockCount(),
+ LCtx,
+ /*CausedByPointerEscape*/true,
+ /*Symbols=*/nullptr);
+
+ SVal ResultVal = UnknownVal();
+ State = State->BindExpr(AE, LCtx, ResultVal);
+ Bldr.generateNode(AE, I, State, nullptr,
+ ProgramPoint::PostStmtKind);
+ }
+
+ getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this);
+}
+
+// A value escapes in three possible cases:
+// (1) We are binding to something that is not a memory region.
+// (2) We are binding to a MemrRegion that does not have stack storage.
+// (3) We are binding to a MemRegion with stack storage that the store
+// does not understand.
+ProgramStateRef ExprEngine::processPointerEscapedOnBind(ProgramStateRef State,
+ SVal Loc,
+ SVal Val,
+ const LocationContext *LCtx) {
+ // Are we storing to something that causes the value to "escape"?
+ bool escapes = true;
+
+ // TODO: Move to StoreManager.
+ if (Optional<loc::MemRegionVal> regionLoc = Loc.getAs<loc::MemRegionVal>()) {
+ escapes = !regionLoc->getRegion()->hasStackStorage();
+
+ if (!escapes) {
+ // To test (3), generate a new state with the binding added. If it is
+ // the same state, then it escapes (since the store cannot represent
+ // the binding).
+ // Do this only if we know that the store is not supposed to generate the
+ // same state.
+ SVal StoredVal = State->getSVal(regionLoc->getRegion());
+ if (StoredVal != Val)
+ escapes = (State == (State->bindLoc(*regionLoc, Val, LCtx)));
+ }
+ }
+
+ // If our store can represent the binding and we aren't storing to something
+ // that doesn't have local storage then just return and have the simulation
+ // state continue as is.
+ if (!escapes)
+ return State;
+
+ // Otherwise, find all symbols referenced by 'val' that we are tracking
+ // and stop tracking them.
+ State = escapeValue(State, Val, PSK_EscapeOnBind);
+ return State;
+}
+
+ProgramStateRef
+ExprEngine::notifyCheckersOfPointerEscape(ProgramStateRef State,
+ const InvalidatedSymbols *Invalidated,
+ ArrayRef<const MemRegion *> ExplicitRegions,
+ const CallEvent *Call,
+ RegionAndSymbolInvalidationTraits &ITraits) {
+ if (!Invalidated || Invalidated->empty())
+ return State;
+
+ if (!Call)
+ return getCheckerManager().runCheckersForPointerEscape(State,
+ *Invalidated,
+ nullptr,
+ PSK_EscapeOther,
+ &ITraits);
+
+ // If the symbols were invalidated by a call, we want to find out which ones
+ // were invalidated directly due to being arguments to the call.
+ InvalidatedSymbols SymbolsDirectlyInvalidated;
+ for (const auto I : ExplicitRegions) {
+ if (const SymbolicRegion *R = I->StripCasts()->getAs<SymbolicRegion>())
+ SymbolsDirectlyInvalidated.insert(R->getSymbol());
+ }
+
+ InvalidatedSymbols SymbolsIndirectlyInvalidated;
+ for (const auto &sym : *Invalidated) {
+ if (SymbolsDirectlyInvalidated.count(sym))
+ continue;
+ SymbolsIndirectlyInvalidated.insert(sym);
+ }
+
+ if (!SymbolsDirectlyInvalidated.empty())
+ State = getCheckerManager().runCheckersForPointerEscape(State,
+ SymbolsDirectlyInvalidated, Call, PSK_DirectEscapeOnCall, &ITraits);
+
+ // Notify about the symbols that get indirectly invalidated by the call.
+ if (!SymbolsIndirectlyInvalidated.empty())
+ State = getCheckerManager().runCheckersForPointerEscape(State,
+ SymbolsIndirectlyInvalidated, Call, PSK_IndirectEscapeOnCall, &ITraits);
+
+ return State;
+}
+
+/// evalBind - Handle the semantics of binding a value to a specific location.
+/// This method is used by evalStore and (soon) VisitDeclStmt, and others.
+void ExprEngine::evalBind(ExplodedNodeSet &Dst, const Stmt *StoreE,
+ ExplodedNode *Pred,
+ SVal location, SVal Val,
+ bool atDeclInit, const ProgramPoint *PP) {
+ const LocationContext *LC = Pred->getLocationContext();
+ PostStmt PS(StoreE, LC);
+ if (!PP)
+ PP = &PS;
+
+ // Do a previsit of the bind.
+ ExplodedNodeSet CheckedSet;
+ getCheckerManager().runCheckersForBind(CheckedSet, Pred, location, Val,
+ StoreE, *this, *PP);
+
+ StmtNodeBuilder Bldr(CheckedSet, Dst, *currBldrCtx);
+
+ // If the location is not a 'Loc', it will already be handled by
+ // the checkers. There is nothing left to do.
+ if (!location.getAs<Loc>()) {
+ const ProgramPoint L = PostStore(StoreE, LC, /*Loc*/nullptr,
+ /*tag*/nullptr);
+ ProgramStateRef state = Pred->getState();
+ state = processPointerEscapedOnBind(state, location, Val, LC);
+ Bldr.generateNode(L, state, Pred);
+ return;
+ }
+
+ for (const auto PredI : CheckedSet) {
+ ProgramStateRef state = PredI->getState();
+
+ state = processPointerEscapedOnBind(state, location, Val, LC);
+
+ // When binding the value, pass on the hint that this is a initialization.
+ // For initializations, we do not need to inform clients of region
+ // changes.
+ state = state->bindLoc(location.castAs<Loc>(),
+ Val, LC, /* notifyChanges = */ !atDeclInit);
+
+ const MemRegion *LocReg = nullptr;
+ if (Optional<loc::MemRegionVal> LocRegVal =
+ location.getAs<loc::MemRegionVal>()) {
+ LocReg = LocRegVal->getRegion();
+ }
+
+ const ProgramPoint L = PostStore(StoreE, LC, LocReg, nullptr);
+ Bldr.generateNode(L, state, PredI);
+ }
+}
+
+/// evalStore - Handle the semantics of a store via an assignment.
+/// @param Dst The node set to store generated state nodes
+/// @param AssignE The assignment expression if the store happens in an
+/// assignment.
+/// @param LocationE The location expression that is stored to.
+/// @param state The current simulation state
+/// @param location The location to store the value
+/// @param Val The value to be stored
+void ExprEngine::evalStore(ExplodedNodeSet &Dst, const Expr *AssignE,
+ const Expr *LocationE,
+ ExplodedNode *Pred,
+ ProgramStateRef state, SVal location, SVal Val,
+ const ProgramPointTag *tag) {
+ // Proceed with the store. We use AssignE as the anchor for the PostStore
+ // ProgramPoint if it is non-NULL, and LocationE otherwise.
+ const Expr *StoreE = AssignE ? AssignE : LocationE;
+
+ // Evaluate the location (checks for bad dereferences).
+ ExplodedNodeSet Tmp;
+ evalLocation(Tmp, AssignE, LocationE, Pred, state, location, false);
+
+ if (Tmp.empty())
+ return;
+
+ if (location.isUndef())
+ return;
+
+ for (const auto I : Tmp)
+ evalBind(Dst, StoreE, I, location, Val, false);
+}
+
+void ExprEngine::evalLoad(ExplodedNodeSet &Dst,
+ const Expr *NodeEx,
+ const Expr *BoundEx,
+ ExplodedNode *Pred,
+ ProgramStateRef state,
+ SVal location,
+ const ProgramPointTag *tag,
+ QualType LoadTy) {
+ assert(!location.getAs<NonLoc>() && "location cannot be a NonLoc.");
+ assert(NodeEx);
+ assert(BoundEx);
+ // Evaluate the location (checks for bad dereferences).
+ ExplodedNodeSet Tmp;
+ evalLocation(Tmp, NodeEx, BoundEx, Pred, state, location, true);
+ if (Tmp.empty())
+ return;
+
+ StmtNodeBuilder Bldr(Tmp, Dst, *currBldrCtx);
+ if (location.isUndef())
+ return;
+
+ // Proceed with the load.
+ for (const auto I : Tmp) {
+ state = I->getState();
+ const LocationContext *LCtx = I->getLocationContext();
+
+ SVal V = UnknownVal();
+ if (location.isValid()) {
+ if (LoadTy.isNull())
+ LoadTy = BoundEx->getType();
+ V = state->getSVal(location.castAs<Loc>(), LoadTy);
+ }
+
+ Bldr.generateNode(NodeEx, I, state->BindExpr(BoundEx, LCtx, V), tag,
+ ProgramPoint::PostLoadKind);
+ }
+}
+
+void ExprEngine::evalLocation(ExplodedNodeSet &Dst,
+ const Stmt *NodeEx,
+ const Stmt *BoundEx,
+ ExplodedNode *Pred,
+ ProgramStateRef state,
+ SVal location,
+ bool isLoad) {
+ StmtNodeBuilder BldrTop(Pred, Dst, *currBldrCtx);
+ // Early checks for performance reason.
+ if (location.isUnknown()) {
+ return;
+ }
+
+ ExplodedNodeSet Src;
+ BldrTop.takeNodes(Pred);
+ StmtNodeBuilder Bldr(Pred, Src, *currBldrCtx);
+ if (Pred->getState() != state) {
+ // Associate this new state with an ExplodedNode.
+ // FIXME: If I pass null tag, the graph is incorrect, e.g for
+ // int *p;
+ // p = 0;
+ // *p = 0xDEADBEEF;
+ // "p = 0" is not noted as "Null pointer value stored to 'p'" but
+ // instead "int *p" is noted as
+ // "Variable 'p' initialized to a null pointer value"
+
+ static SimpleProgramPointTag tag(TagProviderName, "Location");
+ Bldr.generateNode(NodeEx, Pred, state, &tag);
+ }
+ ExplodedNodeSet Tmp;
+ getCheckerManager().runCheckersForLocation(Tmp, Src, location, isLoad,
+ NodeEx, BoundEx, *this);
+ BldrTop.addNodes(Tmp);
+}
+
+std::pair<const ProgramPointTag *, const ProgramPointTag*>
+ExprEngine::geteagerlyAssumeBinOpBifurcationTags() {
+ static SimpleProgramPointTag
+ eagerlyAssumeBinOpBifurcationTrue(TagProviderName,
+ "Eagerly Assume True"),
+ eagerlyAssumeBinOpBifurcationFalse(TagProviderName,
+ "Eagerly Assume False");
+ return std::make_pair(&eagerlyAssumeBinOpBifurcationTrue,
+ &eagerlyAssumeBinOpBifurcationFalse);
+}
+
+void ExprEngine::evalEagerlyAssumeBinOpBifurcation(ExplodedNodeSet &Dst,
+ ExplodedNodeSet &Src,
+ const Expr *Ex) {
+ StmtNodeBuilder Bldr(Src, Dst, *currBldrCtx);
+
+ for (const auto Pred : Src) {
+ // Test if the previous node was as the same expression. This can happen
+ // when the expression fails to evaluate to anything meaningful and
+ // (as an optimization) we don't generate a node.
+ ProgramPoint P = Pred->getLocation();
+ if (!P.getAs<PostStmt>() || P.castAs<PostStmt>().getStmt() != Ex) {
+ continue;
+ }
+
+ ProgramStateRef state = Pred->getState();
+ SVal V = state->getSVal(Ex, Pred->getLocationContext());
+ Optional<nonloc::SymbolVal> SEV = V.getAs<nonloc::SymbolVal>();
+ if (SEV && SEV->isExpression()) {
+ const std::pair<const ProgramPointTag *, const ProgramPointTag*> &tags =
+ geteagerlyAssumeBinOpBifurcationTags();
+
+ ProgramStateRef StateTrue, StateFalse;
+ std::tie(StateTrue, StateFalse) = state->assume(*SEV);
+
+ // First assume that the condition is true.
+ if (StateTrue) {
+ SVal Val = svalBuilder.makeIntVal(1U, Ex->getType());
+ StateTrue = StateTrue->BindExpr(Ex, Pred->getLocationContext(), Val);
+ Bldr.generateNode(Ex, Pred, StateTrue, tags.first);
+ }
+
+ // Next, assume that the condition is false.
+ if (StateFalse) {
+ SVal Val = svalBuilder.makeIntVal(0U, Ex->getType());
+ StateFalse = StateFalse->BindExpr(Ex, Pred->getLocationContext(), Val);
+ Bldr.generateNode(Ex, Pred, StateFalse, tags.second);
+ }
+ }
+ }
+}
+
+void ExprEngine::VisitGCCAsmStmt(const GCCAsmStmt *A, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ // We have processed both the inputs and the outputs. All of the outputs
+ // should evaluate to Locs. Nuke all of their values.
+
+ // FIXME: Some day in the future it would be nice to allow a "plug-in"
+ // which interprets the inline asm and stores proper results in the
+ // outputs.
+
+ ProgramStateRef state = Pred->getState();
+
+ for (const Expr *O : A->outputs()) {
+ SVal X = state->getSVal(O, Pred->getLocationContext());
+ assert(!X.getAs<NonLoc>()); // Should be an Lval, or unknown, undef.
+
+ if (Optional<Loc> LV = X.getAs<Loc>())
+ state = state->bindLoc(*LV, UnknownVal(), Pred->getLocationContext());
+ }
+
+ Bldr.generateNode(A, Pred, state);
+}
+
+void ExprEngine::VisitMSAsmStmt(const MSAsmStmt *A, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ Bldr.generateNode(A, Pred, Pred->getState());
+}
+
+//===----------------------------------------------------------------------===//
+// Visualization.
+//===----------------------------------------------------------------------===//
+
+#ifndef NDEBUG
+namespace llvm {
+
+template<>
+struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
+ DOTGraphTraits (bool isSimple = false) : DefaultDOTGraphTraits(isSimple) {}
+
+ static bool nodeHasBugReport(const ExplodedNode *N) {
+ BugReporter &BR = static_cast<ExprEngine &>(
+ N->getState()->getStateManager().getOwningEngine()).getBugReporter();
+
+ const auto EQClasses =
+ llvm::make_range(BR.EQClasses_begin(), BR.EQClasses_end());
+
+ for (const auto &EQ : EQClasses) {
+ for (const BugReport &Report : EQ) {
+ if (Report.getErrorNode() == N)
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /// \p PreCallback: callback before break.
+ /// \p PostCallback: callback after break.
+ /// \p Stop: stop iteration if returns {@code true}
+ /// \return Whether {@code Stop} ever returned {@code true}.
+ static bool traverseHiddenNodes(
+ const ExplodedNode *N,
+ llvm::function_ref<void(const ExplodedNode *)> PreCallback,
+ llvm::function_ref<void(const ExplodedNode *)> PostCallback,
+ llvm::function_ref<bool(const ExplodedNode *)> Stop) {
+ const ExplodedNode *FirstHiddenNode = N;
+ while (FirstHiddenNode->pred_size() == 1 &&
+ isNodeHidden(*FirstHiddenNode->pred_begin())) {
+ FirstHiddenNode = *FirstHiddenNode->pred_begin();
+ }
+ const ExplodedNode *OtherNode = FirstHiddenNode;
+ while (true) {
+ PreCallback(OtherNode);
+ if (Stop(OtherNode))
+ return true;
+
+ if (OtherNode == N)
+ break;
+ PostCallback(OtherNode);
+
+ OtherNode = *OtherNode->succ_begin();
+ }
+ return false;
+ }
+
+ static std::string getNodeAttributes(const ExplodedNode *N,
+ ExplodedGraph *) {
+ SmallVector<StringRef, 10> Out;
+ auto Noop = [](const ExplodedNode*){};
+ if (traverseHiddenNodes(N, Noop, Noop, &nodeHasBugReport)) {
+ Out.push_back("style=filled");
+ Out.push_back("fillcolor=red");
+ }
+
+ if (traverseHiddenNodes(N, Noop, Noop,
+ [](const ExplodedNode *C) { return C->isSink(); }))
+ Out.push_back("color=blue");
+ return llvm::join(Out, ",");
+ }
+
+ static bool isNodeHidden(const ExplodedNode *N) {
+ return N->isTrivial();
+ }
+
+ static std::string getNodeLabel(const ExplodedNode *N, ExplodedGraph *G){
+ std::string sbuf;
+ llvm::raw_string_ostream Out(sbuf);
+
+ ProgramStateRef State = N->getState();
+
+ // Dump program point for all the previously skipped nodes.
+ traverseHiddenNodes(
+ N,
+ [&](const ExplodedNode *OtherNode) {
+ OtherNode->getLocation().print(/*CR=*/"\\l", Out);
+ if (const ProgramPointTag *Tag = OtherNode->getLocation().getTag())
+ Out << "\\lTag:" << Tag->getTagDescription();
+ if (N->isSink())
+ Out << "\\lNode is sink\\l";
+ if (nodeHasBugReport(N))
+ Out << "\\lBug report attached\\l";
+ },
+ [&](const ExplodedNode *) { Out << "\\l--------\\l"; },
+ [&](const ExplodedNode *) { return false; });
+
+ Out << "\\l\\|";
+
+ Out << "StateID: ST" << State->getID() << ", NodeID: N" << N->getID(G)
+ << " <" << (const void *)N << ">\\|";
+
+ bool SameAsAllPredecessors =
+ std::all_of(N->pred_begin(), N->pred_end(), [&](const ExplodedNode *P) {
+ return P->getState() == State;
+ });
+ if (!SameAsAllPredecessors)
+ State->printDOT(Out, N->getLocationContext());
+ return Out.str();
+ }
+};
+
+} // namespace llvm
+#endif
+
+void ExprEngine::ViewGraph(bool trim) {
+#ifndef NDEBUG
+ std::string Filename = DumpGraph(trim);
+ llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT);
+#endif
+ llvm::errs() << "Warning: viewing graph requires assertions" << "\n";
+}
+
+
+void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode*> Nodes) {
+#ifndef NDEBUG
+ std::string Filename = DumpGraph(Nodes);
+ llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT);
+#endif
+ llvm::errs() << "Warning: viewing graph requires assertions" << "\n";
+}
+
+std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) {
+#ifndef NDEBUG
+ if (trim) {
+ std::vector<const ExplodedNode *> Src;
+
+ // Iterate through the reports and get their nodes.
+ for (BugReporter::EQClasses_iterator
+ EI = BR.EQClasses_begin(), EE = BR.EQClasses_end(); EI != EE; ++EI) {
+ const auto *N = const_cast<ExplodedNode *>(EI->begin()->getErrorNode());
+ if (N) Src.push_back(N);
+ }
+ return DumpGraph(Src, Filename);
+ } else {
+ return llvm::WriteGraph(&G, "ExprEngine", /*ShortNames=*/false,
+ /*Title=*/"Exploded Graph", /*Filename=*/Filename);
+ }
+#endif
+ llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
+ return "";
+}
+
+std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes,
+ StringRef Filename) {
+#ifndef NDEBUG
+ std::unique_ptr<ExplodedGraph> TrimmedG(G.trim(Nodes));
+
+ if (!TrimmedG.get()) {
+ llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n";
+ } else {
+ return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine",
+ /*ShortNames=*/false,
+ /*Title=*/"Trimmed Exploded Graph",
+ /*Filename=*/Filename);
+ }
+#endif
+ llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
+ return "";
+}
+
+void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() {
+ static int index = 0;
+ return &index;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
new file mode 100644
index 000000000000..b980628878e9
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -0,0 +1,1127 @@
+//=-- ExprEngineC.cpp - ExprEngine support for C expressions ----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ExprEngine's support for C expressions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+
+using namespace clang;
+using namespace ento;
+using llvm::APSInt;
+
+/// Optionally conjure and return a symbol for offset when processing
+/// an expression \p Expression.
+/// If \p Other is a location, conjure a symbol for \p Symbol
+/// (offset) if it is unknown so that memory arithmetic always
+/// results in an ElementRegion.
+/// \p Count The number of times the current basic block was visited.
+static SVal conjureOffsetSymbolOnLocation(
+ SVal Symbol, SVal Other, Expr* Expression, SValBuilder &svalBuilder,
+ unsigned Count, const LocationContext *LCtx) {
+ QualType Ty = Expression->getType();
+ if (Other.getAs<Loc>() &&
+ Ty->isIntegralOrEnumerationType() &&
+ Symbol.isUnknown()) {
+ return svalBuilder.conjureSymbolVal(Expression, LCtx, Ty, Count);
+ }
+ return Symbol;
+}
+
+void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ Expr *LHS = B->getLHS()->IgnoreParens();
+ Expr *RHS = B->getRHS()->IgnoreParens();
+
+ // FIXME: Prechecks eventually go in ::Visit().
+ ExplodedNodeSet CheckedSet;
+ ExplodedNodeSet Tmp2;
+ getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, B, *this);
+
+ // With both the LHS and RHS evaluated, process the operation itself.
+ for (ExplodedNodeSet::iterator it=CheckedSet.begin(), ei=CheckedSet.end();
+ it != ei; ++it) {
+
+ ProgramStateRef state = (*it)->getState();
+ const LocationContext *LCtx = (*it)->getLocationContext();
+ SVal LeftV = state->getSVal(LHS, LCtx);
+ SVal RightV = state->getSVal(RHS, LCtx);
+
+ BinaryOperator::Opcode Op = B->getOpcode();
+
+ if (Op == BO_Assign) {
+ // EXPERIMENTAL: "Conjured" symbols.
+ // FIXME: Handle structs.
+ if (RightV.isUnknown()) {
+ unsigned Count = currBldrCtx->blockCount();
+ RightV = svalBuilder.conjureSymbolVal(nullptr, B->getRHS(), LCtx,
+ Count);
+ }
+ // Simulate the effects of a "store": bind the value of the RHS
+ // to the L-Value represented by the LHS.
+ SVal ExprVal = B->isGLValue() ? LeftV : RightV;
+ evalStore(Tmp2, B, LHS, *it, state->BindExpr(B, LCtx, ExprVal),
+ LeftV, RightV);
+ continue;
+ }
+
+ if (!B->isAssignmentOp()) {
+ StmtNodeBuilder Bldr(*it, Tmp2, *currBldrCtx);
+
+ if (B->isAdditiveOp()) {
+ // TODO: This can be removed after we enable history tracking with
+ // SymSymExpr.
+ unsigned Count = currBldrCtx->blockCount();
+ RightV = conjureOffsetSymbolOnLocation(
+ RightV, LeftV, RHS, svalBuilder, Count, LCtx);
+ LeftV = conjureOffsetSymbolOnLocation(
+ LeftV, RightV, LHS, svalBuilder, Count, LCtx);
+ }
+
+ // Although we don't yet model pointers-to-members, we do need to make
+ // sure that the members of temporaries have a valid 'this' pointer for
+ // other checks.
+ if (B->getOpcode() == BO_PtrMemD)
+ state = createTemporaryRegionIfNeeded(state, LCtx, LHS);
+
+ // Process non-assignments except commas or short-circuited
+ // logical expressions (LAnd and LOr).
+ SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType());
+ if (!Result.isUnknown()) {
+ state = state->BindExpr(B, LCtx, Result);
+ }
+
+ Bldr.generateNode(B, *it, state);
+ continue;
+ }
+
+ assert (B->isCompoundAssignmentOp());
+
+ switch (Op) {
+ default:
+ llvm_unreachable("Invalid opcode for compound assignment.");
+ case BO_MulAssign: Op = BO_Mul; break;
+ case BO_DivAssign: Op = BO_Div; break;
+ case BO_RemAssign: Op = BO_Rem; break;
+ case BO_AddAssign: Op = BO_Add; break;
+ case BO_SubAssign: Op = BO_Sub; break;
+ case BO_ShlAssign: Op = BO_Shl; break;
+ case BO_ShrAssign: Op = BO_Shr; break;
+ case BO_AndAssign: Op = BO_And; break;
+ case BO_XorAssign: Op = BO_Xor; break;
+ case BO_OrAssign: Op = BO_Or; break;
+ }
+
+ // Perform a load (the LHS). This performs the checks for
+ // null dereferences, and so on.
+ ExplodedNodeSet Tmp;
+ SVal location = LeftV;
+ evalLoad(Tmp, B, LHS, *it, state, location);
+
+ for (ExplodedNodeSet::iterator I = Tmp.begin(), E = Tmp.end(); I != E;
+ ++I) {
+
+ state = (*I)->getState();
+ const LocationContext *LCtx = (*I)->getLocationContext();
+ SVal V = state->getSVal(LHS, LCtx);
+
+ // Get the computation type.
+ QualType CTy =
+ cast<CompoundAssignOperator>(B)->getComputationResultType();
+ CTy = getContext().getCanonicalType(CTy);
+
+ QualType CLHSTy =
+ cast<CompoundAssignOperator>(B)->getComputationLHSType();
+ CLHSTy = getContext().getCanonicalType(CLHSTy);
+
+ QualType LTy = getContext().getCanonicalType(LHS->getType());
+
+ // Promote LHS.
+ V = svalBuilder.evalCast(V, CLHSTy, LTy);
+
+ // Compute the result of the operation.
+ SVal Result = svalBuilder.evalCast(evalBinOp(state, Op, V, RightV, CTy),
+ B->getType(), CTy);
+
+ // EXPERIMENTAL: "Conjured" symbols.
+ // FIXME: Handle structs.
+
+ SVal LHSVal;
+
+ if (Result.isUnknown()) {
+ // The symbolic value is actually for the type of the left-hand side
+ // expression, not the computation type, as this is the value the
+ // LValue on the LHS will bind to.
+ LHSVal = svalBuilder.conjureSymbolVal(nullptr, B->getRHS(), LCtx, LTy,
+ currBldrCtx->blockCount());
+ // However, we need to convert the symbol to the computation type.
+ Result = svalBuilder.evalCast(LHSVal, CTy, LTy);
+ }
+ else {
+ // The left-hand side may bind to a different value then the
+ // computation type.
+ LHSVal = svalBuilder.evalCast(Result, LTy, CTy);
+ }
+
+ // In C++, assignment and compound assignment operators return an
+ // lvalue.
+ if (B->isGLValue())
+ state = state->BindExpr(B, LCtx, location);
+ else
+ state = state->BindExpr(B, LCtx, Result);
+
+ evalStore(Tmp2, B, LHS, *I, state, location, LHSVal);
+ }
+ }
+
+ // FIXME: postvisits eventually go in ::Visit()
+ getCheckerManager().runCheckersForPostStmt(Dst, Tmp2, B, *this);
+}
+
+void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ CanQualType T = getContext().getCanonicalType(BE->getType());
+
+ const BlockDecl *BD = BE->getBlockDecl();
+ // Get the value of the block itself.
+ SVal V = svalBuilder.getBlockPointer(BD, T,
+ Pred->getLocationContext(),
+ currBldrCtx->blockCount());
+
+ ProgramStateRef State = Pred->getState();
+
+ // If we created a new MemRegion for the block, we should explicitly bind
+ // the captured variables.
+ if (const BlockDataRegion *BDR =
+ dyn_cast_or_null<BlockDataRegion>(V.getAsRegion())) {
+
+ BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
+ E = BDR->referenced_vars_end();
+
+ auto CI = BD->capture_begin();
+ auto CE = BD->capture_end();
+ for (; I != E; ++I) {
+ const VarRegion *capturedR = I.getCapturedRegion();
+ const VarRegion *originalR = I.getOriginalRegion();
+
+ // If the capture had a copy expression, use the result of evaluating
+ // that expression, otherwise use the original value.
+ // We rely on the invariant that the block declaration's capture variables
+ // are a prefix of the BlockDataRegion's referenced vars (which may include
+ // referenced globals, etc.) to enable fast lookup of the capture for a
+ // given referenced var.
+ const Expr *copyExpr = nullptr;
+ if (CI != CE) {
+ assert(CI->getVariable() == capturedR->getDecl());
+ copyExpr = CI->getCopyExpr();
+ CI++;
+ }
+
+ if (capturedR != originalR) {
+ SVal originalV;
+ const LocationContext *LCtx = Pred->getLocationContext();
+ if (copyExpr) {
+ originalV = State->getSVal(copyExpr, LCtx);
+ } else {
+ originalV = State->getSVal(loc::MemRegionVal(originalR));
+ }
+ State = State->bindLoc(loc::MemRegionVal(capturedR), originalV, LCtx);
+ }
+ }
+ }
+
+ ExplodedNodeSet Tmp;
+ StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx);
+ Bldr.generateNode(BE, Pred,
+ State->BindExpr(BE, Pred->getLocationContext(), V),
+ nullptr, ProgramPoint::PostLValueKind);
+
+ // FIXME: Move all post/pre visits to ::Visit().
+ getCheckerManager().runCheckersForPostStmt(Dst, Tmp, BE, *this);
+}
+
+ProgramStateRef ExprEngine::handleLValueBitCast(
+ ProgramStateRef state, const Expr* Ex, const LocationContext* LCtx,
+ QualType T, QualType ExTy, const CastExpr* CastE, StmtNodeBuilder& Bldr,
+ ExplodedNode* Pred) {
+ if (T->isLValueReferenceType()) {
+ assert(!CastE->getType()->isLValueReferenceType());
+ ExTy = getContext().getLValueReferenceType(ExTy);
+ } else if (T->isRValueReferenceType()) {
+ assert(!CastE->getType()->isRValueReferenceType());
+ ExTy = getContext().getRValueReferenceType(ExTy);
+ }
+ // Delegate to SValBuilder to process.
+ SVal OrigV = state->getSVal(Ex, LCtx);
+ SVal V = svalBuilder.evalCast(OrigV, T, ExTy);
+ // Negate the result if we're treating the boolean as a signed i1
+ if (CastE->getCastKind() == CK_BooleanToSignedIntegral)
+ V = evalMinus(V);
+ state = state->BindExpr(CastE, LCtx, V);
+ if (V.isUnknown() && !OrigV.isUnknown()) {
+ state = escapeValue(state, OrigV, PSK_EscapeOther);
+ }
+ Bldr.generateNode(CastE, Pred, state);
+
+ return state;
+}
+
+ProgramStateRef ExprEngine::handleLVectorSplat(
+ ProgramStateRef state, const LocationContext* LCtx, const CastExpr* CastE,
+ StmtNodeBuilder &Bldr, ExplodedNode* Pred) {
+ // Recover some path sensitivity by conjuring a new value.
+ QualType resultType = CastE->getType();
+ if (CastE->isGLValue())
+ resultType = getContext().getPointerType(resultType);
+ SVal result = svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx,
+ resultType,
+ currBldrCtx->blockCount());
+ state = state->BindExpr(CastE, LCtx, result);
+ Bldr.generateNode(CastE, Pred, state);
+
+ return state;
+}
+
+void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+
+ ExplodedNodeSet dstPreStmt;
+ getCheckerManager().runCheckersForPreStmt(dstPreStmt, Pred, CastE, *this);
+
+ if (CastE->getCastKind() == CK_LValueToRValue) {
+ for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
+ I!=E; ++I) {
+ ExplodedNode *subExprNode = *I;
+ ProgramStateRef state = subExprNode->getState();
+ const LocationContext *LCtx = subExprNode->getLocationContext();
+ evalLoad(Dst, CastE, CastE, subExprNode, state, state->getSVal(Ex, LCtx));
+ }
+ return;
+ }
+
+ // All other casts.
+ QualType T = CastE->getType();
+ QualType ExTy = Ex->getType();
+
+ if (const ExplicitCastExpr *ExCast=dyn_cast_or_null<ExplicitCastExpr>(CastE))
+ T = ExCast->getTypeAsWritten();
+
+ StmtNodeBuilder Bldr(dstPreStmt, Dst, *currBldrCtx);
+ for (ExplodedNodeSet::iterator I = dstPreStmt.begin(), E = dstPreStmt.end();
+ I != E; ++I) {
+
+ Pred = *I;
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ switch (CastE->getCastKind()) {
+ case CK_LValueToRValue:
+ llvm_unreachable("LValueToRValue casts handled earlier.");
+ case CK_ToVoid:
+ continue;
+ // The analyzer doesn't do anything special with these casts,
+ // since it understands retain/release semantics already.
+ case CK_ARCProduceObject:
+ case CK_ARCConsumeObject:
+ case CK_ARCReclaimReturnedObject:
+ case CK_ARCExtendBlockObject: // Fall-through.
+ case CK_CopyAndAutoreleaseBlockObject:
+ // The analyser can ignore atomic casts for now, although some future
+ // checkers may want to make certain that you're not modifying the same
+ // value through atomic and nonatomic pointers.
+ case CK_AtomicToNonAtomic:
+ case CK_NonAtomicToAtomic:
+ // True no-ops.
+ case CK_NoOp:
+ case CK_ConstructorConversion:
+ case CK_UserDefinedConversion:
+ case CK_FunctionToPointerDecay:
+ case CK_BuiltinFnToFnPtr: {
+ // Copy the SVal of Ex to CastE.
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal V = state->getSVal(Ex, LCtx);
+ state = state->BindExpr(CastE, LCtx, V);
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
+ case CK_MemberPointerToBoolean:
+ case CK_PointerToBoolean: {
+ SVal V = state->getSVal(Ex, LCtx);
+ auto PTMSV = V.getAs<nonloc::PointerToMember>();
+ if (PTMSV)
+ V = svalBuilder.makeTruthVal(!PTMSV->isNullMemberPointer(), ExTy);
+ if (V.isUndef() || PTMSV) {
+ state = state->BindExpr(CastE, LCtx, V);
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
+ // Explicitly proceed with default handler for this case cascade.
+ state =
+ handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred);
+ continue;
+ }
+ case CK_Dependent:
+ case CK_ArrayToPointerDecay:
+ case CK_BitCast:
+ case CK_AddressSpaceConversion:
+ case CK_BooleanToSignedIntegral:
+ case CK_NullToPointer:
+ case CK_IntegralToPointer:
+ case CK_PointerToIntegral: {
+ SVal V = state->getSVal(Ex, LCtx);
+ if (V.getAs<nonloc::PointerToMember>()) {
+ state = state->BindExpr(CastE, LCtx, UnknownVal());
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
+ // Explicitly proceed with default handler for this case cascade.
+ state =
+ handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred);
+ continue;
+ }
+ case CK_IntegralToBoolean:
+ case CK_IntegralToFloating:
+ case CK_FloatingToIntegral:
+ case CK_FloatingToBoolean:
+ case CK_FloatingCast:
+ case CK_FloatingRealToComplex:
+ case CK_FloatingComplexToReal:
+ case CK_FloatingComplexToBoolean:
+ case CK_FloatingComplexCast:
+ case CK_FloatingComplexToIntegralComplex:
+ case CK_IntegralRealToComplex:
+ case CK_IntegralComplexToReal:
+ case CK_IntegralComplexToBoolean:
+ case CK_IntegralComplexCast:
+ case CK_IntegralComplexToFloatingComplex:
+ case CK_CPointerToObjCPointerCast:
+ case CK_BlockPointerToObjCPointerCast:
+ case CK_AnyPointerToBlockPointerCast:
+ case CK_ObjCObjectLValueCast:
+ case CK_ZeroToOCLOpaqueType:
+ case CK_IntToOCLSampler:
+ case CK_LValueBitCast:
+ case CK_FixedPointCast:
+ case CK_FixedPointToBoolean: {
+ state =
+ handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred);
+ continue;
+ }
+ case CK_IntegralCast: {
+ // Delegate to SValBuilder to process.
+ SVal V = state->getSVal(Ex, LCtx);
+ V = svalBuilder.evalIntegralCast(state, V, T, ExTy);
+ state = state->BindExpr(CastE, LCtx, V);
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
+ case CK_DerivedToBase:
+ case CK_UncheckedDerivedToBase: {
+ // For DerivedToBase cast, delegate to the store manager.
+ SVal val = state->getSVal(Ex, LCtx);
+ val = getStoreManager().evalDerivedToBase(val, CastE);
+ state = state->BindExpr(CastE, LCtx, val);
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
+ // Handle C++ dyn_cast.
+ case CK_Dynamic: {
+ SVal val = state->getSVal(Ex, LCtx);
+
+ // Compute the type of the result.
+ QualType resultType = CastE->getType();
+ if (CastE->isGLValue())
+ resultType = getContext().getPointerType(resultType);
+
+ bool Failed = false;
+
+ // Check if the value being cast evaluates to 0.
+ if (val.isZeroConstant())
+ Failed = true;
+ // Else, evaluate the cast.
+ else
+ val = getStoreManager().attemptDownCast(val, T, Failed);
+
+ if (Failed) {
+ if (T->isReferenceType()) {
+ // A bad_cast exception is thrown if input value is a reference.
+ // Currently, we model this, by generating a sink.
+ Bldr.generateSink(CastE, Pred, state);
+ continue;
+ } else {
+ // If the cast fails on a pointer, bind to 0.
+ state = state->BindExpr(CastE, LCtx, svalBuilder.makeNull());
+ }
+ } else {
+ // If we don't know if the cast succeeded, conjure a new symbol.
+ if (val.isUnknown()) {
+ DefinedOrUnknownSVal NewSym =
+ svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, resultType,
+ currBldrCtx->blockCount());
+ state = state->BindExpr(CastE, LCtx, NewSym);
+ } else
+ // Else, bind to the derived region value.
+ state = state->BindExpr(CastE, LCtx, val);
+ }
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
+ case CK_BaseToDerived: {
+ SVal val = state->getSVal(Ex, LCtx);
+ QualType resultType = CastE->getType();
+ if (CastE->isGLValue())
+ resultType = getContext().getPointerType(resultType);
+
+ bool Failed = false;
+
+ if (!val.isConstant()) {
+ val = getStoreManager().attemptDownCast(val, T, Failed);
+ }
+
+ // Failed to cast or the result is unknown, fall back to conservative.
+ if (Failed || val.isUnknown()) {
+ val =
+ svalBuilder.conjureSymbolVal(nullptr, CastE, LCtx, resultType,
+ currBldrCtx->blockCount());
+ }
+ state = state->BindExpr(CastE, LCtx, val);
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
+ case CK_NullToMemberPointer: {
+ SVal V = svalBuilder.getMemberPointer(nullptr);
+ state = state->BindExpr(CastE, LCtx, V);
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
+ case CK_DerivedToBaseMemberPointer:
+ case CK_BaseToDerivedMemberPointer:
+ case CK_ReinterpretMemberPointer: {
+ SVal V = state->getSVal(Ex, LCtx);
+ if (auto PTMSV = V.getAs<nonloc::PointerToMember>()) {
+ SVal CastedPTMSV = svalBuilder.makePointerToMember(
+ getBasicVals().accumCXXBase(
+ llvm::make_range<CastExpr::path_const_iterator>(
+ CastE->path_begin(), CastE->path_end()), *PTMSV));
+ state = state->BindExpr(CastE, LCtx, CastedPTMSV);
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
+ // Explicitly proceed with default handler for this case cascade.
+ state = handleLVectorSplat(state, LCtx, CastE, Bldr, Pred);
+ continue;
+ }
+ // Various C++ casts that are not handled yet.
+ case CK_ToUnion:
+ case CK_VectorSplat: {
+ state = handleLVectorSplat(state, LCtx, CastE, Bldr, Pred);
+ continue;
+ }
+ }
+ }
+}
+
+void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ StmtNodeBuilder B(Pred, Dst, *currBldrCtx);
+
+ ProgramStateRef State = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ const Expr *Init = CL->getInitializer();
+ SVal V = State->getSVal(CL->getInitializer(), LCtx);
+
+ if (isa<CXXConstructExpr>(Init) || isa<CXXStdInitializerListExpr>(Init)) {
+ // No work needed. Just pass the value up to this expression.
+ } else {
+ assert(isa<InitListExpr>(Init));
+ Loc CLLoc = State->getLValue(CL, LCtx);
+ State = State->bindLoc(CLLoc, V, LCtx);
+
+ if (CL->isGLValue())
+ V = CLLoc;
+ }
+
+ B.generateNode(CL, Pred, State->BindExpr(CL, LCtx, V));
+}
+
+void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ // Assumption: The CFG has one DeclStmt per Decl.
+ const VarDecl *VD = dyn_cast_or_null<VarDecl>(*DS->decl_begin());
+
+ if (!VD) {
+ //TODO:AZ: remove explicit insertion after refactoring is done.
+ Dst.insert(Pred);
+ return;
+ }
+
+ // FIXME: all pre/post visits should eventually be handled by ::Visit().
+ ExplodedNodeSet dstPreVisit;
+ getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, DS, *this);
+
+ ExplodedNodeSet dstEvaluated;
+ StmtNodeBuilder B(dstPreVisit, dstEvaluated, *currBldrCtx);
+ for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
+ I!=E; ++I) {
+ ExplodedNode *N = *I;
+ ProgramStateRef state = N->getState();
+ const LocationContext *LC = N->getLocationContext();
+
+ // Decls without InitExpr are not initialized explicitly.
+ if (const Expr *InitEx = VD->getInit()) {
+
+ // Note in the state that the initialization has occurred.
+ ExplodedNode *UpdatedN = N;
+ SVal InitVal = state->getSVal(InitEx, LC);
+
+ assert(DS->isSingleDecl());
+ if (getObjectUnderConstruction(state, DS, LC)) {
+ state = finishObjectConstruction(state, DS, LC);
+ // We constructed the object directly in the variable.
+ // No need to bind anything.
+ B.generateNode(DS, UpdatedN, state);
+ } else {
+ // Recover some path-sensitivity if a scalar value evaluated to
+ // UnknownVal.
+ if (InitVal.isUnknown()) {
+ QualType Ty = InitEx->getType();
+ if (InitEx->isGLValue()) {
+ Ty = getContext().getPointerType(Ty);
+ }
+
+ InitVal = svalBuilder.conjureSymbolVal(nullptr, InitEx, LC, Ty,
+ currBldrCtx->blockCount());
+ }
+
+
+ B.takeNodes(UpdatedN);
+ ExplodedNodeSet Dst2;
+ evalBind(Dst2, DS, UpdatedN, state->getLValue(VD, LC), InitVal, true);
+ B.addNodes(Dst2);
+ }
+ }
+ else {
+ B.generateNode(DS, N, state);
+ }
+ }
+
+ getCheckerManager().runCheckersForPostStmt(Dst, B.getResults(), DS, *this);
+}
+
+void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ assert(B->getOpcode() == BO_LAnd ||
+ B->getOpcode() == BO_LOr);
+
+ StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ ProgramStateRef state = Pred->getState();
+
+ if (B->getType()->isVectorType()) {
+ // FIXME: We do not model vector arithmetic yet. When adding support for
+ // that, note that the CFG-based reasoning below does not apply, because
+ // logical operators on vectors are not short-circuit. Currently they are
+ // modeled as short-circuit in Clang CFG but this is incorrect.
+ // Do not set the value for the expression. It'd be UnknownVal by default.
+ Bldr.generateNode(B, Pred, state);
+ return;
+ }
+
+ ExplodedNode *N = Pred;
+ while (!N->getLocation().getAs<BlockEntrance>()) {
+ ProgramPoint P = N->getLocation();
+ assert(P.getAs<PreStmt>()|| P.getAs<PreStmtPurgeDeadSymbols>());
+ (void) P;
+ assert(N->pred_size() == 1);
+ N = *N->pred_begin();
+ }
+ assert(N->pred_size() == 1);
+ N = *N->pred_begin();
+ BlockEdge BE = N->getLocation().castAs<BlockEdge>();
+ SVal X;
+
+ // Determine the value of the expression by introspecting how we
+ // got this location in the CFG. This requires looking at the previous
+ // block we were in and what kind of control-flow transfer was involved.
+ const CFGBlock *SrcBlock = BE.getSrc();
+ // The only terminator (if there is one) that makes sense is a logical op.
+ CFGTerminator T = SrcBlock->getTerminator();
+ if (const BinaryOperator *Term = cast_or_null<BinaryOperator>(T.getStmt())) {
+ (void) Term;
+ assert(Term->isLogicalOp());
+ assert(SrcBlock->succ_size() == 2);
+ // Did we take the true or false branch?
+ unsigned constant = (*SrcBlock->succ_begin() == BE.getDst()) ? 1 : 0;
+ X = svalBuilder.makeIntVal(constant, B->getType());
+ }
+ else {
+ // If there is no terminator, by construction the last statement
+ // in SrcBlock is the value of the enclosing expression.
+ // However, we still need to constrain that value to be 0 or 1.
+ assert(!SrcBlock->empty());
+ CFGStmt Elem = SrcBlock->rbegin()->castAs<CFGStmt>();
+ const Expr *RHS = cast<Expr>(Elem.getStmt());
+ SVal RHSVal = N->getState()->getSVal(RHS, Pred->getLocationContext());
+
+ if (RHSVal.isUndef()) {
+ X = RHSVal;
+ } else {
+ // We evaluate "RHSVal != 0" expression which result in 0 if the value is
+ // known to be false, 1 if the value is known to be true and a new symbol
+ // when the assumption is unknown.
+ nonloc::ConcreteInt Zero(getBasicVals().getValue(0, B->getType()));
+ X = evalBinOp(N->getState(), BO_NE,
+ svalBuilder.evalCast(RHSVal, B->getType(), RHS->getType()),
+ Zero, B->getType());
+ }
+ }
+ Bldr.generateNode(B, Pred, state->BindExpr(B, Pred->getLocationContext(), X));
+}
+
+void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ StmtNodeBuilder B(Pred, Dst, *currBldrCtx);
+
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ QualType T = getContext().getCanonicalType(IE->getType());
+ unsigned NumInitElements = IE->getNumInits();
+
+ if (!IE->isGLValue() &&
+ (T->isArrayType() || T->isRecordType() || T->isVectorType() ||
+ T->isAnyComplexType())) {
+ llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList();
+
+ // Handle base case where the initializer has no elements.
+ // e.g: static int* myArray[] = {};
+ if (NumInitElements == 0) {
+ SVal V = svalBuilder.makeCompoundVal(T, vals);
+ B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V));
+ return;
+ }
+
+ for (InitListExpr::const_reverse_iterator it = IE->rbegin(),
+ ei = IE->rend(); it != ei; ++it) {
+ SVal V = state->getSVal(cast<Expr>(*it), LCtx);
+ vals = getBasicVals().prependSVal(V, vals);
+ }
+
+ B.generateNode(IE, Pred,
+ state->BindExpr(IE, LCtx,
+ svalBuilder.makeCompoundVal(T, vals)));
+ return;
+ }
+
+ // Handle scalars: int{5} and int{} and GLvalues.
+ // Note, if the InitListExpr is a GLvalue, it means that there is an address
+ // representing it, so it must have a single init element.
+ assert(NumInitElements <= 1);
+
+ SVal V;
+ if (NumInitElements == 0)
+ V = getSValBuilder().makeZeroVal(T);
+ else
+ V = state->getSVal(IE->getInit(0), LCtx);
+
+ B.generateNode(IE, Pred, state->BindExpr(IE, LCtx, V));
+}
+
+void ExprEngine::VisitGuardedExpr(const Expr *Ex,
+ const Expr *L,
+ const Expr *R,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ assert(L && R);
+
+ StmtNodeBuilder B(Pred, Dst, *currBldrCtx);
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ const CFGBlock *SrcBlock = nullptr;
+
+ // Find the predecessor block.
+ ProgramStateRef SrcState = state;
+ for (const ExplodedNode *N = Pred ; N ; N = *N->pred_begin()) {
+ ProgramPoint PP = N->getLocation();
+ if (PP.getAs<PreStmtPurgeDeadSymbols>() || PP.getAs<BlockEntrance>()) {
+ // If the state N has multiple predecessors P, it means that successors
+ // of P are all equivalent.
+ // In turn, that means that all nodes at P are equivalent in terms
+ // of observable behavior at N, and we can follow any of them.
+ // FIXME: a more robust solution which does not walk up the tree.
+ continue;
+ }
+ SrcBlock = PP.castAs<BlockEdge>().getSrc();
+ SrcState = N->getState();
+ break;
+ }
+
+ assert(SrcBlock && "missing function entry");
+
+ // Find the last expression in the predecessor block. That is the
+ // expression that is used for the value of the ternary expression.
+ bool hasValue = false;
+ SVal V;
+
+ for (CFGElement CE : llvm::reverse(*SrcBlock)) {
+ if (Optional<CFGStmt> CS = CE.getAs<CFGStmt>()) {
+ const Expr *ValEx = cast<Expr>(CS->getStmt());
+ ValEx = ValEx->IgnoreParens();
+
+ // For GNU extension '?:' operator, the left hand side will be an
+ // OpaqueValueExpr, so get the underlying expression.
+ if (const OpaqueValueExpr *OpaqueEx = dyn_cast<OpaqueValueExpr>(L))
+ L = OpaqueEx->getSourceExpr();
+
+ // If the last expression in the predecessor block matches true or false
+ // subexpression, get its the value.
+ if (ValEx == L->IgnoreParens() || ValEx == R->IgnoreParens()) {
+ hasValue = true;
+ V = SrcState->getSVal(ValEx, LCtx);
+ }
+ break;
+ }
+ }
+
+ if (!hasValue)
+ V = svalBuilder.conjureSymbolVal(nullptr, Ex, LCtx,
+ currBldrCtx->blockCount());
+
+ // Generate a new node with the binding from the appropriate path.
+ B.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V, true));
+}
+
+void ExprEngine::
+VisitOffsetOfExpr(const OffsetOfExpr *OOE,
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ StmtNodeBuilder B(Pred, Dst, *currBldrCtx);
+ Expr::EvalResult Result;
+ if (OOE->EvaluateAsInt(Result, getContext())) {
+ APSInt IV = Result.Val.getInt();
+ assert(IV.getBitWidth() == getContext().getTypeSize(OOE->getType()));
+ assert(OOE->getType()->isBuiltinType());
+ assert(OOE->getType()->getAs<BuiltinType>()->isInteger());
+ assert(IV.isSigned() == OOE->getType()->isSignedIntegerType());
+ SVal X = svalBuilder.makeIntVal(IV);
+ B.generateNode(OOE, Pred,
+ Pred->getState()->BindExpr(OOE, Pred->getLocationContext(),
+ X));
+ }
+ // FIXME: Handle the case where __builtin_offsetof is not a constant.
+}
+
+
+void ExprEngine::
+VisitUnaryExprOrTypeTraitExpr(const UnaryExprOrTypeTraitExpr *Ex,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ // FIXME: Prechecks eventually go in ::Visit().
+ ExplodedNodeSet CheckedSet;
+ getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, Ex, *this);
+
+ ExplodedNodeSet EvalSet;
+ StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx);
+
+ QualType T = Ex->getTypeOfArgument();
+
+ for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
+ I != E; ++I) {
+ if (Ex->getKind() == UETT_SizeOf) {
+ if (!T->isIncompleteType() && !T->isConstantSizeType()) {
+ assert(T->isVariableArrayType() && "Unknown non-constant-sized type.");
+
+ // FIXME: Add support for VLA type arguments and VLA expressions.
+ // When that happens, we should probably refactor VLASizeChecker's code.
+ continue;
+ } else if (T->getAs<ObjCObjectType>()) {
+ // Some code tries to take the sizeof an ObjCObjectType, relying that
+ // the compiler has laid out its representation. Just report Unknown
+ // for these.
+ continue;
+ }
+ }
+
+ APSInt Value = Ex->EvaluateKnownConstInt(getContext());
+ CharUnits amt = CharUnits::fromQuantity(Value.getZExtValue());
+
+ ProgramStateRef state = (*I)->getState();
+ state = state->BindExpr(Ex, (*I)->getLocationContext(),
+ svalBuilder.makeIntVal(amt.getQuantity(),
+ Ex->getType()));
+ Bldr.generateNode(Ex, *I, state);
+ }
+
+ getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, Ex, *this);
+}
+
+void ExprEngine::handleUOExtension(ExplodedNodeSet::iterator I,
+ const UnaryOperator *U,
+ StmtNodeBuilder &Bldr) {
+ // FIXME: We can probably just have some magic in Environment::getSVal()
+ // that propagates values, instead of creating a new node here.
+ //
+ // Unary "+" is a no-op, similar to a parentheses. We still have places
+ // where it may be a block-level expression, so we need to
+ // generate an extra node that just propagates the value of the
+ // subexpression.
+ const Expr *Ex = U->getSubExpr()->IgnoreParens();
+ ProgramStateRef state = (*I)->getState();
+ const LocationContext *LCtx = (*I)->getLocationContext();
+ Bldr.generateNode(U, *I, state->BindExpr(U, LCtx,
+ state->getSVal(Ex, LCtx)));
+}
+
+void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ // FIXME: Prechecks eventually go in ::Visit().
+ ExplodedNodeSet CheckedSet;
+ getCheckerManager().runCheckersForPreStmt(CheckedSet, Pred, U, *this);
+
+ ExplodedNodeSet EvalSet;
+ StmtNodeBuilder Bldr(CheckedSet, EvalSet, *currBldrCtx);
+
+ for (ExplodedNodeSet::iterator I = CheckedSet.begin(), E = CheckedSet.end();
+ I != E; ++I) {
+ switch (U->getOpcode()) {
+ default: {
+ Bldr.takeNodes(*I);
+ ExplodedNodeSet Tmp;
+ VisitIncrementDecrementOperator(U, *I, Tmp);
+ Bldr.addNodes(Tmp);
+ break;
+ }
+ case UO_Real: {
+ const Expr *Ex = U->getSubExpr()->IgnoreParens();
+
+ // FIXME: We don't have complex SValues yet.
+ if (Ex->getType()->isAnyComplexType()) {
+ // Just report "Unknown."
+ break;
+ }
+
+ // For all other types, UO_Real is an identity operation.
+ assert (U->getType() == Ex->getType());
+ ProgramStateRef state = (*I)->getState();
+ const LocationContext *LCtx = (*I)->getLocationContext();
+ Bldr.generateNode(U, *I, state->BindExpr(U, LCtx,
+ state->getSVal(Ex, LCtx)));
+ break;
+ }
+
+ case UO_Imag: {
+ const Expr *Ex = U->getSubExpr()->IgnoreParens();
+ // FIXME: We don't have complex SValues yet.
+ if (Ex->getType()->isAnyComplexType()) {
+ // Just report "Unknown."
+ break;
+ }
+ // For all other types, UO_Imag returns 0.
+ ProgramStateRef state = (*I)->getState();
+ const LocationContext *LCtx = (*I)->getLocationContext();
+ SVal X = svalBuilder.makeZeroVal(Ex->getType());
+ Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, X));
+ break;
+ }
+
+ case UO_AddrOf: {
+ // Process pointer-to-member address operation.
+ const Expr *Ex = U->getSubExpr()->IgnoreParens();
+ if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Ex)) {
+ const ValueDecl *VD = DRE->getDecl();
+
+ if (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD)) {
+ ProgramStateRef State = (*I)->getState();
+ const LocationContext *LCtx = (*I)->getLocationContext();
+ SVal SV = svalBuilder.getMemberPointer(cast<DeclaratorDecl>(VD));
+ Bldr.generateNode(U, *I, State->BindExpr(U, LCtx, SV));
+ break;
+ }
+ }
+ // Explicitly proceed with default handler for this case cascade.
+ handleUOExtension(I, U, Bldr);
+ break;
+ }
+ case UO_Plus:
+ assert(!U->isGLValue());
+ LLVM_FALLTHROUGH;
+ case UO_Deref:
+ case UO_Extension: {
+ handleUOExtension(I, U, Bldr);
+ break;
+ }
+
+ case UO_LNot:
+ case UO_Minus:
+ case UO_Not: {
+ assert (!U->isGLValue());
+ const Expr *Ex = U->getSubExpr()->IgnoreParens();
+ ProgramStateRef state = (*I)->getState();
+ const LocationContext *LCtx = (*I)->getLocationContext();
+
+ // Get the value of the subexpression.
+ SVal V = state->getSVal(Ex, LCtx);
+
+ if (V.isUnknownOrUndef()) {
+ Bldr.generateNode(U, *I, state->BindExpr(U, LCtx, V));
+ break;
+ }
+
+ switch (U->getOpcode()) {
+ default:
+ llvm_unreachable("Invalid Opcode.");
+ case UO_Not:
+ // FIXME: Do we need to handle promotions?
+ state = state->BindExpr(U, LCtx, evalComplement(V.castAs<NonLoc>()));
+ break;
+ case UO_Minus:
+ // FIXME: Do we need to handle promotions?
+ state = state->BindExpr(U, LCtx, evalMinus(V.castAs<NonLoc>()));
+ break;
+ case UO_LNot:
+ // C99 6.5.3.3: "The expression !E is equivalent to (0==E)."
+ //
+ // Note: technically we do "E == 0", but this is the same in the
+ // transfer functions as "0 == E".
+ SVal Result;
+ if (Optional<Loc> LV = V.getAs<Loc>()) {
+ Loc X = svalBuilder.makeNullWithType(Ex->getType());
+ Result = evalBinOp(state, BO_EQ, *LV, X, U->getType());
+ } else if (Ex->getType()->isFloatingType()) {
+ // FIXME: handle floating point types.
+ Result = UnknownVal();
+ } else {
+ nonloc::ConcreteInt X(getBasicVals().getValue(0, Ex->getType()));
+ Result = evalBinOp(state, BO_EQ, V.castAs<NonLoc>(), X,
+ U->getType());
+ }
+
+ state = state->BindExpr(U, LCtx, Result);
+ break;
+ }
+ Bldr.generateNode(U, *I, state);
+ break;
+ }
+ }
+ }
+
+ getCheckerManager().runCheckersForPostStmt(Dst, EvalSet, U, *this);
+}
+
+void ExprEngine::VisitIncrementDecrementOperator(const UnaryOperator* U,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ // Handle ++ and -- (both pre- and post-increment).
+ assert (U->isIncrementDecrementOp());
+ const Expr *Ex = U->getSubExpr()->IgnoreParens();
+
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef state = Pred->getState();
+ SVal loc = state->getSVal(Ex, LCtx);
+
+ // Perform a load.
+ ExplodedNodeSet Tmp;
+ evalLoad(Tmp, U, Ex, Pred, state, loc);
+
+ ExplodedNodeSet Dst2;
+ StmtNodeBuilder Bldr(Tmp, Dst2, *currBldrCtx);
+ for (ExplodedNodeSet::iterator I=Tmp.begin(), E=Tmp.end();I!=E;++I) {
+
+ state = (*I)->getState();
+ assert(LCtx == (*I)->getLocationContext());
+ SVal V2_untested = state->getSVal(Ex, LCtx);
+
+ // Propagate unknown and undefined values.
+ if (V2_untested.isUnknownOrUndef()) {
+ state = state->BindExpr(U, LCtx, V2_untested);
+
+ // Perform the store, so that the uninitialized value detection happens.
+ Bldr.takeNodes(*I);
+ ExplodedNodeSet Dst3;
+ evalStore(Dst3, U, Ex, *I, state, loc, V2_untested);
+ Bldr.addNodes(Dst3);
+
+ continue;
+ }
+ DefinedSVal V2 = V2_untested.castAs<DefinedSVal>();
+
+ // Handle all other values.
+ BinaryOperator::Opcode Op = U->isIncrementOp() ? BO_Add : BO_Sub;
+
+ // If the UnaryOperator has non-location type, use its type to create the
+ // constant value. If the UnaryOperator has location type, create the
+ // constant with int type and pointer width.
+ SVal RHS;
+ SVal Result;
+
+ if (U->getType()->isAnyPointerType())
+ RHS = svalBuilder.makeArrayIndex(1);
+ else if (U->getType()->isIntegralOrEnumerationType())
+ RHS = svalBuilder.makeIntVal(1, U->getType());
+ else
+ RHS = UnknownVal();
+
+ // The use of an operand of type bool with the ++ operators is deprecated
+ // but valid until C++17. And if the operand of the ++ operator is of type
+ // bool, it is set to true until C++17. Note that for '_Bool', it is also
+ // set to true when it encounters ++ operator.
+ if (U->getType()->isBooleanType() && U->isIncrementOp())
+ Result = svalBuilder.makeTruthVal(true, U->getType());
+ else
+ Result = evalBinOp(state, Op, V2, RHS, U->getType());
+
+ // Conjure a new symbol if necessary to recover precision.
+ if (Result.isUnknown()){
+ DefinedOrUnknownSVal SymVal =
+ svalBuilder.conjureSymbolVal(nullptr, U, LCtx,
+ currBldrCtx->blockCount());
+ Result = SymVal;
+
+ // If the value is a location, ++/-- should always preserve
+ // non-nullness. Check if the original value was non-null, and if so
+ // propagate that constraint.
+ if (Loc::isLocType(U->getType())) {
+ DefinedOrUnknownSVal Constraint =
+ svalBuilder.evalEQ(state, V2,svalBuilder.makeZeroVal(U->getType()));
+
+ if (!state->assume(Constraint, true)) {
+ // It isn't feasible for the original value to be null.
+ // Propagate this constraint.
+ Constraint = svalBuilder.evalEQ(state, SymVal,
+ svalBuilder.makeZeroVal(U->getType()));
+
+ state = state->assume(Constraint, false);
+ assert(state);
+ }
+ }
+ }
+
+ // Since the lvalue-to-rvalue conversion is explicit in the AST,
+ // we bind an l-value if the operator is prefix and an lvalue (in C++).
+ if (U->isGLValue())
+ state = state->BindExpr(U, LCtx, loc);
+ else
+ state = state->BindExpr(U, LCtx, U->isPostfix() ? V2 : Result);
+
+ // Perform the store.
+ Bldr.takeNodes(*I);
+ ExplodedNodeSet Dst3;
+ evalStore(Dst3, U, Ex, *I, state, loc, Result);
+ Bldr.addNodes(Dst3);
+ }
+ Dst.insert(Dst2);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
new file mode 100644
index 000000000000..6445b9df5a58
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -0,0 +1,908 @@
+//===- ExprEngineCXX.cpp - ExprEngine support for C++ -----------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the C++ expression evaluation engine.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/Analysis/ConstructionContext.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/StmtCXX.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/Basic/PrettyStackTrace.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+
+using namespace clang;
+using namespace ento;
+
+void ExprEngine::CreateCXXTemporaryObject(const MaterializeTemporaryExpr *ME,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ const Expr *tempExpr = ME->GetTemporaryExpr()->IgnoreParens();
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ state = createTemporaryRegionIfNeeded(state, LCtx, tempExpr, ME);
+ Bldr.generateNode(ME, Pred, state);
+}
+
+// FIXME: This is the sort of code that should eventually live in a Core
+// checker rather than as a special case in ExprEngine.
+void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred,
+ const CallEvent &Call) {
+ SVal ThisVal;
+ bool AlwaysReturnsLValue;
+ const CXXRecordDecl *ThisRD = nullptr;
+ if (const CXXConstructorCall *Ctor = dyn_cast<CXXConstructorCall>(&Call)) {
+ assert(Ctor->getDecl()->isTrivial());
+ assert(Ctor->getDecl()->isCopyOrMoveConstructor());
+ ThisVal = Ctor->getCXXThisVal();
+ ThisRD = Ctor->getDecl()->getParent();
+ AlwaysReturnsLValue = false;
+ } else {
+ assert(cast<CXXMethodDecl>(Call.getDecl())->isTrivial());
+ assert(cast<CXXMethodDecl>(Call.getDecl())->getOverloadedOperator() ==
+ OO_Equal);
+ ThisVal = cast<CXXInstanceCall>(Call).getCXXThisVal();
+ ThisRD = cast<CXXMethodDecl>(Call.getDecl())->getParent();
+ AlwaysReturnsLValue = true;
+ }
+
+ assert(ThisRD);
+ if (ThisRD->isEmpty()) {
+ // Do nothing for empty classes. Otherwise it'd retrieve an UnknownVal
+ // and bind it and RegionStore would think that the actual value
+ // in this region at this offset is unknown.
+ return;
+ }
+
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ ExplodedNodeSet Dst;
+ Bldr.takeNodes(Pred);
+
+ SVal V = Call.getArgSVal(0);
+
+ // If the value being copied is not unknown, load from its location to get
+ // an aggregate rvalue.
+ if (Optional<Loc> L = V.getAs<Loc>())
+ V = Pred->getState()->getSVal(*L);
+ else
+ assert(V.isUnknownOrUndef());
+
+ const Expr *CallExpr = Call.getOriginExpr();
+ evalBind(Dst, CallExpr, Pred, ThisVal, V, true);
+
+ PostStmt PS(CallExpr, LCtx);
+ for (ExplodedNodeSet::iterator I = Dst.begin(), E = Dst.end();
+ I != E; ++I) {
+ ProgramStateRef State = (*I)->getState();
+ if (AlwaysReturnsLValue)
+ State = State->BindExpr(CallExpr, LCtx, ThisVal);
+ else
+ State = bindReturnValue(Call, LCtx, State);
+ Bldr.generateNode(PS, State, *I);
+ }
+}
+
+
+SVal ExprEngine::makeZeroElementRegion(ProgramStateRef State, SVal LValue,
+ QualType &Ty, bool &IsArray) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ ASTContext &Ctx = SVB.getContext();
+
+ while (const ArrayType *AT = Ctx.getAsArrayType(Ty)) {
+ Ty = AT->getElementType();
+ LValue = State->getLValue(Ty, SVB.makeZeroArrayIndex(), LValue);
+ IsArray = true;
+ }
+
+ return LValue;
+}
+
+std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
+ const Expr *E, ProgramStateRef State, const LocationContext *LCtx,
+ const ConstructionContext *CC, EvalCallOptions &CallOpts) {
+ SValBuilder &SVB = getSValBuilder();
+ MemRegionManager &MRMgr = SVB.getRegionManager();
+ ASTContext &ACtx = SVB.getContext();
+
+ // See if we're constructing an existing region by looking at the
+ // current construction context.
+ if (CC) {
+ switch (CC->getKind()) {
+ case ConstructionContext::CXX17ElidedCopyVariableKind:
+ case ConstructionContext::SimpleVariableKind: {
+ const auto *DSCC = cast<VariableConstructionContext>(CC);
+ const auto *DS = DSCC->getDeclStmt();
+ const auto *Var = cast<VarDecl>(DS->getSingleDecl());
+ SVal LValue = State->getLValue(Var, LCtx);
+ QualType Ty = Var->getType();
+ LValue =
+ makeZeroElementRegion(State, LValue, Ty, CallOpts.IsArrayCtorOrDtor);
+ State =
+ addObjectUnderConstruction(State, DSCC->getDeclStmt(), LCtx, LValue);
+ return std::make_pair(State, LValue);
+ }
+ case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
+ case ConstructionContext::SimpleConstructorInitializerKind: {
+ const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
+ const auto *Init = ICC->getCXXCtorInitializer();
+ assert(Init->isAnyMemberInitializer());
+ const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
+ Loc ThisPtr =
+ SVB.getCXXThis(CurCtor, LCtx->getStackFrame());
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ const ValueDecl *Field;
+ SVal FieldVal;
+ if (Init->isIndirectMemberInitializer()) {
+ Field = Init->getIndirectMember();
+ FieldVal = State->getLValue(Init->getIndirectMember(), ThisVal);
+ } else {
+ Field = Init->getMember();
+ FieldVal = State->getLValue(Init->getMember(), ThisVal);
+ }
+
+ QualType Ty = Field->getType();
+ FieldVal = makeZeroElementRegion(State, FieldVal, Ty,
+ CallOpts.IsArrayCtorOrDtor);
+ State = addObjectUnderConstruction(State, Init, LCtx, FieldVal);
+ return std::make_pair(State, FieldVal);
+ }
+ case ConstructionContext::NewAllocatedObjectKind: {
+ if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) {
+ const auto *NECC = cast<NewAllocatedObjectConstructionContext>(CC);
+ const auto *NE = NECC->getCXXNewExpr();
+ SVal V = *getObjectUnderConstruction(State, NE, LCtx);
+ if (const SubRegion *MR =
+ dyn_cast_or_null<SubRegion>(V.getAsRegion())) {
+ if (NE->isArray()) {
+ // TODO: In fact, we need to call the constructor for every
+ // allocated element, not just the first one!
+ CallOpts.IsArrayCtorOrDtor = true;
+ return std::make_pair(
+ State, loc::MemRegionVal(getStoreManager().GetElementZeroRegion(
+ MR, NE->getType()->getPointeeType())));
+ }
+ return std::make_pair(State, V);
+ }
+ // TODO: Detect when the allocator returns a null pointer.
+ // Constructor shall not be called in this case.
+ }
+ break;
+ }
+ case ConstructionContext::SimpleReturnedValueKind:
+ case ConstructionContext::CXX17ElidedCopyReturnedValueKind: {
+ // The temporary is to be managed by the parent stack frame.
+ // So build it in the parent stack frame if we're not in the
+ // top frame of the analysis.
+ const StackFrameContext *SFC = LCtx->getStackFrame();
+ if (const LocationContext *CallerLCtx = SFC->getParent()) {
+ auto RTC = (*SFC->getCallSiteBlock())[SFC->getIndex()]
+ .getAs<CFGCXXRecordTypedCall>();
+ if (!RTC) {
+ // We were unable to find the correct construction context for the
+ // call in the parent stack frame. This is equivalent to not being
+ // able to find construction context at all.
+ break;
+ }
+ return prepareForObjectConstruction(
+ cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
+ RTC->getConstructionContext(), CallOpts);
+ } else {
+ // We are on the top frame of the analysis. We do not know where is the
+ // object returned to. Conjure a symbolic region for the return value.
+ // TODO: We probably need a new MemRegion kind to represent the storage
+ // of that SymbolicRegion, so that we cound produce a fancy symbol
+ // instead of an anonymous conjured symbol.
+ // TODO: Do we need to track the region to avoid having it dead
+ // too early? It does die too early, at least in C++17, but because
+ // putting anything into a SymbolicRegion causes an immediate escape,
+ // it doesn't cause any leak false positives.
+ const auto *RCC = cast<ReturnedValueConstructionContext>(CC);
+ // Make sure that this doesn't coincide with any other symbol
+ // conjured for the returned expression.
+ static const int TopLevelSymRegionTag = 0;
+ const Expr *RetE = RCC->getReturnStmt()->getRetValue();
+ assert(RetE && "Void returns should not have a construction context");
+ QualType ReturnTy = RetE->getType();
+ QualType RegionTy = ACtx.getPointerType(ReturnTy);
+ SVal V = SVB.conjureSymbolVal(&TopLevelSymRegionTag, RetE, SFC,
+ RegionTy, currBldrCtx->blockCount());
+ return std::make_pair(State, V);
+ }
+ llvm_unreachable("Unhandled return value construction context!");
+ }
+ case ConstructionContext::ElidedTemporaryObjectKind: {
+ assert(AMgr.getAnalyzerOptions().ShouldElideConstructors);
+ const auto *TCC = cast<ElidedTemporaryObjectConstructionContext>(CC);
+ const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
+ const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
+ const CXXConstructExpr *CE = TCC->getConstructorAfterElision();
+
+ // Support pre-C++17 copy elision. We'll have the elidable copy
+ // constructor in the AST and in the CFG, but we'll skip it
+ // and construct directly into the final object. This call
+ // also sets the CallOpts flags for us.
+ SVal V;
+ // If the elided copy/move constructor is not supported, there's still
+ // benefit in trying to model the non-elided constructor.
+ // Stash our state before trying to elide, as it'll get overwritten.
+ ProgramStateRef PreElideState = State;
+ EvalCallOptions PreElideCallOpts = CallOpts;
+
+ std::tie(State, V) = prepareForObjectConstruction(
+ CE, State, LCtx, TCC->getConstructionContextAfterElision(), CallOpts);
+
+ // FIXME: This definition of "copy elision has not failed" is unreliable.
+ // It doesn't indicate that the constructor will actually be inlined
+ // later; it is still up to evalCall() to decide.
+ if (!CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion) {
+ // Remember that we've elided the constructor.
+ State = addObjectUnderConstruction(State, CE, LCtx, V);
+
+ // Remember that we've elided the destructor.
+ if (BTE)
+ State = elideDestructor(State, BTE, LCtx);
+
+ // Instead of materialization, shamelessly return
+ // the final object destination.
+ if (MTE)
+ State = addObjectUnderConstruction(State, MTE, LCtx, V);
+
+ return std::make_pair(State, V);
+ } else {
+ // Copy elision failed. Revert the changes and proceed as if we have
+ // a simple temporary.
+ State = PreElideState;
+ CallOpts = PreElideCallOpts;
+ }
+ LLVM_FALLTHROUGH;
+ }
+ case ConstructionContext::SimpleTemporaryObjectKind: {
+ const auto *TCC = cast<TemporaryObjectConstructionContext>(CC);
+ const CXXBindTemporaryExpr *BTE = TCC->getCXXBindTemporaryExpr();
+ const MaterializeTemporaryExpr *MTE = TCC->getMaterializedTemporaryExpr();
+ SVal V = UnknownVal();
+
+ if (MTE) {
+ if (const ValueDecl *VD = MTE->getExtendingDecl()) {
+ assert(MTE->getStorageDuration() != SD_FullExpression);
+ if (!VD->getType()->isReferenceType()) {
+ // We're lifetime-extended by a surrounding aggregate.
+ // Automatic destructors aren't quite working in this case
+ // on the CFG side. We should warn the caller about that.
+ // FIXME: Is there a better way to retrieve this information from
+ // the MaterializeTemporaryExpr?
+ CallOpts.IsTemporaryLifetimeExtendedViaAggregate = true;
+ }
+ }
+
+ if (MTE->getStorageDuration() == SD_Static ||
+ MTE->getStorageDuration() == SD_Thread)
+ V = loc::MemRegionVal(MRMgr.getCXXStaticTempObjectRegion(E));
+ }
+
+ if (V.isUnknown())
+ V = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
+
+ if (BTE)
+ State = addObjectUnderConstruction(State, BTE, LCtx, V);
+
+ if (MTE)
+ State = addObjectUnderConstruction(State, MTE, LCtx, V);
+
+ CallOpts.IsTemporaryCtorOrDtor = true;
+ return std::make_pair(State, V);
+ }
+ case ConstructionContext::ArgumentKind: {
+ // Arguments are technically temporaries.
+ CallOpts.IsTemporaryCtorOrDtor = true;
+
+ const auto *ACC = cast<ArgumentConstructionContext>(CC);
+ const Expr *E = ACC->getCallLikeExpr();
+ unsigned Idx = ACC->getIndex();
+ const CXXBindTemporaryExpr *BTE = ACC->getCXXBindTemporaryExpr();
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ SVal V = UnknownVal();
+ auto getArgLoc = [&](CallEventRef<> Caller) -> Optional<SVal> {
+ const LocationContext *FutureSFC = Caller->getCalleeStackFrame();
+ // Return early if we are unable to reliably foresee
+ // the future stack frame.
+ if (!FutureSFC)
+ return None;
+
+ // This should be equivalent to Caller->getDecl() for now, but
+ // FutureSFC->getDecl() is likely to support better stuff (like
+ // virtual functions) earlier.
+ const Decl *CalleeD = FutureSFC->getDecl();
+
+ // FIXME: Support for variadic arguments is not implemented here yet.
+ if (CallEvent::isVariadic(CalleeD))
+ return None;
+
+ // Operator arguments do not correspond to operator parameters
+ // because this-argument is implemented as a normal argument in
+ // operator call expressions but not in operator declarations.
+ const VarRegion *VR = Caller->getParameterLocation(
+ *Caller->getAdjustedParameterIndex(Idx));
+ if (!VR)
+ return None;
+
+ return loc::MemRegionVal(VR);
+ };
+
+ if (const auto *CE = dyn_cast<CallExpr>(E)) {
+ CallEventRef<> Caller = CEMgr.getSimpleCall(CE, State, LCtx);
+ if (auto OptV = getArgLoc(Caller))
+ V = *OptV;
+ else
+ break;
+ State = addObjectUnderConstruction(State, {CE, Idx}, LCtx, V);
+ } else if (const auto *CCE = dyn_cast<CXXConstructExpr>(E)) {
+ // Don't bother figuring out the target region for the future
+ // constructor because we won't need it.
+ CallEventRef<> Caller =
+ CEMgr.getCXXConstructorCall(CCE, /*Target=*/nullptr, State, LCtx);
+ if (auto OptV = getArgLoc(Caller))
+ V = *OptV;
+ else
+ break;
+ State = addObjectUnderConstruction(State, {CCE, Idx}, LCtx, V);
+ } else if (const auto *ME = dyn_cast<ObjCMessageExpr>(E)) {
+ CallEventRef<> Caller = CEMgr.getObjCMethodCall(ME, State, LCtx);
+ if (auto OptV = getArgLoc(Caller))
+ V = *OptV;
+ else
+ break;
+ State = addObjectUnderConstruction(State, {ME, Idx}, LCtx, V);
+ }
+
+ assert(!V.isUnknown());
+
+ if (BTE)
+ State = addObjectUnderConstruction(State, BTE, LCtx, V);
+
+ return std::make_pair(State, V);
+ }
+ }
+ }
+ // If we couldn't find an existing region to construct into, assume we're
+ // constructing a temporary. Notify the caller of our failure.
+ CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
+ return std::make_pair(
+ State, loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx)));
+}
+
+void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &destNodes) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef State = Pred->getState();
+
+ SVal Target = UnknownVal();
+
+ if (Optional<SVal> ElidedTarget =
+ getObjectUnderConstruction(State, CE, LCtx)) {
+ // We've previously modeled an elidable constructor by pretending that it in
+ // fact constructs into the correct target. This constructor can therefore
+ // be skipped.
+ Target = *ElidedTarget;
+ StmtNodeBuilder Bldr(Pred, destNodes, *currBldrCtx);
+ State = finishObjectConstruction(State, CE, LCtx);
+ if (auto L = Target.getAs<Loc>())
+ State = State->BindExpr(CE, LCtx, State->getSVal(*L, CE->getType()));
+ Bldr.generateNode(CE, Pred, State);
+ return;
+ }
+
+ // FIXME: Handle arrays, which run the same constructor for every element.
+ // For now, we just run the first constructor (which should still invalidate
+ // the entire array).
+
+ EvalCallOptions CallOpts;
+ auto C = getCurrentCFGElement().getAs<CFGConstructor>();
+ assert(C || getCurrentCFGElement().getAs<CFGStmt>());
+ const ConstructionContext *CC = C ? C->getConstructionContext() : nullptr;
+
+ switch (CE->getConstructionKind()) {
+ case CXXConstructExpr::CK_Complete: {
+ std::tie(State, Target) =
+ prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts);
+ break;
+ }
+ case CXXConstructExpr::CK_VirtualBase:
+ // Make sure we are not calling virtual base class initializers twice.
+ // Only the most-derived object should initialize virtual base classes.
+ if (const Stmt *Outer = LCtx->getStackFrame()->getCallSite()) {
+ const CXXConstructExpr *OuterCtor = dyn_cast<CXXConstructExpr>(Outer);
+ if (OuterCtor) {
+ switch (OuterCtor->getConstructionKind()) {
+ case CXXConstructExpr::CK_NonVirtualBase:
+ case CXXConstructExpr::CK_VirtualBase:
+ // Bail out!
+ destNodes.Add(Pred);
+ return;
+ case CXXConstructExpr::CK_Complete:
+ case CXXConstructExpr::CK_Delegating:
+ break;
+ }
+ }
+ }
+ LLVM_FALLTHROUGH;
+ case CXXConstructExpr::CK_NonVirtualBase:
+ // In C++17, classes with non-virtual bases may be aggregates, so they would
+ // be initialized as aggregates without a constructor call, so we may have
+ // a base class constructed directly into an initializer list without
+ // having the derived-class constructor call on the previous stack frame.
+ // Initializer lists may be nested into more initializer lists that
+ // correspond to surrounding aggregate initializations.
+ // FIXME: For now this code essentially bails out. We need to find the
+ // correct target region and set it.
+ // FIXME: Instead of relying on the ParentMap, we should have the
+ // trigger-statement (InitListExpr in this case) passed down from CFG or
+ // otherwise always available during construction.
+ if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(CE))) {
+ MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
+ Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(CE, LCtx));
+ CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
+ break;
+ }
+ LLVM_FALLTHROUGH;
+ case CXXConstructExpr::CK_Delegating: {
+ const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
+ Loc ThisPtr = getSValBuilder().getCXXThis(CurCtor,
+ LCtx->getStackFrame());
+ SVal ThisVal = State->getSVal(ThisPtr);
+
+ if (CE->getConstructionKind() == CXXConstructExpr::CK_Delegating) {
+ Target = ThisVal;
+ } else {
+ // Cast to the base type.
+ bool IsVirtual =
+ (CE->getConstructionKind() == CXXConstructExpr::CK_VirtualBase);
+ SVal BaseVal = getStoreManager().evalDerivedToBase(ThisVal, CE->getType(),
+ IsVirtual);
+ Target = BaseVal;
+ }
+ break;
+ }
+ }
+
+ if (State != Pred->getState()) {
+ static SimpleProgramPointTag T("ExprEngine",
+ "Prepare for object construction");
+ ExplodedNodeSet DstPrepare;
+ StmtNodeBuilder BldrPrepare(Pred, DstPrepare, *currBldrCtx);
+ BldrPrepare.generateNode(CE, Pred, State, &T, ProgramPoint::PreStmtKind);
+ assert(DstPrepare.size() <= 1);
+ if (DstPrepare.size() == 0)
+ return;
+ Pred = *BldrPrepare.begin();
+ }
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXConstructorCall> Call =
+ CEMgr.getCXXConstructorCall(CE, Target.getAsRegion(), State, LCtx);
+
+ ExplodedNodeSet DstPreVisit;
+ getCheckerManager().runCheckersForPreStmt(DstPreVisit, Pred, CE, *this);
+
+ // FIXME: Is it possible and/or useful to do this before PreStmt?
+ ExplodedNodeSet PreInitialized;
+ {
+ StmtNodeBuilder Bldr(DstPreVisit, PreInitialized, *currBldrCtx);
+ for (ExplodedNodeSet::iterator I = DstPreVisit.begin(),
+ E = DstPreVisit.end();
+ I != E; ++I) {
+ ProgramStateRef State = (*I)->getState();
+ if (CE->requiresZeroInitialization()) {
+ // FIXME: Once we properly handle constructors in new-expressions, we'll
+ // need to invalidate the region before setting a default value, to make
+ // sure there aren't any lingering bindings around. This probably needs
+ // to happen regardless of whether or not the object is zero-initialized
+ // to handle random fields of a placement-initialized object picking up
+ // old bindings. We might only want to do it when we need to, though.
+ // FIXME: This isn't actually correct for arrays -- we need to zero-
+ // initialize the entire array, not just the first element -- but our
+ // handling of arrays everywhere else is weak as well, so this shouldn't
+ // actually make things worse. Placement new makes this tricky as well,
+ // since it's then possible to be initializing one part of a multi-
+ // dimensional array.
+ State = State->bindDefaultZero(Target, LCtx);
+ }
+
+ Bldr.generateNode(CE, *I, State, /*tag=*/nullptr,
+ ProgramPoint::PreStmtKind);
+ }
+ }
+
+ ExplodedNodeSet DstPreCall;
+ getCheckerManager().runCheckersForPreCall(DstPreCall, PreInitialized,
+ *Call, *this);
+
+ ExplodedNodeSet DstEvaluated;
+ StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);
+
+ if (CE->getConstructor()->isTrivial() &&
+ CE->getConstructor()->isCopyOrMoveConstructor() &&
+ !CallOpts.IsArrayCtorOrDtor) {
+ // FIXME: Handle other kinds of trivial constructors as well.
+ for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
+ I != E; ++I)
+ performTrivialCopy(Bldr, *I, *Call);
+
+ } else {
+ for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
+ I != E; ++I)
+ defaultEvalCall(Bldr, *I, *Call, CallOpts);
+ }
+
+ // If the CFG was constructed without elements for temporary destructors
+ // and the just-called constructor created a temporary object then
+ // stop exploration if the temporary object has a noreturn constructor.
+ // This can lose coverage because the destructor, if it were present
+ // in the CFG, would be called at the end of the full expression or
+ // later (for life-time extended temporaries) -- but avoids infeasible
+ // paths when no-return temporary destructors are used for assertions.
+ const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext();
+ if (!ADC->getCFGBuildOptions().AddTemporaryDtors) {
+ const MemRegion *Target = Call->getCXXThisVal().getAsRegion();
+ if (Target && isa<CXXTempObjectRegion>(Target) &&
+ Call->getDecl()->getParent()->isAnyDestructorNoReturn()) {
+
+ // If we've inlined the constructor, then DstEvaluated would be empty.
+ // In this case we still want a sink, which could be implemented
+ // in processCallExit. But we don't have that implemented at the moment,
+ // so if you hit this assertion, see if you can avoid inlining
+ // the respective constructor when analyzer-config cfg-temporary-dtors
+ // is set to false.
+ // Otherwise there's nothing wrong with inlining such constructor.
+ assert(!DstEvaluated.empty() &&
+ "We should not have inlined this constructor!");
+
+ for (ExplodedNode *N : DstEvaluated) {
+ Bldr.generateSink(CE, N, N->getState());
+ }
+
+ // There is no need to run the PostCall and PostStmt checker
+ // callbacks because we just generated sinks on all nodes in th
+ // frontier.
+ return;
+ }
+ }
+
+ ExplodedNodeSet DstPostArgumentCleanup;
+ for (auto I : DstEvaluated)
+ finishArgumentConstruction(DstPostArgumentCleanup, I, *Call);
+
+ // If there were other constructors called for object-type arguments
+ // of this constructor, clean them up.
+ ExplodedNodeSet DstPostCall;
+ getCheckerManager().runCheckersForPostCall(DstPostCall,
+ DstPostArgumentCleanup,
+ *Call, *this);
+ getCheckerManager().runCheckersForPostStmt(destNodes, DstPostCall, CE, *this);
+}
+
+void ExprEngine::VisitCXXDestructor(QualType ObjectType,
+ const MemRegion *Dest,
+ const Stmt *S,
+ bool IsBaseDtor,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst,
+ const EvalCallOptions &CallOpts) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ ProgramStateRef State = Pred->getState();
+
+ const CXXRecordDecl *RecordDecl = ObjectType->getAsCXXRecordDecl();
+ assert(RecordDecl && "Only CXXRecordDecls should have destructors");
+ const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor();
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXDestructorCall> Call =
+ CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx);
+
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ Call->getSourceRange().getBegin(),
+ "Error evaluating destructor");
+
+ ExplodedNodeSet DstPreCall;
+ getCheckerManager().runCheckersForPreCall(DstPreCall, Pred,
+ *Call, *this);
+
+ ExplodedNodeSet DstInvalidated;
+ StmtNodeBuilder Bldr(DstPreCall, DstInvalidated, *currBldrCtx);
+ for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
+ I != E; ++I)
+ defaultEvalCall(Bldr, *I, *Call, CallOpts);
+
+ ExplodedNodeSet DstPostCall;
+ getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated,
+ *Call, *this);
+}
+
+void ExprEngine::VisitCXXNewAllocatorCall(const CXXNewExpr *CNE,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ ProgramStateRef State = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ PrettyStackTraceLoc CrashInfo(getContext().getSourceManager(),
+ CNE->getBeginLoc(),
+ "Error evaluating New Allocator Call");
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXAllocatorCall> Call =
+ CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
+
+ ExplodedNodeSet DstPreCall;
+ getCheckerManager().runCheckersForPreCall(DstPreCall, Pred,
+ *Call, *this);
+
+ ExplodedNodeSet DstPostCall;
+ StmtNodeBuilder CallBldr(DstPreCall, DstPostCall, *currBldrCtx);
+ for (auto I : DstPreCall) {
+ // FIXME: Provide evalCall for checkers?
+ defaultEvalCall(CallBldr, I, *Call);
+ }
+ // If the call is inlined, DstPostCall will be empty and we bail out now.
+
+ // Store return value of operator new() for future use, until the actual
+ // CXXNewExpr gets processed.
+ ExplodedNodeSet DstPostValue;
+ StmtNodeBuilder ValueBldr(DstPostCall, DstPostValue, *currBldrCtx);
+ for (auto I : DstPostCall) {
+ // FIXME: Because CNE serves as the "call site" for the allocator (due to
+ // lack of a better expression in the AST), the conjured return value symbol
+ // is going to be of the same type (C++ object pointer type). Technically
+ // this is not correct because the operator new's prototype always says that
+ // it returns a 'void *'. So we should change the type of the symbol,
+ // and then evaluate the cast over the symbolic pointer from 'void *' to
+ // the object pointer type. But without changing the symbol's type it
+ // is breaking too much to evaluate the no-op symbolic cast over it, so we
+ // skip it for now.
+ ProgramStateRef State = I->getState();
+ SVal RetVal = State->getSVal(CNE, LCtx);
+
+ // If this allocation function is not declared as non-throwing, failures
+ // /must/ be signalled by exceptions, and thus the return value will never
+ // be NULL. -fno-exceptions does not influence this semantics.
+ // FIXME: GCC has a -fcheck-new option, which forces it to consider the case
+ // where new can return NULL. If we end up supporting that option, we can
+ // consider adding a check for it here.
+ // C++11 [basic.stc.dynamic.allocation]p3.
+ if (const FunctionDecl *FD = CNE->getOperatorNew()) {
+ QualType Ty = FD->getType();
+ if (const auto *ProtoType = Ty->getAs<FunctionProtoType>())
+ if (!ProtoType->isNothrow())
+ State = State->assume(RetVal.castAs<DefinedOrUnknownSVal>(), true);
+ }
+
+ ValueBldr.generateNode(
+ CNE, I, addObjectUnderConstruction(State, CNE, LCtx, RetVal));
+ }
+
+ ExplodedNodeSet DstPostPostCallCallback;
+ getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
+ DstPostValue, *Call, *this);
+ for (auto I : DstPostPostCallCallback) {
+ getCheckerManager().runCheckersForNewAllocator(
+ CNE, *getObjectUnderConstruction(I->getState(), CNE, LCtx), Dst, I,
+ *this);
+ }
+}
+
+void ExprEngine::VisitCXXNewExpr(const CXXNewExpr *CNE, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ // FIXME: Much of this should eventually migrate to CXXAllocatorCall.
+ // Also, we need to decide how allocators actually work -- they're not
+ // really part of the CXXNewExpr because they happen BEFORE the
+ // CXXConstructExpr subexpression. See PR12014 for some discussion.
+
+ unsigned blockCount = currBldrCtx->blockCount();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal symVal = UnknownVal();
+ FunctionDecl *FD = CNE->getOperatorNew();
+
+ bool IsStandardGlobalOpNewFunction =
+ FD->isReplaceableGlobalAllocationFunction();
+
+ ProgramStateRef State = Pred->getState();
+
+ // Retrieve the stored operator new() return value.
+ if (AMgr.getAnalyzerOptions().MayInlineCXXAllocator) {
+ symVal = *getObjectUnderConstruction(State, CNE, LCtx);
+ State = finishObjectConstruction(State, CNE, LCtx);
+ }
+
+ // We assume all standard global 'operator new' functions allocate memory in
+ // heap. We realize this is an approximation that might not correctly model
+ // a custom global allocator.
+ if (symVal.isUnknown()) {
+ if (IsStandardGlobalOpNewFunction)
+ symVal = svalBuilder.getConjuredHeapSymbolVal(CNE, LCtx, blockCount);
+ else
+ symVal = svalBuilder.conjureSymbolVal(nullptr, CNE, LCtx, CNE->getType(),
+ blockCount);
+ }
+
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<CXXAllocatorCall> Call =
+ CEMgr.getCXXAllocatorCall(CNE, State, LCtx);
+
+ if (!AMgr.getAnalyzerOptions().MayInlineCXXAllocator) {
+ // Invalidate placement args.
+ // FIXME: Once we figure out how we want allocators to work,
+ // we should be using the usual pre-/(default-)eval-/post-call checks here.
+ State = Call->invalidateRegions(blockCount);
+ if (!State)
+ return;
+
+ // If this allocation function is not declared as non-throwing, failures
+ // /must/ be signalled by exceptions, and thus the return value will never
+ // be NULL. -fno-exceptions does not influence this semantics.
+ // FIXME: GCC has a -fcheck-new option, which forces it to consider the case
+ // where new can return NULL. If we end up supporting that option, we can
+ // consider adding a check for it here.
+ // C++11 [basic.stc.dynamic.allocation]p3.
+ if (FD) {
+ QualType Ty = FD->getType();
+ if (const auto *ProtoType = Ty->getAs<FunctionProtoType>())
+ if (!ProtoType->isNothrow())
+ if (auto dSymVal = symVal.getAs<DefinedOrUnknownSVal>())
+ State = State->assume(*dSymVal, true);
+ }
+ }
+
+ StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+
+ SVal Result = symVal;
+
+ if (CNE->isArray()) {
+ // FIXME: allocating an array requires simulating the constructors.
+ // For now, just return a symbolicated region.
+ if (const SubRegion *NewReg =
+ dyn_cast_or_null<SubRegion>(symVal.getAsRegion())) {
+ QualType ObjTy = CNE->getType()->getAs<PointerType>()->getPointeeType();
+ const ElementRegion *EleReg =
+ getStoreManager().GetElementZeroRegion(NewReg, ObjTy);
+ Result = loc::MemRegionVal(EleReg);
+ }
+ State = State->BindExpr(CNE, Pred->getLocationContext(), Result);
+ Bldr.generateNode(CNE, Pred, State);
+ return;
+ }
+
+ // FIXME: Once we have proper support for CXXConstructExprs inside
+ // CXXNewExpr, we need to make sure that the constructed object is not
+ // immediately invalidated here. (The placement call should happen before
+ // the constructor call anyway.)
+ if (FD && FD->isReservedGlobalPlacementOperator()) {
+ // Non-array placement new should always return the placement location.
+ SVal PlacementLoc = State->getSVal(CNE->getPlacementArg(0), LCtx);
+ Result = svalBuilder.evalCast(PlacementLoc, CNE->getType(),
+ CNE->getPlacementArg(0)->getType());
+ }
+
+ // Bind the address of the object, then check to see if we cached out.
+ State = State->BindExpr(CNE, LCtx, Result);
+ ExplodedNode *NewN = Bldr.generateNode(CNE, Pred, State);
+ if (!NewN)
+ return;
+
+ // If the type is not a record, we won't have a CXXConstructExpr as an
+ // initializer. Copy the value over.
+ if (const Expr *Init = CNE->getInitializer()) {
+ if (!isa<CXXConstructExpr>(Init)) {
+ assert(Bldr.getResults().size() == 1);
+ Bldr.takeNodes(NewN);
+ evalBind(Dst, CNE, NewN, Result, State->getSVal(Init, LCtx),
+ /*FirstInit=*/IsStandardGlobalOpNewFunction);
+ }
+ }
+}
+
+void ExprEngine::VisitCXXDeleteExpr(const CXXDeleteExpr *CDE,
+ ExplodedNode *Pred, ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ ProgramStateRef state = Pred->getState();
+ Bldr.generateNode(CDE, Pred, state);
+}
+
+void ExprEngine::VisitCXXCatchStmt(const CXXCatchStmt *CS,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ const VarDecl *VD = CS->getExceptionDecl();
+ if (!VD) {
+ Dst.Add(Pred);
+ return;
+ }
+
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal V = svalBuilder.conjureSymbolVal(CS, LCtx, VD->getType(),
+ currBldrCtx->blockCount());
+ ProgramStateRef state = Pred->getState();
+ state = state->bindLoc(state->getLValue(VD, LCtx), V, LCtx);
+
+ StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ Bldr.generateNode(CS, Pred, state);
+}
+
+void ExprEngine::VisitCXXThisExpr(const CXXThisExpr *TE, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ StmtNodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+
+ // Get the this object region from StoreManager.
+ const LocationContext *LCtx = Pred->getLocationContext();
+ const MemRegion *R =
+ svalBuilder.getRegionManager().getCXXThisRegion(
+ getContext().getCanonicalType(TE->getType()),
+ LCtx);
+
+ ProgramStateRef state = Pred->getState();
+ SVal V = state->getSVal(loc::MemRegionVal(R));
+ Bldr.generateNode(TE, Pred, state->BindExpr(TE, LCtx, V));
+}
+
+void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ const LocationContext *LocCtxt = Pred->getLocationContext();
+
+ // Get the region of the lambda itself.
+ const MemRegion *R = svalBuilder.getRegionManager().getCXXTempObjectRegion(
+ LE, LocCtxt);
+ SVal V = loc::MemRegionVal(R);
+
+ ProgramStateRef State = Pred->getState();
+
+ // If we created a new MemRegion for the lambda, we should explicitly bind
+ // the captures.
+ CXXRecordDecl::field_iterator CurField = LE->getLambdaClass()->field_begin();
+ for (LambdaExpr::const_capture_init_iterator i = LE->capture_init_begin(),
+ e = LE->capture_init_end();
+ i != e; ++i, ++CurField) {
+ FieldDecl *FieldForCapture = *CurField;
+ SVal FieldLoc = State->getLValue(FieldForCapture, V);
+
+ SVal InitVal;
+ if (!FieldForCapture->hasCapturedVLAType()) {
+ Expr *InitExpr = *i;
+ assert(InitExpr && "Capture missing initialization expression");
+ InitVal = State->getSVal(InitExpr, LocCtxt);
+ } else {
+ // The field stores the length of a captured variable-length array.
+ // These captures don't have initialization expressions; instead we
+ // get the length from the VLAType size expression.
+ Expr *SizeExpr = FieldForCapture->getCapturedVLAType()->getSizeExpr();
+ InitVal = State->getSVal(SizeExpr, LocCtxt);
+ }
+
+ State = State->bindLoc(FieldLoc, InitVal, LocCtxt);
+ }
+
+ // Decay the Loc into an RValue, because there might be a
+ // MaterializeTemporaryExpr node above this one which expects the bound value
+ // to be an RValue.
+ SVal LambdaRVal = State->getSVal(R);
+
+ ExplodedNodeSet Tmp;
+ StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx);
+ // FIXME: is this the right program point kind?
+ Bldr.generateNode(LE, Pred,
+ State->BindExpr(LE, LocCtxt, LambdaRVal),
+ nullptr, ProgramPoint::PostLValueKind);
+
+ // FIXME: Move all post/pre visits to ::Visit().
+ getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
new file mode 100644
index 000000000000..758195d8d911
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -0,0 +1,1088 @@
+//=-- ExprEngineCallAndReturn.cpp - Support for call/return -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ExprEngine's support for calls and returns.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "PrettyStackTraceLocationContext.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/Analysis/Analyses/LiveVariables.h"
+#include "clang/Analysis/ConstructionContext.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/SaveAndRestore.h"
+
+using namespace clang;
+using namespace ento;
+
+#define DEBUG_TYPE "ExprEngine"
+
+STATISTIC(NumOfDynamicDispatchPathSplits,
+ "The # of times we split the path due to imprecise dynamic dispatch info");
+
+STATISTIC(NumInlinedCalls,
+ "The # of times we inlined a call");
+
+STATISTIC(NumReachedInlineCountMax,
+ "The # of times we reached inline count maximum");
+
+void ExprEngine::processCallEnter(NodeBuilderContext& BC, CallEnter CE,
+ ExplodedNode *Pred) {
+ // Get the entry block in the CFG of the callee.
+ const StackFrameContext *calleeCtx = CE.getCalleeContext();
+ PrettyStackTraceLocationContext CrashInfo(calleeCtx);
+ const CFGBlock *Entry = CE.getEntry();
+
+ // Validate the CFG.
+ assert(Entry->empty());
+ assert(Entry->succ_size() == 1);
+
+ // Get the solitary successor.
+ const CFGBlock *Succ = *(Entry->succ_begin());
+
+ // Construct an edge representing the starting location in the callee.
+ BlockEdge Loc(Entry, Succ, calleeCtx);
+
+ ProgramStateRef state = Pred->getState();
+
+ // Construct a new node, notify checkers that analysis of the function has
+ // begun, and add the resultant nodes to the worklist.
+ bool isNew;
+ ExplodedNode *Node = G.getNode(Loc, state, false, &isNew);
+ Node->addPredecessor(Pred, G);
+ if (isNew) {
+ ExplodedNodeSet DstBegin;
+ processBeginOfFunction(BC, Node, DstBegin, Loc);
+ Engine.enqueue(DstBegin);
+ }
+}
+
+// Find the last statement on the path to the exploded node and the
+// corresponding Block.
+static std::pair<const Stmt*,
+ const CFGBlock*> getLastStmt(const ExplodedNode *Node) {
+ const Stmt *S = nullptr;
+ const CFGBlock *Blk = nullptr;
+ const StackFrameContext *SF = Node->getStackFrame();
+
+ // Back up through the ExplodedGraph until we reach a statement node in this
+ // stack frame.
+ while (Node) {
+ const ProgramPoint &PP = Node->getLocation();
+
+ if (PP.getStackFrame() == SF) {
+ if (Optional<StmtPoint> SP = PP.getAs<StmtPoint>()) {
+ S = SP->getStmt();
+ break;
+ } else if (Optional<CallExitEnd> CEE = PP.getAs<CallExitEnd>()) {
+ S = CEE->getCalleeContext()->getCallSite();
+ if (S)
+ break;
+
+ // If there is no statement, this is an implicitly-generated call.
+ // We'll walk backwards over it and then continue the loop to find
+ // an actual statement.
+ Optional<CallEnter> CE;
+ do {
+ Node = Node->getFirstPred();
+ CE = Node->getLocationAs<CallEnter>();
+ } while (!CE || CE->getCalleeContext() != CEE->getCalleeContext());
+
+ // Continue searching the graph.
+ } else if (Optional<BlockEdge> BE = PP.getAs<BlockEdge>()) {
+ Blk = BE->getSrc();
+ }
+ } else if (Optional<CallEnter> CE = PP.getAs<CallEnter>()) {
+ // If we reached the CallEnter for this function, it has no statements.
+ if (CE->getCalleeContext() == SF)
+ break;
+ }
+
+ if (Node->pred_empty())
+ return std::make_pair(nullptr, nullptr);
+
+ Node = *Node->pred_begin();
+ }
+
+ return std::make_pair(S, Blk);
+}
+
+/// Adjusts a return value when the called function's return type does not
+/// match the caller's expression type. This can happen when a dynamic call
+/// is devirtualized, and the overriding method has a covariant (more specific)
+/// return type than the parent's method. For C++ objects, this means we need
+/// to add base casts.
+static SVal adjustReturnValue(SVal V, QualType ExpectedTy, QualType ActualTy,
+ StoreManager &StoreMgr) {
+ // For now, the only adjustments we handle apply only to locations.
+ if (!V.getAs<Loc>())
+ return V;
+
+ // If the types already match, don't do any unnecessary work.
+ ExpectedTy = ExpectedTy.getCanonicalType();
+ ActualTy = ActualTy.getCanonicalType();
+ if (ExpectedTy == ActualTy)
+ return V;
+
+ // No adjustment is needed between Objective-C pointer types.
+ if (ExpectedTy->isObjCObjectPointerType() &&
+ ActualTy->isObjCObjectPointerType())
+ return V;
+
+ // C++ object pointers may need "derived-to-base" casts.
+ const CXXRecordDecl *ExpectedClass = ExpectedTy->getPointeeCXXRecordDecl();
+ const CXXRecordDecl *ActualClass = ActualTy->getPointeeCXXRecordDecl();
+ if (ExpectedClass && ActualClass) {
+ CXXBasePaths Paths(/*FindAmbiguities=*/true, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+ if (ActualClass->isDerivedFrom(ExpectedClass, Paths) &&
+ !Paths.isAmbiguous(ActualTy->getCanonicalTypeUnqualified())) {
+ return StoreMgr.evalDerivedToBase(V, Paths.front());
+ }
+ }
+
+ // Unfortunately, Objective-C does not enforce that overridden methods have
+ // covariant return types, so we can't assert that that never happens.
+ // Be safe and return UnknownVal().
+ return UnknownVal();
+}
+
+void ExprEngine::removeDeadOnEndOfFunction(NodeBuilderContext& BC,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ // Find the last statement in the function and the corresponding basic block.
+ const Stmt *LastSt = nullptr;
+ const CFGBlock *Blk = nullptr;
+ std::tie(LastSt, Blk) = getLastStmt(Pred);
+ if (!Blk || !LastSt) {
+ Dst.Add(Pred);
+ return;
+ }
+
+ // Here, we destroy the current location context. We use the current
+ // function's entire body as a diagnostic statement, with which the program
+ // point will be associated. However, we only want to use LastStmt as a
+ // reference for what to clean up if it's a ReturnStmt; otherwise, everything
+ // is dead.
+ SaveAndRestore<const NodeBuilderContext *> NodeContextRAII(currBldrCtx, &BC);
+ const LocationContext *LCtx = Pred->getLocationContext();
+ removeDead(Pred, Dst, dyn_cast<ReturnStmt>(LastSt), LCtx,
+ LCtx->getAnalysisDeclContext()->getBody(),
+ ProgramPoint::PostStmtPurgeDeadSymbolsKind);
+}
+
+static bool wasDifferentDeclUsedForInlining(CallEventRef<> Call,
+ const StackFrameContext *calleeCtx) {
+ const Decl *RuntimeCallee = calleeCtx->getDecl();
+ const Decl *StaticDecl = Call->getDecl();
+ assert(RuntimeCallee);
+ if (!StaticDecl)
+ return true;
+ return RuntimeCallee->getCanonicalDecl() != StaticDecl->getCanonicalDecl();
+}
+
+/// The call exit is simulated with a sequence of nodes, which occur between
+/// CallExitBegin and CallExitEnd. The following operations occur between the
+/// two program points:
+/// 1. CallExitBegin (triggers the start of call exit sequence)
+/// 2. Bind the return value
+/// 3. Run Remove dead bindings to clean up the dead symbols from the callee.
+/// 4. CallExitEnd (switch to the caller context)
+/// 5. PostStmt<CallExpr>
+void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
+ // Step 1 CEBNode was generated before the call.
+ PrettyStackTraceLocationContext CrashInfo(CEBNode->getLocationContext());
+ const StackFrameContext *calleeCtx = CEBNode->getStackFrame();
+
+ // The parent context might not be a stack frame, so make sure we
+ // look up the first enclosing stack frame.
+ const StackFrameContext *callerCtx =
+ calleeCtx->getParent()->getStackFrame();
+
+ const Stmt *CE = calleeCtx->getCallSite();
+ ProgramStateRef state = CEBNode->getState();
+ // Find the last statement in the function and the corresponding basic block.
+ const Stmt *LastSt = nullptr;
+ const CFGBlock *Blk = nullptr;
+ std::tie(LastSt, Blk) = getLastStmt(CEBNode);
+
+ // Generate a CallEvent /before/ cleaning the state, so that we can get the
+ // correct value for 'this' (if necessary).
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<> Call = CEMgr.getCaller(calleeCtx, state);
+
+ // Step 2: generate node with bound return value: CEBNode -> BindedRetNode.
+
+ // If the callee returns an expression, bind its value to CallExpr.
+ if (CE) {
+ if (const ReturnStmt *RS = dyn_cast_or_null<ReturnStmt>(LastSt)) {
+ const LocationContext *LCtx = CEBNode->getLocationContext();
+ SVal V = state->getSVal(RS, LCtx);
+
+ // Ensure that the return type matches the type of the returned Expr.
+ if (wasDifferentDeclUsedForInlining(Call, calleeCtx)) {
+ QualType ReturnedTy =
+ CallEvent::getDeclaredResultType(calleeCtx->getDecl());
+ if (!ReturnedTy.isNull()) {
+ if (const Expr *Ex = dyn_cast<Expr>(CE)) {
+ V = adjustReturnValue(V, Ex->getType(), ReturnedTy,
+ getStoreManager());
+ }
+ }
+ }
+
+ state = state->BindExpr(CE, callerCtx, V);
+ }
+
+ // Bind the constructed object value to CXXConstructExpr.
+ if (const CXXConstructExpr *CCE = dyn_cast<CXXConstructExpr>(CE)) {
+ loc::MemRegionVal This =
+ svalBuilder.getCXXThis(CCE->getConstructor()->getParent(), calleeCtx);
+ SVal ThisV = state->getSVal(This);
+ ThisV = state->getSVal(ThisV.castAs<Loc>());
+ state = state->BindExpr(CCE, callerCtx, ThisV);
+ }
+
+ if (const auto *CNE = dyn_cast<CXXNewExpr>(CE)) {
+ // We are currently evaluating a CXXNewAllocator CFGElement. It takes a
+ // while to reach the actual CXXNewExpr element from here, so keep the
+ // region for later use.
+ // Additionally cast the return value of the inlined operator new
+ // (which is of type 'void *') to the correct object type.
+ SVal AllocV = state->getSVal(CNE, callerCtx);
+ AllocV = svalBuilder.evalCast(
+ AllocV, CNE->getType(),
+ getContext().getPointerType(getContext().VoidTy));
+
+ state = addObjectUnderConstruction(state, CNE, calleeCtx->getParent(),
+ AllocV);
+ }
+ }
+
+ // Step 3: BindedRetNode -> CleanedNodes
+ // If we can find a statement and a block in the inlined function, run remove
+ // dead bindings before returning from the call. This is important to ensure
+ // that we report the issues such as leaks in the stack contexts in which
+ // they occurred.
+ ExplodedNodeSet CleanedNodes;
+ if (LastSt && Blk && AMgr.options.AnalysisPurgeOpt != PurgeNone) {
+ static SimpleProgramPointTag retValBind("ExprEngine", "Bind Return Value");
+ PostStmt Loc(LastSt, calleeCtx, &retValBind);
+ bool isNew;
+ ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew);
+ BindedRetNode->addPredecessor(CEBNode, G);
+ if (!isNew)
+ return;
+
+ NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode);
+ currBldrCtx = &Ctx;
+ // Here, we call the Symbol Reaper with 0 statement and callee location
+ // context, telling it to clean up everything in the callee's context
+ // (and its children). We use the callee's function body as a diagnostic
+ // statement, with which the program point will be associated.
+ removeDead(BindedRetNode, CleanedNodes, nullptr, calleeCtx,
+ calleeCtx->getAnalysisDeclContext()->getBody(),
+ ProgramPoint::PostStmtPurgeDeadSymbolsKind);
+ currBldrCtx = nullptr;
+ } else {
+ CleanedNodes.Add(CEBNode);
+ }
+
+ for (ExplodedNodeSet::iterator I = CleanedNodes.begin(),
+ E = CleanedNodes.end(); I != E; ++I) {
+
+ // Step 4: Generate the CallExit and leave the callee's context.
+ // CleanedNodes -> CEENode
+ CallExitEnd Loc(calleeCtx, callerCtx);
+ bool isNew;
+ ProgramStateRef CEEState = (*I == CEBNode) ? state : (*I)->getState();
+
+ ExplodedNode *CEENode = G.getNode(Loc, CEEState, false, &isNew);
+ CEENode->addPredecessor(*I, G);
+ if (!isNew)
+ return;
+
+ // Step 5: Perform the post-condition check of the CallExpr and enqueue the
+ // result onto the work list.
+ // CEENode -> Dst -> WorkList
+ NodeBuilderContext Ctx(Engine, calleeCtx->getCallSiteBlock(), CEENode);
+ SaveAndRestore<const NodeBuilderContext*> NBCSave(currBldrCtx,
+ &Ctx);
+ SaveAndRestore<unsigned> CBISave(currStmtIdx, calleeCtx->getIndex());
+
+ CallEventRef<> UpdatedCall = Call.cloneWithState(CEEState);
+
+ ExplodedNodeSet DstPostCall;
+ if (const CXXNewExpr *CNE = dyn_cast_or_null<CXXNewExpr>(CE)) {
+ ExplodedNodeSet DstPostPostCallCallback;
+ getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
+ CEENode, *UpdatedCall, *this,
+ /*WasInlined=*/true);
+ for (auto I : DstPostPostCallCallback) {
+ getCheckerManager().runCheckersForNewAllocator(
+ CNE,
+ *getObjectUnderConstruction(I->getState(), CNE,
+ calleeCtx->getParent()),
+ DstPostCall, I, *this,
+ /*WasInlined=*/true);
+ }
+ } else {
+ getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode,
+ *UpdatedCall, *this,
+ /*WasInlined=*/true);
+ }
+ ExplodedNodeSet Dst;
+ if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
+ getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg,
+ *this,
+ /*WasInlined=*/true);
+ } else if (CE &&
+ !(isa<CXXNewExpr>(CE) && // Called when visiting CXXNewExpr.
+ AMgr.getAnalyzerOptions().MayInlineCXXAllocator)) {
+ getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE,
+ *this, /*WasInlined=*/true);
+ } else {
+ Dst.insert(DstPostCall);
+ }
+
+ // Enqueue the next element in the block.
+ for (ExplodedNodeSet::iterator PSI = Dst.begin(), PSE = Dst.end();
+ PSI != PSE; ++PSI) {
+ Engine.getWorkList()->enqueue(*PSI, calleeCtx->getCallSiteBlock(),
+ calleeCtx->getIndex()+1);
+ }
+ }
+}
+
+void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx,
+ bool &IsRecursive, unsigned &StackDepth) {
+ IsRecursive = false;
+ StackDepth = 0;
+
+ while (LCtx) {
+ if (const StackFrameContext *SFC = dyn_cast<StackFrameContext>(LCtx)) {
+ const Decl *DI = SFC->getDecl();
+
+ // Mark recursive (and mutually recursive) functions and always count
+ // them when measuring the stack depth.
+ if (DI == D) {
+ IsRecursive = true;
+ ++StackDepth;
+ LCtx = LCtx->getParent();
+ continue;
+ }
+
+ // Do not count the small functions when determining the stack depth.
+ AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(DI);
+ const CFG *CalleeCFG = CalleeADC->getCFG();
+ if (CalleeCFG->getNumBlockIDs() > AMgr.options.AlwaysInlineSize)
+ ++StackDepth;
+ }
+ LCtx = LCtx->getParent();
+ }
+}
+
+// The GDM component containing the dynamic dispatch bifurcation info. When
+// the exact type of the receiver is not known, we want to explore both paths -
+// one on which we do inline it and the other one on which we don't. This is
+// done to ensure we do not drop coverage.
+// This is the map from the receiver region to a bool, specifying either we
+// consider this region's information precise or not along the given path.
+namespace {
+ enum DynamicDispatchMode {
+ DynamicDispatchModeInlined = 1,
+ DynamicDispatchModeConservative
+ };
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(DynamicDispatchBifurcationMap,
+ const MemRegion *, unsigned)
+
+bool ExprEngine::inlineCall(const CallEvent &Call, const Decl *D,
+ NodeBuilder &Bldr, ExplodedNode *Pred,
+ ProgramStateRef State) {
+ assert(D);
+
+ const LocationContext *CurLC = Pred->getLocationContext();
+ const StackFrameContext *CallerSFC = CurLC->getStackFrame();
+ const LocationContext *ParentOfCallee = CallerSFC;
+ if (Call.getKind() == CE_Block &&
+ !cast<BlockCall>(Call).isConversionFromLambda()) {
+ const BlockDataRegion *BR = cast<BlockCall>(Call).getBlockRegion();
+ assert(BR && "If we have the block definition we should have its region");
+ AnalysisDeclContext *BlockCtx = AMgr.getAnalysisDeclContext(D);
+ ParentOfCallee = BlockCtx->getBlockInvocationContext(CallerSFC,
+ cast<BlockDecl>(D),
+ BR);
+ }
+
+ // This may be NULL, but that's fine.
+ const Expr *CallE = Call.getOriginExpr();
+
+ // Construct a new stack frame for the callee.
+ AnalysisDeclContext *CalleeADC = AMgr.getAnalysisDeclContext(D);
+ const StackFrameContext *CalleeSFC =
+ CalleeADC->getStackFrame(ParentOfCallee, CallE,
+ currBldrCtx->getBlock(),
+ currStmtIdx);
+
+ CallEnter Loc(CallE, CalleeSFC, CurLC);
+
+ // Construct a new state which contains the mapping from actual to
+ // formal arguments.
+ State = State->enterStackFrame(Call, CalleeSFC);
+
+ bool isNew;
+ if (ExplodedNode *N = G.getNode(Loc, State, false, &isNew)) {
+ N->addPredecessor(Pred, G);
+ if (isNew)
+ Engine.getWorkList()->enqueue(N);
+ }
+
+ // If we decided to inline the call, the successor has been manually
+ // added onto the work list so remove it from the node builder.
+ Bldr.takeNodes(Pred);
+
+ NumInlinedCalls++;
+ Engine.FunctionSummaries->bumpNumTimesInlined(D);
+
+ // Mark the decl as visited.
+ if (VisitedCallees)
+ VisitedCallees->insert(D);
+
+ return true;
+}
+
+static ProgramStateRef getInlineFailedState(ProgramStateRef State,
+ const Stmt *CallE) {
+ const void *ReplayState = State->get<ReplayWithoutInlining>();
+ if (!ReplayState)
+ return nullptr;
+
+ assert(ReplayState == CallE && "Backtracked to the wrong call.");
+ (void)CallE;
+
+ return State->remove<ReplayWithoutInlining>();
+}
+
+void ExprEngine::VisitCallExpr(const CallExpr *CE, ExplodedNode *Pred,
+ ExplodedNodeSet &dst) {
+ // Perform the previsit of the CallExpr.
+ ExplodedNodeSet dstPreVisit;
+ getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, CE, *this);
+
+ // Get the call in its initial state. We use this as a template to perform
+ // all the checks.
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<> CallTemplate
+ = CEMgr.getSimpleCall(CE, Pred->getState(), Pred->getLocationContext());
+
+ // Evaluate the function call. We try each of the checkers
+ // to see if the can evaluate the function call.
+ ExplodedNodeSet dstCallEvaluated;
+ for (ExplodedNodeSet::iterator I = dstPreVisit.begin(), E = dstPreVisit.end();
+ I != E; ++I) {
+ evalCall(dstCallEvaluated, *I, *CallTemplate);
+ }
+
+ // Finally, perform the post-condition check of the CallExpr and store
+ // the created nodes in 'Dst'.
+ // Note that if the call was inlined, dstCallEvaluated will be empty.
+ // The post-CallExpr check will occur in processCallExit.
+ getCheckerManager().runCheckersForPostStmt(dst, dstCallEvaluated, CE,
+ *this);
+}
+
+ProgramStateRef ExprEngine::finishArgumentConstruction(ProgramStateRef State,
+ const CallEvent &Call) {
+ const Expr *E = Call.getOriginExpr();
+ // FIXME: Constructors to placement arguments of operator new
+ // are not supported yet.
+ if (!E || isa<CXXNewExpr>(E))
+ return State;
+
+ const LocationContext *LC = Call.getLocationContext();
+ for (unsigned CallI = 0, CallN = Call.getNumArgs(); CallI != CallN; ++CallI) {
+ unsigned I = Call.getASTArgumentIndex(CallI);
+ if (Optional<SVal> V =
+ getObjectUnderConstruction(State, {E, I}, LC)) {
+ SVal VV = *V;
+ (void)VV;
+ assert(cast<VarRegion>(VV.castAs<loc::MemRegionVal>().getRegion())
+ ->getStackFrame()->getParent()
+ ->getStackFrame() == LC->getStackFrame());
+ State = finishObjectConstruction(State, {E, I}, LC);
+ }
+ }
+
+ return State;
+}
+
+void ExprEngine::finishArgumentConstruction(ExplodedNodeSet &Dst,
+ ExplodedNode *Pred,
+ const CallEvent &Call) {
+ ProgramStateRef State = Pred->getState();
+ ProgramStateRef CleanedState = finishArgumentConstruction(State, Call);
+ if (CleanedState == State) {
+ Dst.insert(Pred);
+ return;
+ }
+
+ const Expr *E = Call.getOriginExpr();
+ const LocationContext *LC = Call.getLocationContext();
+ NodeBuilder B(Pred, Dst, *currBldrCtx);
+ static SimpleProgramPointTag Tag("ExprEngine",
+ "Finish argument construction");
+ PreStmt PP(E, LC, &Tag);
+ B.generateNode(PP, CleanedState, Pred);
+}
+
+void ExprEngine::evalCall(ExplodedNodeSet &Dst, ExplodedNode *Pred,
+ const CallEvent &Call) {
+ // WARNING: At this time, the state attached to 'Call' may be older than the
+ // state in 'Pred'. This is a minor optimization since CheckerManager will
+ // use an updated CallEvent instance when calling checkers, but if 'Call' is
+ // ever used directly in this function all callers should be updated to pass
+ // the most recent state. (It is probably not worth doing the work here since
+ // for some callers this will not be necessary.)
+
+ // Run any pre-call checks using the generic call interface.
+ ExplodedNodeSet dstPreVisit;
+ getCheckerManager().runCheckersForPreCall(dstPreVisit, Pred,
+ Call, *this);
+
+ // Actually evaluate the function call. We try each of the checkers
+ // to see if the can evaluate the function call, and get a callback at
+ // defaultEvalCall if all of them fail.
+ ExplodedNodeSet dstCallEvaluated;
+ getCheckerManager().runCheckersForEvalCall(dstCallEvaluated, dstPreVisit,
+ Call, *this);
+
+ // If there were other constructors called for object-type arguments
+ // of this call, clean them up.
+ ExplodedNodeSet dstArgumentCleanup;
+ for (auto I : dstCallEvaluated)
+ finishArgumentConstruction(dstArgumentCleanup, I, Call);
+
+ // Finally, run any post-call checks.
+ getCheckerManager().runCheckersForPostCall(Dst, dstArgumentCleanup,
+ Call, *this);
+}
+
+ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
+ const LocationContext *LCtx,
+ ProgramStateRef State) {
+ const Expr *E = Call.getOriginExpr();
+ if (!E)
+ return State;
+
+ // Some method families have known return values.
+ if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(&Call)) {
+ switch (Msg->getMethodFamily()) {
+ default:
+ break;
+ case OMF_autorelease:
+ case OMF_retain:
+ case OMF_self: {
+ // These methods return their receivers.
+ return State->BindExpr(E, LCtx, Msg->getReceiverSVal());
+ }
+ }
+ } else if (const CXXConstructorCall *C = dyn_cast<CXXConstructorCall>(&Call)){
+ SVal ThisV = C->getCXXThisVal();
+ ThisV = State->getSVal(ThisV.castAs<Loc>());
+ return State->BindExpr(E, LCtx, ThisV);
+ }
+
+ SVal R;
+ QualType ResultTy = Call.getResultType();
+ unsigned Count = currBldrCtx->blockCount();
+ if (auto RTC = getCurrentCFGElement().getAs<CFGCXXRecordTypedCall>()) {
+ // Conjure a temporary if the function returns an object by value.
+ SVal Target;
+ assert(RTC->getStmt() == Call.getOriginExpr());
+ EvalCallOptions CallOpts; // FIXME: We won't really need those.
+ std::tie(State, Target) =
+ prepareForObjectConstruction(Call.getOriginExpr(), State, LCtx,
+ RTC->getConstructionContext(), CallOpts);
+ assert(Target.getAsRegion());
+ // Invalidate the region so that it didn't look uninitialized. Don't notify
+ // the checkers.
+ State = State->invalidateRegions(Target.getAsRegion(), E, Count, LCtx,
+ /* CausedByPointerEscape=*/false, nullptr,
+ &Call, nullptr);
+
+ R = State->getSVal(Target.castAs<Loc>(), E->getType());
+ } else {
+ // Conjure a symbol if the return value is unknown.
+
+ // See if we need to conjure a heap pointer instead of
+ // a regular unknown pointer.
+ bool IsHeapPointer = false;
+ if (const auto *CNE = dyn_cast<CXXNewExpr>(E))
+ if (CNE->getOperatorNew()->isReplaceableGlobalAllocationFunction()) {
+ // FIXME: Delegate this to evalCall in MallocChecker?
+ IsHeapPointer = true;
+ }
+
+ R = IsHeapPointer ? svalBuilder.getConjuredHeapSymbolVal(E, LCtx, Count)
+ : svalBuilder.conjureSymbolVal(nullptr, E, LCtx, ResultTy,
+ Count);
+ }
+ return State->BindExpr(E, LCtx, R);
+}
+
+// Conservatively evaluate call by invalidating regions and binding
+// a conjured return value.
+void ExprEngine::conservativeEvalCall(const CallEvent &Call, NodeBuilder &Bldr,
+ ExplodedNode *Pred,
+ ProgramStateRef State) {
+ State = Call.invalidateRegions(currBldrCtx->blockCount(), State);
+ State = bindReturnValue(Call, Pred->getLocationContext(), State);
+
+ // And make the result node.
+ Bldr.generateNode(Call.getProgramPoint(), State, Pred);
+}
+
+ExprEngine::CallInlinePolicy
+ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
+ AnalyzerOptions &Opts,
+ const ExprEngine::EvalCallOptions &CallOpts) {
+ const LocationContext *CurLC = Pred->getLocationContext();
+ const StackFrameContext *CallerSFC = CurLC->getStackFrame();
+ switch (Call.getKind()) {
+ case CE_Function:
+ case CE_Block:
+ break;
+ case CE_CXXMember:
+ case CE_CXXMemberOperator:
+ if (!Opts.mayInlineCXXMemberFunction(CIMK_MemberFunctions))
+ return CIP_DisallowedAlways;
+ break;
+ case CE_CXXConstructor: {
+ if (!Opts.mayInlineCXXMemberFunction(CIMK_Constructors))
+ return CIP_DisallowedAlways;
+
+ const CXXConstructorCall &Ctor = cast<CXXConstructorCall>(Call);
+
+ const CXXConstructExpr *CtorExpr = Ctor.getOriginExpr();
+
+ auto CCE = getCurrentCFGElement().getAs<CFGConstructor>();
+ const ConstructionContext *CC = CCE ? CCE->getConstructionContext()
+ : nullptr;
+
+ if (CC && isa<NewAllocatedObjectConstructionContext>(CC) &&
+ !Opts.MayInlineCXXAllocator)
+ return CIP_DisallowedOnce;
+
+ // FIXME: We don't handle constructors or destructors for arrays properly.
+ // Even once we do, we still need to be careful about implicitly-generated
+ // initializers for array fields in default move/copy constructors.
+ // We still allow construction into ElementRegion targets when they don't
+ // represent array elements.
+ if (CallOpts.IsArrayCtorOrDtor)
+ return CIP_DisallowedOnce;
+
+ // Inlining constructors requires including initializers in the CFG.
+ const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
+ assert(ADC->getCFGBuildOptions().AddInitializers && "No CFG initializers");
+ (void)ADC;
+
+ // If the destructor is trivial, it's always safe to inline the constructor.
+ if (Ctor.getDecl()->getParent()->hasTrivialDestructor())
+ break;
+
+ // For other types, only inline constructors if destructor inlining is
+ // also enabled.
+ if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
+ return CIP_DisallowedAlways;
+
+ if (CtorExpr->getConstructionKind() == CXXConstructExpr::CK_Complete) {
+ // If we don't handle temporary destructors, we shouldn't inline
+ // their constructors.
+ if (CallOpts.IsTemporaryCtorOrDtor &&
+ !Opts.ShouldIncludeTemporaryDtorsInCFG)
+ return CIP_DisallowedOnce;
+
+ // If we did not find the correct this-region, it would be pointless
+ // to inline the constructor. Instead we will simply invalidate
+ // the fake temporary target.
+ if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion)
+ return CIP_DisallowedOnce;
+
+ // If the temporary is lifetime-extended by binding it to a reference-type
+ // field within an aggregate, automatic destructors don't work properly.
+ if (CallOpts.IsTemporaryLifetimeExtendedViaAggregate)
+ return CIP_DisallowedOnce;
+ }
+
+ break;
+ }
+ case CE_CXXDestructor: {
+ if (!Opts.mayInlineCXXMemberFunction(CIMK_Destructors))
+ return CIP_DisallowedAlways;
+
+ // Inlining destructors requires building the CFG correctly.
+ const AnalysisDeclContext *ADC = CallerSFC->getAnalysisDeclContext();
+ assert(ADC->getCFGBuildOptions().AddImplicitDtors && "No CFG destructors");
+ (void)ADC;
+
+ // FIXME: We don't handle constructors or destructors for arrays properly.
+ if (CallOpts.IsArrayCtorOrDtor)
+ return CIP_DisallowedOnce;
+
+ // Allow disabling temporary destructor inlining with a separate option.
+ if (CallOpts.IsTemporaryCtorOrDtor &&
+ !Opts.MayInlineCXXTemporaryDtors)
+ return CIP_DisallowedOnce;
+
+ // If we did not find the correct this-region, it would be pointless
+ // to inline the destructor. Instead we will simply invalidate
+ // the fake temporary target.
+ if (CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion)
+ return CIP_DisallowedOnce;
+ break;
+ }
+ case CE_CXXAllocator:
+ if (Opts.MayInlineCXXAllocator)
+ break;
+ // Do not inline allocators until we model deallocators.
+ // This is unfortunate, but basically necessary for smart pointers and such.
+ return CIP_DisallowedAlways;
+ case CE_ObjCMessage:
+ if (!Opts.MayInlineObjCMethod)
+ return CIP_DisallowedAlways;
+ if (!(Opts.getIPAMode() == IPAK_DynamicDispatch ||
+ Opts.getIPAMode() == IPAK_DynamicDispatchBifurcate))
+ return CIP_DisallowedAlways;
+ break;
+ }
+
+ return CIP_Allowed;
+}
+
+/// Returns true if the given C++ class contains a member with the given name.
+static bool hasMember(const ASTContext &Ctx, const CXXRecordDecl *RD,
+ StringRef Name) {
+ const IdentifierInfo &II = Ctx.Idents.get(Name);
+ DeclarationName DeclName = Ctx.DeclarationNames.getIdentifier(&II);
+ if (!RD->lookup(DeclName).empty())
+ return true;
+
+ CXXBasePaths Paths(false, false, false);
+ if (RD->lookupInBases(
+ [DeclName](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {
+ return CXXRecordDecl::FindOrdinaryMember(Specifier, Path, DeclName);
+ },
+ Paths))
+ return true;
+
+ return false;
+}
+
+/// Returns true if the given C++ class is a container or iterator.
+///
+/// Our heuristic for this is whether it contains a method named 'begin()' or a
+/// nested type named 'iterator' or 'iterator_category'.
+static bool isContainerClass(const ASTContext &Ctx, const CXXRecordDecl *RD) {
+ return hasMember(Ctx, RD, "begin") ||
+ hasMember(Ctx, RD, "iterator") ||
+ hasMember(Ctx, RD, "iterator_category");
+}
+
+/// Returns true if the given function refers to a method of a C++ container
+/// or iterator.
+///
+/// We generally do a poor job modeling most containers right now, and might
+/// prefer not to inline their methods.
+static bool isContainerMethod(const ASTContext &Ctx,
+ const FunctionDecl *FD) {
+ if (const CXXMethodDecl *MD = dyn_cast<CXXMethodDecl>(FD))
+ return isContainerClass(Ctx, MD->getParent());
+ return false;
+}
+
+/// Returns true if the given function is the destructor of a class named
+/// "shared_ptr".
+static bool isCXXSharedPtrDtor(const FunctionDecl *FD) {
+ const CXXDestructorDecl *Dtor = dyn_cast<CXXDestructorDecl>(FD);
+ if (!Dtor)
+ return false;
+
+ const CXXRecordDecl *RD = Dtor->getParent();
+ if (const IdentifierInfo *II = RD->getDeclName().getAsIdentifierInfo())
+ if (II->isStr("shared_ptr"))
+ return true;
+
+ return false;
+}
+
+/// Returns true if the function in \p CalleeADC may be inlined in general.
+///
+/// This checks static properties of the function, such as its signature and
+/// CFG, to determine whether the analyzer should ever consider inlining it,
+/// in any context.
+static bool mayInlineDecl(AnalysisManager &AMgr,
+ AnalysisDeclContext *CalleeADC) {
+ AnalyzerOptions &Opts = AMgr.getAnalyzerOptions();
+ // FIXME: Do not inline variadic calls.
+ if (CallEvent::isVariadic(CalleeADC->getDecl()))
+ return false;
+
+ // Check certain C++-related inlining policies.
+ ASTContext &Ctx = CalleeADC->getASTContext();
+ if (Ctx.getLangOpts().CPlusPlus) {
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CalleeADC->getDecl())) {
+ // Conditionally control the inlining of template functions.
+ if (!Opts.MayInlineTemplateFunctions)
+ if (FD->getTemplatedKind() != FunctionDecl::TK_NonTemplate)
+ return false;
+
+ // Conditionally control the inlining of C++ standard library functions.
+ if (!Opts.MayInlineCXXStandardLibrary)
+ if (Ctx.getSourceManager().isInSystemHeader(FD->getLocation()))
+ if (AnalysisDeclContext::isInStdNamespace(FD))
+ return false;
+
+ // Conditionally control the inlining of methods on objects that look
+ // like C++ containers.
+ if (!Opts.MayInlineCXXContainerMethods)
+ if (!AMgr.isInCodeFile(FD->getLocation()))
+ if (isContainerMethod(Ctx, FD))
+ return false;
+
+ // Conditionally control the inlining of the destructor of C++ shared_ptr.
+ // We don't currently do a good job modeling shared_ptr because we can't
+ // see the reference count, so treating as opaque is probably the best
+ // idea.
+ if (!Opts.MayInlineCXXSharedPtrDtor)
+ if (isCXXSharedPtrDtor(FD))
+ return false;
+ }
+ }
+
+ // It is possible that the CFG cannot be constructed.
+ // Be safe, and check if the CalleeCFG is valid.
+ const CFG *CalleeCFG = CalleeADC->getCFG();
+ if (!CalleeCFG)
+ return false;
+
+ // Do not inline large functions.
+ if (CalleeCFG->getNumBlockIDs() > Opts.MaxInlinableSize)
+ return false;
+
+ // It is possible that the live variables analysis cannot be
+ // run. If so, bail out.
+ if (!CalleeADC->getAnalysis<RelaxedLiveVariables>())
+ return false;
+
+ return true;
+}
+
+bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
+ const ExplodedNode *Pred,
+ const EvalCallOptions &CallOpts) {
+ if (!D)
+ return false;
+
+ AnalysisManager &AMgr = getAnalysisManager();
+ AnalyzerOptions &Opts = AMgr.options;
+ AnalysisDeclContextManager &ADCMgr = AMgr.getAnalysisDeclContextManager();
+ AnalysisDeclContext *CalleeADC = ADCMgr.getContext(D);
+
+ // The auto-synthesized bodies are essential to inline as they are
+ // usually small and commonly used. Note: we should do this check early on to
+ // ensure we always inline these calls.
+ if (CalleeADC->isBodyAutosynthesized())
+ return true;
+
+ if (!AMgr.shouldInlineCall())
+ return false;
+
+ // Check if this function has been marked as non-inlinable.
+ Optional<bool> MayInline = Engine.FunctionSummaries->mayInline(D);
+ if (MayInline.hasValue()) {
+ if (!MayInline.getValue())
+ return false;
+
+ } else {
+ // We haven't actually checked the static properties of this function yet.
+ // Do that now, and record our decision in the function summaries.
+ if (mayInlineDecl(getAnalysisManager(), CalleeADC)) {
+ Engine.FunctionSummaries->markMayInline(D);
+ } else {
+ Engine.FunctionSummaries->markShouldNotInline(D);
+ return false;
+ }
+ }
+
+ // Check if we should inline a call based on its kind.
+ // FIXME: this checks both static and dynamic properties of the call, which
+ // means we're redoing a bit of work that could be cached in the function
+ // summary.
+ CallInlinePolicy CIP = mayInlineCallKind(Call, Pred, Opts, CallOpts);
+ if (CIP != CIP_Allowed) {
+ if (CIP == CIP_DisallowedAlways) {
+ assert(!MayInline.hasValue() || MayInline.getValue());
+ Engine.FunctionSummaries->markShouldNotInline(D);
+ }
+ return false;
+ }
+
+ const CFG *CalleeCFG = CalleeADC->getCFG();
+
+ // Do not inline if recursive or we've reached max stack frame count.
+ bool IsRecursive = false;
+ unsigned StackDepth = 0;
+ examineStackFrames(D, Pred->getLocationContext(), IsRecursive, StackDepth);
+ if ((StackDepth >= Opts.InlineMaxStackDepth) &&
+ ((CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize)
+ || IsRecursive))
+ return false;
+
+ // Do not inline large functions too many times.
+ if ((Engine.FunctionSummaries->getNumTimesInlined(D) >
+ Opts.MaxTimesInlineLarge) &&
+ CalleeCFG->getNumBlockIDs() >=
+ Opts.MinCFGSizeTreatFunctionsAsLarge) {
+ NumReachedInlineCountMax++;
+ return false;
+ }
+
+ if (HowToInline == Inline_Minimal &&
+ (CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize
+ || IsRecursive))
+ return false;
+
+ return true;
+}
+
+static bool isTrivialObjectAssignment(const CallEvent &Call) {
+ const CXXInstanceCall *ICall = dyn_cast<CXXInstanceCall>(&Call);
+ if (!ICall)
+ return false;
+
+ const CXXMethodDecl *MD = dyn_cast_or_null<CXXMethodDecl>(ICall->getDecl());
+ if (!MD)
+ return false;
+ if (!(MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator()))
+ return false;
+
+ return MD->isTrivial();
+}
+
+void ExprEngine::defaultEvalCall(NodeBuilder &Bldr, ExplodedNode *Pred,
+ const CallEvent &CallTemplate,
+ const EvalCallOptions &CallOpts) {
+ // Make sure we have the most recent state attached to the call.
+ ProgramStateRef State = Pred->getState();
+ CallEventRef<> Call = CallTemplate.cloneWithState(State);
+
+ // Special-case trivial assignment operators.
+ if (isTrivialObjectAssignment(*Call)) {
+ performTrivialCopy(Bldr, Pred, *Call);
+ return;
+ }
+
+ // Try to inline the call.
+ // The origin expression here is just used as a kind of checksum;
+ // this should still be safe even for CallEvents that don't come from exprs.
+ const Expr *E = Call->getOriginExpr();
+
+ ProgramStateRef InlinedFailedState = getInlineFailedState(State, E);
+ if (InlinedFailedState) {
+ // If we already tried once and failed, make sure we don't retry later.
+ State = InlinedFailedState;
+ } else {
+ RuntimeDefinition RD = Call->getRuntimeDefinition();
+ const Decl *D = RD.getDecl();
+ if (shouldInlineCall(*Call, D, Pred, CallOpts)) {
+ if (RD.mayHaveOtherDefinitions()) {
+ AnalyzerOptions &Options = getAnalysisManager().options;
+
+ // Explore with and without inlining the call.
+ if (Options.getIPAMode() == IPAK_DynamicDispatchBifurcate) {
+ BifurcateCall(RD.getDispatchRegion(), *Call, D, Bldr, Pred);
+ return;
+ }
+
+ // Don't inline if we're not in any dynamic dispatch mode.
+ if (Options.getIPAMode() != IPAK_DynamicDispatch) {
+ conservativeEvalCall(*Call, Bldr, Pred, State);
+ return;
+ }
+ }
+
+ // We are not bifurcating and we do have a Decl, so just inline.
+ if (inlineCall(*Call, D, Bldr, Pred, State))
+ return;
+ }
+ }
+
+ // If we can't inline it, handle the return value and invalidate the regions.
+ conservativeEvalCall(*Call, Bldr, Pred, State);
+}
+
+void ExprEngine::BifurcateCall(const MemRegion *BifurReg,
+ const CallEvent &Call, const Decl *D,
+ NodeBuilder &Bldr, ExplodedNode *Pred) {
+ assert(BifurReg);
+ BifurReg = BifurReg->StripCasts();
+
+ // Check if we've performed the split already - note, we only want
+ // to split the path once per memory region.
+ ProgramStateRef State = Pred->getState();
+ const unsigned *BState =
+ State->get<DynamicDispatchBifurcationMap>(BifurReg);
+ if (BState) {
+ // If we are on "inline path", keep inlining if possible.
+ if (*BState == DynamicDispatchModeInlined)
+ if (inlineCall(Call, D, Bldr, Pred, State))
+ return;
+ // If inline failed, or we are on the path where we assume we
+ // don't have enough info about the receiver to inline, conjure the
+ // return value and invalidate the regions.
+ conservativeEvalCall(Call, Bldr, Pred, State);
+ return;
+ }
+
+ // If we got here, this is the first time we process a message to this
+ // region, so split the path.
+ ProgramStateRef IState =
+ State->set<DynamicDispatchBifurcationMap>(BifurReg,
+ DynamicDispatchModeInlined);
+ inlineCall(Call, D, Bldr, Pred, IState);
+
+ ProgramStateRef NoIState =
+ State->set<DynamicDispatchBifurcationMap>(BifurReg,
+ DynamicDispatchModeConservative);
+ conservativeEvalCall(Call, Bldr, Pred, NoIState);
+
+ NumOfDynamicDispatchPathSplits++;
+}
+
+void ExprEngine::VisitReturnStmt(const ReturnStmt *RS, ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ ExplodedNodeSet dstPreVisit;
+ getCheckerManager().runCheckersForPreStmt(dstPreVisit, Pred, RS, *this);
+
+ StmtNodeBuilder B(dstPreVisit, Dst, *currBldrCtx);
+
+ if (RS->getRetValue()) {
+ for (ExplodedNodeSet::iterator it = dstPreVisit.begin(),
+ ei = dstPreVisit.end(); it != ei; ++it) {
+ B.generateNode(RS, *it, (*it)->getState());
+ }
+ }
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
new file mode 100644
index 000000000000..6b8402f621e0
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -0,0 +1,288 @@
+//=-- ExprEngineObjC.cpp - ExprEngine support for Objective-C ---*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines ExprEngine's support for Objective-C expressions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/StmtObjC.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+
+using namespace clang;
+using namespace ento;
+
+void ExprEngine::VisitLvalObjCIvarRefExpr(const ObjCIvarRefExpr *Ex,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+ SVal baseVal = state->getSVal(Ex->getBase(), LCtx);
+ SVal location = state->getLValue(Ex->getDecl(), baseVal);
+
+ ExplodedNodeSet dstIvar;
+ StmtNodeBuilder Bldr(Pred, dstIvar, *currBldrCtx);
+ Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, location));
+
+ // Perform the post-condition check of the ObjCIvarRefExpr and store
+ // the created nodes in 'Dst'.
+ getCheckerManager().runCheckersForPostStmt(Dst, dstIvar, Ex, *this);
+}
+
+void ExprEngine::VisitObjCAtSynchronizedStmt(const ObjCAtSynchronizedStmt *S,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ getCheckerManager().runCheckersForPreStmt(Dst, Pred, S, *this);
+}
+
+/// Generate a node in \p Bldr for an iteration statement using ObjC
+/// for-loop iterator.
+static void populateObjCForDestinationSet(
+ ExplodedNodeSet &dstLocation, SValBuilder &svalBuilder,
+ const ObjCForCollectionStmt *S, const Stmt *elem, SVal elementV,
+ SymbolManager &SymMgr, const NodeBuilderContext *currBldrCtx,
+ StmtNodeBuilder &Bldr, bool hasElements) {
+
+ for (ExplodedNode *Pred : dstLocation) {
+ ProgramStateRef state = Pred->getState();
+ const LocationContext *LCtx = Pred->getLocationContext();
+
+ SVal hasElementsV = svalBuilder.makeTruthVal(hasElements);
+
+ // FIXME: S is not an expression. We should not be binding values to it.
+ ProgramStateRef nextState = state->BindExpr(S, LCtx, hasElementsV);
+
+ if (auto MV = elementV.getAs<loc::MemRegionVal>())
+ if (const auto *R = dyn_cast<TypedValueRegion>(MV->getRegion())) {
+ // FIXME: The proper thing to do is to really iterate over the
+ // container. We will do this with dispatch logic to the store.
+ // For now, just 'conjure' up a symbolic value.
+ QualType T = R->getValueType();
+ assert(Loc::isLocType(T));
+
+ SVal V;
+ if (hasElements) {
+ SymbolRef Sym = SymMgr.conjureSymbol(elem, LCtx, T,
+ currBldrCtx->blockCount());
+ V = svalBuilder.makeLoc(Sym);
+ } else {
+ V = svalBuilder.makeIntVal(0, T);
+ }
+
+ nextState = nextState->bindLoc(elementV, V, LCtx);
+ }
+
+ Bldr.generateNode(S, Pred, nextState);
+ }
+}
+
+void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+
+ // ObjCForCollectionStmts are processed in two places. This method
+ // handles the case where an ObjCForCollectionStmt* occurs as one of the
+ // statements within a basic block. This transfer function does two things:
+ //
+ // (1) binds the next container value to 'element'. This creates a new
+ // node in the ExplodedGraph.
+ //
+ // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating
+ // whether or not the container has any more elements. This value
+ // will be tested in ProcessBranch. We need to explicitly bind
+ // this value because a container can contain nil elements.
+ //
+ // FIXME: Eventually this logic should actually do dispatches to
+ // 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration).
+ // This will require simulating a temporary NSFastEnumerationState, either
+ // through an SVal or through the use of MemRegions. This value can
+ // be affixed to the ObjCForCollectionStmt* instead of 0/1; when the loop
+ // terminates we reclaim the temporary (it goes out of scope) and we
+ // we can test if the SVal is 0 or if the MemRegion is null (depending
+ // on what approach we take).
+ //
+ // For now: simulate (1) by assigning either a symbol or nil if the
+ // container is empty. Thus this transfer function will by default
+ // result in state splitting.
+
+ const Stmt *elem = S->getElement();
+ const Stmt *collection = S->getCollection();
+ ProgramStateRef state = Pred->getState();
+ SVal collectionV = state->getSVal(collection, Pred->getLocationContext());
+
+ SVal elementV;
+ if (const auto *DS = dyn_cast<DeclStmt>(elem)) {
+ const VarDecl *elemD = cast<VarDecl>(DS->getSingleDecl());
+ assert(elemD->getInit() == nullptr);
+ elementV = state->getLValue(elemD, Pred->getLocationContext());
+ } else {
+ elementV = state->getSVal(elem, Pred->getLocationContext());
+ }
+
+ bool isContainerNull = state->isNull(collectionV).isConstrainedTrue();
+
+ ExplodedNodeSet dstLocation;
+ evalLocation(dstLocation, S, elem, Pred, state, elementV, false);
+
+ ExplodedNodeSet Tmp;
+ StmtNodeBuilder Bldr(Pred, Tmp, *currBldrCtx);
+
+ if (!isContainerNull)
+ populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV,
+ SymMgr, currBldrCtx, Bldr,
+ /*hasElements=*/true);
+
+ populateObjCForDestinationSet(dstLocation, svalBuilder, S, elem, elementV,
+ SymMgr, currBldrCtx, Bldr,
+ /*hasElements=*/false);
+
+ // Finally, run any custom checkers.
+ // FIXME: Eventually all pre- and post-checks should live in VisitStmt.
+ getCheckerManager().runCheckersForPostStmt(Dst, Tmp, S, *this);
+}
+
+void ExprEngine::VisitObjCMessage(const ObjCMessageExpr *ME,
+ ExplodedNode *Pred,
+ ExplodedNodeSet &Dst) {
+ CallEventManager &CEMgr = getStateManager().getCallEventManager();
+ CallEventRef<ObjCMethodCall> Msg =
+ CEMgr.getObjCMethodCall(ME, Pred->getState(), Pred->getLocationContext());
+
+ // There are three cases for the receiver:
+ // (1) it is definitely nil,
+ // (2) it is definitely non-nil, and
+ // (3) we don't know.
+ //
+ // If the receiver is definitely nil, we skip the pre/post callbacks and
+ // instead call the ObjCMessageNil callbacks and return.
+ //
+ // If the receiver is definitely non-nil, we call the pre- callbacks,
+ // evaluate the call, and call the post- callbacks.
+ //
+ // If we don't know, we drop the potential nil flow and instead
+ // continue from the assumed non-nil state as in (2). This approach
+ // intentionally drops coverage in order to prevent false alarms
+ // in the following scenario:
+ //
+ // id result = [o someMethod]
+ // if (result) {
+ // if (!o) {
+ // // <-- This program point should be unreachable because if o is nil
+ // // it must the case that result is nil as well.
+ // }
+ // }
+ //
+ // We could avoid dropping coverage by performing an explicit case split
+ // on each method call -- but this would get very expensive. An alternative
+ // would be to introduce lazy constraints.
+ // FIXME: This ignores many potential bugs (<rdar://problem/11733396>).
+ // Revisit once we have lazier constraints.
+ if (Msg->isInstanceMessage()) {
+ SVal recVal = Msg->getReceiverSVal();
+ if (!recVal.isUndef()) {
+ // Bifurcate the state into nil and non-nil ones.
+ DefinedOrUnknownSVal receiverVal =
+ recVal.castAs<DefinedOrUnknownSVal>();
+ ProgramStateRef State = Pred->getState();
+
+ ProgramStateRef notNilState, nilState;
+ std::tie(notNilState, nilState) = State->assume(receiverVal);
+
+ // Receiver is definitely nil, so run ObjCMessageNil callbacks and return.
+ if (nilState && !notNilState) {
+ ExplodedNodeSet dstNil;
+ StmtNodeBuilder Bldr(Pred, dstNil, *currBldrCtx);
+ bool HasTag = Pred->getLocation().getTag();
+ Pred = Bldr.generateNode(ME, Pred, nilState, nullptr,
+ ProgramPoint::PreStmtKind);
+ assert((Pred || HasTag) && "Should have cached out already!");
+ (void)HasTag;
+ if (!Pred)
+ return;
+
+ ExplodedNodeSet dstPostCheckers;
+ getCheckerManager().runCheckersForObjCMessageNil(dstPostCheckers, Pred,
+ *Msg, *this);
+ for (auto I : dstPostCheckers)
+ finishArgumentConstruction(Dst, I, *Msg);
+ return;
+ }
+
+ ExplodedNodeSet dstNonNil;
+ StmtNodeBuilder Bldr(Pred, dstNonNil, *currBldrCtx);
+ // Generate a transition to the non-nil state, dropping any potential
+ // nil flow.
+ if (notNilState != State) {
+ bool HasTag = Pred->getLocation().getTag();
+ Pred = Bldr.generateNode(ME, Pred, notNilState);
+ assert((Pred || HasTag) && "Should have cached out already!");
+ (void)HasTag;
+ if (!Pred)
+ return;
+ }
+ }
+ }
+
+ // Handle the previsits checks.
+ ExplodedNodeSet dstPrevisit;
+ getCheckerManager().runCheckersForPreObjCMessage(dstPrevisit, Pred,
+ *Msg, *this);
+ ExplodedNodeSet dstGenericPrevisit;
+ getCheckerManager().runCheckersForPreCall(dstGenericPrevisit, dstPrevisit,
+ *Msg, *this);
+
+ // Proceed with evaluate the message expression.
+ ExplodedNodeSet dstEval;
+ StmtNodeBuilder Bldr(dstGenericPrevisit, dstEval, *currBldrCtx);
+
+ for (ExplodedNodeSet::iterator DI = dstGenericPrevisit.begin(),
+ DE = dstGenericPrevisit.end(); DI != DE; ++DI) {
+ ExplodedNode *Pred = *DI;
+ ProgramStateRef State = Pred->getState();
+ CallEventRef<ObjCMethodCall> UpdatedMsg = Msg.cloneWithState(State);
+
+ if (UpdatedMsg->isInstanceMessage()) {
+ SVal recVal = UpdatedMsg->getReceiverSVal();
+ if (!recVal.isUndef()) {
+ if (ObjCNoRet.isImplicitNoReturn(ME)) {
+ // If we raise an exception, for now treat it as a sink.
+ // Eventually we will want to handle exceptions properly.
+ Bldr.generateSink(ME, Pred, State);
+ continue;
+ }
+ }
+ } else {
+ // Check for special class methods that are known to not return
+ // and that we should treat as a sink.
+ if (ObjCNoRet.isImplicitNoReturn(ME)) {
+ // If we raise an exception, for now treat it as a sink.
+ // Eventually we will want to handle exceptions properly.
+ Bldr.generateSink(ME, Pred, Pred->getState());
+ continue;
+ }
+ }
+
+ defaultEvalCall(Bldr, Pred, *UpdatedMsg);
+ }
+
+ // If there were constructors called for object-type arguments, clean them up.
+ ExplodedNodeSet dstArgCleanup;
+ for (auto I : dstEval)
+ finishArgumentConstruction(dstArgCleanup, I, *Msg);
+
+ ExplodedNodeSet dstPostvisit;
+ getCheckerManager().runCheckersForPostCall(dstPostvisit, dstArgCleanup,
+ *Msg, *this);
+
+ // Finally, perform the post-condition check of the ObjCMessageExpr and store
+ // the created nodes in 'Dst'.
+ getCheckerManager().runCheckersForPostObjCMessage(Dst, dstPostvisit,
+ *Msg, *this);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp
new file mode 100644
index 000000000000..94edd84d15d2
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/FunctionSummary.cpp
@@ -0,0 +1,31 @@
+//===- FunctionSummary.cpp - Stores summaries of functions. ---------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a summary of a function gathered/used by static analysis.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/FunctionSummary.h"
+
+using namespace clang;
+using namespace ento;
+
+unsigned FunctionSummariesTy::getTotalNumBasicBlocks() {
+ unsigned Total = 0;
+ for (const auto &I : Map)
+ Total += I.second.TotalBasicBlocks;
+ return Total;
+}
+
+unsigned FunctionSummariesTy::getTotalNumVisitedBasicBlocks() {
+ unsigned Total = 0;
+ for (const auto &I : Map)
+ Total += I.second.VisitedBasicBlocks.count();
+ return Total;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
new file mode 100644
index 000000000000..fc82f1176942
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -0,0 +1,1017 @@
+//===- HTMLDiagnostics.cpp - HTML Diagnostics for Paths -------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the HTMLDiagnostics object.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/Stmt.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/Token.h"
+#include "clang/Rewrite/Core/HTMLRewrite.h"
+#include "clang/Rewrite/Core/Rewriter.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/IssueHash.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/iterator_range.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Errc.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+#include <cassert>
+#include <map>
+#include <memory>
+#include <set>
+#include <sstream>
+#include <string>
+#include <system_error>
+#include <utility>
+#include <vector>
+
+using namespace clang;
+using namespace ento;
+
+//===----------------------------------------------------------------------===//
+// Boilerplate.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class HTMLDiagnostics : public PathDiagnosticConsumer {
+ std::string Directory;
+ bool createdDir = false;
+ bool noDir = false;
+ const Preprocessor &PP;
+ AnalyzerOptions &AnalyzerOpts;
+ const bool SupportsCrossFileDiagnostics;
+
+public:
+ HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts,
+ const std::string& prefix,
+ const Preprocessor &pp,
+ bool supportsMultipleFiles)
+ : Directory(prefix), PP(pp), AnalyzerOpts(AnalyzerOpts),
+ SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
+
+ ~HTMLDiagnostics() override { FlushDiagnostics(nullptr); }
+
+ void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
+ FilesMade *filesMade) override;
+
+ StringRef getName() const override {
+ return "HTMLDiagnostics";
+ }
+
+ bool supportsCrossFileDiagnostics() const override {
+ return SupportsCrossFileDiagnostics;
+ }
+
+ unsigned ProcessMacroPiece(raw_ostream &os,
+ const PathDiagnosticMacroPiece& P,
+ unsigned num);
+
+ void HandlePiece(Rewriter& R, FileID BugFileID,
+ const PathDiagnosticPiece& P, unsigned num, unsigned max);
+
+ void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range,
+ const char *HighlightStart = "<span class=\"mrange\">",
+ const char *HighlightEnd = "</span>");
+
+ void ReportDiag(const PathDiagnostic& D,
+ FilesMade *filesMade);
+
+ // Generate the full HTML report
+ std::string GenerateHTML(const PathDiagnostic& D, Rewriter &R,
+ const SourceManager& SMgr, const PathPieces& path,
+ const char *declName);
+
+ // Add HTML header/footers to file specified by FID
+ void FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
+ const SourceManager& SMgr, const PathPieces& path,
+ FileID FID, const FileEntry *Entry, const char *declName);
+
+ // Rewrite the file specified by FID with HTML formatting.
+ void RewriteFile(Rewriter &R, const PathPieces& path, FileID FID);
+
+
+private:
+ /// \return Javascript for displaying shortcuts help;
+ StringRef showHelpJavascript();
+
+ /// \return Javascript for navigating the HTML report using j/k keys.
+ StringRef generateKeyboardNavigationJavascript();
+
+ /// \return JavaScript for an option to only show relevant lines.
+ std::string showRelevantLinesJavascript(
+ const PathDiagnostic &D, const PathPieces &path);
+
+ /// Write executed lines from \p D in JSON format into \p os.
+ void dumpCoverageData(const PathDiagnostic &D,
+ const PathPieces &path,
+ llvm::raw_string_ostream &os);
+};
+
+} // namespace
+
+void ento::createHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
+ PathDiagnosticConsumers &C,
+ const std::string& prefix,
+ const Preprocessor &PP) {
+ C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, true));
+}
+
+void ento::createHTMLSingleFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
+ PathDiagnosticConsumers &C,
+ const std::string& prefix,
+ const Preprocessor &PP) {
+ C.push_back(new HTMLDiagnostics(AnalyzerOpts, prefix, PP, false));
+}
+
+//===----------------------------------------------------------------------===//
+// Report processing.
+//===----------------------------------------------------------------------===//
+
+void HTMLDiagnostics::FlushDiagnosticsImpl(
+ std::vector<const PathDiagnostic *> &Diags,
+ FilesMade *filesMade) {
+ for (const auto Diag : Diags)
+ ReportDiag(*Diag, filesMade);
+}
+
+void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
+ FilesMade *filesMade) {
+ // Create the HTML directory if it is missing.
+ if (!createdDir) {
+ createdDir = true;
+ if (std::error_code ec = llvm::sys::fs::create_directories(Directory)) {
+ llvm::errs() << "warning: could not create directory '"
+ << Directory << "': " << ec.message() << '\n';
+ noDir = true;
+ return;
+ }
+ }
+
+ if (noDir)
+ return;
+
+ // First flatten out the entire path to make it easier to use.
+ PathPieces path = D.path.flatten(/*ShouldFlattenMacros=*/false);
+
+ // The path as already been prechecked that the path is non-empty.
+ assert(!path.empty());
+ const SourceManager &SMgr = path.front()->getLocation().getManager();
+
+ // Create a new rewriter to generate HTML.
+ Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts());
+
+ // The file for the first path element is considered the main report file, it
+ // will usually be equivalent to SMgr.getMainFileID(); however, it might be a
+ // header when -analyzer-opt-analyze-headers is used.
+ FileID ReportFile = path.front()->getLocation().asLocation().getExpansionLoc().getFileID();
+
+ // Get the function/method name
+ SmallString<128> declName("unknown");
+ int offsetDecl = 0;
+ if (const Decl *DeclWithIssue = D.getDeclWithIssue()) {
+ if (const auto *ND = dyn_cast<NamedDecl>(DeclWithIssue))
+ declName = ND->getDeclName().getAsString();
+
+ if (const Stmt *Body = DeclWithIssue->getBody()) {
+ // Retrieve the relative position of the declaration which will be used
+ // for the file name
+ FullSourceLoc L(
+ SMgr.getExpansionLoc(path.back()->getLocation().asLocation()),
+ SMgr);
+ FullSourceLoc FunL(SMgr.getExpansionLoc(Body->getBeginLoc()), SMgr);
+ offsetDecl = L.getExpansionLineNumber() - FunL.getExpansionLineNumber();
+ }
+ }
+
+ std::string report = GenerateHTML(D, R, SMgr, path, declName.c_str());
+ if (report.empty()) {
+ llvm::errs() << "warning: no diagnostics generated for main file.\n";
+ return;
+ }
+
+ // Create a path for the target HTML file.
+ int FD;
+ SmallString<128> Model, ResultPath;
+
+ if (!AnalyzerOpts.ShouldWriteStableReportFilename) {
+ llvm::sys::path::append(Model, Directory, "report-%%%%%%.html");
+ if (std::error_code EC =
+ llvm::sys::fs::make_absolute(Model)) {
+ llvm::errs() << "warning: could not make '" << Model
+ << "' absolute: " << EC.message() << '\n';
+ return;
+ }
+ if (std::error_code EC =
+ llvm::sys::fs::createUniqueFile(Model, FD, ResultPath)) {
+ llvm::errs() << "warning: could not create file in '" << Directory
+ << "': " << EC.message() << '\n';
+ return;
+ }
+ } else {
+ int i = 1;
+ std::error_code EC;
+ do {
+ // Find a filename which is not already used
+ const FileEntry* Entry = SMgr.getFileEntryForID(ReportFile);
+ std::stringstream filename;
+ Model = "";
+ filename << "report-"
+ << llvm::sys::path::filename(Entry->getName()).str()
+ << "-" << declName.c_str()
+ << "-" << offsetDecl
+ << "-" << i << ".html";
+ llvm::sys::path::append(Model, Directory,
+ filename.str());
+ EC = llvm::sys::fs::openFileForReadWrite(
+ Model, FD, llvm::sys::fs::CD_CreateNew, llvm::sys::fs::OF_None);
+ if (EC && EC != llvm::errc::file_exists) {
+ llvm::errs() << "warning: could not create file '" << Model
+ << "': " << EC.message() << '\n';
+ return;
+ }
+ i++;
+ } while (EC);
+ }
+
+ llvm::raw_fd_ostream os(FD, true);
+
+ if (filesMade)
+ filesMade->addDiagnostic(D, getName(),
+ llvm::sys::path::filename(ResultPath));
+
+ // Emit the HTML to disk.
+ os << report;
+}
+
+std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R,
+ const SourceManager& SMgr, const PathPieces& path, const char *declName) {
+ // Rewrite source files as HTML for every new file the path crosses
+ std::vector<FileID> FileIDs;
+ for (auto I : path) {
+ FileID FID = I->getLocation().asLocation().getExpansionLoc().getFileID();
+ if (std::find(FileIDs.begin(), FileIDs.end(), FID) != FileIDs.end())
+ continue;
+
+ FileIDs.push_back(FID);
+ RewriteFile(R, path, FID);
+ }
+
+ if (SupportsCrossFileDiagnostics && FileIDs.size() > 1) {
+ // Prefix file names, anchor tags, and nav cursors to every file
+ for (auto I = FileIDs.begin(), E = FileIDs.end(); I != E; I++) {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+
+ if (I != FileIDs.begin())
+ os << "<hr class=divider>\n";
+
+ os << "<div id=File" << I->getHashValue() << ">\n";
+
+ // Left nav arrow
+ if (I != FileIDs.begin())
+ os << "<div class=FileNav><a href=\"#File" << (I - 1)->getHashValue()
+ << "\">&#x2190;</a></div>";
+
+ os << "<h4 class=FileName>" << SMgr.getFileEntryForID(*I)->getName()
+ << "</h4>\n";
+
+ // Right nav arrow
+ if (I + 1 != E)
+ os << "<div class=FileNav><a href=\"#File" << (I + 1)->getHashValue()
+ << "\">&#x2192;</a></div>";
+
+ os << "</div>\n";
+
+ R.InsertTextBefore(SMgr.getLocForStartOfFile(*I), os.str());
+ }
+
+ // Append files to the main report file in the order they appear in the path
+ for (auto I : llvm::make_range(FileIDs.begin() + 1, FileIDs.end())) {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+
+ const RewriteBuffer *Buf = R.getRewriteBufferFor(I);
+ for (auto BI : *Buf)
+ os << BI;
+
+ R.InsertTextAfter(SMgr.getLocForEndOfFile(FileIDs[0]), os.str());
+ }
+ }
+
+ const RewriteBuffer *Buf = R.getRewriteBufferFor(FileIDs[0]);
+ if (!Buf)
+ return {};
+
+ // Add CSS, header, and footer.
+ FileID FID =
+ path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
+ const FileEntry* Entry = SMgr.getFileEntryForID(FID);
+ FinalizeHTML(D, R, SMgr, path, FileIDs[0], Entry, declName);
+
+ std::string file;
+ llvm::raw_string_ostream os(file);
+ for (auto BI : *Buf)
+ os << BI;
+
+ return os.str();
+}
+
+void HTMLDiagnostics::dumpCoverageData(
+ const PathDiagnostic &D,
+ const PathPieces &path,
+ llvm::raw_string_ostream &os) {
+
+ const FilesToLineNumsMap &ExecutedLines = D.getExecutedLines();
+
+ os << "var relevant_lines = {";
+ for (auto I = ExecutedLines.begin(),
+ E = ExecutedLines.end(); I != E; ++I) {
+ if (I != ExecutedLines.begin())
+ os << ", ";
+
+ os << "\"" << I->first.getHashValue() << "\": {";
+ for (unsigned LineNo : I->second) {
+ if (LineNo != *(I->second.begin()))
+ os << ", ";
+
+ os << "\"" << LineNo << "\": 1";
+ }
+ os << "}";
+ }
+
+ os << "};";
+}
+
+std::string HTMLDiagnostics::showRelevantLinesJavascript(
+ const PathDiagnostic &D, const PathPieces &path) {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ os << "<script type='text/javascript'>\n";
+ dumpCoverageData(D, path, os);
+ os << R"<<<(
+
+var filterCounterexample = function (hide) {
+ var tables = document.getElementsByClassName("code");
+ for (var t=0; t<tables.length; t++) {
+ var table = tables[t];
+ var file_id = table.getAttribute("data-fileid");
+ var lines_in_fid = relevant_lines[file_id];
+ if (!lines_in_fid) {
+ lines_in_fid = {};
+ }
+ var lines = table.getElementsByClassName("codeline");
+ for (var i=0; i<lines.length; i++) {
+ var el = lines[i];
+ var lineNo = el.getAttribute("data-linenumber");
+ if (!lines_in_fid[lineNo]) {
+ if (hide) {
+ el.setAttribute("hidden", "");
+ } else {
+ el.removeAttribute("hidden");
+ }
+ }
+ }
+ }
+}
+
+window.addEventListener("keydown", function (event) {
+ if (event.defaultPrevented) {
+ return;
+ }
+ if (event.key == "S") {
+ var checked = document.getElementsByName("showCounterexample")[0].checked;
+ filterCounterexample(!checked);
+ document.getElementsByName("showCounterexample")[0].checked = !checked;
+ } else {
+ return;
+ }
+ event.preventDefault();
+}, true);
+
+document.addEventListener("DOMContentLoaded", function() {
+ document.querySelector('input[name="showCounterexample"]').onchange=
+ function (event) {
+ filterCounterexample(this.checked);
+ };
+});
+</script>
+
+<form>
+ <input type="checkbox" name="showCounterexample" id="showCounterexample" />
+ <label for="showCounterexample">
+ Show only relevant lines
+ </label>
+</form>
+)<<<";
+
+ return os.str();
+}
+
+void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
+ const SourceManager& SMgr, const PathPieces& path, FileID FID,
+ const FileEntry *Entry, const char *declName) {
+ // This is a cludge; basically we want to append either the full
+ // working directory if we have no directory information. This is
+ // a work in progress.
+
+ llvm::SmallString<0> DirName;
+
+ if (llvm::sys::path::is_relative(Entry->getName())) {
+ llvm::sys::fs::current_path(DirName);
+ DirName += '/';
+ }
+
+ int LineNumber = path.back()->getLocation().asLocation().getExpansionLineNumber();
+ int ColumnNumber = path.back()->getLocation().asLocation().getExpansionColumnNumber();
+
+ R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), showHelpJavascript());
+
+ R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
+ generateKeyboardNavigationJavascript());
+
+ // Checkbox and javascript for filtering the output to the counterexample.
+ R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
+ showRelevantLinesJavascript(D, path));
+
+ // Add the name of the file as an <h1> tag.
+ {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+
+ os << "<!-- REPORTHEADER -->\n"
+ << "<h3>Bug Summary</h3>\n<table class=\"simpletable\">\n"
+ "<tr><td class=\"rowname\">File:</td><td>"
+ << html::EscapeText(DirName)
+ << html::EscapeText(Entry->getName())
+ << "</td></tr>\n<tr><td class=\"rowname\">Warning:</td><td>"
+ "<a href=\"#EndPath\">line "
+ << LineNumber
+ << ", column "
+ << ColumnNumber
+ << "</a><br />"
+ << D.getVerboseDescription() << "</td></tr>\n";
+
+ // The navigation across the extra notes pieces.
+ unsigned NumExtraPieces = 0;
+ for (const auto &Piece : path) {
+ if (const auto *P = dyn_cast<PathDiagnosticNotePiece>(Piece.get())) {
+ int LineNumber =
+ P->getLocation().asLocation().getExpansionLineNumber();
+ int ColumnNumber =
+ P->getLocation().asLocation().getExpansionColumnNumber();
+ os << "<tr><td class=\"rowname\">Note:</td><td>"
+ << "<a href=\"#Note" << NumExtraPieces << "\">line "
+ << LineNumber << ", column " << ColumnNumber << "</a><br />"
+ << P->getString() << "</td></tr>";
+ ++NumExtraPieces;
+ }
+ }
+
+ // Output any other meta data.
+
+ for (PathDiagnostic::meta_iterator I = D.meta_begin(), E = D.meta_end();
+ I != E; ++I) {
+ os << "<tr><td></td><td>" << html::EscapeText(*I) << "</td></tr>\n";
+ }
+
+ os << R"<<<(
+</table>
+<!-- REPORTSUMMARYEXTRA -->
+<h3>Annotated Source Code</h3>
+<p>Press <a href="#" onclick="toggleHelp(); return false;">'?'</a>
+ to see keyboard shortcuts</p>
+<input type="checkbox" class="spoilerhider" id="showinvocation" />
+<label for="showinvocation" >Show analyzer invocation</label>
+<div class="spoiler">clang -cc1 )<<<";
+ os << html::EscapeText(AnalyzerOpts.FullCompilerInvocation);
+ os << R"<<<(
+</div>
+<div id='tooltiphint' hidden="true">
+ <p>Keyboard shortcuts: </p>
+ <ul>
+ <li>Use 'j/k' keys for keyboard navigation</li>
+ <li>Use 'Shift+S' to show/hide relevant lines</li>
+ <li>Use '?' to toggle this window</li>
+ </ul>
+ <a href="#" onclick="toggleHelp(); return false;">Close</a>
+</div>
+)<<<";
+ R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
+ }
+
+ // Embed meta-data tags.
+ {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+
+ StringRef BugDesc = D.getVerboseDescription();
+ if (!BugDesc.empty())
+ os << "\n<!-- BUGDESC " << BugDesc << " -->\n";
+
+ StringRef BugType = D.getBugType();
+ if (!BugType.empty())
+ os << "\n<!-- BUGTYPE " << BugType << " -->\n";
+
+ PathDiagnosticLocation UPDLoc = D.getUniqueingLoc();
+ FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid()
+ ? UPDLoc.asLocation()
+ : D.getLocation().asLocation()),
+ SMgr);
+ const Decl *DeclWithIssue = D.getDeclWithIssue();
+
+ StringRef BugCategory = D.getCategory();
+ if (!BugCategory.empty())
+ os << "\n<!-- BUGCATEGORY " << BugCategory << " -->\n";
+
+ os << "\n<!-- BUGFILE " << DirName << Entry->getName() << " -->\n";
+
+ os << "\n<!-- FILENAME " << llvm::sys::path::filename(Entry->getName()) << " -->\n";
+
+ os << "\n<!-- FUNCTIONNAME " << declName << " -->\n";
+
+ os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT "
+ << GetIssueHash(SMgr, L, D.getCheckName(), D.getBugType(), DeclWithIssue,
+ PP.getLangOpts()) << " -->\n";
+
+ os << "\n<!-- BUGLINE "
+ << LineNumber
+ << " -->\n";
+
+ os << "\n<!-- BUGCOLUMN "
+ << ColumnNumber
+ << " -->\n";
+
+ os << "\n<!-- BUGPATHLENGTH " << path.size() << " -->\n";
+
+ // Mark the end of the tags.
+ os << "\n<!-- BUGMETAEND -->\n";
+
+ // Insert the text.
+ R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
+ }
+
+ html::AddHeaderFooterInternalBuiltinCSS(R, FID, Entry->getName());
+}
+
+StringRef HTMLDiagnostics::showHelpJavascript() {
+ return R"<<<(
+<script type='text/javascript'>
+
+var toggleHelp = function() {
+ var hint = document.querySelector("#tooltiphint");
+ var attributeName = "hidden";
+ if (hint.hasAttribute(attributeName)) {
+ hint.removeAttribute(attributeName);
+ } else {
+ hint.setAttribute("hidden", "true");
+ }
+};
+window.addEventListener("keydown", function (event) {
+ if (event.defaultPrevented) {
+ return;
+ }
+ if (event.key == "?") {
+ toggleHelp();
+ } else {
+ return;
+ }
+ event.preventDefault();
+});
+</script>
+)<<<";
+}
+
+void HTMLDiagnostics::RewriteFile(Rewriter &R,
+ const PathPieces& path, FileID FID) {
+ // Process the path.
+ // Maintain the counts of extra note pieces separately.
+ unsigned TotalPieces = path.size();
+ unsigned TotalNotePieces =
+ std::count_if(path.begin(), path.end(),
+ [](const std::shared_ptr<PathDiagnosticPiece> &p) {
+ return isa<PathDiagnosticNotePiece>(*p);
+ });
+
+ unsigned TotalRegularPieces = TotalPieces - TotalNotePieces;
+ unsigned NumRegularPieces = TotalRegularPieces;
+ unsigned NumNotePieces = TotalNotePieces;
+
+ for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) {
+ if (isa<PathDiagnosticNotePiece>(I->get())) {
+ // This adds diagnostic bubbles, but not navigation.
+ // Navigation through note pieces would be added later,
+ // as a separate pass through the piece list.
+ HandlePiece(R, FID, **I, NumNotePieces, TotalNotePieces);
+ --NumNotePieces;
+ } else {
+ HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces);
+ --NumRegularPieces;
+ }
+ }
+
+ // Add line numbers, header, footer, etc.
+
+ html::EscapeText(R, FID);
+ html::AddLineNumbers(R, FID);
+
+ // If we have a preprocessor, relex the file and syntax highlight.
+ // We might not have a preprocessor if we come from a deserialized AST file,
+ // for example.
+
+ html::SyntaxHighlight(R, FID, PP);
+ html::HighlightMacros(R, FID, PP);
+}
+
+void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
+ const PathDiagnosticPiece& P,
+ unsigned num, unsigned max) {
+ // For now, just draw a box above the line in question, and emit the
+ // warning.
+ FullSourceLoc Pos = P.getLocation().asLocation();
+
+ if (!Pos.isValid())
+ return;
+
+ SourceManager &SM = R.getSourceMgr();
+ assert(&Pos.getManager() == &SM && "SourceManagers are different!");
+ std::pair<FileID, unsigned> LPosInfo = SM.getDecomposedExpansionLoc(Pos);
+
+ if (LPosInfo.first != BugFileID)
+ return;
+
+ const llvm::MemoryBuffer *Buf = SM.getBuffer(LPosInfo.first);
+ const char* FileStart = Buf->getBufferStart();
+
+ // Compute the column number. Rewind from the current position to the start
+ // of the line.
+ unsigned ColNo = SM.getColumnNumber(LPosInfo.first, LPosInfo.second);
+ const char *TokInstantiationPtr =Pos.getExpansionLoc().getCharacterData();
+ const char *LineStart = TokInstantiationPtr-ColNo;
+
+ // Compute LineEnd.
+ const char *LineEnd = TokInstantiationPtr;
+ const char* FileEnd = Buf->getBufferEnd();
+ while (*LineEnd != '\n' && LineEnd != FileEnd)
+ ++LineEnd;
+
+ // Compute the margin offset by counting tabs and non-tabs.
+ unsigned PosNo = 0;
+ for (const char* c = LineStart; c != TokInstantiationPtr; ++c)
+ PosNo += *c == '\t' ? 8 : 1;
+
+ // Create the html for the message.
+
+ const char *Kind = nullptr;
+ bool IsNote = false;
+ bool SuppressIndex = (max == 1);
+ switch (P.getKind()) {
+ case PathDiagnosticPiece::Call:
+ llvm_unreachable("Calls and extra notes should already be handled");
+ case PathDiagnosticPiece::Event: Kind = "Event"; break;
+ case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break;
+ // Setting Kind to "Control" is intentional.
+ case PathDiagnosticPiece::Macro: Kind = "Control"; break;
+ case PathDiagnosticPiece::Note:
+ Kind = "Note";
+ IsNote = true;
+ SuppressIndex = true;
+ break;
+ }
+
+ std::string sbuf;
+ llvm::raw_string_ostream os(sbuf);
+
+ os << "\n<tr><td class=\"num\"></td><td class=\"line\"><div id=\"";
+
+ if (IsNote)
+ os << "Note" << num;
+ else if (num == max)
+ os << "EndPath";
+ else
+ os << "Path" << num;
+
+ os << "\" class=\"msg";
+ if (Kind)
+ os << " msg" << Kind;
+ os << "\" style=\"margin-left:" << PosNo << "ex";
+
+ // Output a maximum size.
+ if (!isa<PathDiagnosticMacroPiece>(P)) {
+ // Get the string and determining its maximum substring.
+ const auto &Msg = P.getString();
+ unsigned max_token = 0;
+ unsigned cnt = 0;
+ unsigned len = Msg.size();
+
+ for (char C : Msg)
+ switch (C) {
+ default:
+ ++cnt;
+ continue;
+ case ' ':
+ case '\t':
+ case '\n':
+ if (cnt > max_token) max_token = cnt;
+ cnt = 0;
+ }
+
+ if (cnt > max_token)
+ max_token = cnt;
+
+ // Determine the approximate size of the message bubble in em.
+ unsigned em;
+ const unsigned max_line = 120;
+
+ if (max_token >= max_line)
+ em = max_token / 2;
+ else {
+ unsigned characters = max_line;
+ unsigned lines = len / max_line;
+
+ if (lines > 0) {
+ for (; characters > max_token; --characters)
+ if (len / characters > lines) {
+ ++characters;
+ break;
+ }
+ }
+
+ em = characters / 2;
+ }
+
+ if (em < max_line/2)
+ os << "; max-width:" << em << "em";
+ }
+ else
+ os << "; max-width:100em";
+
+ os << "\">";
+
+ if (!SuppressIndex) {
+ os << "<table class=\"msgT\"><tr><td valign=\"top\">";
+ os << "<div class=\"PathIndex";
+ if (Kind) os << " PathIndex" << Kind;
+ os << "\">" << num << "</div>";
+
+ if (num > 1) {
+ os << "</td><td><div class=\"PathNav\"><a href=\"#Path"
+ << (num - 1)
+ << "\" title=\"Previous event ("
+ << (num - 1)
+ << ")\">&#x2190;</a></div></td>";
+ }
+
+ os << "</td><td>";
+ }
+
+ if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(&P)) {
+ os << "Within the expansion of the macro '";
+
+ // Get the name of the macro by relexing it.
+ {
+ FullSourceLoc L = MP->getLocation().asLocation().getExpansionLoc();
+ assert(L.isFileID());
+ StringRef BufferInfo = L.getBufferData();
+ std::pair<FileID, unsigned> LocInfo = L.getDecomposedLoc();
+ const char* MacroName = LocInfo.second + BufferInfo.data();
+ Lexer rawLexer(SM.getLocForStartOfFile(LocInfo.first), PP.getLangOpts(),
+ BufferInfo.begin(), MacroName, BufferInfo.end());
+
+ Token TheTok;
+ rawLexer.LexFromRawLexer(TheTok);
+ for (unsigned i = 0, n = TheTok.getLength(); i < n; ++i)
+ os << MacroName[i];
+ }
+
+ os << "':\n";
+
+ if (!SuppressIndex) {
+ os << "</td>";
+ if (num < max) {
+ os << "<td><div class=\"PathNav\"><a href=\"#";
+ if (num == max - 1)
+ os << "EndPath";
+ else
+ os << "Path" << (num + 1);
+ os << "\" title=\"Next event ("
+ << (num + 1)
+ << ")\">&#x2192;</a></div></td>";
+ }
+
+ os << "</tr></table>";
+ }
+
+ // Within a macro piece. Write out each event.
+ ProcessMacroPiece(os, *MP, 0);
+ }
+ else {
+ os << html::EscapeText(P.getString());
+
+ if (!SuppressIndex) {
+ os << "</td>";
+ if (num < max) {
+ os << "<td><div class=\"PathNav\"><a href=\"#";
+ if (num == max - 1)
+ os << "EndPath";
+ else
+ os << "Path" << (num + 1);
+ os << "\" title=\"Next event ("
+ << (num + 1)
+ << ")\">&#x2192;</a></div></td>";
+ }
+
+ os << "</tr></table>";
+ }
+ }
+
+ os << "</div></td></tr>";
+
+ // Insert the new html.
+ unsigned DisplayPos = LineEnd - FileStart;
+ SourceLocation Loc =
+ SM.getLocForStartOfFile(LPosInfo.first).getLocWithOffset(DisplayPos);
+
+ R.InsertTextBefore(Loc, os.str());
+
+ // Now highlight the ranges.
+ ArrayRef<SourceRange> Ranges = P.getRanges();
+ for (const auto &Range : Ranges)
+ HighlightRange(R, LPosInfo.first, Range);
+}
+
+static void EmitAlphaCounter(raw_ostream &os, unsigned n) {
+ unsigned x = n % ('z' - 'a');
+ n /= 'z' - 'a';
+
+ if (n > 0)
+ EmitAlphaCounter(os, n);
+
+ os << char('a' + x);
+}
+
+unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
+ const PathDiagnosticMacroPiece& P,
+ unsigned num) {
+ for (const auto &subPiece : P.subPieces) {
+ if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(subPiece.get())) {
+ num = ProcessMacroPiece(os, *MP, num);
+ continue;
+ }
+
+ if (const auto *EP = dyn_cast<PathDiagnosticEventPiece>(subPiece.get())) {
+ os << "<div class=\"msg msgEvent\" style=\"width:94%; "
+ "margin-left:5px\">"
+ "<table class=\"msgT\"><tr>"
+ "<td valign=\"top\"><div class=\"PathIndex PathIndexEvent\">";
+ EmitAlphaCounter(os, num++);
+ os << "</div></td><td valign=\"top\">"
+ << html::EscapeText(EP->getString())
+ << "</td></tr></table></div>\n";
+ }
+ }
+
+ return num;
+}
+
+void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
+ SourceRange Range,
+ const char *HighlightStart,
+ const char *HighlightEnd) {
+ SourceManager &SM = R.getSourceMgr();
+ const LangOptions &LangOpts = R.getLangOpts();
+
+ SourceLocation InstantiationStart = SM.getExpansionLoc(Range.getBegin());
+ unsigned StartLineNo = SM.getExpansionLineNumber(InstantiationStart);
+
+ SourceLocation InstantiationEnd = SM.getExpansionLoc(Range.getEnd());
+ unsigned EndLineNo = SM.getExpansionLineNumber(InstantiationEnd);
+
+ if (EndLineNo < StartLineNo)
+ return;
+
+ if (SM.getFileID(InstantiationStart) != BugFileID ||
+ SM.getFileID(InstantiationEnd) != BugFileID)
+ return;
+
+ // Compute the column number of the end.
+ unsigned EndColNo = SM.getExpansionColumnNumber(InstantiationEnd);
+ unsigned OldEndColNo = EndColNo;
+
+ if (EndColNo) {
+ // Add in the length of the token, so that we cover multi-char tokens.
+ EndColNo += Lexer::MeasureTokenLength(Range.getEnd(), SM, LangOpts)-1;
+ }
+
+ // Highlight the range. Make the span tag the outermost tag for the
+ // selected range.
+
+ SourceLocation E =
+ InstantiationEnd.getLocWithOffset(EndColNo - OldEndColNo);
+
+ html::HighlightRange(R, InstantiationStart, E, HighlightStart, HighlightEnd);
+}
+
+StringRef HTMLDiagnostics::generateKeyboardNavigationJavascript() {
+ return R"<<<(
+<script type='text/javascript'>
+var digitMatcher = new RegExp("[0-9]+");
+
+document.addEventListener("DOMContentLoaded", function() {
+ document.querySelectorAll(".PathNav > a").forEach(
+ function(currentValue, currentIndex) {
+ var hrefValue = currentValue.getAttribute("href");
+ currentValue.onclick = function() {
+ scrollTo(document.querySelector(hrefValue));
+ return false;
+ };
+ });
+});
+
+var findNum = function() {
+ var s = document.querySelector(".selected");
+ if (!s || s.id == "EndPath") {
+ return 0;
+ }
+ var out = parseInt(digitMatcher.exec(s.id)[0]);
+ return out;
+};
+
+var scrollTo = function(el) {
+ document.querySelectorAll(".selected").forEach(function(s) {
+ s.classList.remove("selected");
+ });
+ el.classList.add("selected");
+ window.scrollBy(0, el.getBoundingClientRect().top -
+ (window.innerHeight / 2));
+}
+
+var move = function(num, up, numItems) {
+ if (num == 1 && up || num == numItems - 1 && !up) {
+ return 0;
+ } else if (num == 0 && up) {
+ return numItems - 1;
+ } else if (num == 0 && !up) {
+ return 1 % numItems;
+ }
+ return up ? num - 1 : num + 1;
+}
+
+var numToId = function(num) {
+ if (num == 0) {
+ return document.getElementById("EndPath")
+ }
+ return document.getElementById("Path" + num);
+};
+
+var navigateTo = function(up) {
+ var numItems = document.querySelectorAll(
+ ".line > .msgEvent, .line > .msgControl").length;
+ var currentSelected = findNum();
+ var newSelected = move(currentSelected, up, numItems);
+ var newEl = numToId(newSelected, numItems);
+
+ // Scroll element into center.
+ scrollTo(newEl);
+};
+
+window.addEventListener("keydown", function (event) {
+ if (event.defaultPrevented) {
+ return;
+ }
+ if (event.key == "j") {
+ navigateTo(/*up=*/false);
+ } else if (event.key == "k") {
+ navigateTo(/*up=*/true);
+ } else {
+ return;
+ }
+ event.preventDefault();
+}, true);
+</script>
+ )<<<";
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/IssueHash.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/IssueHash.cpp
new file mode 100644
index 000000000000..6c55c61dd399
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/IssueHash.cpp
@@ -0,0 +1,205 @@
+//===---------- IssueHash.cpp - Generate identification hashes --*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+#include "clang/StaticAnalyzer/Core/IssueHash.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Specifiers.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/LineIterator.h"
+#include "llvm/Support/MD5.h"
+#include "llvm/Support/Path.h"
+
+#include <functional>
+#include <sstream>
+#include <string>
+
+using namespace clang;
+
+// Get a string representation of the parts of the signature that can be
+// overloaded on.
+static std::string GetSignature(const FunctionDecl *Target) {
+ if (!Target)
+ return "";
+ std::string Signature;
+
+ // When a flow sensitive bug happens in templated code we should not generate
+ // distinct hash value for every instantiation. Use the signature from the
+ // primary template.
+ if (const FunctionDecl *InstantiatedFrom =
+ Target->getTemplateInstantiationPattern())
+ Target = InstantiatedFrom;
+
+ if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) &&
+ !isa<CXXConversionDecl>(Target))
+ Signature.append(Target->getReturnType().getAsString()).append(" ");
+ Signature.append(Target->getQualifiedNameAsString()).append("(");
+
+ for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) {
+ if (i)
+ Signature.append(", ");
+ Signature.append(Target->getParamDecl(i)->getType().getAsString());
+ }
+
+ if (Target->isVariadic())
+ Signature.append(", ...");
+ Signature.append(")");
+
+ const auto *TargetT =
+ llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr());
+
+ if (!TargetT || !isa<CXXMethodDecl>(Target))
+ return Signature;
+
+ if (TargetT->isConst())
+ Signature.append(" const");
+ if (TargetT->isVolatile())
+ Signature.append(" volatile");
+ if (TargetT->isRestrict())
+ Signature.append(" restrict");
+
+ if (const auto *TargetPT =
+ dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) {
+ switch (TargetPT->getRefQualifier()) {
+ case RQ_LValue:
+ Signature.append(" &");
+ break;
+ case RQ_RValue:
+ Signature.append(" &&");
+ break;
+ default:
+ break;
+ }
+ }
+
+ return Signature;
+}
+
+static std::string GetEnclosingDeclContextSignature(const Decl *D) {
+ if (!D)
+ return "";
+
+ if (const auto *ND = dyn_cast<NamedDecl>(D)) {
+ std::string DeclName;
+
+ switch (ND->getKind()) {
+ case Decl::Namespace:
+ case Decl::Record:
+ case Decl::CXXRecord:
+ case Decl::Enum:
+ DeclName = ND->getQualifiedNameAsString();
+ break;
+ case Decl::CXXConstructor:
+ case Decl::CXXDestructor:
+ case Decl::CXXConversion:
+ case Decl::CXXMethod:
+ case Decl::Function:
+ DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND));
+ break;
+ case Decl::ObjCMethod:
+ // ObjC Methods can not be overloaded, qualified name uniquely identifies
+ // the method.
+ DeclName = ND->getQualifiedNameAsString();
+ break;
+ default:
+ break;
+ }
+
+ return DeclName;
+ }
+
+ return "";
+}
+
+static StringRef GetNthLineOfFile(llvm::MemoryBuffer *Buffer, int Line) {
+ if (!Buffer)
+ return "";
+
+ llvm::line_iterator LI(*Buffer, false);
+ for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)
+ ;
+
+ return *LI;
+}
+
+static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L,
+ const LangOptions &LangOpts) {
+ static StringRef Whitespaces = " \t\n";
+
+ StringRef Str = GetNthLineOfFile(SM.getBuffer(L.getFileID(), L),
+ L.getExpansionLineNumber());
+ StringRef::size_type col = Str.find_first_not_of(Whitespaces);
+ if (col == StringRef::npos)
+ col = 1; // The line only contains whitespace.
+ else
+ col++;
+ SourceLocation StartOfLine =
+ SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);
+ llvm::MemoryBuffer *Buffer =
+ SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine);
+ if (!Buffer)
+ return {};
+
+ const char *BufferPos = SM.getCharacterData(StartOfLine);
+
+ Token Token;
+ Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,
+ Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
+
+ size_t NextStart = 0;
+ std::ostringstream LineBuff;
+ while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {
+ if (Token.isAtStartOfLine() && NextStart++ > 0)
+ continue;
+ LineBuff << std::string(SM.getCharacterData(Token.getLocation()),
+ Token.getLength());
+ }
+
+ return LineBuff.str();
+}
+
+static llvm::SmallString<32> GetHashOfContent(StringRef Content) {
+ llvm::MD5 Hash;
+ llvm::MD5::MD5Result MD5Res;
+ SmallString<32> Res;
+
+ Hash.update(Content);
+ Hash.final(MD5Res);
+ llvm::MD5::stringifyResult(MD5Res, Res);
+
+ return Res;
+}
+
+std::string clang::GetIssueString(const SourceManager &SM,
+ FullSourceLoc &IssueLoc,
+ StringRef CheckerName, StringRef BugType,
+ const Decl *D,
+ const LangOptions &LangOpts) {
+ static StringRef Delimiter = "$";
+
+ return (llvm::Twine(CheckerName) + Delimiter +
+ GetEnclosingDeclContextSignature(D) + Delimiter +
+ Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +
+ NormalizeLine(SM, IssueLoc, LangOpts) + Delimiter + BugType)
+ .str();
+}
+
+SmallString<32> clang::GetIssueHash(const SourceManager &SM,
+ FullSourceLoc &IssueLoc,
+ StringRef CheckerName, StringRef BugType,
+ const Decl *D,
+ const LangOptions &LangOpts) {
+
+ return GetHashOfContent(
+ GetIssueString(SM, IssueLoc, CheckerName, BugType, D, LangOpts));
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
new file mode 100644
index 000000000000..da4574c61515
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
@@ -0,0 +1,294 @@
+//===--- LoopUnrolling.cpp - Unroll loops -----------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// This file contains functions which are used to decide if a loop worth to be
+/// unrolled. Moreover, these functions manages the stack of loop which is
+/// tracked by the ProgramState.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/LoopUnrolling.h"
+
+using namespace clang;
+using namespace ento;
+using namespace clang::ast_matchers;
+
+static const int MAXIMUM_STEP_UNROLLED = 128;
+
+struct LoopState {
+private:
+ enum Kind { Normal, Unrolled } K;
+ const Stmt *LoopStmt;
+ const LocationContext *LCtx;
+ unsigned maxStep;
+ LoopState(Kind InK, const Stmt *S, const LocationContext *L, unsigned N)
+ : K(InK), LoopStmt(S), LCtx(L), maxStep(N) {}
+
+public:
+ static LoopState getNormal(const Stmt *S, const LocationContext *L,
+ unsigned N) {
+ return LoopState(Normal, S, L, N);
+ }
+ static LoopState getUnrolled(const Stmt *S, const LocationContext *L,
+ unsigned N) {
+ return LoopState(Unrolled, S, L, N);
+ }
+ bool isUnrolled() const { return K == Unrolled; }
+ unsigned getMaxStep() const { return maxStep; }
+ const Stmt *getLoopStmt() const { return LoopStmt; }
+ const LocationContext *getLocationContext() const { return LCtx; }
+ bool operator==(const LoopState &X) const {
+ return K == X.K && LoopStmt == X.LoopStmt;
+ }
+ void Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(K);
+ ID.AddPointer(LoopStmt);
+ ID.AddPointer(LCtx);
+ ID.AddInteger(maxStep);
+ }
+};
+
+// The tracked stack of loops. The stack indicates that which loops the
+// simulated element contained by. The loops are marked depending if we decided
+// to unroll them.
+// TODO: The loop stack should not need to be in the program state since it is
+// lexical in nature. Instead, the stack of loops should be tracked in the
+// LocationContext.
+REGISTER_LIST_WITH_PROGRAMSTATE(LoopStack, LoopState)
+
+namespace clang {
+namespace ento {
+
+static bool isLoopStmt(const Stmt *S) {
+ return S && (isa<ForStmt>(S) || isa<WhileStmt>(S) || isa<DoStmt>(S));
+}
+
+ProgramStateRef processLoopEnd(const Stmt *LoopStmt, ProgramStateRef State) {
+ auto LS = State->get<LoopStack>();
+ if (!LS.isEmpty() && LS.getHead().getLoopStmt() == LoopStmt)
+ State = State->set<LoopStack>(LS.getTail());
+ return State;
+}
+
+static internal::Matcher<Stmt> simpleCondition(StringRef BindName) {
+ return binaryOperator(anyOf(hasOperatorName("<"), hasOperatorName(">"),
+ hasOperatorName("<="), hasOperatorName(">="),
+ hasOperatorName("!=")),
+ hasEitherOperand(ignoringParenImpCasts(declRefExpr(
+ to(varDecl(hasType(isInteger())).bind(BindName))))),
+ hasEitherOperand(ignoringParenImpCasts(
+ integerLiteral().bind("boundNum"))))
+ .bind("conditionOperator");
+}
+
+static internal::Matcher<Stmt>
+changeIntBoundNode(internal::Matcher<Decl> VarNodeMatcher) {
+ return anyOf(
+ unaryOperator(anyOf(hasOperatorName("--"), hasOperatorName("++")),
+ hasUnaryOperand(ignoringParenImpCasts(
+ declRefExpr(to(varDecl(VarNodeMatcher)))))),
+ binaryOperator(isAssignmentOperator(),
+ hasLHS(ignoringParenImpCasts(
+ declRefExpr(to(varDecl(VarNodeMatcher)))))));
+}
+
+static internal::Matcher<Stmt>
+callByRef(internal::Matcher<Decl> VarNodeMatcher) {
+ return callExpr(forEachArgumentWithParam(
+ declRefExpr(to(varDecl(VarNodeMatcher))),
+ parmVarDecl(hasType(references(qualType(unless(isConstQualified())))))));
+}
+
+static internal::Matcher<Stmt>
+assignedToRef(internal::Matcher<Decl> VarNodeMatcher) {
+ return declStmt(hasDescendant(varDecl(
+ allOf(hasType(referenceType()),
+ hasInitializer(anyOf(
+ initListExpr(has(declRefExpr(to(varDecl(VarNodeMatcher))))),
+ declRefExpr(to(varDecl(VarNodeMatcher)))))))));
+}
+
+static internal::Matcher<Stmt>
+getAddrTo(internal::Matcher<Decl> VarNodeMatcher) {
+ return unaryOperator(
+ hasOperatorName("&"),
+ hasUnaryOperand(declRefExpr(hasDeclaration(VarNodeMatcher))));
+}
+
+static internal::Matcher<Stmt> hasSuspiciousStmt(StringRef NodeName) {
+ return hasDescendant(stmt(
+ anyOf(gotoStmt(), switchStmt(), returnStmt(),
+ // Escaping and not known mutation of the loop counter is handled
+ // by exclusion of assigning and address-of operators and
+ // pass-by-ref function calls on the loop counter from the body.
+ changeIntBoundNode(equalsBoundNode(NodeName)),
+ callByRef(equalsBoundNode(NodeName)),
+ getAddrTo(equalsBoundNode(NodeName)),
+ assignedToRef(equalsBoundNode(NodeName)))));
+}
+
+static internal::Matcher<Stmt> forLoopMatcher() {
+ return forStmt(
+ hasCondition(simpleCondition("initVarName")),
+ // Initialization should match the form: 'int i = 6' or 'i = 42'.
+ hasLoopInit(
+ anyOf(declStmt(hasSingleDecl(
+ varDecl(allOf(hasInitializer(ignoringParenImpCasts(
+ integerLiteral().bind("initNum"))),
+ equalsBoundNode("initVarName"))))),
+ binaryOperator(hasLHS(declRefExpr(to(varDecl(
+ equalsBoundNode("initVarName"))))),
+ hasRHS(ignoringParenImpCasts(
+ integerLiteral().bind("initNum")))))),
+ // Incrementation should be a simple increment or decrement
+ // operator call.
+ hasIncrement(unaryOperator(
+ anyOf(hasOperatorName("++"), hasOperatorName("--")),
+ hasUnaryOperand(declRefExpr(
+ to(varDecl(allOf(equalsBoundNode("initVarName"),
+ hasType(isInteger())))))))),
+ unless(hasBody(hasSuspiciousStmt("initVarName")))).bind("forLoop");
+}
+
+static bool isPossiblyEscaped(const VarDecl *VD, ExplodedNode *N) {
+ // Global variables assumed as escaped variables.
+ if (VD->hasGlobalStorage())
+ return true;
+
+ while (!N->pred_empty()) {
+ const Stmt *S = PathDiagnosticLocation::getStmt(N);
+ if (!S) {
+ N = N->getFirstPred();
+ continue;
+ }
+
+ if (const DeclStmt *DS = dyn_cast<DeclStmt>(S)) {
+ for (const Decl *D : DS->decls()) {
+ // Once we reach the declaration of the VD we can return.
+ if (D->getCanonicalDecl() == VD)
+ return false;
+ }
+ }
+ // Check the usage of the pass-by-ref function calls and adress-of operator
+ // on VD and reference initialized by VD.
+ ASTContext &ASTCtx =
+ N->getLocationContext()->getAnalysisDeclContext()->getASTContext();
+ auto Match =
+ match(stmt(anyOf(callByRef(equalsNode(VD)), getAddrTo(equalsNode(VD)),
+ assignedToRef(equalsNode(VD)))),
+ *S, ASTCtx);
+ if (!Match.empty())
+ return true;
+
+ N = N->getFirstPred();
+ }
+ llvm_unreachable("Reached root without finding the declaration of VD");
+}
+
+bool shouldCompletelyUnroll(const Stmt *LoopStmt, ASTContext &ASTCtx,
+ ExplodedNode *Pred, unsigned &maxStep) {
+
+ if (!isLoopStmt(LoopStmt))
+ return false;
+
+ // TODO: Match the cases where the bound is not a concrete literal but an
+ // integer with known value
+ auto Matches = match(forLoopMatcher(), *LoopStmt, ASTCtx);
+ if (Matches.empty())
+ return false;
+
+ auto CounterVar = Matches[0].getNodeAs<VarDecl>("initVarName");
+ llvm::APInt BoundNum =
+ Matches[0].getNodeAs<IntegerLiteral>("boundNum")->getValue();
+ llvm::APInt InitNum =
+ Matches[0].getNodeAs<IntegerLiteral>("initNum")->getValue();
+ auto CondOp = Matches[0].getNodeAs<BinaryOperator>("conditionOperator");
+ if (InitNum.getBitWidth() != BoundNum.getBitWidth()) {
+ InitNum = InitNum.zextOrSelf(BoundNum.getBitWidth());
+ BoundNum = BoundNum.zextOrSelf(InitNum.getBitWidth());
+ }
+
+ if (CondOp->getOpcode() == BO_GE || CondOp->getOpcode() == BO_LE)
+ maxStep = (BoundNum - InitNum + 1).abs().getZExtValue();
+ else
+ maxStep = (BoundNum - InitNum).abs().getZExtValue();
+
+ // Check if the counter of the loop is not escaped before.
+ return !isPossiblyEscaped(CounterVar->getCanonicalDecl(), Pred);
+}
+
+bool madeNewBranch(ExplodedNode *N, const Stmt *LoopStmt) {
+ const Stmt *S = nullptr;
+ while (!N->pred_empty()) {
+ if (N->succ_size() > 1)
+ return true;
+
+ ProgramPoint P = N->getLocation();
+ if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>())
+ S = BE->getBlock()->getTerminator();
+
+ if (S == LoopStmt)
+ return false;
+
+ N = N->getFirstPred();
+ }
+
+ llvm_unreachable("Reached root without encountering the previous step");
+}
+
+// updateLoopStack is called on every basic block, therefore it needs to be fast
+ProgramStateRef updateLoopStack(const Stmt *LoopStmt, ASTContext &ASTCtx,
+ ExplodedNode *Pred, unsigned maxVisitOnPath) {
+ auto State = Pred->getState();
+ auto LCtx = Pred->getLocationContext();
+
+ if (!isLoopStmt(LoopStmt))
+ return State;
+
+ auto LS = State->get<LoopStack>();
+ if (!LS.isEmpty() && LoopStmt == LS.getHead().getLoopStmt() &&
+ LCtx == LS.getHead().getLocationContext()) {
+ if (LS.getHead().isUnrolled() && madeNewBranch(Pred, LoopStmt)) {
+ State = State->set<LoopStack>(LS.getTail());
+ State = State->add<LoopStack>(
+ LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath));
+ }
+ return State;
+ }
+ unsigned maxStep;
+ if (!shouldCompletelyUnroll(LoopStmt, ASTCtx, Pred, maxStep)) {
+ State = State->add<LoopStack>(
+ LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath));
+ return State;
+ }
+
+ unsigned outerStep = (LS.isEmpty() ? 1 : LS.getHead().getMaxStep());
+
+ unsigned innerMaxStep = maxStep * outerStep;
+ if (innerMaxStep > MAXIMUM_STEP_UNROLLED)
+ State = State->add<LoopStack>(
+ LoopState::getNormal(LoopStmt, LCtx, maxVisitOnPath));
+ else
+ State = State->add<LoopStack>(
+ LoopState::getUnrolled(LoopStmt, LCtx, innerMaxStep));
+ return State;
+}
+
+bool isUnrolledState(ProgramStateRef State) {
+ auto LS = State->get<LoopStack>();
+ if (LS.isEmpty() || !LS.getHead().isUnrolled())
+ return false;
+ return true;
+}
+}
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
new file mode 100644
index 000000000000..8f6cb9a6b09e
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
@@ -0,0 +1,100 @@
+//===--- LoopWidening.cpp - Widen loops -------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// This file contains functions which are used to widen loops. A loop may be
+/// widened to approximate the exit state(s), without analyzing every
+/// iteration. The widening is done by invalidating anything which might be
+/// modified by the body of the loop.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/AST.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/LoopWidening.h"
+
+using namespace clang;
+using namespace ento;
+using namespace clang::ast_matchers;
+
+const auto MatchRef = "matchref";
+
+/// Return the loops condition Stmt or NULL if LoopStmt is not a loop
+static const Expr *getLoopCondition(const Stmt *LoopStmt) {
+ switch (LoopStmt->getStmtClass()) {
+ default:
+ return nullptr;
+ case Stmt::ForStmtClass:
+ return cast<ForStmt>(LoopStmt)->getCond();
+ case Stmt::WhileStmtClass:
+ return cast<WhileStmt>(LoopStmt)->getCond();
+ case Stmt::DoStmtClass:
+ return cast<DoStmt>(LoopStmt)->getCond();
+ }
+}
+
+namespace clang {
+namespace ento {
+
+ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState,
+ const LocationContext *LCtx,
+ unsigned BlockCount, const Stmt *LoopStmt) {
+
+ assert(isa<ForStmt>(LoopStmt) || isa<WhileStmt>(LoopStmt) ||
+ isa<DoStmt>(LoopStmt));
+
+ // Invalidate values in the current state.
+ // TODO Make this more conservative by only invalidating values that might
+ // be modified by the body of the loop.
+ // TODO Nested loops are currently widened as a result of the invalidation
+ // being so inprecise. When the invalidation is improved, the handling
+ // of nested loops will also need to be improved.
+ ASTContext &ASTCtx = LCtx->getAnalysisDeclContext()->getASTContext();
+ const StackFrameContext *STC = LCtx->getStackFrame();
+ MemRegionManager &MRMgr = PrevState->getStateManager().getRegionManager();
+ const MemRegion *Regions[] = {MRMgr.getStackLocalsRegion(STC),
+ MRMgr.getStackArgumentsRegion(STC),
+ MRMgr.getGlobalsRegion()};
+ RegionAndSymbolInvalidationTraits ITraits;
+ for (auto *Region : Regions) {
+ ITraits.setTrait(Region,
+ RegionAndSymbolInvalidationTraits::TK_EntireMemSpace);
+ }
+
+ // References should not be invalidated.
+ auto Matches = match(findAll(stmt(hasDescendant(varDecl(hasType(referenceType())).bind(MatchRef)))),
+ *LCtx->getDecl()->getBody(), ASTCtx);
+ for (BoundNodes Match : Matches) {
+ const VarDecl *VD = Match.getNodeAs<VarDecl>(MatchRef);
+ assert(VD);
+ const VarRegion *VarMem = MRMgr.getVarRegion(VD, LCtx);
+ ITraits.setTrait(VarMem,
+ RegionAndSymbolInvalidationTraits::TK_PreserveContents);
+ }
+
+
+ // 'this' pointer is not an lvalue, we should not invalidate it. If the loop
+ // is located in a method, constructor or destructor, the value of 'this'
+ // pointer should remain unchanged. Ignore static methods, since they do not
+ // have 'this' pointers.
+ const CXXMethodDecl *CXXMD = dyn_cast<CXXMethodDecl>(STC->getDecl());
+ if (CXXMD && !CXXMD->isStatic()) {
+ const CXXThisRegion *ThisR =
+ MRMgr.getCXXThisRegion(CXXMD->getThisType(), STC);
+ ITraits.setTrait(ThisR,
+ RegionAndSymbolInvalidationTraits::TK_PreserveContents);
+ }
+
+ return PrevState->invalidateRegions(Regions, getLoopCondition(LoopStmt),
+ BlockCount, LCtx, true, nullptr, nullptr,
+ &ITraits);
+}
+
+} // end namespace ento
+} // end namespace clang
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
new file mode 100644
index 000000000000..9a1d4d73c20b
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -0,0 +1,1618 @@
+//===- MemRegion.cpp - Abstract memory regions for static analysis --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines MemRegion and its subclasses. MemRegion defines a
+// partially-typed abstraction of memory useful for path-sensitive dataflow
+// analyses.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/PrettyPrinter.h"
+#include "clang/AST/RecordLayout.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/Support/BumpVector.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/APInt.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/PointerUnion.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/Twine.h"
+#include "llvm/Support/Allocator.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/CheckedArithmetic.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstdint>
+#include <functional>
+#include <iterator>
+#include <string>
+#include <tuple>
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+#define DEBUG_TYPE "MemRegion"
+
+//===----------------------------------------------------------------------===//
+// MemRegion Construction.
+//===----------------------------------------------------------------------===//
+
+template <typename RegionTy, typename SuperTy, typename Arg1Ty>
+RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1,
+ const SuperTy *superRegion) {
+ llvm::FoldingSetNodeID ID;
+ RegionTy::ProfileRegion(ID, arg1, superRegion);
+ void *InsertPos;
+ auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos));
+
+ if (!R) {
+ R = A.Allocate<RegionTy>();
+ new (R) RegionTy(arg1, superRegion);
+ Regions.InsertNode(R, InsertPos);
+ }
+
+ return R;
+}
+
+template <typename RegionTy, typename SuperTy, typename Arg1Ty, typename Arg2Ty>
+RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2,
+ const SuperTy *superRegion) {
+ llvm::FoldingSetNodeID ID;
+ RegionTy::ProfileRegion(ID, arg1, arg2, superRegion);
+ void *InsertPos;
+ auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos));
+
+ if (!R) {
+ R = A.Allocate<RegionTy>();
+ new (R) RegionTy(arg1, arg2, superRegion);
+ Regions.InsertNode(R, InsertPos);
+ }
+
+ return R;
+}
+
+template <typename RegionTy, typename SuperTy,
+ typename Arg1Ty, typename Arg2Ty, typename Arg3Ty>
+RegionTy* MemRegionManager::getSubRegion(const Arg1Ty arg1, const Arg2Ty arg2,
+ const Arg3Ty arg3,
+ const SuperTy *superRegion) {
+ llvm::FoldingSetNodeID ID;
+ RegionTy::ProfileRegion(ID, arg1, arg2, arg3, superRegion);
+ void *InsertPos;
+ auto *R = cast_or_null<RegionTy>(Regions.FindNodeOrInsertPos(ID, InsertPos));
+
+ if (!R) {
+ R = A.Allocate<RegionTy>();
+ new (R) RegionTy(arg1, arg2, arg3, superRegion);
+ Regions.InsertNode(R, InsertPos);
+ }
+
+ return R;
+}
+
+//===----------------------------------------------------------------------===//
+// Object destruction.
+//===----------------------------------------------------------------------===//
+
+MemRegion::~MemRegion() = default;
+
+// All regions and their data are BumpPtrAllocated. No need to call their
+// destructors.
+MemRegionManager::~MemRegionManager() = default;
+
+//===----------------------------------------------------------------------===//
+// Basic methods.
+//===----------------------------------------------------------------------===//
+
+bool SubRegion::isSubRegionOf(const MemRegion* R) const {
+ const MemRegion* r = this;
+ do {
+ if (r == R)
+ return true;
+ if (const auto *sr = dyn_cast<SubRegion>(r))
+ r = sr->getSuperRegion();
+ else
+ break;
+ } while (r != nullptr);
+ return false;
+}
+
+MemRegionManager* SubRegion::getMemRegionManager() const {
+ const SubRegion* r = this;
+ do {
+ const MemRegion *superRegion = r->getSuperRegion();
+ if (const auto *sr = dyn_cast<SubRegion>(superRegion)) {
+ r = sr;
+ continue;
+ }
+ return superRegion->getMemRegionManager();
+ } while (true);
+}
+
+const StackFrameContext *VarRegion::getStackFrame() const {
+ const auto *SSR = dyn_cast<StackSpaceRegion>(getMemorySpace());
+ return SSR ? SSR->getStackFrame() : nullptr;
+}
+
+//===----------------------------------------------------------------------===//
+// Region extents.
+//===----------------------------------------------------------------------===//
+
+DefinedOrUnknownSVal TypedValueRegion::getExtent(SValBuilder &svalBuilder) const {
+ ASTContext &Ctx = svalBuilder.getContext();
+ QualType T = getDesugaredValueType(Ctx);
+
+ if (isa<VariableArrayType>(T))
+ return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this));
+ if (T->isIncompleteType())
+ return UnknownVal();
+
+ CharUnits size = Ctx.getTypeSizeInChars(T);
+ QualType sizeTy = svalBuilder.getArrayIndexType();
+ return svalBuilder.makeIntVal(size.getQuantity(), sizeTy);
+}
+
+DefinedOrUnknownSVal FieldRegion::getExtent(SValBuilder &svalBuilder) const {
+ // Force callers to deal with bitfields explicitly.
+ if (getDecl()->isBitField())
+ return UnknownVal();
+
+ DefinedOrUnknownSVal Extent = DeclRegion::getExtent(svalBuilder);
+
+ // A zero-length array at the end of a struct often stands for dynamically-
+ // allocated extra memory.
+ if (Extent.isZeroConstant()) {
+ QualType T = getDesugaredValueType(svalBuilder.getContext());
+
+ if (isa<ConstantArrayType>(T))
+ return UnknownVal();
+ }
+
+ return Extent;
+}
+
+DefinedOrUnknownSVal AllocaRegion::getExtent(SValBuilder &svalBuilder) const {
+ return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this));
+}
+
+DefinedOrUnknownSVal SymbolicRegion::getExtent(SValBuilder &svalBuilder) const {
+ return nonloc::SymbolVal(svalBuilder.getSymbolManager().getExtentSymbol(this));
+}
+
+DefinedOrUnknownSVal StringRegion::getExtent(SValBuilder &svalBuilder) const {
+ return svalBuilder.makeIntVal(getStringLiteral()->getByteLength()+1,
+ svalBuilder.getArrayIndexType());
+}
+
+ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg)
+ : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {}
+
+const ObjCIvarDecl *ObjCIvarRegion::getDecl() const {
+ return cast<ObjCIvarDecl>(D);
+}
+
+QualType ObjCIvarRegion::getValueType() const {
+ return getDecl()->getType();
+}
+
+QualType CXXBaseObjectRegion::getValueType() const {
+ return QualType(getDecl()->getTypeForDecl(), 0);
+}
+
+QualType CXXDerivedObjectRegion::getValueType() const {
+ return QualType(getDecl()->getTypeForDecl(), 0);
+}
+
+//===----------------------------------------------------------------------===//
+// FoldingSet profiling.
+//===----------------------------------------------------------------------===//
+
+void MemSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(static_cast<unsigned>(getKind()));
+}
+
+void StackSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(static_cast<unsigned>(getKind()));
+ ID.AddPointer(getStackFrame());
+}
+
+void StaticGlobalSpaceRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(static_cast<unsigned>(getKind()));
+ ID.AddPointer(getCodeRegion());
+}
+
+void StringRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ const StringLiteral *Str,
+ const MemRegion *superRegion) {
+ ID.AddInteger(static_cast<unsigned>(StringRegionKind));
+ ID.AddPointer(Str);
+ ID.AddPointer(superRegion);
+}
+
+void ObjCStringRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ const ObjCStringLiteral *Str,
+ const MemRegion *superRegion) {
+ ID.AddInteger(static_cast<unsigned>(ObjCStringRegionKind));
+ ID.AddPointer(Str);
+ ID.AddPointer(superRegion);
+}
+
+void AllocaRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
+ const Expr *Ex, unsigned cnt,
+ const MemRegion *superRegion) {
+ ID.AddInteger(static_cast<unsigned>(AllocaRegionKind));
+ ID.AddPointer(Ex);
+ ID.AddInteger(cnt);
+ ID.AddPointer(superRegion);
+}
+
+void AllocaRegion::Profile(llvm::FoldingSetNodeID& ID) const {
+ ProfileRegion(ID, Ex, Cnt, superRegion);
+}
+
+void CompoundLiteralRegion::Profile(llvm::FoldingSetNodeID& ID) const {
+ CompoundLiteralRegion::ProfileRegion(ID, CL, superRegion);
+}
+
+void CompoundLiteralRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
+ const CompoundLiteralExpr *CL,
+ const MemRegion* superRegion) {
+ ID.AddInteger(static_cast<unsigned>(CompoundLiteralRegionKind));
+ ID.AddPointer(CL);
+ ID.AddPointer(superRegion);
+}
+
+void CXXThisRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ const PointerType *PT,
+ const MemRegion *sRegion) {
+ ID.AddInteger(static_cast<unsigned>(CXXThisRegionKind));
+ ID.AddPointer(PT);
+ ID.AddPointer(sRegion);
+}
+
+void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion);
+}
+
+void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
+ const ObjCIvarDecl *ivd,
+ const MemRegion* superRegion) {
+ DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind);
+}
+
+void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D,
+ const MemRegion* superRegion, Kind k) {
+ ID.AddInteger(static_cast<unsigned>(k));
+ ID.AddPointer(D);
+ ID.AddPointer(superRegion);
+}
+
+void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const {
+ DeclRegion::ProfileRegion(ID, D, superRegion, getKind());
+}
+
+void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ VarRegion::ProfileRegion(ID, getDecl(), superRegion);
+}
+
+void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym,
+ const MemRegion *sreg) {
+ ID.AddInteger(static_cast<unsigned>(MemRegion::SymbolicRegionKind));
+ ID.Add(sym);
+ ID.AddPointer(sreg);
+}
+
+void SymbolicRegion::Profile(llvm::FoldingSetNodeID& ID) const {
+ SymbolicRegion::ProfileRegion(ID, sym, getSuperRegion());
+}
+
+void ElementRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
+ QualType ElementType, SVal Idx,
+ const MemRegion* superRegion) {
+ ID.AddInteger(MemRegion::ElementRegionKind);
+ ID.Add(ElementType);
+ ID.AddPointer(superRegion);
+ Idx.Profile(ID);
+}
+
+void ElementRegion::Profile(llvm::FoldingSetNodeID& ID) const {
+ ElementRegion::ProfileRegion(ID, ElementType, Index, superRegion);
+}
+
+void FunctionCodeRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
+ const NamedDecl *FD,
+ const MemRegion*) {
+ ID.AddInteger(MemRegion::FunctionCodeRegionKind);
+ ID.AddPointer(FD);
+}
+
+void FunctionCodeRegion::Profile(llvm::FoldingSetNodeID& ID) const {
+ FunctionCodeRegion::ProfileRegion(ID, FD, superRegion);
+}
+
+void BlockCodeRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
+ const BlockDecl *BD, CanQualType,
+ const AnalysisDeclContext *AC,
+ const MemRegion*) {
+ ID.AddInteger(MemRegion::BlockCodeRegionKind);
+ ID.AddPointer(BD);
+}
+
+void BlockCodeRegion::Profile(llvm::FoldingSetNodeID& ID) const {
+ BlockCodeRegion::ProfileRegion(ID, BD, locTy, AC, superRegion);
+}
+
+void BlockDataRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
+ const BlockCodeRegion *BC,
+ const LocationContext *LC,
+ unsigned BlkCount,
+ const MemRegion *sReg) {
+ ID.AddInteger(MemRegion::BlockDataRegionKind);
+ ID.AddPointer(BC);
+ ID.AddPointer(LC);
+ ID.AddInteger(BlkCount);
+ ID.AddPointer(sReg);
+}
+
+void BlockDataRegion::Profile(llvm::FoldingSetNodeID& ID) const {
+ BlockDataRegion::ProfileRegion(ID, BC, LC, BlockCount, getSuperRegion());
+}
+
+void CXXTempObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ Expr const *Ex,
+ const MemRegion *sReg) {
+ ID.AddPointer(Ex);
+ ID.AddPointer(sReg);
+}
+
+void CXXTempObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, Ex, getSuperRegion());
+}
+
+void CXXBaseObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ const CXXRecordDecl *RD,
+ bool IsVirtual,
+ const MemRegion *SReg) {
+ ID.AddPointer(RD);
+ ID.AddBoolean(IsVirtual);
+ ID.AddPointer(SReg);
+}
+
+void CXXBaseObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getDecl(), isVirtual(), superRegion);
+}
+
+void CXXDerivedObjectRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ const CXXRecordDecl *RD,
+ const MemRegion *SReg) {
+ ID.AddPointer(RD);
+ ID.AddPointer(SReg);
+}
+
+void CXXDerivedObjectRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getDecl(), superRegion);
+}
+
+//===----------------------------------------------------------------------===//
+// Region anchors.
+//===----------------------------------------------------------------------===//
+
+void GlobalsSpaceRegion::anchor() {}
+
+void NonStaticGlobalSpaceRegion::anchor() {}
+
+void StackSpaceRegion::anchor() {}
+
+void TypedRegion::anchor() {}
+
+void TypedValueRegion::anchor() {}
+
+void CodeTextRegion::anchor() {}
+
+void SubRegion::anchor() {}
+
+//===----------------------------------------------------------------------===//
+// Region pretty-printing.
+//===----------------------------------------------------------------------===//
+
+LLVM_DUMP_METHOD void MemRegion::dump() const {
+ dumpToStream(llvm::errs());
+}
+
+std::string MemRegion::getString() const {
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ dumpToStream(os);
+ return os.str();
+}
+
+void MemRegion::dumpToStream(raw_ostream &os) const {
+ os << "<Unknown Region>";
+}
+
+void AllocaRegion::dumpToStream(raw_ostream &os) const {
+ os << "alloca{S" << Ex->getID(getContext()) << ',' << Cnt << '}';
+}
+
+void FunctionCodeRegion::dumpToStream(raw_ostream &os) const {
+ os << "code{" << getDecl()->getDeclName().getAsString() << '}';
+}
+
+void BlockCodeRegion::dumpToStream(raw_ostream &os) const {
+ os << "block_code{" << static_cast<const void *>(this) << '}';
+}
+
+void BlockDataRegion::dumpToStream(raw_ostream &os) const {
+ os << "block_data{" << BC;
+ os << "; ";
+ for (BlockDataRegion::referenced_vars_iterator
+ I = referenced_vars_begin(),
+ E = referenced_vars_end(); I != E; ++I)
+ os << "(" << I.getCapturedRegion() << "<-" <<
+ I.getOriginalRegion() << ") ";
+ os << '}';
+}
+
+void CompoundLiteralRegion::dumpToStream(raw_ostream &os) const {
+ // FIXME: More elaborate pretty-printing.
+ os << "{ S" << CL->getID(getContext()) << " }";
+}
+
+void CXXTempObjectRegion::dumpToStream(raw_ostream &os) const {
+ os << "temp_object{" << getValueType().getAsString() << ", "
+ << "S" << Ex->getID(getContext()) << '}';
+}
+
+void CXXBaseObjectRegion::dumpToStream(raw_ostream &os) const {
+ os << "Base{" << superRegion << ',' << getDecl()->getName() << '}';
+}
+
+void CXXDerivedObjectRegion::dumpToStream(raw_ostream &os) const {
+ os << "Derived{" << superRegion << ',' << getDecl()->getName() << '}';
+}
+
+void CXXThisRegion::dumpToStream(raw_ostream &os) const {
+ os << "this";
+}
+
+void ElementRegion::dumpToStream(raw_ostream &os) const {
+ os << "Element{" << superRegion << ','
+ << Index << ',' << getElementType().getAsString() << '}';
+}
+
+void FieldRegion::dumpToStream(raw_ostream &os) const {
+ os << superRegion << "->" << *getDecl();
+}
+
+void ObjCIvarRegion::dumpToStream(raw_ostream &os) const {
+ os << "Ivar{" << superRegion << ',' << *getDecl() << '}';
+}
+
+void StringRegion::dumpToStream(raw_ostream &os) const {
+ assert(Str != nullptr && "Expecting non-null StringLiteral");
+ Str->printPretty(os, nullptr, PrintingPolicy(getContext().getLangOpts()));
+}
+
+void ObjCStringRegion::dumpToStream(raw_ostream &os) const {
+ assert(Str != nullptr && "Expecting non-null ObjCStringLiteral");
+ Str->printPretty(os, nullptr, PrintingPolicy(getContext().getLangOpts()));
+}
+
+void SymbolicRegion::dumpToStream(raw_ostream &os) const {
+ if (isa<HeapSpaceRegion>(getSuperRegion()))
+ os << "Heap";
+ os << "SymRegion{" << sym << '}';
+}
+
+void VarRegion::dumpToStream(raw_ostream &os) const {
+ const auto *VD = cast<VarDecl>(D);
+ if (const IdentifierInfo *ID = VD->getIdentifier())
+ os << ID->getName();
+ else
+ os << "VarRegion{D" << VD->getID() << '}';
+}
+
+LLVM_DUMP_METHOD void RegionRawOffset::dump() const {
+ dumpToStream(llvm::errs());
+}
+
+void RegionRawOffset::dumpToStream(raw_ostream &os) const {
+ os << "raw_offset{" << getRegion() << ',' << getOffset().getQuantity() << '}';
+}
+
+void CodeSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "CodeSpaceRegion";
+}
+
+void StaticGlobalSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "StaticGlobalsMemSpace{" << CR << '}';
+}
+
+void GlobalInternalSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "GlobalInternalSpaceRegion";
+}
+
+void GlobalSystemSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "GlobalSystemSpaceRegion";
+}
+
+void GlobalImmutableSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "GlobalImmutableSpaceRegion";
+}
+
+void HeapSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "HeapSpaceRegion";
+}
+
+void UnknownSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "UnknownSpaceRegion";
+}
+
+void StackArgumentsSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "StackArgumentsSpaceRegion";
+}
+
+void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) const {
+ os << "StackLocalsSpaceRegion";
+}
+
+bool MemRegion::canPrintPretty() const {
+ return canPrintPrettyAsExpr();
+}
+
+bool MemRegion::canPrintPrettyAsExpr() const {
+ return false;
+}
+
+void MemRegion::printPretty(raw_ostream &os) const {
+ assert(canPrintPretty() && "This region cannot be printed pretty.");
+ os << "'";
+ printPrettyAsExpr(os);
+ os << "'";
+}
+
+void MemRegion::printPrettyAsExpr(raw_ostream &) const {
+ llvm_unreachable("This region cannot be printed pretty.");
+}
+
+bool VarRegion::canPrintPrettyAsExpr() const {
+ return true;
+}
+
+void VarRegion::printPrettyAsExpr(raw_ostream &os) const {
+ os << getDecl()->getName();
+}
+
+bool ObjCIvarRegion::canPrintPrettyAsExpr() const {
+ return true;
+}
+
+void ObjCIvarRegion::printPrettyAsExpr(raw_ostream &os) const {
+ os << getDecl()->getName();
+}
+
+bool FieldRegion::canPrintPretty() const {
+ return true;
+}
+
+bool FieldRegion::canPrintPrettyAsExpr() const {
+ return superRegion->canPrintPrettyAsExpr();
+}
+
+void FieldRegion::printPrettyAsExpr(raw_ostream &os) const {
+ assert(canPrintPrettyAsExpr());
+ superRegion->printPrettyAsExpr(os);
+ os << "." << getDecl()->getName();
+}
+
+void FieldRegion::printPretty(raw_ostream &os) const {
+ if (canPrintPrettyAsExpr()) {
+ os << "\'";
+ printPrettyAsExpr(os);
+ os << "'";
+ } else {
+ os << "field " << "\'" << getDecl()->getName() << "'";
+ }
+}
+
+bool CXXBaseObjectRegion::canPrintPrettyAsExpr() const {
+ return superRegion->canPrintPrettyAsExpr();
+}
+
+void CXXBaseObjectRegion::printPrettyAsExpr(raw_ostream &os) const {
+ superRegion->printPrettyAsExpr(os);
+}
+
+bool CXXDerivedObjectRegion::canPrintPrettyAsExpr() const {
+ return superRegion->canPrintPrettyAsExpr();
+}
+
+void CXXDerivedObjectRegion::printPrettyAsExpr(raw_ostream &os) const {
+ superRegion->printPrettyAsExpr(os);
+}
+
+std::string MemRegion::getDescriptiveName(bool UseQuotes) const {
+ std::string VariableName;
+ std::string ArrayIndices;
+ const MemRegion *R = this;
+ SmallString<50> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ // Obtain array indices to add them to the variable name.
+ const ElementRegion *ER = nullptr;
+ while ((ER = R->getAs<ElementRegion>())) {
+ // Index is a ConcreteInt.
+ if (auto CI = ER->getIndex().getAs<nonloc::ConcreteInt>()) {
+ llvm::SmallString<2> Idx;
+ CI->getValue().toString(Idx);
+ ArrayIndices = (llvm::Twine("[") + Idx.str() + "]" + ArrayIndices).str();
+ }
+ // If not a ConcreteInt, try to obtain the variable
+ // name by calling 'getDescriptiveName' recursively.
+ else {
+ std::string Idx = ER->getDescriptiveName(false);
+ if (!Idx.empty()) {
+ ArrayIndices = (llvm::Twine("[") + Idx + "]" + ArrayIndices).str();
+ }
+ }
+ R = ER->getSuperRegion();
+ }
+
+ // Get variable name.
+ if (R && R->canPrintPrettyAsExpr()) {
+ R->printPrettyAsExpr(os);
+ if (UseQuotes)
+ return (llvm::Twine("'") + os.str() + ArrayIndices + "'").str();
+ else
+ return (llvm::Twine(os.str()) + ArrayIndices).str();
+ }
+
+ return VariableName;
+}
+
+SourceRange MemRegion::sourceRange() const {
+ const auto *const VR = dyn_cast<VarRegion>(this->getBaseRegion());
+ const auto *const FR = dyn_cast<FieldRegion>(this);
+
+ // Check for more specific regions first.
+ // FieldRegion
+ if (FR) {
+ return FR->getDecl()->getSourceRange();
+ }
+ // VarRegion
+ else if (VR) {
+ return VR->getDecl()->getSourceRange();
+ }
+ // Return invalid source range (can be checked by client).
+ else
+ return {};
+}
+
+//===----------------------------------------------------------------------===//
+// MemRegionManager methods.
+//===----------------------------------------------------------------------===//
+
+template <typename REG>
+const REG *MemRegionManager::LazyAllocate(REG*& region) {
+ if (!region) {
+ region = A.Allocate<REG>();
+ new (region) REG(this);
+ }
+
+ return region;
+}
+
+template <typename REG, typename ARG>
+const REG *MemRegionManager::LazyAllocate(REG*& region, ARG a) {
+ if (!region) {
+ region = A.Allocate<REG>();
+ new (region) REG(this, a);
+ }
+
+ return region;
+}
+
+const StackLocalsSpaceRegion*
+MemRegionManager::getStackLocalsRegion(const StackFrameContext *STC) {
+ assert(STC);
+ StackLocalsSpaceRegion *&R = StackLocalsSpaceRegions[STC];
+
+ if (R)
+ return R;
+
+ R = A.Allocate<StackLocalsSpaceRegion>();
+ new (R) StackLocalsSpaceRegion(this, STC);
+ return R;
+}
+
+const StackArgumentsSpaceRegion *
+MemRegionManager::getStackArgumentsRegion(const StackFrameContext *STC) {
+ assert(STC);
+ StackArgumentsSpaceRegion *&R = StackArgumentsSpaceRegions[STC];
+
+ if (R)
+ return R;
+
+ R = A.Allocate<StackArgumentsSpaceRegion>();
+ new (R) StackArgumentsSpaceRegion(this, STC);
+ return R;
+}
+
+const GlobalsSpaceRegion
+*MemRegionManager::getGlobalsRegion(MemRegion::Kind K,
+ const CodeTextRegion *CR) {
+ if (!CR) {
+ if (K == MemRegion::GlobalSystemSpaceRegionKind)
+ return LazyAllocate(SystemGlobals);
+ if (K == MemRegion::GlobalImmutableSpaceRegionKind)
+ return LazyAllocate(ImmutableGlobals);
+ assert(K == MemRegion::GlobalInternalSpaceRegionKind);
+ return LazyAllocate(InternalGlobals);
+ }
+
+ assert(K == MemRegion::StaticGlobalSpaceRegionKind);
+ StaticGlobalSpaceRegion *&R = StaticsGlobalSpaceRegions[CR];
+ if (R)
+ return R;
+
+ R = A.Allocate<StaticGlobalSpaceRegion>();
+ new (R) StaticGlobalSpaceRegion(this, CR);
+ return R;
+}
+
+const HeapSpaceRegion *MemRegionManager::getHeapRegion() {
+ return LazyAllocate(heap);
+}
+
+const UnknownSpaceRegion *MemRegionManager::getUnknownRegion() {
+ return LazyAllocate(unknown);
+}
+
+const CodeSpaceRegion *MemRegionManager::getCodeRegion() {
+ return LazyAllocate(code);
+}
+
+//===----------------------------------------------------------------------===//
+// Constructing regions.
+//===----------------------------------------------------------------------===//
+
+const StringRegion *MemRegionManager::getStringRegion(const StringLiteral *Str){
+ return getSubRegion<StringRegion>(
+ Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion()));
+}
+
+const ObjCStringRegion *
+MemRegionManager::getObjCStringRegion(const ObjCStringLiteral *Str){
+ return getSubRegion<ObjCStringRegion>(
+ Str, cast<GlobalInternalSpaceRegion>(getGlobalsRegion()));
+}
+
+/// Look through a chain of LocationContexts to either find the
+/// StackFrameContext that matches a DeclContext, or find a VarRegion
+/// for a variable captured by a block.
+static llvm::PointerUnion<const StackFrameContext *, const VarRegion *>
+getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
+ const DeclContext *DC,
+ const VarDecl *VD) {
+ while (LC) {
+ if (const auto *SFC = dyn_cast<StackFrameContext>(LC)) {
+ if (cast<DeclContext>(SFC->getDecl()) == DC)
+ return SFC;
+ }
+ if (const auto *BC = dyn_cast<BlockInvocationContext>(LC)) {
+ const auto *BR =
+ static_cast<const BlockDataRegion *>(BC->getContextData());
+ // FIXME: This can be made more efficient.
+ for (BlockDataRegion::referenced_vars_iterator
+ I = BR->referenced_vars_begin(),
+ E = BR->referenced_vars_end(); I != E; ++I) {
+ const VarRegion *VR = I.getOriginalRegion();
+ if (VR->getDecl() == VD)
+ return cast<VarRegion>(I.getCapturedRegion());
+ }
+ }
+
+ LC = LC->getParent();
+ }
+ return (const StackFrameContext *)nullptr;
+}
+
+const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
+ const LocationContext *LC) {
+ const MemRegion *sReg = nullptr;
+
+ if (D->hasGlobalStorage() && !D->isStaticLocal()) {
+
+ // First handle the globals defined in system headers.
+ if (C.getSourceManager().isInSystemHeader(D->getLocation())) {
+ // Whitelist the system globals which often DO GET modified, assume the
+ // rest are immutable.
+ if (D->getName().find("errno") != StringRef::npos)
+ sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
+ else
+ sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
+
+ // Treat other globals as GlobalInternal unless they are constants.
+ } else {
+ QualType GQT = D->getType();
+ const Type *GT = GQT.getTypePtrOrNull();
+ // TODO: We could walk the complex types here and see if everything is
+ // constified.
+ if (GT && GQT.isConstQualified() && GT->isArithmeticType())
+ sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
+ else
+ sReg = getGlobalsRegion();
+ }
+
+ // Finally handle static locals.
+ } else {
+ // FIXME: Once we implement scope handling, we will need to properly lookup
+ // 'D' to the proper LocationContext.
+ const DeclContext *DC = D->getDeclContext();
+ llvm::PointerUnion<const StackFrameContext *, const VarRegion *> V =
+ getStackOrCaptureRegionForDeclContext(LC, DC, D);
+
+ if (V.is<const VarRegion*>())
+ return V.get<const VarRegion*>();
+
+ const auto *STC = V.get<const StackFrameContext *>();
+
+ if (!STC) {
+ // FIXME: Assign a more sensible memory space to static locals
+ // we see from within blocks that we analyze as top-level declarations.
+ sReg = getUnknownRegion();
+ } else {
+ if (D->hasLocalStorage()) {
+ sReg = isa<ParmVarDecl>(D) || isa<ImplicitParamDecl>(D)
+ ? static_cast<const MemRegion*>(getStackArgumentsRegion(STC))
+ : static_cast<const MemRegion*>(getStackLocalsRegion(STC));
+ }
+ else {
+ assert(D->isStaticLocal());
+ const Decl *STCD = STC->getDecl();
+ if (isa<FunctionDecl>(STCD) || isa<ObjCMethodDecl>(STCD))
+ sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind,
+ getFunctionCodeRegion(cast<NamedDecl>(STCD)));
+ else if (const auto *BD = dyn_cast<BlockDecl>(STCD)) {
+ // FIXME: The fallback type here is totally bogus -- though it should
+ // never be queried, it will prevent uniquing with the real
+ // BlockCodeRegion. Ideally we'd fix the AST so that we always had a
+ // signature.
+ QualType T;
+ if (const TypeSourceInfo *TSI = BD->getSignatureAsWritten())
+ T = TSI->getType();
+ if (T.isNull())
+ T = getContext().VoidTy;
+ if (!T->getAs<FunctionType>())
+ T = getContext().getFunctionNoProtoType(T);
+ T = getContext().getBlockPointerType(T);
+
+ const BlockCodeRegion *BTR =
+ getBlockCodeRegion(BD, C.getCanonicalType(T),
+ STC->getAnalysisDeclContext());
+ sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind,
+ BTR);
+ }
+ else {
+ sReg = getGlobalsRegion();
+ }
+ }
+ }
+ }
+
+ return getSubRegion<VarRegion>(D, sReg);
+}
+
+const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
+ const MemRegion *superR) {
+ return getSubRegion<VarRegion>(D, superR);
+}
+
+const BlockDataRegion *
+MemRegionManager::getBlockDataRegion(const BlockCodeRegion *BC,
+ const LocationContext *LC,
+ unsigned blockCount) {
+ const MemSpaceRegion *sReg = nullptr;
+ const BlockDecl *BD = BC->getDecl();
+ if (!BD->hasCaptures()) {
+ // This handles 'static' blocks.
+ sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
+ }
+ else {
+ if (LC) {
+ // FIXME: Once we implement scope handling, we want the parent region
+ // to be the scope.
+ const StackFrameContext *STC = LC->getStackFrame();
+ assert(STC);
+ sReg = getStackLocalsRegion(STC);
+ }
+ else {
+ // We allow 'LC' to be NULL for cases where want BlockDataRegions
+ // without context-sensitivity.
+ sReg = getUnknownRegion();
+ }
+ }
+
+ return getSubRegion<BlockDataRegion>(BC, LC, blockCount, sReg);
+}
+
+const CXXTempObjectRegion *
+MemRegionManager::getCXXStaticTempObjectRegion(const Expr *Ex) {
+ return getSubRegion<CXXTempObjectRegion>(
+ Ex, getGlobalsRegion(MemRegion::GlobalInternalSpaceRegionKind, nullptr));
+}
+
+const CompoundLiteralRegion*
+MemRegionManager::getCompoundLiteralRegion(const CompoundLiteralExpr *CL,
+ const LocationContext *LC) {
+ const MemSpaceRegion *sReg = nullptr;
+
+ if (CL->isFileScope())
+ sReg = getGlobalsRegion();
+ else {
+ const StackFrameContext *STC = LC->getStackFrame();
+ assert(STC);
+ sReg = getStackLocalsRegion(STC);
+ }
+
+ return getSubRegion<CompoundLiteralRegion>(CL, sReg);
+}
+
+const ElementRegion*
+MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx,
+ const SubRegion* superRegion,
+ ASTContext &Ctx){
+ QualType T = Ctx.getCanonicalType(elementType).getUnqualifiedType();
+
+ llvm::FoldingSetNodeID ID;
+ ElementRegion::ProfileRegion(ID, T, Idx, superRegion);
+
+ void *InsertPos;
+ MemRegion* data = Regions.FindNodeOrInsertPos(ID, InsertPos);
+ auto *R = cast_or_null<ElementRegion>(data);
+
+ if (!R) {
+ R = A.Allocate<ElementRegion>();
+ new (R) ElementRegion(T, Idx, superRegion);
+ Regions.InsertNode(R, InsertPos);
+ }
+
+ return R;
+}
+
+const FunctionCodeRegion *
+MemRegionManager::getFunctionCodeRegion(const NamedDecl *FD) {
+ return getSubRegion<FunctionCodeRegion>(FD, getCodeRegion());
+}
+
+const BlockCodeRegion *
+MemRegionManager::getBlockCodeRegion(const BlockDecl *BD, CanQualType locTy,
+ AnalysisDeclContext *AC) {
+ return getSubRegion<BlockCodeRegion>(BD, locTy, AC, getCodeRegion());
+}
+
+/// getSymbolicRegion - Retrieve or create a "symbolic" memory region.
+const SymbolicRegion *MemRegionManager::getSymbolicRegion(SymbolRef sym) {
+ return getSubRegion<SymbolicRegion>(sym, getUnknownRegion());
+}
+
+const SymbolicRegion *MemRegionManager::getSymbolicHeapRegion(SymbolRef Sym) {
+ return getSubRegion<SymbolicRegion>(Sym, getHeapRegion());
+}
+
+const FieldRegion*
+MemRegionManager::getFieldRegion(const FieldDecl *d,
+ const SubRegion* superRegion){
+ return getSubRegion<FieldRegion>(d, superRegion);
+}
+
+const ObjCIvarRegion*
+MemRegionManager::getObjCIvarRegion(const ObjCIvarDecl *d,
+ const SubRegion* superRegion) {
+ return getSubRegion<ObjCIvarRegion>(d, superRegion);
+}
+
+const CXXTempObjectRegion*
+MemRegionManager::getCXXTempObjectRegion(Expr const *E,
+ LocationContext const *LC) {
+ const StackFrameContext *SFC = LC->getStackFrame();
+ assert(SFC);
+ return getSubRegion<CXXTempObjectRegion>(E, getStackLocalsRegion(SFC));
+}
+
+/// Checks whether \p BaseClass is a valid virtual or direct non-virtual base
+/// class of the type of \p Super.
+static bool isValidBaseClass(const CXXRecordDecl *BaseClass,
+ const TypedValueRegion *Super,
+ bool IsVirtual) {
+ BaseClass = BaseClass->getCanonicalDecl();
+
+ const CXXRecordDecl *Class = Super->getValueType()->getAsCXXRecordDecl();
+ if (!Class)
+ return true;
+
+ if (IsVirtual)
+ return Class->isVirtuallyDerivedFrom(BaseClass);
+
+ for (const auto &I : Class->bases()) {
+ if (I.getType()->getAsCXXRecordDecl()->getCanonicalDecl() == BaseClass)
+ return true;
+ }
+
+ return false;
+}
+
+const CXXBaseObjectRegion *
+MemRegionManager::getCXXBaseObjectRegion(const CXXRecordDecl *RD,
+ const SubRegion *Super,
+ bool IsVirtual) {
+ if (isa<TypedValueRegion>(Super)) {
+ assert(isValidBaseClass(RD, dyn_cast<TypedValueRegion>(Super), IsVirtual));
+ (void)&isValidBaseClass;
+
+ if (IsVirtual) {
+ // Virtual base regions should not be layered, since the layout rules
+ // are different.
+ while (const auto *Base = dyn_cast<CXXBaseObjectRegion>(Super))
+ Super = cast<SubRegion>(Base->getSuperRegion());
+ assert(Super && !isa<MemSpaceRegion>(Super));
+ }
+ }
+
+ return getSubRegion<CXXBaseObjectRegion>(RD, IsVirtual, Super);
+}
+
+const CXXDerivedObjectRegion *
+MemRegionManager::getCXXDerivedObjectRegion(const CXXRecordDecl *RD,
+ const SubRegion *Super) {
+ return getSubRegion<CXXDerivedObjectRegion>(RD, Super);
+}
+
+const CXXThisRegion*
+MemRegionManager::getCXXThisRegion(QualType thisPointerTy,
+ const LocationContext *LC) {
+ const auto *PT = thisPointerTy->getAs<PointerType>();
+ assert(PT);
+ // Inside the body of the operator() of a lambda a this expr might refer to an
+ // object in one of the parent location contexts.
+ const auto *D = dyn_cast<CXXMethodDecl>(LC->getDecl());
+ // FIXME: when operator() of lambda is analyzed as a top level function and
+ // 'this' refers to a this to the enclosing scope, there is no right region to
+ // return.
+ while (!LC->inTopFrame() && (!D || D->isStatic() ||
+ PT != D->getThisType()->getAs<PointerType>())) {
+ LC = LC->getParent();
+ D = dyn_cast<CXXMethodDecl>(LC->getDecl());
+ }
+ const StackFrameContext *STC = LC->getStackFrame();
+ assert(STC);
+ return getSubRegion<CXXThisRegion>(PT, getStackArgumentsRegion(STC));
+}
+
+const AllocaRegion*
+MemRegionManager::getAllocaRegion(const Expr *E, unsigned cnt,
+ const LocationContext *LC) {
+ const StackFrameContext *STC = LC->getStackFrame();
+ assert(STC);
+ return getSubRegion<AllocaRegion>(E, cnt, getStackLocalsRegion(STC));
+}
+
+const MemSpaceRegion *MemRegion::getMemorySpace() const {
+ const MemRegion *R = this;
+ const auto *SR = dyn_cast<SubRegion>(this);
+
+ while (SR) {
+ R = SR->getSuperRegion();
+ SR = dyn_cast<SubRegion>(R);
+ }
+
+ return dyn_cast<MemSpaceRegion>(R);
+}
+
+bool MemRegion::hasStackStorage() const {
+ return isa<StackSpaceRegion>(getMemorySpace());
+}
+
+bool MemRegion::hasStackNonParametersStorage() const {
+ return isa<StackLocalsSpaceRegion>(getMemorySpace());
+}
+
+bool MemRegion::hasStackParametersStorage() const {
+ return isa<StackArgumentsSpaceRegion>(getMemorySpace());
+}
+
+bool MemRegion::hasGlobalsOrParametersStorage() const {
+ const MemSpaceRegion *MS = getMemorySpace();
+ return isa<StackArgumentsSpaceRegion>(MS) ||
+ isa<GlobalsSpaceRegion>(MS);
+}
+
+// getBaseRegion strips away all elements and fields, and get the base region
+// of them.
+const MemRegion *MemRegion::getBaseRegion() const {
+ const MemRegion *R = this;
+ while (true) {
+ switch (R->getKind()) {
+ case MemRegion::ElementRegionKind:
+ case MemRegion::FieldRegionKind:
+ case MemRegion::ObjCIvarRegionKind:
+ case MemRegion::CXXBaseObjectRegionKind:
+ case MemRegion::CXXDerivedObjectRegionKind:
+ R = cast<SubRegion>(R)->getSuperRegion();
+ continue;
+ default:
+ break;
+ }
+ break;
+ }
+ return R;
+}
+
+// getgetMostDerivedObjectRegion gets the region of the root class of a C++
+// class hierarchy.
+const MemRegion *MemRegion::getMostDerivedObjectRegion() const {
+ const MemRegion *R = this;
+ while (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R))
+ R = BR->getSuperRegion();
+ return R;
+}
+
+bool MemRegion::isSubRegionOf(const MemRegion *) const {
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// View handling.
+//===----------------------------------------------------------------------===//
+
+const MemRegion *MemRegion::StripCasts(bool StripBaseAndDerivedCasts) const {
+ const MemRegion *R = this;
+ while (true) {
+ switch (R->getKind()) {
+ case ElementRegionKind: {
+ const auto *ER = cast<ElementRegion>(R);
+ if (!ER->getIndex().isZeroConstant())
+ return R;
+ R = ER->getSuperRegion();
+ break;
+ }
+ case CXXBaseObjectRegionKind:
+ case CXXDerivedObjectRegionKind:
+ if (!StripBaseAndDerivedCasts)
+ return R;
+ R = cast<TypedValueRegion>(R)->getSuperRegion();
+ break;
+ default:
+ return R;
+ }
+ }
+}
+
+const SymbolicRegion *MemRegion::getSymbolicBase() const {
+ const auto *SubR = dyn_cast<SubRegion>(this);
+
+ while (SubR) {
+ if (const auto *SymR = dyn_cast<SymbolicRegion>(SubR))
+ return SymR;
+ SubR = dyn_cast<SubRegion>(SubR->getSuperRegion());
+ }
+ return nullptr;
+}
+
+RegionRawOffset ElementRegion::getAsArrayOffset() const {
+ int64_t offset = 0;
+ const ElementRegion *ER = this;
+ const MemRegion *superR = nullptr;
+ ASTContext &C = getContext();
+
+ // FIXME: Handle multi-dimensional arrays.
+
+ while (ER) {
+ superR = ER->getSuperRegion();
+
+ // FIXME: generalize to symbolic offsets.
+ SVal index = ER->getIndex();
+ if (auto CI = index.getAs<nonloc::ConcreteInt>()) {
+ // Update the offset.
+ int64_t i = CI->getValue().getSExtValue();
+
+ if (i != 0) {
+ QualType elemType = ER->getElementType();
+
+ // If we are pointing to an incomplete type, go no further.
+ if (elemType->isIncompleteType()) {
+ superR = ER;
+ break;
+ }
+
+ int64_t size = C.getTypeSizeInChars(elemType).getQuantity();
+ if (auto NewOffset = llvm::checkedMulAdd(i, size, offset)) {
+ offset = *NewOffset;
+ } else {
+ LLVM_DEBUG(llvm::dbgs() << "MemRegion::getAsArrayOffset: "
+ << "offset overflowing, returning unknown\n");
+
+ return nullptr;
+ }
+ }
+
+ // Go to the next ElementRegion (if any).
+ ER = dyn_cast<ElementRegion>(superR);
+ continue;
+ }
+
+ return nullptr;
+ }
+
+ assert(superR && "super region cannot be NULL");
+ return RegionRawOffset(superR, CharUnits::fromQuantity(offset));
+}
+
+/// Returns true if \p Base is an immediate base class of \p Child
+static bool isImmediateBase(const CXXRecordDecl *Child,
+ const CXXRecordDecl *Base) {
+ assert(Child && "Child must not be null");
+ // Note that we do NOT canonicalize the base class here, because
+ // ASTRecordLayout doesn't either. If that leads us down the wrong path,
+ // so be it; at least we won't crash.
+ for (const auto &I : Child->bases()) {
+ if (I.getType()->getAsCXXRecordDecl() == Base)
+ return true;
+ }
+
+ return false;
+}
+
+static RegionOffset calculateOffset(const MemRegion *R) {
+ const MemRegion *SymbolicOffsetBase = nullptr;
+ int64_t Offset = 0;
+
+ while (true) {
+ switch (R->getKind()) {
+ case MemRegion::CodeSpaceRegionKind:
+ case MemRegion::StackLocalsSpaceRegionKind:
+ case MemRegion::StackArgumentsSpaceRegionKind:
+ case MemRegion::HeapSpaceRegionKind:
+ case MemRegion::UnknownSpaceRegionKind:
+ case MemRegion::StaticGlobalSpaceRegionKind:
+ case MemRegion::GlobalInternalSpaceRegionKind:
+ case MemRegion::GlobalSystemSpaceRegionKind:
+ case MemRegion::GlobalImmutableSpaceRegionKind:
+ // Stores can bind directly to a region space to set a default value.
+ assert(Offset == 0 && !SymbolicOffsetBase);
+ goto Finish;
+
+ case MemRegion::FunctionCodeRegionKind:
+ case MemRegion::BlockCodeRegionKind:
+ case MemRegion::BlockDataRegionKind:
+ // These will never have bindings, but may end up having values requested
+ // if the user does some strange casting.
+ if (Offset != 0)
+ SymbolicOffsetBase = R;
+ goto Finish;
+
+ case MemRegion::SymbolicRegionKind:
+ case MemRegion::AllocaRegionKind:
+ case MemRegion::CompoundLiteralRegionKind:
+ case MemRegion::CXXThisRegionKind:
+ case MemRegion::StringRegionKind:
+ case MemRegion::ObjCStringRegionKind:
+ case MemRegion::VarRegionKind:
+ case MemRegion::CXXTempObjectRegionKind:
+ // Usual base regions.
+ goto Finish;
+
+ case MemRegion::ObjCIvarRegionKind:
+ // This is a little strange, but it's a compromise between
+ // ObjCIvarRegions having unknown compile-time offsets (when using the
+ // non-fragile runtime) and yet still being distinct, non-overlapping
+ // regions. Thus we treat them as "like" base regions for the purposes
+ // of computing offsets.
+ goto Finish;
+
+ case MemRegion::CXXBaseObjectRegionKind: {
+ const auto *BOR = cast<CXXBaseObjectRegion>(R);
+ R = BOR->getSuperRegion();
+
+ QualType Ty;
+ bool RootIsSymbolic = false;
+ if (const auto *TVR = dyn_cast<TypedValueRegion>(R)) {
+ Ty = TVR->getDesugaredValueType(R->getContext());
+ } else if (const auto *SR = dyn_cast<SymbolicRegion>(R)) {
+ // If our base region is symbolic, we don't know what type it really is.
+ // Pretend the type of the symbol is the true dynamic type.
+ // (This will at least be self-consistent for the life of the symbol.)
+ Ty = SR->getSymbol()->getType()->getPointeeType();
+ RootIsSymbolic = true;
+ }
+
+ const CXXRecordDecl *Child = Ty->getAsCXXRecordDecl();
+ if (!Child) {
+ // We cannot compute the offset of the base class.
+ SymbolicOffsetBase = R;
+ } else {
+ if (RootIsSymbolic) {
+ // Base layers on symbolic regions may not be type-correct.
+ // Double-check the inheritance here, and revert to a symbolic offset
+ // if it's invalid (e.g. due to a reinterpret_cast).
+ if (BOR->isVirtual()) {
+ if (!Child->isVirtuallyDerivedFrom(BOR->getDecl()))
+ SymbolicOffsetBase = R;
+ } else {
+ if (!isImmediateBase(Child, BOR->getDecl()))
+ SymbolicOffsetBase = R;
+ }
+ }
+ }
+
+ // Don't bother calculating precise offsets if we already have a
+ // symbolic offset somewhere in the chain.
+ if (SymbolicOffsetBase)
+ continue;
+
+ CharUnits BaseOffset;
+ const ASTRecordLayout &Layout = R->getContext().getASTRecordLayout(Child);
+ if (BOR->isVirtual())
+ BaseOffset = Layout.getVBaseClassOffset(BOR->getDecl());
+ else
+ BaseOffset = Layout.getBaseClassOffset(BOR->getDecl());
+
+ // The base offset is in chars, not in bits.
+ Offset += BaseOffset.getQuantity() * R->getContext().getCharWidth();
+ break;
+ }
+
+ case MemRegion::CXXDerivedObjectRegionKind: {
+ // TODO: Store the base type in the CXXDerivedObjectRegion and use it.
+ goto Finish;
+ }
+
+ case MemRegion::ElementRegionKind: {
+ const auto *ER = cast<ElementRegion>(R);
+ R = ER->getSuperRegion();
+
+ QualType EleTy = ER->getValueType();
+ if (EleTy->isIncompleteType()) {
+ // We cannot compute the offset of the base class.
+ SymbolicOffsetBase = R;
+ continue;
+ }
+
+ SVal Index = ER->getIndex();
+ if (Optional<nonloc::ConcreteInt> CI =
+ Index.getAs<nonloc::ConcreteInt>()) {
+ // Don't bother calculating precise offsets if we already have a
+ // symbolic offset somewhere in the chain.
+ if (SymbolicOffsetBase)
+ continue;
+
+ int64_t i = CI->getValue().getSExtValue();
+ // This type size is in bits.
+ Offset += i * R->getContext().getTypeSize(EleTy);
+ } else {
+ // We cannot compute offset for non-concrete index.
+ SymbolicOffsetBase = R;
+ }
+ break;
+ }
+ case MemRegion::FieldRegionKind: {
+ const auto *FR = cast<FieldRegion>(R);
+ R = FR->getSuperRegion();
+
+ const RecordDecl *RD = FR->getDecl()->getParent();
+ if (RD->isUnion() || !RD->isCompleteDefinition()) {
+ // We cannot compute offset for incomplete type.
+ // For unions, we could treat everything as offset 0, but we'd rather
+ // treat each field as a symbolic offset so they aren't stored on top
+ // of each other, since we depend on things in typed regions actually
+ // matching their types.
+ SymbolicOffsetBase = R;
+ }
+
+ // Don't bother calculating precise offsets if we already have a
+ // symbolic offset somewhere in the chain.
+ if (SymbolicOffsetBase)
+ continue;
+
+ // Get the field number.
+ unsigned idx = 0;
+ for (RecordDecl::field_iterator FI = RD->field_begin(),
+ FE = RD->field_end(); FI != FE; ++FI, ++idx) {
+ if (FR->getDecl() == *FI)
+ break;
+ }
+ const ASTRecordLayout &Layout = R->getContext().getASTRecordLayout(RD);
+ // This is offset in bits.
+ Offset += Layout.getFieldOffset(idx);
+ break;
+ }
+ }
+ }
+
+ Finish:
+ if (SymbolicOffsetBase)
+ return RegionOffset(SymbolicOffsetBase, RegionOffset::Symbolic);
+ return RegionOffset(R, Offset);
+}
+
+RegionOffset MemRegion::getAsOffset() const {
+ if (!cachedOffset)
+ cachedOffset = calculateOffset(this);
+ return *cachedOffset;
+}
+
+//===----------------------------------------------------------------------===//
+// BlockDataRegion
+//===----------------------------------------------------------------------===//
+
+std::pair<const VarRegion *, const VarRegion *>
+BlockDataRegion::getCaptureRegions(const VarDecl *VD) {
+ MemRegionManager &MemMgr = *getMemRegionManager();
+ const VarRegion *VR = nullptr;
+ const VarRegion *OriginalVR = nullptr;
+
+ if (!VD->hasAttr<BlocksAttr>() && VD->hasLocalStorage()) {
+ VR = MemMgr.getVarRegion(VD, this);
+ OriginalVR = MemMgr.getVarRegion(VD, LC);
+ }
+ else {
+ if (LC) {
+ VR = MemMgr.getVarRegion(VD, LC);
+ OriginalVR = VR;
+ }
+ else {
+ VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion());
+ OriginalVR = MemMgr.getVarRegion(VD, LC);
+ }
+ }
+ return std::make_pair(VR, OriginalVR);
+}
+
+void BlockDataRegion::LazyInitializeReferencedVars() {
+ if (ReferencedVars)
+ return;
+
+ AnalysisDeclContext *AC = getCodeRegion()->getAnalysisDeclContext();
+ const auto &ReferencedBlockVars = AC->getReferencedBlockVars(BC->getDecl());
+ auto NumBlockVars =
+ std::distance(ReferencedBlockVars.begin(), ReferencedBlockVars.end());
+
+ if (NumBlockVars == 0) {
+ ReferencedVars = (void*) 0x1;
+ return;
+ }
+
+ MemRegionManager &MemMgr = *getMemRegionManager();
+ llvm::BumpPtrAllocator &A = MemMgr.getAllocator();
+ BumpVectorContext BC(A);
+
+ using VarVec = BumpVector<const MemRegion *>;
+
+ auto *BV = A.Allocate<VarVec>();
+ new (BV) VarVec(BC, NumBlockVars);
+ auto *BVOriginal = A.Allocate<VarVec>();
+ new (BVOriginal) VarVec(BC, NumBlockVars);
+
+ for (const auto *VD : ReferencedBlockVars) {
+ const VarRegion *VR = nullptr;
+ const VarRegion *OriginalVR = nullptr;
+ std::tie(VR, OriginalVR) = getCaptureRegions(VD);
+ assert(VR);
+ assert(OriginalVR);
+ BV->push_back(VR, BC);
+ BVOriginal->push_back(OriginalVR, BC);
+ }
+
+ ReferencedVars = BV;
+ OriginalVars = BVOriginal;
+}
+
+BlockDataRegion::referenced_vars_iterator
+BlockDataRegion::referenced_vars_begin() const {
+ const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars();
+
+ auto *Vec = static_cast<BumpVector<const MemRegion *> *>(ReferencedVars);
+
+ if (Vec == (void*) 0x1)
+ return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr);
+
+ auto *VecOriginal =
+ static_cast<BumpVector<const MemRegion *> *>(OriginalVars);
+
+ return BlockDataRegion::referenced_vars_iterator(Vec->begin(),
+ VecOriginal->begin());
+}
+
+BlockDataRegion::referenced_vars_iterator
+BlockDataRegion::referenced_vars_end() const {
+ const_cast<BlockDataRegion*>(this)->LazyInitializeReferencedVars();
+
+ auto *Vec = static_cast<BumpVector<const MemRegion *> *>(ReferencedVars);
+
+ if (Vec == (void*) 0x1)
+ return BlockDataRegion::referenced_vars_iterator(nullptr, nullptr);
+
+ auto *VecOriginal =
+ static_cast<BumpVector<const MemRegion *> *>(OriginalVars);
+
+ return BlockDataRegion::referenced_vars_iterator(Vec->end(),
+ VecOriginal->end());
+}
+
+const VarRegion *BlockDataRegion::getOriginalRegion(const VarRegion *R) const {
+ for (referenced_vars_iterator I = referenced_vars_begin(),
+ E = referenced_vars_end();
+ I != E; ++I) {
+ if (I.getCapturedRegion() == R)
+ return I.getOriginalRegion();
+ }
+ return nullptr;
+}
+
+//===----------------------------------------------------------------------===//
+// RegionAndSymbolInvalidationTraits
+//===----------------------------------------------------------------------===//
+
+void RegionAndSymbolInvalidationTraits::setTrait(SymbolRef Sym,
+ InvalidationKinds IK) {
+ SymTraitsMap[Sym] |= IK;
+}
+
+void RegionAndSymbolInvalidationTraits::setTrait(const MemRegion *MR,
+ InvalidationKinds IK) {
+ assert(MR);
+ if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
+ setTrait(SR->getSymbol(), IK);
+ else
+ MRTraitsMap[MR] |= IK;
+}
+
+bool RegionAndSymbolInvalidationTraits::hasTrait(SymbolRef Sym,
+ InvalidationKinds IK) const {
+ const_symbol_iterator I = SymTraitsMap.find(Sym);
+ if (I != SymTraitsMap.end())
+ return I->second & IK;
+
+ return false;
+}
+
+bool RegionAndSymbolInvalidationTraits::hasTrait(const MemRegion *MR,
+ InvalidationKinds IK) const {
+ if (!MR)
+ return false;
+
+ if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
+ return hasTrait(SR->getSymbol(), IK);
+
+ const_region_iterator I = MRTraitsMap.find(MR);
+ if (I != MRTraitsMap.end())
+ return I->second & IK;
+
+ return false;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
new file mode 100644
index 000000000000..3e93bb6a7c4f
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -0,0 +1,1416 @@
+//===- PathDiagnostic.cpp - Path-Specific Diagnostic Handling -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the PathDiagnostic-related interfaces.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclBase.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/OperationKinds.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/ADT/StringExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <cstring>
+#include <memory>
+#include <utility>
+#include <vector>
+
+using namespace clang;
+using namespace ento;
+
+bool PathDiagnosticMacroPiece::containsEvent() const {
+ for (const auto &P : subPieces) {
+ if (isa<PathDiagnosticEventPiece>(*P))
+ return true;
+ if (const auto *MP = dyn_cast<PathDiagnosticMacroPiece>(P.get()))
+ if (MP->containsEvent())
+ return true;
+ }
+ return false;
+}
+
+static StringRef StripTrailingDots(StringRef s) {
+ for (StringRef::size_type i = s.size(); i != 0; --i)
+ if (s[i - 1] != '.')
+ return s.substr(0, i);
+ return {};
+}
+
+PathDiagnosticPiece::PathDiagnosticPiece(StringRef s,
+ Kind k, DisplayHint hint)
+ : str(StripTrailingDots(s)), kind(k), Hint(hint) {}
+
+PathDiagnosticPiece::PathDiagnosticPiece(Kind k, DisplayHint hint)
+ : kind(k), Hint(hint) {}
+
+PathDiagnosticPiece::~PathDiagnosticPiece() = default;
+
+PathDiagnosticEventPiece::~PathDiagnosticEventPiece() = default;
+
+PathDiagnosticCallPiece::~PathDiagnosticCallPiece() = default;
+
+PathDiagnosticControlFlowPiece::~PathDiagnosticControlFlowPiece() = default;
+
+PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default;
+
+PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default;
+
+void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,
+ bool ShouldFlattenMacros) const {
+ for (auto &Piece : *this) {
+ switch (Piece->getKind()) {
+ case PathDiagnosticPiece::Call: {
+ auto &Call = cast<PathDiagnosticCallPiece>(*Piece);
+ if (auto CallEnter = Call.getCallEnterEvent())
+ Current.push_back(std::move(CallEnter));
+ Call.path.flattenTo(Primary, Primary, ShouldFlattenMacros);
+ if (auto callExit = Call.getCallExitEvent())
+ Current.push_back(std::move(callExit));
+ break;
+ }
+ case PathDiagnosticPiece::Macro: {
+ auto &Macro = cast<PathDiagnosticMacroPiece>(*Piece);
+ if (ShouldFlattenMacros) {
+ Macro.subPieces.flattenTo(Primary, Primary, ShouldFlattenMacros);
+ } else {
+ Current.push_back(Piece);
+ PathPieces NewPath;
+ Macro.subPieces.flattenTo(Primary, NewPath, ShouldFlattenMacros);
+ // FIXME: This probably shouldn't mutate the original path piece.
+ Macro.subPieces = NewPath;
+ }
+ break;
+ }
+ case PathDiagnosticPiece::Event:
+ case PathDiagnosticPiece::ControlFlow:
+ case PathDiagnosticPiece::Note:
+ Current.push_back(Piece);
+ break;
+ }
+ }
+}
+
+PathDiagnostic::~PathDiagnostic() = default;
+
+PathDiagnostic::PathDiagnostic(
+ StringRef CheckName, const Decl *declWithIssue, StringRef bugtype,
+ StringRef verboseDesc, StringRef shortDesc, StringRef category,
+ PathDiagnosticLocation LocationToUnique, const Decl *DeclToUnique,
+ std::unique_ptr<FilesToLineNumsMap> ExecutedLines)
+ : CheckName(CheckName), DeclWithIssue(declWithIssue),
+ BugType(StripTrailingDots(bugtype)),
+ VerboseDesc(StripTrailingDots(verboseDesc)),
+ ShortDesc(StripTrailingDots(shortDesc)),
+ Category(StripTrailingDots(category)), UniqueingLoc(LocationToUnique),
+ UniqueingDecl(DeclToUnique), ExecutedLines(std::move(ExecutedLines)),
+ path(pathImpl) {}
+
+static PathDiagnosticCallPiece *
+getFirstStackedCallToHeaderFile(PathDiagnosticCallPiece *CP,
+ const SourceManager &SMgr) {
+ SourceLocation CallLoc = CP->callEnter.asLocation();
+
+ // If the call is within a macro, don't do anything (for now).
+ if (CallLoc.isMacroID())
+ return nullptr;
+
+ assert(AnalysisManager::isInCodeFile(CallLoc, SMgr) &&
+ "The call piece should not be in a header file.");
+
+ // Check if CP represents a path through a function outside of the main file.
+ if (!AnalysisManager::isInCodeFile(CP->callEnterWithin.asLocation(), SMgr))
+ return CP;
+
+ const PathPieces &Path = CP->path;
+ if (Path.empty())
+ return nullptr;
+
+ // Check if the last piece in the callee path is a call to a function outside
+ // of the main file.
+ if (auto *CPInner = dyn_cast<PathDiagnosticCallPiece>(Path.back().get()))
+ return getFirstStackedCallToHeaderFile(CPInner, SMgr);
+
+ // Otherwise, the last piece is in the main file.
+ return nullptr;
+}
+
+void PathDiagnostic::resetDiagnosticLocationToMainFile() {
+ if (path.empty())
+ return;
+
+ PathDiagnosticPiece *LastP = path.back().get();
+ assert(LastP);
+ const SourceManager &SMgr = LastP->getLocation().getManager();
+
+ // We only need to check if the report ends inside headers, if the last piece
+ // is a call piece.
+ if (auto *CP = dyn_cast<PathDiagnosticCallPiece>(LastP)) {
+ CP = getFirstStackedCallToHeaderFile(CP, SMgr);
+ if (CP) {
+ // Mark the piece.
+ CP->setAsLastInMainSourceFile();
+
+ // Update the path diagnostic message.
+ const auto *ND = dyn_cast<NamedDecl>(CP->getCallee());
+ if (ND) {
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
+ os << " (within a call to '" << ND->getDeclName() << "')";
+ appendToDesc(os.str());
+ }
+
+ // Reset the report containing declaration and location.
+ DeclWithIssue = CP->getCaller();
+ Loc = CP->getLocation();
+
+ return;
+ }
+ }
+}
+
+void PathDiagnosticConsumer::anchor() {}
+
+PathDiagnosticConsumer::~PathDiagnosticConsumer() {
+ // Delete the contents of the FoldingSet if it isn't empty already.
+ for (auto &Diag : Diags)
+ delete &Diag;
+}
+
+void PathDiagnosticConsumer::HandlePathDiagnostic(
+ std::unique_ptr<PathDiagnostic> D) {
+ if (!D || D->path.empty())
+ return;
+
+ // We need to flatten the locations (convert Stmt* to locations) because
+ // the referenced statements may be freed by the time the diagnostics
+ // are emitted.
+ D->flattenLocations();
+
+ // If the PathDiagnosticConsumer does not support diagnostics that
+ // cross file boundaries, prune out such diagnostics now.
+ if (!supportsCrossFileDiagnostics()) {
+ // Verify that the entire path is from the same FileID.
+ FileID FID;
+ const SourceManager &SMgr = D->path.front()->getLocation().getManager();
+ SmallVector<const PathPieces *, 5> WorkList;
+ WorkList.push_back(&D->path);
+ SmallString<128> buf;
+ llvm::raw_svector_ostream warning(buf);
+ warning << "warning: Path diagnostic report is not generated. Current "
+ << "output format does not support diagnostics that cross file "
+ << "boundaries. Refer to --analyzer-output for valid output "
+ << "formats\n";
+
+ while (!WorkList.empty()) {
+ const PathPieces &path = *WorkList.pop_back_val();
+
+ for (const auto &I : path) {
+ const PathDiagnosticPiece *piece = I.get();
+ FullSourceLoc L = piece->getLocation().asLocation().getExpansionLoc();
+
+ if (FID.isInvalid()) {
+ FID = SMgr.getFileID(L);
+ } else if (SMgr.getFileID(L) != FID) {
+ llvm::errs() << warning.str();
+ return;
+ }
+
+ // Check the source ranges.
+ ArrayRef<SourceRange> Ranges = piece->getRanges();
+ for (const auto &I : Ranges) {
+ SourceLocation L = SMgr.getExpansionLoc(I.getBegin());
+ if (!L.isFileID() || SMgr.getFileID(L) != FID) {
+ llvm::errs() << warning.str();
+ return;
+ }
+ L = SMgr.getExpansionLoc(I.getEnd());
+ if (!L.isFileID() || SMgr.getFileID(L) != FID) {
+ llvm::errs() << warning.str();
+ return;
+ }
+ }
+
+ if (const auto *call = dyn_cast<PathDiagnosticCallPiece>(piece))
+ WorkList.push_back(&call->path);
+ else if (const auto *macro = dyn_cast<PathDiagnosticMacroPiece>(piece))
+ WorkList.push_back(&macro->subPieces);
+ }
+ }
+
+ if (FID.isInvalid())
+ return; // FIXME: Emit a warning?
+ }
+
+ // Profile the node to see if we already have something matching it
+ llvm::FoldingSetNodeID profile;
+ D->Profile(profile);
+ void *InsertPos = nullptr;
+
+ if (PathDiagnostic *orig = Diags.FindNodeOrInsertPos(profile, InsertPos)) {
+ // Keep the PathDiagnostic with the shorter path.
+ // Note, the enclosing routine is called in deterministic order, so the
+ // results will be consistent between runs (no reason to break ties if the
+ // size is the same).
+ const unsigned orig_size = orig->full_size();
+ const unsigned new_size = D->full_size();
+ if (orig_size <= new_size)
+ return;
+
+ assert(orig != D.get());
+ Diags.RemoveNode(orig);
+ delete orig;
+ }
+
+ Diags.InsertNode(D.release());
+}
+
+static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y);
+
+static Optional<bool>
+compareControlFlow(const PathDiagnosticControlFlowPiece &X,
+ const PathDiagnosticControlFlowPiece &Y) {
+ FullSourceLoc XSL = X.getStartLocation().asLocation();
+ FullSourceLoc YSL = Y.getStartLocation().asLocation();
+ if (XSL != YSL)
+ return XSL.isBeforeInTranslationUnitThan(YSL);
+ FullSourceLoc XEL = X.getEndLocation().asLocation();
+ FullSourceLoc YEL = Y.getEndLocation().asLocation();
+ if (XEL != YEL)
+ return XEL.isBeforeInTranslationUnitThan(YEL);
+ return None;
+}
+
+static Optional<bool> compareMacro(const PathDiagnosticMacroPiece &X,
+ const PathDiagnosticMacroPiece &Y) {
+ return comparePath(X.subPieces, Y.subPieces);
+}
+
+static Optional<bool> compareCall(const PathDiagnosticCallPiece &X,
+ const PathDiagnosticCallPiece &Y) {
+ FullSourceLoc X_CEL = X.callEnter.asLocation();
+ FullSourceLoc Y_CEL = Y.callEnter.asLocation();
+ if (X_CEL != Y_CEL)
+ return X_CEL.isBeforeInTranslationUnitThan(Y_CEL);
+ FullSourceLoc X_CEWL = X.callEnterWithin.asLocation();
+ FullSourceLoc Y_CEWL = Y.callEnterWithin.asLocation();
+ if (X_CEWL != Y_CEWL)
+ return X_CEWL.isBeforeInTranslationUnitThan(Y_CEWL);
+ FullSourceLoc X_CRL = X.callReturn.asLocation();
+ FullSourceLoc Y_CRL = Y.callReturn.asLocation();
+ if (X_CRL != Y_CRL)
+ return X_CRL.isBeforeInTranslationUnitThan(Y_CRL);
+ return comparePath(X.path, Y.path);
+}
+
+static Optional<bool> comparePiece(const PathDiagnosticPiece &X,
+ const PathDiagnosticPiece &Y) {
+ if (X.getKind() != Y.getKind())
+ return X.getKind() < Y.getKind();
+
+ FullSourceLoc XL = X.getLocation().asLocation();
+ FullSourceLoc YL = Y.getLocation().asLocation();
+ if (XL != YL)
+ return XL.isBeforeInTranslationUnitThan(YL);
+
+ if (X.getString() != Y.getString())
+ return X.getString() < Y.getString();
+
+ if (X.getRanges().size() != Y.getRanges().size())
+ return X.getRanges().size() < Y.getRanges().size();
+
+ const SourceManager &SM = XL.getManager();
+
+ for (unsigned i = 0, n = X.getRanges().size(); i < n; ++i) {
+ SourceRange XR = X.getRanges()[i];
+ SourceRange YR = Y.getRanges()[i];
+ if (XR != YR) {
+ if (XR.getBegin() != YR.getBegin())
+ return SM.isBeforeInTranslationUnit(XR.getBegin(), YR.getBegin());
+ return SM.isBeforeInTranslationUnit(XR.getEnd(), YR.getEnd());
+ }
+ }
+
+ switch (X.getKind()) {
+ case PathDiagnosticPiece::ControlFlow:
+ return compareControlFlow(cast<PathDiagnosticControlFlowPiece>(X),
+ cast<PathDiagnosticControlFlowPiece>(Y));
+ case PathDiagnosticPiece::Event:
+ case PathDiagnosticPiece::Note:
+ return None;
+ case PathDiagnosticPiece::Macro:
+ return compareMacro(cast<PathDiagnosticMacroPiece>(X),
+ cast<PathDiagnosticMacroPiece>(Y));
+ case PathDiagnosticPiece::Call:
+ return compareCall(cast<PathDiagnosticCallPiece>(X),
+ cast<PathDiagnosticCallPiece>(Y));
+ }
+ llvm_unreachable("all cases handled");
+}
+
+static Optional<bool> comparePath(const PathPieces &X, const PathPieces &Y) {
+ if (X.size() != Y.size())
+ return X.size() < Y.size();
+
+ PathPieces::const_iterator X_I = X.begin(), X_end = X.end();
+ PathPieces::const_iterator Y_I = Y.begin(), Y_end = Y.end();
+
+ for ( ; X_I != X_end && Y_I != Y_end; ++X_I, ++Y_I) {
+ Optional<bool> b = comparePiece(**X_I, **Y_I);
+ if (b.hasValue())
+ return b.getValue();
+ }
+
+ return None;
+}
+
+static bool compareCrossTUSourceLocs(FullSourceLoc XL, FullSourceLoc YL) {
+ std::pair<FileID, unsigned> XOffs = XL.getDecomposedLoc();
+ std::pair<FileID, unsigned> YOffs = YL.getDecomposedLoc();
+ const SourceManager &SM = XL.getManager();
+ std::pair<bool, bool> InSameTU = SM.isInTheSameTranslationUnit(XOffs, YOffs);
+ if (InSameTU.first)
+ return XL.isBeforeInTranslationUnitThan(YL);
+ const FileEntry *XFE = SM.getFileEntryForID(XL.getSpellingLoc().getFileID());
+ const FileEntry *YFE = SM.getFileEntryForID(YL.getSpellingLoc().getFileID());
+ if (!XFE || !YFE)
+ return XFE && !YFE;
+ int NameCmp = XFE->getName().compare(YFE->getName());
+ if (NameCmp != 0)
+ return NameCmp == -1;
+ // Last resort: Compare raw file IDs that are possibly expansions.
+ return XL.getFileID() < YL.getFileID();
+}
+
+static bool compare(const PathDiagnostic &X, const PathDiagnostic &Y) {
+ FullSourceLoc XL = X.getLocation().asLocation();
+ FullSourceLoc YL = Y.getLocation().asLocation();
+ if (XL != YL)
+ return compareCrossTUSourceLocs(XL, YL);
+ if (X.getBugType() != Y.getBugType())
+ return X.getBugType() < Y.getBugType();
+ if (X.getCategory() != Y.getCategory())
+ return X.getCategory() < Y.getCategory();
+ if (X.getVerboseDescription() != Y.getVerboseDescription())
+ return X.getVerboseDescription() < Y.getVerboseDescription();
+ if (X.getShortDescription() != Y.getShortDescription())
+ return X.getShortDescription() < Y.getShortDescription();
+ if (X.getDeclWithIssue() != Y.getDeclWithIssue()) {
+ const Decl *XD = X.getDeclWithIssue();
+ if (!XD)
+ return true;
+ const Decl *YD = Y.getDeclWithIssue();
+ if (!YD)
+ return false;
+ SourceLocation XDL = XD->getLocation();
+ SourceLocation YDL = YD->getLocation();
+ if (XDL != YDL) {
+ const SourceManager &SM = XL.getManager();
+ return compareCrossTUSourceLocs(FullSourceLoc(XDL, SM),
+ FullSourceLoc(YDL, SM));
+ }
+ }
+ PathDiagnostic::meta_iterator XI = X.meta_begin(), XE = X.meta_end();
+ PathDiagnostic::meta_iterator YI = Y.meta_begin(), YE = Y.meta_end();
+ if (XE - XI != YE - YI)
+ return (XE - XI) < (YE - YI);
+ for ( ; XI != XE ; ++XI, ++YI) {
+ if (*XI != *YI)
+ return (*XI) < (*YI);
+ }
+ Optional<bool> b = comparePath(X.path, Y.path);
+ assert(b.hasValue());
+ return b.getValue();
+}
+
+void PathDiagnosticConsumer::FlushDiagnostics(
+ PathDiagnosticConsumer::FilesMade *Files) {
+ if (flushed)
+ return;
+
+ flushed = true;
+
+ std::vector<const PathDiagnostic *> BatchDiags;
+ for (const auto &D : Diags)
+ BatchDiags.push_back(&D);
+
+ // Sort the diagnostics so that they are always emitted in a deterministic
+ // order.
+ int (*Comp)(const PathDiagnostic *const *, const PathDiagnostic *const *) =
+ [](const PathDiagnostic *const *X, const PathDiagnostic *const *Y) {
+ assert(*X != *Y && "PathDiagnostics not uniqued!");
+ if (compare(**X, **Y))
+ return -1;
+ assert(compare(**Y, **X) && "Not a total order!");
+ return 1;
+ };
+ array_pod_sort(BatchDiags.begin(), BatchDiags.end(), Comp);
+
+ FlushDiagnosticsImpl(BatchDiags, Files);
+
+ // Delete the flushed diagnostics.
+ for (const auto D : BatchDiags)
+ delete D;
+
+ // Clear out the FoldingSet.
+ Diags.clear();
+}
+
+PathDiagnosticConsumer::FilesMade::~FilesMade() {
+ for (PDFileEntry &Entry : Set)
+ Entry.~PDFileEntry();
+}
+
+void PathDiagnosticConsumer::FilesMade::addDiagnostic(const PathDiagnostic &PD,
+ StringRef ConsumerName,
+ StringRef FileName) {
+ llvm::FoldingSetNodeID NodeID;
+ NodeID.Add(PD);
+ void *InsertPos;
+ PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos);
+ if (!Entry) {
+ Entry = Alloc.Allocate<PDFileEntry>();
+ Entry = new (Entry) PDFileEntry(NodeID);
+ Set.InsertNode(Entry, InsertPos);
+ }
+
+ // Allocate persistent storage for the file name.
+ char *FileName_cstr = (char*) Alloc.Allocate(FileName.size(), 1);
+ memcpy(FileName_cstr, FileName.data(), FileName.size());
+
+ Entry->files.push_back(std::make_pair(ConsumerName,
+ StringRef(FileName_cstr,
+ FileName.size())));
+}
+
+PathDiagnosticConsumer::PDFileEntry::ConsumerFiles *
+PathDiagnosticConsumer::FilesMade::getFiles(const PathDiagnostic &PD) {
+ llvm::FoldingSetNodeID NodeID;
+ NodeID.Add(PD);
+ void *InsertPos;
+ PDFileEntry *Entry = Set.FindNodeOrInsertPos(NodeID, InsertPos);
+ if (!Entry)
+ return nullptr;
+ return &Entry->files;
+}
+
+//===----------------------------------------------------------------------===//
+// PathDiagnosticLocation methods.
+//===----------------------------------------------------------------------===//
+
+static SourceLocation getValidSourceLocation(const Stmt* S,
+ LocationOrAnalysisDeclContext LAC,
+ bool UseEnd = false) {
+ SourceLocation L = UseEnd ? S->getEndLoc() : S->getBeginLoc();
+ assert(!LAC.isNull() && "A valid LocationContext or AnalysisDeclContext should "
+ "be passed to PathDiagnosticLocation upon creation.");
+
+ // S might be a temporary statement that does not have a location in the
+ // source code, so find an enclosing statement and use its location.
+ if (!L.isValid()) {
+ AnalysisDeclContext *ADC;
+ if (LAC.is<const LocationContext*>())
+ ADC = LAC.get<const LocationContext*>()->getAnalysisDeclContext();
+ else
+ ADC = LAC.get<AnalysisDeclContext*>();
+
+ ParentMap &PM = ADC->getParentMap();
+
+ const Stmt *Parent = S;
+ do {
+ Parent = PM.getParent(Parent);
+
+ // In rare cases, we have implicit top-level expressions,
+ // such as arguments for implicit member initializers.
+ // In this case, fall back to the start of the body (even if we were
+ // asked for the statement end location).
+ if (!Parent) {
+ const Stmt *Body = ADC->getBody();
+ if (Body)
+ L = Body->getBeginLoc();
+ else
+ L = ADC->getDecl()->getEndLoc();
+ break;
+ }
+
+ L = UseEnd ? Parent->getEndLoc() : Parent->getBeginLoc();
+ } while (!L.isValid());
+ }
+
+ return L;
+}
+
+static PathDiagnosticLocation
+getLocationForCaller(const StackFrameContext *SFC,
+ const LocationContext *CallerCtx,
+ const SourceManager &SM) {
+ const CFGBlock &Block = *SFC->getCallSiteBlock();
+ CFGElement Source = Block[SFC->getIndex()];
+
+ switch (Source.getKind()) {
+ case CFGElement::Statement:
+ case CFGElement::Constructor:
+ case CFGElement::CXXRecordTypedCall:
+ return PathDiagnosticLocation(Source.castAs<CFGStmt>().getStmt(),
+ SM, CallerCtx);
+ case CFGElement::Initializer: {
+ const CFGInitializer &Init = Source.castAs<CFGInitializer>();
+ return PathDiagnosticLocation(Init.getInitializer()->getInit(),
+ SM, CallerCtx);
+ }
+ case CFGElement::AutomaticObjectDtor: {
+ const CFGAutomaticObjDtor &Dtor = Source.castAs<CFGAutomaticObjDtor>();
+ return PathDiagnosticLocation::createEnd(Dtor.getTriggerStmt(),
+ SM, CallerCtx);
+ }
+ case CFGElement::DeleteDtor: {
+ const CFGDeleteDtor &Dtor = Source.castAs<CFGDeleteDtor>();
+ return PathDiagnosticLocation(Dtor.getDeleteExpr(), SM, CallerCtx);
+ }
+ case CFGElement::BaseDtor:
+ case CFGElement::MemberDtor: {
+ const AnalysisDeclContext *CallerInfo = CallerCtx->getAnalysisDeclContext();
+ if (const Stmt *CallerBody = CallerInfo->getBody())
+ return PathDiagnosticLocation::createEnd(CallerBody, SM, CallerCtx);
+ return PathDiagnosticLocation::create(CallerInfo->getDecl(), SM);
+ }
+ case CFGElement::NewAllocator: {
+ const CFGNewAllocator &Alloc = Source.castAs<CFGNewAllocator>();
+ return PathDiagnosticLocation(Alloc.getAllocatorExpr(), SM, CallerCtx);
+ }
+ case CFGElement::TemporaryDtor: {
+ // Temporary destructors are for temporaries. They die immediately at around
+ // the location of CXXBindTemporaryExpr. If they are lifetime-extended,
+ // they'd be dealt with via an AutomaticObjectDtor instead.
+ const auto &Dtor = Source.castAs<CFGTemporaryDtor>();
+ return PathDiagnosticLocation::createEnd(Dtor.getBindTemporaryExpr(), SM,
+ CallerCtx);
+ }
+ case CFGElement::ScopeBegin:
+ case CFGElement::ScopeEnd:
+ llvm_unreachable("not yet implemented!");
+ case CFGElement::LifetimeEnds:
+ case CFGElement::LoopExit:
+ llvm_unreachable("CFGElement kind should not be on callsite!");
+ }
+
+ llvm_unreachable("Unknown CFGElement kind");
+}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createBegin(const Decl *D,
+ const SourceManager &SM) {
+ return PathDiagnosticLocation(D->getBeginLoc(), SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createBegin(const Stmt *S,
+ const SourceManager &SM,
+ LocationOrAnalysisDeclContext LAC) {
+ return PathDiagnosticLocation(getValidSourceLocation(S, LAC),
+ SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createEnd(const Stmt *S,
+ const SourceManager &SM,
+ LocationOrAnalysisDeclContext LAC) {
+ if (const auto *CS = dyn_cast<CompoundStmt>(S))
+ return createEndBrace(CS, SM);
+ return PathDiagnosticLocation(getValidSourceLocation(S, LAC, /*End=*/true),
+ SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createOperatorLoc(const BinaryOperator *BO,
+ const SourceManager &SM) {
+ return PathDiagnosticLocation(BO->getOperatorLoc(), SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createConditionalColonLoc(
+ const ConditionalOperator *CO,
+ const SourceManager &SM) {
+ return PathDiagnosticLocation(CO->getColonLoc(), SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
+ const SourceManager &SM) {
+ return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createBeginBrace(const CompoundStmt *CS,
+ const SourceManager &SM) {
+ SourceLocation L = CS->getLBracLoc();
+ return PathDiagnosticLocation(L, SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createEndBrace(const CompoundStmt *CS,
+ const SourceManager &SM) {
+ SourceLocation L = CS->getRBracLoc();
+ return PathDiagnosticLocation(L, SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createDeclBegin(const LocationContext *LC,
+ const SourceManager &SM) {
+ // FIXME: Should handle CXXTryStmt if analyser starts supporting C++.
+ if (const auto *CS = dyn_cast_or_null<CompoundStmt>(LC->getDecl()->getBody()))
+ if (!CS->body_empty()) {
+ SourceLocation Loc = (*CS->body_begin())->getBeginLoc();
+ return PathDiagnosticLocation(Loc, SM, SingleLocK);
+ }
+
+ return PathDiagnosticLocation();
+}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::createDeclEnd(const LocationContext *LC,
+ const SourceManager &SM) {
+ SourceLocation L = LC->getDecl()->getBodyRBrace();
+ return PathDiagnosticLocation(L, SM, SingleLocK);
+}
+
+PathDiagnosticLocation
+PathDiagnosticLocation::create(const ProgramPoint& P,
+ const SourceManager &SMng) {
+ const Stmt* S = nullptr;
+ if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
+ const CFGBlock *BSrc = BE->getSrc();
+ S = BSrc->getTerminatorCondition();
+ } else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) {
+ S = SP->getStmt();
+ if (P.getAs<PostStmtPurgeDeadSymbols>())
+ return PathDiagnosticLocation::createEnd(S, SMng, P.getLocationContext());
+ } else if (Optional<PostInitializer> PIP = P.getAs<PostInitializer>()) {
+ return PathDiagnosticLocation(PIP->getInitializer()->getSourceLocation(),
+ SMng);
+ } else if (Optional<PreImplicitCall> PIC = P.getAs<PreImplicitCall>()) {
+ return PathDiagnosticLocation(PIC->getLocation(), SMng);
+ } else if (Optional<PostImplicitCall> PIE = P.getAs<PostImplicitCall>()) {
+ return PathDiagnosticLocation(PIE->getLocation(), SMng);
+ } else if (Optional<CallEnter> CE = P.getAs<CallEnter>()) {
+ return getLocationForCaller(CE->getCalleeContext(),
+ CE->getLocationContext(),
+ SMng);
+ } else if (Optional<CallExitEnd> CEE = P.getAs<CallExitEnd>()) {
+ return getLocationForCaller(CEE->getCalleeContext(),
+ CEE->getLocationContext(),
+ SMng);
+ } else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
+ CFGElement BlockFront = BE->getBlock()->front();
+ if (auto StmtElt = BlockFront.getAs<CFGStmt>()) {
+ return PathDiagnosticLocation(StmtElt->getStmt()->getBeginLoc(), SMng);
+ } else if (auto NewAllocElt = BlockFront.getAs<CFGNewAllocator>()) {
+ return PathDiagnosticLocation(
+ NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng);
+ }
+ llvm_unreachable("Unexpected CFG element at front of block");
+ } else {
+ llvm_unreachable("Unexpected ProgramPoint");
+ }
+
+ return PathDiagnosticLocation(S, SMng, P.getLocationContext());
+}
+
+static const LocationContext *
+findTopAutosynthesizedParentContext(const LocationContext *LC) {
+ assert(LC->getAnalysisDeclContext()->isBodyAutosynthesized());
+ const LocationContext *ParentLC = LC->getParent();
+ assert(ParentLC && "We don't start analysis from autosynthesized code");
+ while (ParentLC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
+ LC = ParentLC;
+ ParentLC = LC->getParent();
+ assert(ParentLC && "We don't start analysis from autosynthesized code");
+ }
+ return LC;
+}
+
+const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) {
+ // We cannot place diagnostics on autosynthesized code.
+ // Put them onto the call site through which we jumped into autosynthesized
+ // code for the first time.
+ const LocationContext *LC = N->getLocationContext();
+ if (LC->getAnalysisDeclContext()->isBodyAutosynthesized()) {
+ // It must be a stack frame because we only autosynthesize functions.
+ return cast<StackFrameContext>(findTopAutosynthesizedParentContext(LC))
+ ->getCallSite();
+ }
+ // Otherwise, see if the node's program point directly points to a statement.
+ ProgramPoint P = N->getLocation();
+ if (auto SP = P.getAs<StmtPoint>())
+ return SP->getStmt();
+ if (auto BE = P.getAs<BlockEdge>())
+ return BE->getSrc()->getTerminator();
+ if (auto CE = P.getAs<CallEnter>())
+ return CE->getCallExpr();
+ if (auto CEE = P.getAs<CallExitEnd>())
+ return CEE->getCalleeContext()->getCallSite();
+ if (auto PIPP = P.getAs<PostInitializer>())
+ return PIPP->getInitializer()->getInit();
+ if (auto CEB = P.getAs<CallExitBegin>())
+ return CEB->getReturnStmt();
+ if (auto FEP = P.getAs<FunctionExitPoint>())
+ return FEP->getStmt();
+
+ return nullptr;
+}
+
+const Stmt *PathDiagnosticLocation::getNextStmt(const ExplodedNode *N) {
+ for (N = N->getFirstSucc(); N; N = N->getFirstSucc()) {
+ if (const Stmt *S = getStmt(N)) {
+ // Check if the statement is '?' or '&&'/'||'. These are "merges",
+ // not actual statement points.
+ switch (S->getStmtClass()) {
+ case Stmt::ChooseExprClass:
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass:
+ continue;
+ case Stmt::BinaryOperatorClass: {
+ BinaryOperatorKind Op = cast<BinaryOperator>(S)->getOpcode();
+ if (Op == BO_LAnd || Op == BO_LOr)
+ continue;
+ break;
+ }
+ default:
+ break;
+ }
+ // We found the statement, so return it.
+ return S;
+ }
+ }
+
+ return nullptr;
+}
+
+PathDiagnosticLocation
+ PathDiagnosticLocation::createEndOfPath(const ExplodedNode *N,
+ const SourceManager &SM) {
+ assert(N && "Cannot create a location with a null node.");
+ const Stmt *S = getStmt(N);
+ const LocationContext *LC = N->getLocationContext();
+
+ if (!S) {
+ // If this is an implicit call, return the implicit call point location.
+ if (Optional<PreImplicitCall> PIE = N->getLocationAs<PreImplicitCall>())
+ return PathDiagnosticLocation(PIE->getLocation(), SM);
+ if (auto FE = N->getLocationAs<FunctionExitPoint>()) {
+ if (const ReturnStmt *RS = FE->getStmt())
+ return PathDiagnosticLocation::createBegin(RS, SM, LC);
+ }
+ S = getNextStmt(N);
+ }
+
+ if (S) {
+ ProgramPoint P = N->getLocation();
+
+ // For member expressions, return the location of the '.' or '->'.
+ if (const auto *ME = dyn_cast<MemberExpr>(S))
+ return PathDiagnosticLocation::createMemberLoc(ME, SM);
+
+ // For binary operators, return the location of the operator.
+ if (const auto *B = dyn_cast<BinaryOperator>(S))
+ return PathDiagnosticLocation::createOperatorLoc(B, SM);
+
+ if (P.getAs<PostStmtPurgeDeadSymbols>())
+ return PathDiagnosticLocation::createEnd(S, SM, LC);
+
+ if (S->getBeginLoc().isValid())
+ return PathDiagnosticLocation(S, SM, LC);
+ return PathDiagnosticLocation(getValidSourceLocation(S, LC), SM);
+ }
+
+ return createDeclEnd(N->getLocationContext(), SM);
+}
+
+PathDiagnosticLocation PathDiagnosticLocation::createSingleLocation(
+ const PathDiagnosticLocation &PDL) {
+ FullSourceLoc L = PDL.asLocation();
+ return PathDiagnosticLocation(L, L.getManager(), SingleLocK);
+}
+
+FullSourceLoc
+ PathDiagnosticLocation::genLocation(SourceLocation L,
+ LocationOrAnalysisDeclContext LAC) const {
+ assert(isValid());
+ // Note that we want a 'switch' here so that the compiler can warn us in
+ // case we add more cases.
+ switch (K) {
+ case SingleLocK:
+ case RangeK:
+ break;
+ case StmtK:
+ // Defensive checking.
+ if (!S)
+ break;
+ return FullSourceLoc(getValidSourceLocation(S, LAC),
+ const_cast<SourceManager&>(*SM));
+ case DeclK:
+ // Defensive checking.
+ if (!D)
+ break;
+ return FullSourceLoc(D->getLocation(), const_cast<SourceManager&>(*SM));
+ }
+
+ return FullSourceLoc(L, const_cast<SourceManager&>(*SM));
+}
+
+PathDiagnosticRange
+ PathDiagnosticLocation::genRange(LocationOrAnalysisDeclContext LAC) const {
+ assert(isValid());
+ // Note that we want a 'switch' here so that the compiler can warn us in
+ // case we add more cases.
+ switch (K) {
+ case SingleLocK:
+ return PathDiagnosticRange(SourceRange(Loc,Loc), true);
+ case RangeK:
+ break;
+ case StmtK: {
+ const Stmt *S = asStmt();
+ switch (S->getStmtClass()) {
+ default:
+ break;
+ case Stmt::DeclStmtClass: {
+ const auto *DS = cast<DeclStmt>(S);
+ if (DS->isSingleDecl()) {
+ // Should always be the case, but we'll be defensive.
+ return SourceRange(DS->getBeginLoc(),
+ DS->getSingleDecl()->getLocation());
+ }
+ break;
+ }
+ // FIXME: Provide better range information for different
+ // terminators.
+ case Stmt::IfStmtClass:
+ case Stmt::WhileStmtClass:
+ case Stmt::DoStmtClass:
+ case Stmt::ForStmtClass:
+ case Stmt::ChooseExprClass:
+ case Stmt::IndirectGotoStmtClass:
+ case Stmt::SwitchStmtClass:
+ case Stmt::BinaryConditionalOperatorClass:
+ case Stmt::ConditionalOperatorClass:
+ case Stmt::ObjCForCollectionStmtClass: {
+ SourceLocation L = getValidSourceLocation(S, LAC);
+ return SourceRange(L, L);
+ }
+ }
+ SourceRange R = S->getSourceRange();
+ if (R.isValid())
+ return R;
+ break;
+ }
+ case DeclK:
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
+ return MD->getSourceRange();
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ if (Stmt *Body = FD->getBody())
+ return Body->getSourceRange();
+ }
+ else {
+ SourceLocation L = D->getLocation();
+ return PathDiagnosticRange(SourceRange(L, L), true);
+ }
+ }
+
+ return SourceRange(Loc, Loc);
+}
+
+void PathDiagnosticLocation::flatten() {
+ if (K == StmtK) {
+ K = RangeK;
+ S = nullptr;
+ D = nullptr;
+ }
+ else if (K == DeclK) {
+ K = SingleLocK;
+ S = nullptr;
+ D = nullptr;
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Manipulation of PathDiagnosticCallPieces.
+//===----------------------------------------------------------------------===//
+
+std::shared_ptr<PathDiagnosticCallPiece>
+PathDiagnosticCallPiece::construct(const CallExitEnd &CE,
+ const SourceManager &SM) {
+ const Decl *caller = CE.getLocationContext()->getDecl();
+ PathDiagnosticLocation pos = getLocationForCaller(CE.getCalleeContext(),
+ CE.getLocationContext(),
+ SM);
+ return std::shared_ptr<PathDiagnosticCallPiece>(
+ new PathDiagnosticCallPiece(caller, pos));
+}
+
+PathDiagnosticCallPiece *
+PathDiagnosticCallPiece::construct(PathPieces &path,
+ const Decl *caller) {
+ std::shared_ptr<PathDiagnosticCallPiece> C(
+ new PathDiagnosticCallPiece(path, caller));
+ path.clear();
+ auto *R = C.get();
+ path.push_front(std::move(C));
+ return R;
+}
+
+void PathDiagnosticCallPiece::setCallee(const CallEnter &CE,
+ const SourceManager &SM) {
+ const StackFrameContext *CalleeCtx = CE.getCalleeContext();
+ Callee = CalleeCtx->getDecl();
+
+ callEnterWithin = PathDiagnosticLocation::createBegin(Callee, SM);
+ callEnter = getLocationForCaller(CalleeCtx, CE.getLocationContext(), SM);
+
+ // Autosynthesized property accessors are special because we'd never
+ // pop back up to non-autosynthesized code until we leave them.
+ // This is not generally true for autosynthesized callees, which may call
+ // non-autosynthesized callbacks.
+ // Unless set here, the IsCalleeAnAutosynthesizedPropertyAccessor flag
+ // defaults to false.
+ if (const auto *MD = dyn_cast<ObjCMethodDecl>(Callee))
+ IsCalleeAnAutosynthesizedPropertyAccessor = (
+ MD->isPropertyAccessor() &&
+ CalleeCtx->getAnalysisDeclContext()->isBodyAutosynthesized());
+}
+
+static void describeTemplateParameters(raw_ostream &Out,
+ const ArrayRef<TemplateArgument> TAList,
+ const LangOptions &LO,
+ StringRef Prefix = StringRef(),
+ StringRef Postfix = StringRef());
+
+static void describeTemplateParameter(raw_ostream &Out,
+ const TemplateArgument &TArg,
+ const LangOptions &LO) {
+
+ if (TArg.getKind() == TemplateArgument::ArgKind::Pack) {
+ describeTemplateParameters(Out, TArg.getPackAsArray(), LO);
+ } else {
+ TArg.print(PrintingPolicy(LO), Out);
+ }
+}
+
+static void describeTemplateParameters(raw_ostream &Out,
+ const ArrayRef<TemplateArgument> TAList,
+ const LangOptions &LO,
+ StringRef Prefix, StringRef Postfix) {
+ if (TAList.empty())
+ return;
+
+ Out << Prefix;
+ for (int I = 0, Last = TAList.size() - 1; I != Last; ++I) {
+ describeTemplateParameter(Out, TAList[I], LO);
+ Out << ", ";
+ }
+ describeTemplateParameter(Out, TAList[TAList.size() - 1], LO);
+ Out << Postfix;
+}
+
+static void describeClass(raw_ostream &Out, const CXXRecordDecl *D,
+ StringRef Prefix = StringRef()) {
+ if (!D->getIdentifier())
+ return;
+ Out << Prefix << '\'' << *D;
+ if (const auto T = dyn_cast<ClassTemplateSpecializationDecl>(D))
+ describeTemplateParameters(Out, T->getTemplateArgs().asArray(),
+ D->getASTContext().getLangOpts(), "<", ">");
+
+ Out << '\'';
+}
+
+static bool describeCodeDecl(raw_ostream &Out, const Decl *D,
+ bool ExtendedDescription,
+ StringRef Prefix = StringRef()) {
+ if (!D)
+ return false;
+
+ if (isa<BlockDecl>(D)) {
+ if (ExtendedDescription)
+ Out << Prefix << "anonymous block";
+ return ExtendedDescription;
+ }
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
+ Out << Prefix;
+ if (ExtendedDescription && !MD->isUserProvided()) {
+ if (MD->isExplicitlyDefaulted())
+ Out << "defaulted ";
+ else
+ Out << "implicit ";
+ }
+
+ if (const auto *CD = dyn_cast<CXXConstructorDecl>(MD)) {
+ if (CD->isDefaultConstructor())
+ Out << "default ";
+ else if (CD->isCopyConstructor())
+ Out << "copy ";
+ else if (CD->isMoveConstructor())
+ Out << "move ";
+
+ Out << "constructor";
+ describeClass(Out, MD->getParent(), " for ");
+ } else if (isa<CXXDestructorDecl>(MD)) {
+ if (!MD->isUserProvided()) {
+ Out << "destructor";
+ describeClass(Out, MD->getParent(), " for ");
+ } else {
+ // Use ~Foo for explicitly-written destructors.
+ Out << "'" << *MD << "'";
+ }
+ } else if (MD->isCopyAssignmentOperator()) {
+ Out << "copy assignment operator";
+ describeClass(Out, MD->getParent(), " for ");
+ } else if (MD->isMoveAssignmentOperator()) {
+ Out << "move assignment operator";
+ describeClass(Out, MD->getParent(), " for ");
+ } else {
+ if (MD->getParent()->getIdentifier())
+ Out << "'" << *MD->getParent() << "::" << *MD << "'";
+ else
+ Out << "'" << *MD << "'";
+ }
+
+ return true;
+ }
+
+ Out << Prefix << '\'' << cast<NamedDecl>(*D);
+
+ // Adding template parameters.
+ if (const auto FD = dyn_cast<FunctionDecl>(D))
+ if (const TemplateArgumentList *TAList =
+ FD->getTemplateSpecializationArgs())
+ describeTemplateParameters(Out, TAList->asArray(),
+ FD->getASTContext().getLangOpts(), "<", ">");
+
+ Out << '\'';
+ return true;
+}
+
+std::shared_ptr<PathDiagnosticEventPiece>
+PathDiagnosticCallPiece::getCallEnterEvent() const {
+ // We do not produce call enters and call exits for autosynthesized property
+ // accessors. We do generally produce them for other functions coming from
+ // the body farm because they may call callbacks that bring us back into
+ // visible code.
+ if (!Callee || IsCalleeAnAutosynthesizedPropertyAccessor)
+ return nullptr;
+
+ SmallString<256> buf;
+ llvm::raw_svector_ostream Out(buf);
+
+ Out << "Calling ";
+ describeCodeDecl(Out, Callee, /*ExtendedDescription=*/true);
+
+ assert(callEnter.asLocation().isValid());
+ return std::make_shared<PathDiagnosticEventPiece>(callEnter, Out.str());
+}
+
+std::shared_ptr<PathDiagnosticEventPiece>
+PathDiagnosticCallPiece::getCallEnterWithinCallerEvent() const {
+ if (!callEnterWithin.asLocation().isValid())
+ return nullptr;
+ if (Callee->isImplicit() || !Callee->hasBody())
+ return nullptr;
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(Callee))
+ if (MD->isDefaulted())
+ return nullptr;
+
+ SmallString<256> buf;
+ llvm::raw_svector_ostream Out(buf);
+
+ Out << "Entered call";
+ describeCodeDecl(Out, Caller, /*ExtendedDescription=*/false, " from ");
+
+ return std::make_shared<PathDiagnosticEventPiece>(callEnterWithin, Out.str());
+}
+
+std::shared_ptr<PathDiagnosticEventPiece>
+PathDiagnosticCallPiece::getCallExitEvent() const {
+ // We do not produce call enters and call exits for autosynthesized property
+ // accessors. We do generally produce them for other functions coming from
+ // the body farm because they may call callbacks that bring us back into
+ // visible code.
+ if (NoExit || IsCalleeAnAutosynthesizedPropertyAccessor)
+ return nullptr;
+
+ SmallString<256> buf;
+ llvm::raw_svector_ostream Out(buf);
+
+ if (!CallStackMessage.empty()) {
+ Out << CallStackMessage;
+ } else {
+ bool DidDescribe = describeCodeDecl(Out, Callee,
+ /*ExtendedDescription=*/false,
+ "Returning from ");
+ if (!DidDescribe)
+ Out << "Returning to caller";
+ }
+
+ assert(callReturn.asLocation().isValid());
+ return std::make_shared<PathDiagnosticEventPiece>(callReturn, Out.str());
+}
+
+static void compute_path_size(const PathPieces &pieces, unsigned &size) {
+ for (const auto &I : pieces) {
+ const PathDiagnosticPiece *piece = I.get();
+ if (const auto *cp = dyn_cast<PathDiagnosticCallPiece>(piece))
+ compute_path_size(cp->path, size);
+ else
+ ++size;
+ }
+}
+
+unsigned PathDiagnostic::full_size() {
+ unsigned size = 0;
+ compute_path_size(path, size);
+ return size;
+}
+
+//===----------------------------------------------------------------------===//
+// FoldingSet profiling methods.
+//===----------------------------------------------------------------------===//
+
+void PathDiagnosticLocation::Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger(Range.getBegin().getRawEncoding());
+ ID.AddInteger(Range.getEnd().getRawEncoding());
+ ID.AddInteger(Loc.getRawEncoding());
+}
+
+void PathDiagnosticPiece::Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.AddInteger((unsigned) getKind());
+ ID.AddString(str);
+ // FIXME: Add profiling support for code hints.
+ ID.AddInteger((unsigned) getDisplayHint());
+ ArrayRef<SourceRange> Ranges = getRanges();
+ for (const auto &I : Ranges) {
+ ID.AddInteger(I.getBegin().getRawEncoding());
+ ID.AddInteger(I.getEnd().getRawEncoding());
+ }
+}
+
+void PathDiagnosticCallPiece::Profile(llvm::FoldingSetNodeID &ID) const {
+ PathDiagnosticPiece::Profile(ID);
+ for (const auto &I : path)
+ ID.Add(*I);
+}
+
+void PathDiagnosticSpotPiece::Profile(llvm::FoldingSetNodeID &ID) const {
+ PathDiagnosticPiece::Profile(ID);
+ ID.Add(Pos);
+}
+
+void PathDiagnosticControlFlowPiece::Profile(llvm::FoldingSetNodeID &ID) const {
+ PathDiagnosticPiece::Profile(ID);
+ for (const auto &I : *this)
+ ID.Add(I);
+}
+
+void PathDiagnosticMacroPiece::Profile(llvm::FoldingSetNodeID &ID) const {
+ PathDiagnosticSpotPiece::Profile(ID);
+ for (const auto &I : subPieces)
+ ID.Add(*I);
+}
+
+void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const {
+ PathDiagnosticSpotPiece::Profile(ID);
+}
+
+void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
+ ID.Add(getLocation());
+ ID.AddString(BugType);
+ ID.AddString(VerboseDesc);
+ ID.AddString(Category);
+}
+
+void PathDiagnostic::FullProfile(llvm::FoldingSetNodeID &ID) const {
+ Profile(ID);
+ for (const auto &I : path)
+ ID.Add(*I);
+ for (meta_iterator I = meta_begin(), E = meta_end(); I != E; ++I)
+ ID.AddString(*I);
+}
+
+StackHintGenerator::~StackHintGenerator() = default;
+
+std::string StackHintGeneratorForSymbol::getMessage(const ExplodedNode *N){
+ if (!N)
+ return getMessageForSymbolNotFound();
+
+ ProgramPoint P = N->getLocation();
+ CallExitEnd CExit = P.castAs<CallExitEnd>();
+
+ // FIXME: Use CallEvent to abstract this over all calls.
+ const Stmt *CallSite = CExit.getCalleeContext()->getCallSite();
+ const auto *CE = dyn_cast_or_null<CallExpr>(CallSite);
+ if (!CE)
+ return {};
+
+ // Check if one of the parameters are set to the interesting symbol.
+ unsigned ArgIndex = 0;
+ for (CallExpr::const_arg_iterator I = CE->arg_begin(),
+ E = CE->arg_end(); I != E; ++I, ++ArgIndex){
+ SVal SV = N->getSVal(*I);
+
+ // Check if the variable corresponding to the symbol is passed by value.
+ SymbolRef AS = SV.getAsLocSymbol();
+ if (AS == Sym) {
+ return getMessageForArg(*I, ArgIndex);
+ }
+
+ // Check if the parameter is a pointer to the symbol.
+ if (Optional<loc::MemRegionVal> Reg = SV.getAs<loc::MemRegionVal>()) {
+ // Do not attempt to dereference void*.
+ if ((*I)->getType()->isVoidPointerType())
+ continue;
+ SVal PSV = N->getState()->getSVal(Reg->getRegion());
+ SymbolRef AS = PSV.getAsLocSymbol();
+ if (AS == Sym) {
+ return getMessageForArg(*I, ArgIndex);
+ }
+ }
+ }
+
+ // Check if we are returning the interesting symbol.
+ SVal SV = N->getSVal(CE);
+ SymbolRef RetSym = SV.getAsLocSymbol();
+ if (RetSym == Sym) {
+ return getMessageForReturn(CE);
+ }
+
+ return getMessageForSymbolNotFound();
+}
+
+std::string StackHintGeneratorForSymbol::getMessageForArg(const Expr *ArgE,
+ unsigned ArgIndex) {
+ // Printed parameters start at 1, not 0.
+ ++ArgIndex;
+
+ SmallString<200> buf;
+ llvm::raw_svector_ostream os(buf);
+
+ os << Msg << " via " << ArgIndex << llvm::getOrdinalSuffix(ArgIndex)
+ << " parameter";
+
+ return os.str();
+}
+
+LLVM_DUMP_METHOD void PathPieces::dump() const {
+ unsigned index = 0;
+ for (PathPieces::const_iterator I = begin(), E = end(); I != E; ++I) {
+ llvm::errs() << "[" << index++ << "] ";
+ (*I)->dump();
+ llvm::errs() << "\n";
+ }
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticCallPiece::dump() const {
+ llvm::errs() << "CALL\n--------------\n";
+
+ if (const Stmt *SLoc = getLocation().getStmtOrNull())
+ SLoc->dump();
+ else if (const auto *ND = dyn_cast_or_null<NamedDecl>(getCallee()))
+ llvm::errs() << *ND << "\n";
+ else
+ getLocation().dump();
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticEventPiece::dump() const {
+ llvm::errs() << "EVENT\n--------------\n";
+ llvm::errs() << getString() << "\n";
+ llvm::errs() << " ---- at ----\n";
+ getLocation().dump();
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticControlFlowPiece::dump() const {
+ llvm::errs() << "CONTROL\n--------------\n";
+ getStartLocation().dump();
+ llvm::errs() << " ---- to ----\n";
+ getEndLocation().dump();
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticMacroPiece::dump() const {
+ llvm::errs() << "MACRO\n--------------\n";
+ // FIXME: Print which macro is being invoked.
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const {
+ llvm::errs() << "NOTE\n--------------\n";
+ llvm::errs() << getString() << "\n";
+ llvm::errs() << " ---- at ----\n";
+ getLocation().dump();
+}
+
+LLVM_DUMP_METHOD void PathDiagnosticLocation::dump() const {
+ if (!isValid()) {
+ llvm::errs() << "<INVALID>\n";
+ return;
+ }
+
+ switch (K) {
+ case RangeK:
+ // FIXME: actually print the range.
+ llvm::errs() << "<range>\n";
+ break;
+ case SingleLocK:
+ asLocation().dump();
+ llvm::errs() << "\n";
+ break;
+ case StmtK:
+ if (S)
+ S->dump();
+ else
+ llvm::errs() << "<NULL STMT>\n";
+ break;
+ case DeclK:
+ if (const auto *ND = dyn_cast_or_null<NamedDecl>(D))
+ llvm::errs() << *ND << "\n";
+ else if (isa<BlockDecl>(D))
+ // FIXME: Make this nicer.
+ llvm::errs() << "<block>\n";
+ else if (D)
+ llvm::errs() << "<unknown decl>\n";
+ else
+ llvm::errs() << "<NULL DECL>\n";
+ break;
+ }
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
new file mode 100644
index 000000000000..db4cf76578d8
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -0,0 +1,1117 @@
+//===--- PlistDiagnostics.cpp - Plist Diagnostics for Paths -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the PlistDiagnostics object.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/FileManager.h"
+#include "clang/Basic/PlistSupport.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/Version.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Lex/TokenConcatenation.h"
+#include "clang/Rewrite/Core/HTMLRewrite.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/IssueHash.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+
+using namespace clang;
+using namespace ento;
+using namespace markup;
+
+//===----------------------------------------------------------------------===//
+// Declarations of helper classes and functions for emitting bug reports in
+// plist format.
+//===----------------------------------------------------------------------===//
+
+namespace {
+ class PlistDiagnostics : public PathDiagnosticConsumer {
+ const std::string OutputFile;
+ const Preprocessor &PP;
+ AnalyzerOptions &AnOpts;
+ const bool SupportsCrossFileDiagnostics;
+ public:
+ PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
+ const std::string& prefix,
+ const Preprocessor &PP,
+ bool supportsMultipleFiles);
+
+ ~PlistDiagnostics() override {}
+
+ void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
+ FilesMade *filesMade) override;
+
+ StringRef getName() const override {
+ return "PlistDiagnostics";
+ }
+
+ PathGenerationScheme getGenerationScheme() const override {
+ return Extensive;
+ }
+ bool supportsLogicalOpControlFlow() const override { return true; }
+ bool supportsCrossFileDiagnostics() const override {
+ return SupportsCrossFileDiagnostics;
+ }
+ };
+} // end anonymous namespace
+
+namespace {
+
+/// A helper class for emitting a single report.
+class PlistPrinter {
+ const FIDMap& FM;
+ AnalyzerOptions &AnOpts;
+ const Preprocessor &PP;
+ llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces;
+
+public:
+ PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts,
+ const Preprocessor &PP)
+ : FM(FM), AnOpts(AnOpts), PP(PP) {
+ }
+
+ void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) {
+ ReportPiece(o, P, /*indent*/ 4, /*depth*/ 0, /*includeControlFlow*/ true);
+
+ // Don't emit a warning about an unused private field.
+ (void)AnOpts;
+ }
+
+ /// Print the expansions of the collected macro pieces.
+ ///
+ /// Each time ReportDiag is called on a PathDiagnosticMacroPiece (or, if one
+ /// is found through a call piece, etc), it's subpieces are reported, and the
+ /// piece itself is collected. Call this function after the entire bugpath
+ /// was reported.
+ void ReportMacroExpansions(raw_ostream &o, unsigned indent);
+
+private:
+ void ReportPiece(raw_ostream &o, const PathDiagnosticPiece &P,
+ unsigned indent, unsigned depth, bool includeControlFlow,
+ bool isKeyEvent = false) {
+ switch (P.getKind()) {
+ case PathDiagnosticPiece::ControlFlow:
+ if (includeControlFlow)
+ ReportControlFlow(o, cast<PathDiagnosticControlFlowPiece>(P), indent);
+ break;
+ case PathDiagnosticPiece::Call:
+ ReportCall(o, cast<PathDiagnosticCallPiece>(P), indent,
+ depth);
+ break;
+ case PathDiagnosticPiece::Event:
+ ReportEvent(o, cast<PathDiagnosticEventPiece>(P), indent, depth,
+ isKeyEvent);
+ break;
+ case PathDiagnosticPiece::Macro:
+ ReportMacroSubPieces(o, cast<PathDiagnosticMacroPiece>(P), indent,
+ depth);
+ break;
+ case PathDiagnosticPiece::Note:
+ ReportNote(o, cast<PathDiagnosticNotePiece>(P), indent);
+ break;
+ }
+ }
+
+ void EmitRanges(raw_ostream &o, const ArrayRef<SourceRange> Ranges,
+ unsigned indent);
+ void EmitMessage(raw_ostream &o, StringRef Message, unsigned indent);
+
+ void ReportControlFlow(raw_ostream &o,
+ const PathDiagnosticControlFlowPiece& P,
+ unsigned indent);
+ void ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P,
+ unsigned indent, unsigned depth, bool isKeyEvent = false);
+ void ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P,
+ unsigned indent, unsigned depth);
+ void ReportMacroSubPieces(raw_ostream &o, const PathDiagnosticMacroPiece& P,
+ unsigned indent, unsigned depth);
+ void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
+ unsigned indent);
+};
+
+} // end of anonymous namespace
+
+namespace {
+
+struct ExpansionInfo {
+ std::string MacroName;
+ std::string Expansion;
+ ExpansionInfo(std::string N, std::string E)
+ : MacroName(std::move(N)), Expansion(std::move(E)) {}
+};
+
+} // end of anonymous namespace
+
+static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
+ AnalyzerOptions &AnOpts,
+ const Preprocessor &PP,
+ const PathPieces &Path);
+
+/// Print coverage information to output stream {@code o}.
+/// May modify the used list of files {@code Fids} by inserting new ones.
+static void printCoverage(const PathDiagnostic *D,
+ unsigned InputIndentLevel,
+ SmallVectorImpl<FileID> &Fids,
+ FIDMap &FM,
+ llvm::raw_fd_ostream &o);
+
+static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc,
+ const Preprocessor &PP);
+
+//===----------------------------------------------------------------------===//
+// Methods of PlistPrinter.
+//===----------------------------------------------------------------------===//
+
+void PlistPrinter::EmitRanges(raw_ostream &o,
+ const ArrayRef<SourceRange> Ranges,
+ unsigned indent) {
+
+ if (Ranges.empty())
+ return;
+
+ Indent(o, indent) << "<key>ranges</key>\n";
+ Indent(o, indent) << "<array>\n";
+ ++indent;
+
+ const SourceManager &SM = PP.getSourceManager();
+ const LangOptions &LangOpts = PP.getLangOpts();
+
+ for (auto &R : Ranges)
+ EmitRange(o, SM,
+ Lexer::getAsCharRange(SM.getExpansionRange(R), SM, LangOpts),
+ FM, indent + 1);
+ --indent;
+ Indent(o, indent) << "</array>\n";
+}
+
+void PlistPrinter::EmitMessage(raw_ostream &o, StringRef Message,
+ unsigned indent) {
+ // Output the text.
+ assert(!Message.empty());
+ Indent(o, indent) << "<key>extended_message</key>\n";
+ Indent(o, indent);
+ EmitString(o, Message) << '\n';
+
+ // Output the short text.
+ // FIXME: Really use a short string.
+ Indent(o, indent) << "<key>message</key>\n";
+ Indent(o, indent);
+ EmitString(o, Message) << '\n';
+}
+
+void PlistPrinter::ReportControlFlow(raw_ostream &o,
+ const PathDiagnosticControlFlowPiece& P,
+ unsigned indent) {
+
+ const SourceManager &SM = PP.getSourceManager();
+ const LangOptions &LangOpts = PP.getLangOpts();
+
+ Indent(o, indent) << "<dict>\n";
+ ++indent;
+
+ Indent(o, indent) << "<key>kind</key><string>control</string>\n";
+
+ // Emit edges.
+ Indent(o, indent) << "<key>edges</key>\n";
+ ++indent;
+ Indent(o, indent) << "<array>\n";
+ ++indent;
+ for (PathDiagnosticControlFlowPiece::const_iterator I=P.begin(), E=P.end();
+ I!=E; ++I) {
+ Indent(o, indent) << "<dict>\n";
+ ++indent;
+
+ // Make the ranges of the start and end point self-consistent with adjacent edges
+ // by forcing to use only the beginning of the range. This simplifies the layout
+ // logic for clients.
+ Indent(o, indent) << "<key>start</key>\n";
+ SourceRange StartEdge(
+ SM.getExpansionLoc(I->getStart().asRange().getBegin()));
+ EmitRange(o, SM, Lexer::getAsCharRange(StartEdge, SM, LangOpts), FM,
+ indent + 1);
+
+ Indent(o, indent) << "<key>end</key>\n";
+ SourceRange EndEdge(SM.getExpansionLoc(I->getEnd().asRange().getBegin()));
+ EmitRange(o, SM, Lexer::getAsCharRange(EndEdge, SM, LangOpts), FM,
+ indent + 1);
+
+ --indent;
+ Indent(o, indent) << "</dict>\n";
+ }
+ --indent;
+ Indent(o, indent) << "</array>\n";
+ --indent;
+
+ // Output any helper text.
+ const auto &s = P.getString();
+ if (!s.empty()) {
+ Indent(o, indent) << "<key>alternate</key>";
+ EmitString(o, s) << '\n';
+ }
+
+ --indent;
+ Indent(o, indent) << "</dict>\n";
+}
+
+void PlistPrinter::ReportEvent(raw_ostream &o, const PathDiagnosticEventPiece& P,
+ unsigned indent, unsigned depth,
+ bool isKeyEvent) {
+
+ const SourceManager &SM = PP.getSourceManager();
+
+ Indent(o, indent) << "<dict>\n";
+ ++indent;
+
+ Indent(o, indent) << "<key>kind</key><string>event</string>\n";
+
+ if (isKeyEvent) {
+ Indent(o, indent) << "<key>key_event</key><true/>\n";
+ }
+
+ // Output the location.
+ FullSourceLoc L = P.getLocation().asLocation();
+
+ Indent(o, indent) << "<key>location</key>\n";
+ EmitLocation(o, SM, L, FM, indent);
+
+ // Output the ranges (if any).
+ ArrayRef<SourceRange> Ranges = P.getRanges();
+ EmitRanges(o, Ranges, indent);
+
+ // Output the call depth.
+ Indent(o, indent) << "<key>depth</key>";
+ EmitInteger(o, depth) << '\n';
+
+ // Output the text.
+ EmitMessage(o, P.getString(), indent);
+
+ // Finish up.
+ --indent;
+ Indent(o, indent); o << "</dict>\n";
+}
+
+void PlistPrinter::ReportCall(raw_ostream &o, const PathDiagnosticCallPiece &P,
+ unsigned indent,
+ unsigned depth) {
+
+ if (auto callEnter = P.getCallEnterEvent())
+ ReportPiece(o, *callEnter, indent, depth, /*includeControlFlow*/ true,
+ P.isLastInMainSourceFile());
+
+
+ ++depth;
+
+ if (auto callEnterWithinCaller = P.getCallEnterWithinCallerEvent())
+ ReportPiece(o, *callEnterWithinCaller, indent, depth,
+ /*includeControlFlow*/ true);
+
+ for (PathPieces::const_iterator I = P.path.begin(), E = P.path.end();I!=E;++I)
+ ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ true);
+
+ --depth;
+
+ if (auto callExit = P.getCallExitEvent())
+ ReportPiece(o, *callExit, indent, depth, /*includeControlFlow*/ true);
+}
+
+void PlistPrinter::ReportMacroSubPieces(raw_ostream &o,
+ const PathDiagnosticMacroPiece& P,
+ unsigned indent, unsigned depth) {
+ MacroPieces.push_back(&P);
+
+ for (PathPieces::const_iterator I = P.subPieces.begin(),
+ E = P.subPieces.end();
+ I != E; ++I) {
+ ReportPiece(o, **I, indent, depth, /*includeControlFlow*/ false);
+ }
+}
+
+void PlistPrinter::ReportMacroExpansions(raw_ostream &o, unsigned indent) {
+
+ for (const PathDiagnosticMacroPiece *P : MacroPieces) {
+ const SourceManager &SM = PP.getSourceManager();
+ ExpansionInfo EI = getExpandedMacro(P->getLocation().asLocation(), PP);
+
+ Indent(o, indent) << "<dict>\n";
+ ++indent;
+
+ // Output the location.
+ FullSourceLoc L = P->getLocation().asLocation();
+
+ Indent(o, indent) << "<key>location</key>\n";
+ EmitLocation(o, SM, L, FM, indent);
+
+ // Output the ranges (if any).
+ ArrayRef<SourceRange> Ranges = P->getRanges();
+ EmitRanges(o, Ranges, indent);
+
+ // Output the macro name.
+ Indent(o, indent) << "<key>name</key>";
+ EmitString(o, EI.MacroName) << '\n';
+
+ // Output what it expands into.
+ Indent(o, indent) << "<key>expansion</key>";
+ EmitString(o, EI.Expansion) << '\n';
+
+ // Finish up.
+ --indent;
+ Indent(o, indent);
+ o << "</dict>\n";
+ }
+}
+
+void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
+ unsigned indent) {
+
+ const SourceManager &SM = PP.getSourceManager();
+
+ Indent(o, indent) << "<dict>\n";
+ ++indent;
+
+ // Output the location.
+ FullSourceLoc L = P.getLocation().asLocation();
+
+ Indent(o, indent) << "<key>location</key>\n";
+ EmitLocation(o, SM, L, FM, indent);
+
+ // Output the ranges (if any).
+ ArrayRef<SourceRange> Ranges = P.getRanges();
+ EmitRanges(o, Ranges, indent);
+
+ // Output the text.
+ EmitMessage(o, P.getString(), indent);
+
+ // Finish up.
+ --indent;
+ Indent(o, indent); o << "</dict>\n";
+}
+
+//===----------------------------------------------------------------------===//
+// Static function definitions.
+//===----------------------------------------------------------------------===//
+
+/// Print coverage information to output stream {@code o}.
+/// May modify the used list of files {@code Fids} by inserting new ones.
+static void printCoverage(const PathDiagnostic *D,
+ unsigned InputIndentLevel,
+ SmallVectorImpl<FileID> &Fids,
+ FIDMap &FM,
+ llvm::raw_fd_ostream &o) {
+ unsigned IndentLevel = InputIndentLevel;
+
+ Indent(o, IndentLevel) << "<key>ExecutedLines</key>\n";
+ Indent(o, IndentLevel) << "<dict>\n";
+ IndentLevel++;
+
+ // Mapping from file IDs to executed lines.
+ const FilesToLineNumsMap &ExecutedLines = D->getExecutedLines();
+ for (auto I = ExecutedLines.begin(), E = ExecutedLines.end(); I != E; ++I) {
+ unsigned FileKey = AddFID(FM, Fids, I->first);
+ Indent(o, IndentLevel) << "<key>" << FileKey << "</key>\n";
+ Indent(o, IndentLevel) << "<array>\n";
+ IndentLevel++;
+ for (unsigned LineNo : I->second) {
+ Indent(o, IndentLevel);
+ EmitInteger(o, LineNo) << "\n";
+ }
+ IndentLevel--;
+ Indent(o, IndentLevel) << "</array>\n";
+ }
+ IndentLevel--;
+ Indent(o, IndentLevel) << "</dict>\n";
+
+ assert(IndentLevel == InputIndentLevel);
+}
+
+static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
+ AnalyzerOptions &AnOpts,
+ const Preprocessor &PP,
+ const PathPieces &Path) {
+ PlistPrinter Printer(FM, AnOpts, PP);
+ assert(std::is_partitioned(
+ Path.begin(), Path.end(),
+ [](const std::shared_ptr<PathDiagnosticPiece> &E)
+ { return E->getKind() == PathDiagnosticPiece::Note; }) &&
+ "PathDiagnostic is not partitioned so that notes precede the rest");
+
+ PathPieces::const_iterator FirstNonNote = std::partition_point(
+ Path.begin(), Path.end(),
+ [](const std::shared_ptr<PathDiagnosticPiece> &E)
+ { return E->getKind() == PathDiagnosticPiece::Note; });
+
+ PathPieces::const_iterator I = Path.begin();
+
+ if (FirstNonNote != Path.begin()) {
+ o << " <key>notes</key>\n"
+ " <array>\n";
+
+ for (; I != FirstNonNote; ++I)
+ Printer.ReportDiag(o, **I);
+
+ o << " </array>\n";
+ }
+
+ o << " <key>path</key>\n";
+
+ o << " <array>\n";
+
+ for (PathPieces::const_iterator E = Path.end(); I != E; ++I)
+ Printer.ReportDiag(o, **I);
+
+ o << " </array>\n";
+
+ if (!AnOpts.ShouldDisplayMacroExpansions)
+ return;
+
+ o << " <key>macro_expansions</key>\n"
+ " <array>\n";
+ Printer.ReportMacroExpansions(o, /* indent */ 4);
+ o << " </array>\n";
+}
+
+//===----------------------------------------------------------------------===//
+// Methods of PlistDiagnostics.
+//===----------------------------------------------------------------------===//
+
+PlistDiagnostics::PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
+ const std::string& output,
+ const Preprocessor &PP,
+ bool supportsMultipleFiles)
+ : OutputFile(output), PP(PP), AnOpts(AnalyzerOpts),
+ SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
+
+void ento::createPlistDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
+ PathDiagnosticConsumers &C,
+ const std::string& s,
+ const Preprocessor &PP) {
+ C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP,
+ /*supportsMultipleFiles*/ false));
+}
+
+void ento::createPlistMultiFileDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
+ PathDiagnosticConsumers &C,
+ const std::string &s,
+ const Preprocessor &PP) {
+ C.push_back(new PlistDiagnostics(AnalyzerOpts, s, PP,
+ /*supportsMultipleFiles*/ true));
+}
+void PlistDiagnostics::FlushDiagnosticsImpl(
+ std::vector<const PathDiagnostic *> &Diags,
+ FilesMade *filesMade) {
+ // Build up a set of FIDs that we use by scanning the locations and
+ // ranges of the diagnostics.
+ FIDMap FM;
+ SmallVector<FileID, 10> Fids;
+ const SourceManager& SM = PP.getSourceManager();
+ const LangOptions &LangOpts = PP.getLangOpts();
+
+ auto AddPieceFID = [&FM, &Fids, &SM](const PathDiagnosticPiece &Piece) {
+ AddFID(FM, Fids, SM, Piece.getLocation().asLocation());
+ ArrayRef<SourceRange> Ranges = Piece.getRanges();
+ for (const SourceRange &Range : Ranges) {
+ AddFID(FM, Fids, SM, Range.getBegin());
+ AddFID(FM, Fids, SM, Range.getEnd());
+ }
+ };
+
+ for (const PathDiagnostic *D : Diags) {
+
+ SmallVector<const PathPieces *, 5> WorkList;
+ WorkList.push_back(&D->path);
+
+ while (!WorkList.empty()) {
+ const PathPieces &Path = *WorkList.pop_back_val();
+
+ for (const auto &Iter : Path) {
+ const PathDiagnosticPiece &Piece = *Iter;
+ AddPieceFID(Piece);
+
+ if (const PathDiagnosticCallPiece *Call =
+ dyn_cast<PathDiagnosticCallPiece>(&Piece)) {
+ if (auto CallEnterWithin = Call->getCallEnterWithinCallerEvent())
+ AddPieceFID(*CallEnterWithin);
+
+ if (auto CallEnterEvent = Call->getCallEnterEvent())
+ AddPieceFID(*CallEnterEvent);
+
+ WorkList.push_back(&Call->path);
+ } else if (const PathDiagnosticMacroPiece *Macro =
+ dyn_cast<PathDiagnosticMacroPiece>(&Piece)) {
+ WorkList.push_back(&Macro->subPieces);
+ }
+ }
+ }
+ }
+
+ // Open the file.
+ std::error_code EC;
+ llvm::raw_fd_ostream o(OutputFile, EC, llvm::sys::fs::F_Text);
+ if (EC) {
+ llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
+ return;
+ }
+
+ EmitPlistHeader(o);
+
+ // Write the root object: a <dict> containing...
+ // - "clang_version", the string representation of clang version
+ // - "files", an <array> mapping from FIDs to file names
+ // - "diagnostics", an <array> containing the path diagnostics
+ o << "<dict>\n" <<
+ " <key>clang_version</key>\n";
+ EmitString(o, getClangFullVersion()) << '\n';
+ o << " <key>diagnostics</key>\n"
+ " <array>\n";
+
+ for (std::vector<const PathDiagnostic*>::iterator DI=Diags.begin(),
+ DE = Diags.end(); DI!=DE; ++DI) {
+
+ o << " <dict>\n";
+
+ const PathDiagnostic *D = *DI;
+ printBugPath(o, FM, AnOpts, PP, D->path);
+
+ // Output the bug type and bug category.
+ o << " <key>description</key>";
+ EmitString(o, D->getShortDescription()) << '\n';
+ o << " <key>category</key>";
+ EmitString(o, D->getCategory()) << '\n';
+ o << " <key>type</key>";
+ EmitString(o, D->getBugType()) << '\n';
+ o << " <key>check_name</key>";
+ EmitString(o, D->getCheckName()) << '\n';
+
+ o << " <!-- This hash is experimental and going to change! -->\n";
+ o << " <key>issue_hash_content_of_line_in_context</key>";
+ PathDiagnosticLocation UPDLoc = D->getUniqueingLoc();
+ FullSourceLoc L(SM.getExpansionLoc(UPDLoc.isValid()
+ ? UPDLoc.asLocation()
+ : D->getLocation().asLocation()),
+ SM);
+ const Decl *DeclWithIssue = D->getDeclWithIssue();
+ EmitString(o, GetIssueHash(SM, L, D->getCheckName(), D->getBugType(),
+ DeclWithIssue, LangOpts))
+ << '\n';
+
+ // Output information about the semantic context where
+ // the issue occurred.
+ if (const Decl *DeclWithIssue = D->getDeclWithIssue()) {
+ // FIXME: handle blocks, which have no name.
+ if (const NamedDecl *ND = dyn_cast<NamedDecl>(DeclWithIssue)) {
+ StringRef declKind;
+ switch (ND->getKind()) {
+ case Decl::CXXRecord:
+ declKind = "C++ class";
+ break;
+ case Decl::CXXMethod:
+ declKind = "C++ method";
+ break;
+ case Decl::ObjCMethod:
+ declKind = "Objective-C method";
+ break;
+ case Decl::Function:
+ declKind = "function";
+ break;
+ default:
+ break;
+ }
+ if (!declKind.empty()) {
+ const std::string &declName = ND->getDeclName().getAsString();
+ o << " <key>issue_context_kind</key>";
+ EmitString(o, declKind) << '\n';
+ o << " <key>issue_context</key>";
+ EmitString(o, declName) << '\n';
+ }
+
+ // Output the bug hash for issue unique-ing. Currently, it's just an
+ // offset from the beginning of the function.
+ if (const Stmt *Body = DeclWithIssue->getBody()) {
+
+ // If the bug uniqueing location exists, use it for the hash.
+ // For example, this ensures that two leaks reported on the same line
+ // will have different issue_hashes and that the hash will identify
+ // the leak location even after code is added between the allocation
+ // site and the end of scope (leak report location).
+ if (UPDLoc.isValid()) {
+ FullSourceLoc UFunL(
+ SM.getExpansionLoc(
+ D->getUniqueingDecl()->getBody()->getBeginLoc()),
+ SM);
+ o << " <key>issue_hash_function_offset</key><string>"
+ << L.getExpansionLineNumber() - UFunL.getExpansionLineNumber()
+ << "</string>\n";
+
+ // Otherwise, use the location on which the bug is reported.
+ } else {
+ FullSourceLoc FunL(SM.getExpansionLoc(Body->getBeginLoc()), SM);
+ o << " <key>issue_hash_function_offset</key><string>"
+ << L.getExpansionLineNumber() - FunL.getExpansionLineNumber()
+ << "</string>\n";
+ }
+
+ }
+ }
+ }
+
+ // Output the location of the bug.
+ o << " <key>location</key>\n";
+ EmitLocation(o, SM, D->getLocation().asLocation(), FM, 2);
+
+ // Output the diagnostic to the sub-diagnostic client, if any.
+ if (!filesMade->empty()) {
+ StringRef lastName;
+ PDFileEntry::ConsumerFiles *files = filesMade->getFiles(*D);
+ if (files) {
+ for (PDFileEntry::ConsumerFiles::const_iterator CI = files->begin(),
+ CE = files->end(); CI != CE; ++CI) {
+ StringRef newName = CI->first;
+ if (newName != lastName) {
+ if (!lastName.empty()) {
+ o << " </array>\n";
+ }
+ lastName = newName;
+ o << " <key>" << lastName << "_files</key>\n";
+ o << " <array>\n";
+ }
+ o << " <string>" << CI->second << "</string>\n";
+ }
+ o << " </array>\n";
+ }
+ }
+
+ printCoverage(D, /*IndentLevel=*/2, Fids, FM, o);
+
+ // Close up the entry.
+ o << " </dict>\n";
+ }
+
+ o << " </array>\n";
+
+ o << " <key>files</key>\n"
+ " <array>\n";
+ for (FileID FID : Fids)
+ EmitString(o << " ", SM.getFileEntryForID(FID)->getName()) << '\n';
+ o << " </array>\n";
+
+ if (llvm::AreStatisticsEnabled() && AnOpts.ShouldSerializeStats) {
+ o << " <key>statistics</key>\n";
+ std::string stats;
+ llvm::raw_string_ostream os(stats);
+ llvm::PrintStatisticsJSON(os);
+ os.flush();
+ EmitString(o, html::EscapeText(stats)) << '\n';
+ }
+
+ // Finish.
+ o << "</dict>\n</plist>";
+}
+
+//===----------------------------------------------------------------------===//
+// Declarations of helper functions and data structures for expanding macros.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+using ExpArgTokens = llvm::SmallVector<Token, 2>;
+
+/// Maps unexpanded macro arguments to expanded arguments. A macro argument may
+/// need to expanded further when it is nested inside another macro.
+class MacroArgMap : public std::map<const IdentifierInfo *, ExpArgTokens> {
+public:
+ void expandFromPrevMacro(const MacroArgMap &Super);
+};
+
+struct MacroNameAndArgs {
+ std::string Name;
+ const MacroInfo *MI = nullptr;
+ MacroArgMap Args;
+
+ MacroNameAndArgs(std::string N, const MacroInfo *MI, MacroArgMap M)
+ : Name(std::move(N)), MI(MI), Args(std::move(M)) {}
+};
+
+class TokenPrinter {
+ llvm::raw_ostream &OS;
+ const Preprocessor &PP;
+
+ Token PrevTok, PrevPrevTok;
+ TokenConcatenation ConcatInfo;
+
+public:
+ TokenPrinter(llvm::raw_ostream &OS, const Preprocessor &PP)
+ : OS(OS), PP(PP), ConcatInfo(PP) {
+ PrevTok.setKind(tok::unknown);
+ PrevPrevTok.setKind(tok::unknown);
+ }
+
+ void printToken(const Token &Tok);
+};
+
+} // end of anonymous namespace
+
+/// The implementation method of getMacroExpansion: It prints the expansion of
+/// a macro to \p Printer, and returns with the name of the macro.
+///
+/// Since macros can be nested in one another, this function may call itself
+/// recursively.
+///
+/// Unfortunately, macro arguments have to expanded manually. To understand why,
+/// observe the following example:
+///
+/// #define PRINT(x) print(x)
+/// #define DO_SOMETHING(str) PRINT(str)
+///
+/// DO_SOMETHING("Cute panda cubs.");
+///
+/// As we expand the last line, we'll immediately replace PRINT(str) with
+/// print(x). The information that both 'str' and 'x' refers to the same string
+/// is an information we have to forward, hence the argument \p PrevArgs.
+static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer,
+ SourceLocation MacroLoc,
+ const Preprocessor &PP,
+ const MacroArgMap &PrevArgs);
+
+/// Retrieves the name of the macro and what it's arguments expand into
+/// at \p ExpanLoc.
+///
+/// For example, for the following macro expansion:
+///
+/// #define SET_TO_NULL(x) x = 0
+/// #define NOT_SUSPICIOUS(a) \
+/// { \
+/// int b = 0; \
+/// } \
+/// SET_TO_NULL(a)
+///
+/// int *ptr = new int(4);
+/// NOT_SUSPICIOUS(&ptr);
+/// *ptr = 5;
+///
+/// When \p ExpanLoc references the last line, the macro name "NOT_SUSPICIOUS"
+/// and the MacroArgMap map { (a, &ptr) } will be returned.
+///
+/// When \p ExpanLoc references "SET_TO_NULL(a)" within the definition of
+/// "NOT_SUSPICOUS", the macro name "SET_TO_NULL" and the MacroArgMap map
+/// { (x, a) } will be returned.
+static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
+ const Preprocessor &PP);
+
+/// Retrieves the ')' token that matches '(' \p It points to.
+static MacroInfo::tokens_iterator getMatchingRParen(
+ MacroInfo::tokens_iterator It,
+ MacroInfo::tokens_iterator End);
+
+/// Retrieves the macro info for \p II refers to at \p Loc. This is important
+/// because macros can be redefined or undefined.
+static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP,
+ const SourceManager &SM,
+ const IdentifierInfo *II,
+ SourceLocation Loc);
+
+//===----------------------------------------------------------------------===//
+// Definitions of helper functions and methods for expanding macros.
+//===----------------------------------------------------------------------===//
+
+static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc,
+ const Preprocessor &PP) {
+
+ llvm::SmallString<200> ExpansionBuf;
+ llvm::raw_svector_ostream OS(ExpansionBuf);
+ TokenPrinter Printer(OS, PP);
+ std::string MacroName =
+ getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{});
+ return { MacroName, OS.str() };
+}
+
+static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer,
+ SourceLocation MacroLoc,
+ const Preprocessor &PP,
+ const MacroArgMap &PrevArgs) {
+
+ const SourceManager &SM = PP.getSourceManager();
+
+ MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP);
+
+ // Manually expand its arguments from the previous macro.
+ Info.Args.expandFromPrevMacro(PrevArgs);
+
+ // Iterate over the macro's tokens and stringify them.
+ for (auto It = Info.MI->tokens_begin(), E = Info.MI->tokens_end(); It != E;
+ ++It) {
+ Token T = *It;
+
+ // If this token is not an identifier, we only need to print it.
+ if (T.isNot(tok::identifier)) {
+ Printer.printToken(T);
+ continue;
+ }
+
+ const auto *II = T.getIdentifierInfo();
+ assert(II &&
+ "This token is an identifier but has no IdentifierInfo!");
+
+ // If this token is a macro that should be expanded inside the current
+ // macro.
+ if (const MacroInfo *MI =
+ getMacroInfoForLocation(PP, SM, II, T.getLocation())) {
+ getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args);
+
+ // If this is a function-like macro, skip its arguments, as
+ // getExpandedMacro() already printed them. If this is the case, let's
+ // first jump to the '(' token.
+ if (MI->getNumParams() != 0)
+ It = getMatchingRParen(++It, E);
+ continue;
+ }
+
+ // If this token is the current macro's argument, we should expand it.
+ auto ArgMapIt = Info.Args.find(II);
+ if (ArgMapIt != Info.Args.end()) {
+ for (MacroInfo::tokens_iterator ArgIt = ArgMapIt->second.begin(),
+ ArgEnd = ArgMapIt->second.end();
+ ArgIt != ArgEnd; ++ArgIt) {
+
+ // These tokens may still be macros, if that is the case, handle it the
+ // same way we did above.
+ const auto *ArgII = ArgIt->getIdentifierInfo();
+ if (!ArgII) {
+ Printer.printToken(*ArgIt);
+ continue;
+ }
+
+ const auto *MI = PP.getMacroInfo(ArgII);
+ if (!MI) {
+ Printer.printToken(*ArgIt);
+ continue;
+ }
+
+ getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP,
+ Info.Args);
+ if (MI->getNumParams() != 0)
+ ArgIt = getMatchingRParen(++ArgIt, ArgEnd);
+ }
+ continue;
+ }
+
+ // If control reached here, then this token isn't a macro identifier, nor an
+ // unexpanded macro argument that we need to handle, print it.
+ Printer.printToken(T);
+ }
+
+ return Info.Name;
+}
+
+static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
+ const Preprocessor &PP) {
+
+ const SourceManager &SM = PP.getSourceManager();
+ const LangOptions &LangOpts = PP.getLangOpts();
+
+ // First, we create a Lexer to lex *at the expansion location* the tokens
+ // referring to the macro's name and its arguments.
+ std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ExpanLoc);
+ const llvm::MemoryBuffer *MB = SM.getBuffer(LocInfo.first);
+ const char *MacroNameTokenPos = MB->getBufferStart() + LocInfo.second;
+
+ Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts,
+ MB->getBufferStart(), MacroNameTokenPos, MB->getBufferEnd());
+
+ // Acquire the macro's name.
+ Token TheTok;
+ RawLexer.LexFromRawLexer(TheTok);
+
+ std::string MacroName = PP.getSpelling(TheTok);
+
+ const auto *II = PP.getIdentifierInfo(MacroName);
+ assert(II && "Failed to acquire the IndetifierInfo for the macro!");
+
+ const MacroInfo *MI = getMacroInfoForLocation(PP, SM, II, ExpanLoc);
+ assert(MI && "The macro must've been defined at it's expansion location!");
+
+ // Acquire the macro's arguments.
+ //
+ // The rough idea here is to lex from the first left parentheses to the last
+ // right parentheses, and map the macro's unexpanded arguments to what they
+ // will be expanded to. An expanded macro argument may contain several tokens
+ // (like '3 + 4'), so we'll lex until we find a tok::comma or tok::r_paren, at
+ // which point we start lexing the next argument or finish.
+ ArrayRef<const IdentifierInfo *> MacroArgs = MI->params();
+ if (MacroArgs.empty())
+ return { MacroName, MI, {} };
+
+ RawLexer.LexFromRawLexer(TheTok);
+ assert(TheTok.is(tok::l_paren) &&
+ "The token after the macro's identifier token should be '('!");
+
+ MacroArgMap Args;
+
+ // When the macro's argument is a function call, like
+ // CALL_FN(someFunctionName(param1, param2))
+ // we will find tok::l_paren, tok::r_paren, and tok::comma that do not divide
+ // actual macro arguments, or do not represent the macro argument's closing
+ // parentheses, so we'll count how many parentheses aren't closed yet.
+ // If ParanthesesDepth
+ // * = 0, then there are no more arguments to lex.
+ // * = 1, then if we find a tok::comma, we can start lexing the next arg.
+ // * > 1, then tok::comma is a part of the current arg.
+ int ParenthesesDepth = 1;
+
+ // If we encounter __VA_ARGS__, we will lex until the closing tok::r_paren,
+ // even if we lex a tok::comma and ParanthesesDepth == 1.
+ const IdentifierInfo *__VA_ARGS__II = PP.getIdentifierInfo("__VA_ARGS__");
+
+ for (const IdentifierInfo *UnexpArgII : MacroArgs) {
+ MacroArgMap::mapped_type ExpandedArgTokens;
+
+ // One could also simply not supply a single argument to __VA_ARGS__ -- this
+ // results in a preprocessor warning, but is not an error:
+ // #define VARIADIC(ptr, ...) \
+ // someVariadicTemplateFunction(__VA_ARGS__)
+ //
+ // int *ptr;
+ // VARIADIC(ptr); // Note that there are no commas, this isn't just an
+ // // empty parameter -- there are no parameters for '...'.
+ // In any other case, ParenthesesDepth mustn't be 0 here.
+ if (ParenthesesDepth != 0) {
+
+ // Lex the first token of the next macro parameter.
+ RawLexer.LexFromRawLexer(TheTok);
+
+ while (!(ParenthesesDepth == 1 &&
+ (UnexpArgII == __VA_ARGS__II ? false : TheTok.is(tok::comma)))) {
+ assert(TheTok.isNot(tok::eof) &&
+ "EOF encountered while looking for expanded macro args!");
+
+ if (TheTok.is(tok::l_paren))
+ ++ParenthesesDepth;
+
+ if (TheTok.is(tok::r_paren))
+ --ParenthesesDepth;
+
+ if (ParenthesesDepth == 0)
+ break;
+
+ if (TheTok.is(tok::raw_identifier))
+ PP.LookUpIdentifierInfo(TheTok);
+
+ ExpandedArgTokens.push_back(TheTok);
+ RawLexer.LexFromRawLexer(TheTok);
+ }
+ } else {
+ assert(UnexpArgII == __VA_ARGS__II);
+ }
+
+ Args.emplace(UnexpArgII, std::move(ExpandedArgTokens));
+ }
+
+ assert(TheTok.is(tok::r_paren) &&
+ "Expanded macro argument acquisition failed! After the end of the loop"
+ " this token should be ')'!");
+
+ return { MacroName, MI, Args };
+}
+
+static MacroInfo::tokens_iterator getMatchingRParen(
+ MacroInfo::tokens_iterator It,
+ MacroInfo::tokens_iterator End) {
+
+ assert(It->is(tok::l_paren) && "This token should be '('!");
+
+ // Skip until we find the closing ')'.
+ int ParenthesesDepth = 1;
+ while (ParenthesesDepth != 0) {
+ ++It;
+
+ assert(It->isNot(tok::eof) &&
+ "Encountered EOF while attempting to skip macro arguments!");
+ assert(It != End &&
+ "End of the macro definition reached before finding ')'!");
+
+ if (It->is(tok::l_paren))
+ ++ParenthesesDepth;
+
+ if (It->is(tok::r_paren))
+ --ParenthesesDepth;
+ }
+ return It;
+}
+
+static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP,
+ const SourceManager &SM,
+ const IdentifierInfo *II,
+ SourceLocation Loc) {
+
+ const MacroDirective *MD = PP.getLocalMacroDirectiveHistory(II);
+ if (!MD)
+ return nullptr;
+
+ return MD->findDirectiveAtLoc(Loc, SM).getMacroInfo();
+}
+
+void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) {
+
+ for (value_type &Pair : *this) {
+ ExpArgTokens &CurrExpArgTokens = Pair.second;
+
+ // For each token in the expanded macro argument.
+ auto It = CurrExpArgTokens.begin();
+ while (It != CurrExpArgTokens.end()) {
+ if (It->isNot(tok::identifier)) {
+ ++It;
+ continue;
+ }
+
+ const auto *II = It->getIdentifierInfo();
+ assert(II);
+
+ // Is this an argument that "Super" expands further?
+ if (!Super.count(II)) {
+ ++It;
+ continue;
+ }
+
+ const ExpArgTokens &SuperExpArgTokens = Super.at(II);
+
+ It = CurrExpArgTokens.insert(
+ It, SuperExpArgTokens.begin(), SuperExpArgTokens.end());
+ std::advance(It, SuperExpArgTokens.size());
+ It = CurrExpArgTokens.erase(It);
+ }
+ }
+}
+
+void TokenPrinter::printToken(const Token &Tok) {
+ // If this is the first token to be printed, don't print space.
+ if (PrevTok.isNot(tok::unknown)) {
+ // If the tokens were already space separated, or if they must be to avoid
+ // them being implicitly pasted, add a space between them.
+ if(Tok.hasLeadingSpace() || ConcatInfo.AvoidConcat(PrevPrevTok, PrevTok,
+ Tok)) {
+ // AvoidConcat doesn't check for ##, don't print a space around it.
+ if (PrevTok.isNot(tok::hashhash) && Tok.isNot(tok::hashhash)) {
+ OS << ' ';
+ }
+ }
+ }
+
+ if (!Tok.isOneOf(tok::hash, tok::hashhash)) {
+ if (PrevTok.is(tok::hash))
+ OS << '\"' << PP.getSpelling(Tok) << '\"';
+ else
+ OS << PP.getSpelling(Tok);
+ }
+
+ PrevPrevTok = PrevTok;
+ PrevTok = Tok;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h
new file mode 100644
index 000000000000..4bb694819c2a
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h
@@ -0,0 +1,45 @@
+//==- PrettyStackTraceLocationContext.h - show analysis backtrace --*- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CORE_PRETTYSTACKTRACELOCATIONCONTEXT_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CORE_PRETTYSTACKTRACELOCATIONCONTEXT_H
+
+#include "clang/Analysis/AnalysisDeclContext.h"
+
+namespace clang {
+namespace ento {
+
+/// While alive, includes the current analysis stack in a crash trace.
+///
+/// Example:
+/// \code
+/// 0. Program arguments: ...
+/// 1. <eof> parser at end of file
+/// 2. While analyzing stack:
+/// #0 void inlined()
+/// #1 void test()
+/// 3. crash-trace.c:6:3: Error evaluating statement
+/// \endcode
+class PrettyStackTraceLocationContext : public llvm::PrettyStackTraceEntry {
+ const LocationContext *LCtx;
+public:
+ PrettyStackTraceLocationContext(const LocationContext *LC) : LCtx(LC) {
+ assert(LCtx);
+ }
+
+ void print(raw_ostream &OS) const override {
+ OS << "While analyzing stack: \n";
+ LCtx->dumpStack(OS, "\t");
+ }
+};
+
+} // end ento namespace
+} // end clang namespace
+
+#endif
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
new file mode 100644
index 000000000000..2e2e2ec94f39
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -0,0 +1,823 @@
+//= ProgramState.cpp - Path-Sensitive "State" for tracking values --*- C++ -*--=
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements ProgramState and ProgramStateManager.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace clang { namespace ento {
+/// Increments the number of times this state is referenced.
+
+void ProgramStateRetain(const ProgramState *state) {
+ ++const_cast<ProgramState*>(state)->refCount;
+}
+
+/// Decrement the number of times this state is referenced.
+void ProgramStateRelease(const ProgramState *state) {
+ assert(state->refCount > 0);
+ ProgramState *s = const_cast<ProgramState*>(state);
+ if (--s->refCount == 0) {
+ ProgramStateManager &Mgr = s->getStateManager();
+ Mgr.StateSet.RemoveNode(s);
+ s->~ProgramState();
+ Mgr.freeStates.push_back(s);
+ }
+}
+}}
+
+ProgramState::ProgramState(ProgramStateManager *mgr, const Environment& env,
+ StoreRef st, GenericDataMap gdm)
+ : stateMgr(mgr),
+ Env(env),
+ store(st.getStore()),
+ GDM(gdm),
+ refCount(0) {
+ stateMgr->getStoreManager().incrementReferenceCount(store);
+}
+
+ProgramState::ProgramState(const ProgramState &RHS)
+ : llvm::FoldingSetNode(),
+ stateMgr(RHS.stateMgr),
+ Env(RHS.Env),
+ store(RHS.store),
+ GDM(RHS.GDM),
+ refCount(0) {
+ stateMgr->getStoreManager().incrementReferenceCount(store);
+}
+
+ProgramState::~ProgramState() {
+ if (store)
+ stateMgr->getStoreManager().decrementReferenceCount(store);
+}
+
+int64_t ProgramState::getID() const {
+ return getStateManager().Alloc.identifyKnownAlignedObject<ProgramState>(this);
+}
+
+ProgramStateManager::ProgramStateManager(ASTContext &Ctx,
+ StoreManagerCreator CreateSMgr,
+ ConstraintManagerCreator CreateCMgr,
+ llvm::BumpPtrAllocator &alloc,
+ SubEngine *SubEng)
+ : Eng(SubEng), EnvMgr(alloc), GDMFactory(alloc),
+ svalBuilder(createSimpleSValBuilder(alloc, Ctx, *this)),
+ CallEventMgr(new CallEventManager(alloc)), Alloc(alloc) {
+ StoreMgr = (*CreateSMgr)(*this);
+ ConstraintMgr = (*CreateCMgr)(*this, SubEng);
+}
+
+
+ProgramStateManager::~ProgramStateManager() {
+ for (GDMContextsTy::iterator I=GDMContexts.begin(), E=GDMContexts.end();
+ I!=E; ++I)
+ I->second.second(I->second.first);
+}
+
+ProgramStateRef
+ProgramStateManager::removeDeadBindings(ProgramStateRef state,
+ const StackFrameContext *LCtx,
+ SymbolReaper& SymReaper) {
+
+ // This code essentially performs a "mark-and-sweep" of the VariableBindings.
+ // The roots are any Block-level exprs and Decls that our liveness algorithm
+ // tells us are live. We then see what Decls they may reference, and keep
+ // those around. This code more than likely can be made faster, and the
+ // frequency of which this method is called should be experimented with
+ // for optimum performance.
+ ProgramState NewState = *state;
+
+ NewState.Env = EnvMgr.removeDeadBindings(NewState.Env, SymReaper, state);
+
+ // Clean up the store.
+ StoreRef newStore = StoreMgr->removeDeadBindings(NewState.getStore(), LCtx,
+ SymReaper);
+ NewState.setStore(newStore);
+ SymReaper.setReapedStore(newStore);
+
+ ProgramStateRef Result = getPersistentState(NewState);
+ return ConstraintMgr->removeDeadBindings(Result, SymReaper);
+}
+
+ProgramStateRef ProgramState::bindLoc(Loc LV,
+ SVal V,
+ const LocationContext *LCtx,
+ bool notifyChanges) const {
+ ProgramStateManager &Mgr = getStateManager();
+ ProgramStateRef newState = makeWithStore(Mgr.StoreMgr->Bind(getStore(),
+ LV, V));
+ const MemRegion *MR = LV.getAsRegion();
+ if (MR && notifyChanges)
+ return Mgr.getOwningEngine().processRegionChange(newState, MR, LCtx);
+
+ return newState;
+}
+
+ProgramStateRef
+ProgramState::bindDefaultInitial(SVal loc, SVal V,
+ const LocationContext *LCtx) const {
+ ProgramStateManager &Mgr = getStateManager();
+ const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
+ const StoreRef &newStore = Mgr.StoreMgr->BindDefaultInitial(getStore(), R, V);
+ ProgramStateRef new_state = makeWithStore(newStore);
+ return Mgr.getOwningEngine().processRegionChange(new_state, R, LCtx);
+}
+
+ProgramStateRef
+ProgramState::bindDefaultZero(SVal loc, const LocationContext *LCtx) const {
+ ProgramStateManager &Mgr = getStateManager();
+ const MemRegion *R = loc.castAs<loc::MemRegionVal>().getRegion();
+ const StoreRef &newStore = Mgr.StoreMgr->BindDefaultZero(getStore(), R);
+ ProgramStateRef new_state = makeWithStore(newStore);
+ return Mgr.getOwningEngine().processRegionChange(new_state, R, LCtx);
+}
+
+typedef ArrayRef<const MemRegion *> RegionList;
+typedef ArrayRef<SVal> ValueList;
+
+ProgramStateRef
+ProgramState::invalidateRegions(RegionList Regions,
+ const Expr *E, unsigned Count,
+ const LocationContext *LCtx,
+ bool CausedByPointerEscape,
+ InvalidatedSymbols *IS,
+ const CallEvent *Call,
+ RegionAndSymbolInvalidationTraits *ITraits) const {
+ SmallVector<SVal, 8> Values;
+ for (RegionList::const_iterator I = Regions.begin(),
+ End = Regions.end(); I != End; ++I)
+ Values.push_back(loc::MemRegionVal(*I));
+
+ return invalidateRegionsImpl(Values, E, Count, LCtx, CausedByPointerEscape,
+ IS, ITraits, Call);
+}
+
+ProgramStateRef
+ProgramState::invalidateRegions(ValueList Values,
+ const Expr *E, unsigned Count,
+ const LocationContext *LCtx,
+ bool CausedByPointerEscape,
+ InvalidatedSymbols *IS,
+ const CallEvent *Call,
+ RegionAndSymbolInvalidationTraits *ITraits) const {
+
+ return invalidateRegionsImpl(Values, E, Count, LCtx, CausedByPointerEscape,
+ IS, ITraits, Call);
+}
+
+ProgramStateRef
+ProgramState::invalidateRegionsImpl(ValueList Values,
+ const Expr *E, unsigned Count,
+ const LocationContext *LCtx,
+ bool CausedByPointerEscape,
+ InvalidatedSymbols *IS,
+ RegionAndSymbolInvalidationTraits *ITraits,
+ const CallEvent *Call) const {
+ ProgramStateManager &Mgr = getStateManager();
+ SubEngine &Eng = Mgr.getOwningEngine();
+
+ InvalidatedSymbols InvalidatedSyms;
+ if (!IS)
+ IS = &InvalidatedSyms;
+
+ RegionAndSymbolInvalidationTraits ITraitsLocal;
+ if (!ITraits)
+ ITraits = &ITraitsLocal;
+
+ StoreManager::InvalidatedRegions TopLevelInvalidated;
+ StoreManager::InvalidatedRegions Invalidated;
+ const StoreRef &newStore
+ = Mgr.StoreMgr->invalidateRegions(getStore(), Values, E, Count, LCtx, Call,
+ *IS, *ITraits, &TopLevelInvalidated,
+ &Invalidated);
+
+ ProgramStateRef newState = makeWithStore(newStore);
+
+ if (CausedByPointerEscape) {
+ newState = Eng.notifyCheckersOfPointerEscape(newState, IS,
+ TopLevelInvalidated,
+ Call,
+ *ITraits);
+ }
+
+ return Eng.processRegionChanges(newState, IS, TopLevelInvalidated,
+ Invalidated, LCtx, Call);
+}
+
+ProgramStateRef ProgramState::killBinding(Loc LV) const {
+ assert(!LV.getAs<loc::MemRegionVal>() && "Use invalidateRegion instead.");
+
+ Store OldStore = getStore();
+ const StoreRef &newStore =
+ getStateManager().StoreMgr->killBinding(OldStore, LV);
+
+ if (newStore.getStore() == OldStore)
+ return this;
+
+ return makeWithStore(newStore);
+}
+
+ProgramStateRef
+ProgramState::enterStackFrame(const CallEvent &Call,
+ const StackFrameContext *CalleeCtx) const {
+ const StoreRef &NewStore =
+ getStateManager().StoreMgr->enterStackFrame(getStore(), Call, CalleeCtx);
+ return makeWithStore(NewStore);
+}
+
+SVal ProgramState::getSValAsScalarOrLoc(const MemRegion *R) const {
+ // We only want to do fetches from regions that we can actually bind
+ // values. For example, SymbolicRegions of type 'id<...>' cannot
+ // have direct bindings (but their can be bindings on their subregions).
+ if (!R->isBoundable())
+ return UnknownVal();
+
+ if (const TypedValueRegion *TR = dyn_cast<TypedValueRegion>(R)) {
+ QualType T = TR->getValueType();
+ if (Loc::isLocType(T) || T->isIntegralOrEnumerationType())
+ return getSVal(R);
+ }
+
+ return UnknownVal();
+}
+
+SVal ProgramState::getSVal(Loc location, QualType T) const {
+ SVal V = getRawSVal(location, T);
+
+ // If 'V' is a symbolic value that is *perfectly* constrained to
+ // be a constant value, use that value instead to lessen the burden
+ // on later analysis stages (so we have less symbolic values to reason
+ // about).
+ // We only go into this branch if we can convert the APSInt value we have
+ // to the type of T, which is not always the case (e.g. for void).
+ if (!T.isNull() && (T->isIntegralOrEnumerationType() || Loc::isLocType(T))) {
+ if (SymbolRef sym = V.getAsSymbol()) {
+ if (const llvm::APSInt *Int = getStateManager()
+ .getConstraintManager()
+ .getSymVal(this, sym)) {
+ // FIXME: Because we don't correctly model (yet) sign-extension
+ // and truncation of symbolic values, we need to convert
+ // the integer value to the correct signedness and bitwidth.
+ //
+ // This shows up in the following:
+ //
+ // char foo();
+ // unsigned x = foo();
+ // if (x == 54)
+ // ...
+ //
+ // The symbolic value stored to 'x' is actually the conjured
+ // symbol for the call to foo(); the type of that symbol is 'char',
+ // not unsigned.
+ const llvm::APSInt &NewV = getBasicVals().Convert(T, *Int);
+
+ if (V.getAs<Loc>())
+ return loc::ConcreteInt(NewV);
+ else
+ return nonloc::ConcreteInt(NewV);
+ }
+ }
+ }
+
+ return V;
+}
+
+ProgramStateRef ProgramState::BindExpr(const Stmt *S,
+ const LocationContext *LCtx,
+ SVal V, bool Invalidate) const{
+ Environment NewEnv =
+ getStateManager().EnvMgr.bindExpr(Env, EnvironmentEntry(S, LCtx), V,
+ Invalidate);
+ if (NewEnv == Env)
+ return this;
+
+ ProgramState NewSt = *this;
+ NewSt.Env = NewEnv;
+ return getStateManager().getPersistentState(NewSt);
+}
+
+ProgramStateRef ProgramState::assumeInBound(DefinedOrUnknownSVal Idx,
+ DefinedOrUnknownSVal UpperBound,
+ bool Assumption,
+ QualType indexTy) const {
+ if (Idx.isUnknown() || UpperBound.isUnknown())
+ return this;
+
+ // Build an expression for 0 <= Idx < UpperBound.
+ // This is the same as Idx + MIN < UpperBound + MIN, if overflow is allowed.
+ // FIXME: This should probably be part of SValBuilder.
+ ProgramStateManager &SM = getStateManager();
+ SValBuilder &svalBuilder = SM.getSValBuilder();
+ ASTContext &Ctx = svalBuilder.getContext();
+
+ // Get the offset: the minimum value of the array index type.
+ BasicValueFactory &BVF = svalBuilder.getBasicValueFactory();
+ if (indexTy.isNull())
+ indexTy = svalBuilder.getArrayIndexType();
+ nonloc::ConcreteInt Min(BVF.getMinValue(indexTy));
+
+ // Adjust the index.
+ SVal newIdx = svalBuilder.evalBinOpNN(this, BO_Add,
+ Idx.castAs<NonLoc>(), Min, indexTy);
+ if (newIdx.isUnknownOrUndef())
+ return this;
+
+ // Adjust the upper bound.
+ SVal newBound =
+ svalBuilder.evalBinOpNN(this, BO_Add, UpperBound.castAs<NonLoc>(),
+ Min, indexTy);
+
+ if (newBound.isUnknownOrUndef())
+ return this;
+
+ // Build the actual comparison.
+ SVal inBound = svalBuilder.evalBinOpNN(this, BO_LT, newIdx.castAs<NonLoc>(),
+ newBound.castAs<NonLoc>(), Ctx.IntTy);
+ if (inBound.isUnknownOrUndef())
+ return this;
+
+ // Finally, let the constraint manager take care of it.
+ ConstraintManager &CM = SM.getConstraintManager();
+ return CM.assume(this, inBound.castAs<DefinedSVal>(), Assumption);
+}
+
+ConditionTruthVal ProgramState::isNonNull(SVal V) const {
+ ConditionTruthVal IsNull = isNull(V);
+ if (IsNull.isUnderconstrained())
+ return IsNull;
+ return ConditionTruthVal(!IsNull.getValue());
+}
+
+ConditionTruthVal ProgramState::areEqual(SVal Lhs, SVal Rhs) const {
+ return stateMgr->getSValBuilder().areEqual(this, Lhs, Rhs);
+}
+
+ConditionTruthVal ProgramState::isNull(SVal V) const {
+ if (V.isZeroConstant())
+ return true;
+
+ if (V.isConstant())
+ return false;
+
+ SymbolRef Sym = V.getAsSymbol(/* IncludeBaseRegion */ true);
+ if (!Sym)
+ return ConditionTruthVal();
+
+ return getStateManager().ConstraintMgr->isNull(this, Sym);
+}
+
+ProgramStateRef ProgramStateManager::getInitialState(const LocationContext *InitLoc) {
+ ProgramState State(this,
+ EnvMgr.getInitialEnvironment(),
+ StoreMgr->getInitialStore(InitLoc),
+ GDMFactory.getEmptyMap());
+
+ return getPersistentState(State);
+}
+
+ProgramStateRef ProgramStateManager::getPersistentStateWithGDM(
+ ProgramStateRef FromState,
+ ProgramStateRef GDMState) {
+ ProgramState NewState(*FromState);
+ NewState.GDM = GDMState->GDM;
+ return getPersistentState(NewState);
+}
+
+ProgramStateRef ProgramStateManager::getPersistentState(ProgramState &State) {
+
+ llvm::FoldingSetNodeID ID;
+ State.Profile(ID);
+ void *InsertPos;
+
+ if (ProgramState *I = StateSet.FindNodeOrInsertPos(ID, InsertPos))
+ return I;
+
+ ProgramState *newState = nullptr;
+ if (!freeStates.empty()) {
+ newState = freeStates.back();
+ freeStates.pop_back();
+ }
+ else {
+ newState = (ProgramState*) Alloc.Allocate<ProgramState>();
+ }
+ new (newState) ProgramState(State);
+ StateSet.InsertNode(newState, InsertPos);
+ return newState;
+}
+
+ProgramStateRef ProgramState::makeWithStore(const StoreRef &store) const {
+ ProgramState NewSt(*this);
+ NewSt.setStore(store);
+ return getStateManager().getPersistentState(NewSt);
+}
+
+void ProgramState::setStore(const StoreRef &newStore) {
+ Store newStoreStore = newStore.getStore();
+ if (newStoreStore)
+ stateMgr->getStoreManager().incrementReferenceCount(newStoreStore);
+ if (store)
+ stateMgr->getStoreManager().decrementReferenceCount(store);
+ store = newStoreStore;
+}
+
+//===----------------------------------------------------------------------===//
+// State pretty-printing.
+//===----------------------------------------------------------------------===//
+
+void ProgramState::print(raw_ostream &Out,
+ const char *NL, const char *Sep,
+ const LocationContext *LC) const {
+ // Print the store.
+ ProgramStateManager &Mgr = getStateManager();
+ const ASTContext &Context = getStateManager().getContext();
+ Mgr.getStoreManager().print(getStore(), Out, NL);
+
+ // Print out the environment.
+ Env.print(Out, NL, Sep, Context, LC);
+
+ // Print out the constraints.
+ Mgr.getConstraintManager().print(this, Out, NL, Sep);
+
+ // Print out the tracked dynamic types.
+ printDynamicTypeInfo(this, Out, NL, Sep);
+
+ // Print out tainted symbols.
+ printTaint(Out, NL);
+
+ // Print checker-specific data.
+ Mgr.getOwningEngine().printState(Out, this, NL, Sep, LC);
+}
+
+void ProgramState::printDOT(raw_ostream &Out,
+ const LocationContext *LC) const {
+ print(Out, "\\l", "\\|", LC);
+}
+
+LLVM_DUMP_METHOD void ProgramState::dump() const {
+ print(llvm::errs());
+}
+
+void ProgramState::printTaint(raw_ostream &Out,
+ const char *NL) const {
+ TaintMapImpl TM = get<TaintMap>();
+
+ if (!TM.isEmpty())
+ Out <<"Tainted symbols:" << NL;
+
+ for (TaintMapImpl::iterator I = TM.begin(), E = TM.end(); I != E; ++I) {
+ Out << I->first << " : " << I->second << NL;
+ }
+}
+
+void ProgramState::dumpTaint() const {
+ printTaint(llvm::errs());
+}
+
+AnalysisManager& ProgramState::getAnalysisManager() const {
+ return stateMgr->getOwningEngine().getAnalysisManager();
+}
+
+//===----------------------------------------------------------------------===//
+// Generic Data Map.
+//===----------------------------------------------------------------------===//
+
+void *const* ProgramState::FindGDM(void *K) const {
+ return GDM.lookup(K);
+}
+
+void*
+ProgramStateManager::FindGDMContext(void *K,
+ void *(*CreateContext)(llvm::BumpPtrAllocator&),
+ void (*DeleteContext)(void*)) {
+
+ std::pair<void*, void (*)(void*)>& p = GDMContexts[K];
+ if (!p.first) {
+ p.first = CreateContext(Alloc);
+ p.second = DeleteContext;
+ }
+
+ return p.first;
+}
+
+ProgramStateRef ProgramStateManager::addGDM(ProgramStateRef St, void *Key, void *Data){
+ ProgramState::GenericDataMap M1 = St->getGDM();
+ ProgramState::GenericDataMap M2 = GDMFactory.add(M1, Key, Data);
+
+ if (M1 == M2)
+ return St;
+
+ ProgramState NewSt = *St;
+ NewSt.GDM = M2;
+ return getPersistentState(NewSt);
+}
+
+ProgramStateRef ProgramStateManager::removeGDM(ProgramStateRef state, void *Key) {
+ ProgramState::GenericDataMap OldM = state->getGDM();
+ ProgramState::GenericDataMap NewM = GDMFactory.remove(OldM, Key);
+
+ if (NewM == OldM)
+ return state;
+
+ ProgramState NewState = *state;
+ NewState.GDM = NewM;
+ return getPersistentState(NewState);
+}
+
+bool ScanReachableSymbols::scan(nonloc::LazyCompoundVal val) {
+ bool wasVisited = !visited.insert(val.getCVData()).second;
+ if (wasVisited)
+ return true;
+
+ StoreManager &StoreMgr = state->getStateManager().getStoreManager();
+ // FIXME: We don't really want to use getBaseRegion() here because pointer
+ // arithmetic doesn't apply, but scanReachableSymbols only accepts base
+ // regions right now.
+ const MemRegion *R = val.getRegion()->getBaseRegion();
+ return StoreMgr.scanReachableSymbols(val.getStore(), R, *this);
+}
+
+bool ScanReachableSymbols::scan(nonloc::CompoundVal val) {
+ for (nonloc::CompoundVal::iterator I=val.begin(), E=val.end(); I!=E; ++I)
+ if (!scan(*I))
+ return false;
+
+ return true;
+}
+
+bool ScanReachableSymbols::scan(const SymExpr *sym) {
+ for (SymExpr::symbol_iterator SI = sym->symbol_begin(),
+ SE = sym->symbol_end();
+ SI != SE; ++SI) {
+ bool wasVisited = !visited.insert(*SI).second;
+ if (wasVisited)
+ continue;
+
+ if (!visitor.VisitSymbol(*SI))
+ return false;
+ }
+
+ return true;
+}
+
+bool ScanReachableSymbols::scan(SVal val) {
+ if (Optional<loc::MemRegionVal> X = val.getAs<loc::MemRegionVal>())
+ return scan(X->getRegion());
+
+ if (Optional<nonloc::LazyCompoundVal> X =
+ val.getAs<nonloc::LazyCompoundVal>())
+ return scan(*X);
+
+ if (Optional<nonloc::LocAsInteger> X = val.getAs<nonloc::LocAsInteger>())
+ return scan(X->getLoc());
+
+ if (SymbolRef Sym = val.getAsSymbol())
+ return scan(Sym);
+
+ if (const SymExpr *Sym = val.getAsSymbolicExpression())
+ return scan(Sym);
+
+ if (Optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>())
+ return scan(*X);
+
+ return true;
+}
+
+bool ScanReachableSymbols::scan(const MemRegion *R) {
+ if (isa<MemSpaceRegion>(R))
+ return true;
+
+ bool wasVisited = !visited.insert(R).second;
+ if (wasVisited)
+ return true;
+
+ if (!visitor.VisitMemRegion(R))
+ return false;
+
+ // If this is a symbolic region, visit the symbol for the region.
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
+ if (!visitor.VisitSymbol(SR->getSymbol()))
+ return false;
+
+ // If this is a subregion, also visit the parent regions.
+ if (const SubRegion *SR = dyn_cast<SubRegion>(R)) {
+ const MemRegion *Super = SR->getSuperRegion();
+ if (!scan(Super))
+ return false;
+
+ // When we reach the topmost region, scan all symbols in it.
+ if (isa<MemSpaceRegion>(Super)) {
+ StoreManager &StoreMgr = state->getStateManager().getStoreManager();
+ if (!StoreMgr.scanReachableSymbols(state->getStore(), SR, *this))
+ return false;
+ }
+ }
+
+ // Regions captured by a block are also implicitly reachable.
+ if (const BlockDataRegion *BDR = dyn_cast<BlockDataRegion>(R)) {
+ BlockDataRegion::referenced_vars_iterator I = BDR->referenced_vars_begin(),
+ E = BDR->referenced_vars_end();
+ for ( ; I != E; ++I) {
+ if (!scan(I.getCapturedRegion()))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool ProgramState::scanReachableSymbols(SVal val, SymbolVisitor& visitor) const {
+ ScanReachableSymbols S(this, visitor);
+ return S.scan(val);
+}
+
+bool ProgramState::scanReachableSymbols(
+ llvm::iterator_range<region_iterator> Reachable,
+ SymbolVisitor &visitor) const {
+ ScanReachableSymbols S(this, visitor);
+ for (const MemRegion *R : Reachable) {
+ if (!S.scan(R))
+ return false;
+ }
+ return true;
+}
+
+ProgramStateRef ProgramState::addTaint(const Stmt *S,
+ const LocationContext *LCtx,
+ TaintTagType Kind) const {
+ if (const Expr *E = dyn_cast_or_null<Expr>(S))
+ S = E->IgnoreParens();
+
+ return addTaint(getSVal(S, LCtx), Kind);
+}
+
+ProgramStateRef ProgramState::addTaint(SVal V,
+ TaintTagType Kind) const {
+ SymbolRef Sym = V.getAsSymbol();
+ if (Sym)
+ return addTaint(Sym, Kind);
+
+ // If the SVal represents a structure, try to mass-taint all values within the
+ // structure. For now it only works efficiently on lazy compound values that
+ // were conjured during a conservative evaluation of a function - either as
+ // return values of functions that return structures or arrays by value, or as
+ // values of structures or arrays passed into the function by reference,
+ // directly or through pointer aliasing. Such lazy compound values are
+ // characterized by having exactly one binding in their captured store within
+ // their parent region, which is a conjured symbol default-bound to the base
+ // region of the parent region.
+ if (auto LCV = V.getAs<nonloc::LazyCompoundVal>()) {
+ if (Optional<SVal> binding = getStateManager().StoreMgr->getDefaultBinding(*LCV)) {
+ if (SymbolRef Sym = binding->getAsSymbol())
+ return addPartialTaint(Sym, LCV->getRegion(), Kind);
+ }
+ }
+
+ const MemRegion *R = V.getAsRegion();
+ return addTaint(R, Kind);
+}
+
+ProgramStateRef ProgramState::addTaint(const MemRegion *R,
+ TaintTagType Kind) const {
+ if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R))
+ return addTaint(SR->getSymbol(), Kind);
+ return this;
+}
+
+ProgramStateRef ProgramState::addTaint(SymbolRef Sym,
+ TaintTagType Kind) const {
+ // If this is a symbol cast, remove the cast before adding the taint. Taint
+ // is cast agnostic.
+ while (const SymbolCast *SC = dyn_cast<SymbolCast>(Sym))
+ Sym = SC->getOperand();
+
+ ProgramStateRef NewState = set<TaintMap>(Sym, Kind);
+ assert(NewState);
+ return NewState;
+}
+
+ProgramStateRef ProgramState::addPartialTaint(SymbolRef ParentSym,
+ const SubRegion *SubRegion,
+ TaintTagType Kind) const {
+ // Ignore partial taint if the entire parent symbol is already tainted.
+ if (contains<TaintMap>(ParentSym) && *get<TaintMap>(ParentSym) == Kind)
+ return this;
+
+ // Partial taint applies if only a portion of the symbol is tainted.
+ if (SubRegion == SubRegion->getBaseRegion())
+ return addTaint(ParentSym, Kind);
+
+ const TaintedSubRegions *SavedRegs = get<DerivedSymTaint>(ParentSym);
+ TaintedSubRegions Regs =
+ SavedRegs ? *SavedRegs : stateMgr->TSRFactory.getEmptyMap();
+
+ Regs = stateMgr->TSRFactory.add(Regs, SubRegion, Kind);
+ ProgramStateRef NewState = set<DerivedSymTaint>(ParentSym, Regs);
+ assert(NewState);
+ return NewState;
+}
+
+bool ProgramState::isTainted(const Stmt *S, const LocationContext *LCtx,
+ TaintTagType Kind) const {
+ if (const Expr *E = dyn_cast_or_null<Expr>(S))
+ S = E->IgnoreParens();
+
+ SVal val = getSVal(S, LCtx);
+ return isTainted(val, Kind);
+}
+
+bool ProgramState::isTainted(SVal V, TaintTagType Kind) const {
+ if (const SymExpr *Sym = V.getAsSymExpr())
+ return isTainted(Sym, Kind);
+ if (const MemRegion *Reg = V.getAsRegion())
+ return isTainted(Reg, Kind);
+ return false;
+}
+
+bool ProgramState::isTainted(const MemRegion *Reg, TaintTagType K) const {
+ if (!Reg)
+ return false;
+
+ // Element region (array element) is tainted if either the base or the offset
+ // are tainted.
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(Reg))
+ return isTainted(ER->getSuperRegion(), K) || isTainted(ER->getIndex(), K);
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg))
+ return isTainted(SR->getSymbol(), K);
+
+ if (const SubRegion *ER = dyn_cast<SubRegion>(Reg))
+ return isTainted(ER->getSuperRegion(), K);
+
+ return false;
+}
+
+bool ProgramState::isTainted(SymbolRef Sym, TaintTagType Kind) const {
+ if (!Sym)
+ return false;
+
+ // Traverse all the symbols this symbol depends on to see if any are tainted.
+ for (SymExpr::symbol_iterator SI = Sym->symbol_begin(), SE =Sym->symbol_end();
+ SI != SE; ++SI) {
+ if (!isa<SymbolData>(*SI))
+ continue;
+
+ if (const TaintTagType *Tag = get<TaintMap>(*SI)) {
+ if (*Tag == Kind)
+ return true;
+ }
+
+ if (const SymbolDerived *SD = dyn_cast<SymbolDerived>(*SI)) {
+ // If this is a SymbolDerived with a tainted parent, it's also tainted.
+ if (isTainted(SD->getParentSymbol(), Kind))
+ return true;
+
+ // If this is a SymbolDerived with the same parent symbol as another
+ // tainted SymbolDerived and a region that's a sub-region of that tainted
+ // symbol, it's also tainted.
+ if (const TaintedSubRegions *Regs =
+ get<DerivedSymTaint>(SD->getParentSymbol())) {
+ const TypedValueRegion *R = SD->getRegion();
+ for (auto I : *Regs) {
+ // FIXME: The logic to identify tainted regions could be more
+ // complete. For example, this would not currently identify
+ // overlapping fields in a union as tainted. To identify this we can
+ // check for overlapping/nested byte offsets.
+ if (Kind == I.second && R->isSubRegionOf(I.first))
+ return true;
+ }
+ }
+ }
+
+ // If memory region is tainted, data is also tainted.
+ if (const SymbolRegionValue *SRV = dyn_cast<SymbolRegionValue>(*SI)) {
+ if (isTainted(SRV->getRegion(), Kind))
+ return true;
+ }
+
+ // If this is a SymbolCast from a tainted value, it's also tainted.
+ if (const SymbolCast *SC = dyn_cast<SymbolCast>(*SI)) {
+ if (isTainted(SC->getOperand(), Kind))
+ return true;
+ }
+ }
+
+ return false;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
new file mode 100644
index 000000000000..d9b58d0f5185
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -0,0 +1,751 @@
+//== RangeConstraintManager.cpp - Manage range constraints.------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines RangeConstraintManager, a class that tracks simple
+// equality and inequality constraints on symbolic values of ProgramState.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/ImmutableSet.h"
+#include "llvm/Support/raw_ostream.h"
+
+using namespace clang;
+using namespace ento;
+
+void RangeSet::IntersectInRange(BasicValueFactory &BV, Factory &F,
+ const llvm::APSInt &Lower, const llvm::APSInt &Upper,
+ PrimRangeSet &newRanges, PrimRangeSet::iterator &i,
+ PrimRangeSet::iterator &e) const {
+ // There are six cases for each range R in the set:
+ // 1. R is entirely before the intersection range.
+ // 2. R is entirely after the intersection range.
+ // 3. R contains the entire intersection range.
+ // 4. R starts before the intersection range and ends in the middle.
+ // 5. R starts in the middle of the intersection range and ends after it.
+ // 6. R is entirely contained in the intersection range.
+ // These correspond to each of the conditions below.
+ for (/* i = begin(), e = end() */; i != e; ++i) {
+ if (i->To() < Lower) {
+ continue;
+ }
+ if (i->From() > Upper) {
+ break;
+ }
+
+ if (i->Includes(Lower)) {
+ if (i->Includes(Upper)) {
+ newRanges =
+ F.add(newRanges, Range(BV.getValue(Lower), BV.getValue(Upper)));
+ break;
+ } else
+ newRanges = F.add(newRanges, Range(BV.getValue(Lower), i->To()));
+ } else {
+ if (i->Includes(Upper)) {
+ newRanges = F.add(newRanges, Range(i->From(), BV.getValue(Upper)));
+ break;
+ } else
+ newRanges = F.add(newRanges, *i);
+ }
+ }
+}
+
+const llvm::APSInt &RangeSet::getMinValue() const {
+ assert(!isEmpty());
+ return ranges.begin()->From();
+}
+
+bool RangeSet::pin(llvm::APSInt &Lower, llvm::APSInt &Upper) const {
+ // This function has nine cases, the cartesian product of range-testing
+ // both the upper and lower bounds against the symbol's type.
+ // Each case requires a different pinning operation.
+ // The function returns false if the described range is entirely outside
+ // the range of values for the associated symbol.
+ APSIntType Type(getMinValue());
+ APSIntType::RangeTestResultKind LowerTest = Type.testInRange(Lower, true);
+ APSIntType::RangeTestResultKind UpperTest = Type.testInRange(Upper, true);
+
+ switch (LowerTest) {
+ case APSIntType::RTR_Below:
+ switch (UpperTest) {
+ case APSIntType::RTR_Below:
+ // The entire range is outside the symbol's set of possible values.
+ // If this is a conventionally-ordered range, the state is infeasible.
+ if (Lower <= Upper)
+ return false;
+
+ // However, if the range wraps around, it spans all possible values.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
+ break;
+ case APSIntType::RTR_Within:
+ // The range starts below what's possible but ends within it. Pin.
+ Lower = Type.getMinValue();
+ Type.apply(Upper);
+ break;
+ case APSIntType::RTR_Above:
+ // The range spans all possible values for the symbol. Pin.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
+ break;
+ }
+ break;
+ case APSIntType::RTR_Within:
+ switch (UpperTest) {
+ case APSIntType::RTR_Below:
+ // The range wraps around, but all lower values are not possible.
+ Type.apply(Lower);
+ Upper = Type.getMaxValue();
+ break;
+ case APSIntType::RTR_Within:
+ // The range may or may not wrap around, but both limits are valid.
+ Type.apply(Lower);
+ Type.apply(Upper);
+ break;
+ case APSIntType::RTR_Above:
+ // The range starts within what's possible but ends above it. Pin.
+ Type.apply(Lower);
+ Upper = Type.getMaxValue();
+ break;
+ }
+ break;
+ case APSIntType::RTR_Above:
+ switch (UpperTest) {
+ case APSIntType::RTR_Below:
+ // The range wraps but is outside the symbol's set of possible values.
+ return false;
+ case APSIntType::RTR_Within:
+ // The range starts above what's possible but ends within it (wrap).
+ Lower = Type.getMinValue();
+ Type.apply(Upper);
+ break;
+ case APSIntType::RTR_Above:
+ // The entire range is outside the symbol's set of possible values.
+ // If this is a conventionally-ordered range, the state is infeasible.
+ if (Lower <= Upper)
+ return false;
+
+ // However, if the range wraps around, it spans all possible values.
+ Lower = Type.getMinValue();
+ Upper = Type.getMaxValue();
+ break;
+ }
+ break;
+ }
+
+ return true;
+}
+
+// Returns a set containing the values in the receiving set, intersected with
+// the closed range [Lower, Upper]. Unlike the Range type, this range uses
+// modular arithmetic, corresponding to the common treatment of C integer
+// overflow. Thus, if the Lower bound is greater than the Upper bound, the
+// range is taken to wrap around. This is equivalent to taking the
+// intersection with the two ranges [Min, Upper] and [Lower, Max],
+// or, alternatively, /removing/ all integers between Upper and Lower.
+RangeSet RangeSet::Intersect(BasicValueFactory &BV, Factory &F,
+ llvm::APSInt Lower, llvm::APSInt Upper) const {
+ if (!pin(Lower, Upper))
+ return F.getEmptySet();
+
+ PrimRangeSet newRanges = F.getEmptySet();
+
+ PrimRangeSet::iterator i = begin(), e = end();
+ if (Lower <= Upper)
+ IntersectInRange(BV, F, Lower, Upper, newRanges, i, e);
+ else {
+ // The order of the next two statements is important!
+ // IntersectInRange() does not reset the iteration state for i and e.
+ // Therefore, the lower range most be handled first.
+ IntersectInRange(BV, F, BV.getMinValue(Upper), Upper, newRanges, i, e);
+ IntersectInRange(BV, F, Lower, BV.getMaxValue(Lower), newRanges, i, e);
+ }
+
+ return newRanges;
+}
+
+// Turn all [A, B] ranges to [-B, -A]. Ranges [MIN, B] are turned to range set
+// [MIN, MIN] U [-B, MAX], when MIN and MAX are the minimal and the maximal
+// signed values of the type.
+RangeSet RangeSet::Negate(BasicValueFactory &BV, Factory &F) const {
+ PrimRangeSet newRanges = F.getEmptySet();
+
+ for (iterator i = begin(), e = end(); i != e; ++i) {
+ const llvm::APSInt &from = i->From(), &to = i->To();
+ const llvm::APSInt &newTo = (from.isMinSignedValue() ?
+ BV.getMaxValue(from) :
+ BV.getValue(- from));
+ if (to.isMaxSignedValue() && !newRanges.isEmpty() &&
+ newRanges.begin()->From().isMinSignedValue()) {
+ assert(newRanges.begin()->To().isMinSignedValue() &&
+ "Ranges should not overlap");
+ assert(!from.isMinSignedValue() && "Ranges should not overlap");
+ const llvm::APSInt &newFrom = newRanges.begin()->From();
+ newRanges =
+ F.add(F.remove(newRanges, *newRanges.begin()), Range(newFrom, newTo));
+ } else if (!to.isMinSignedValue()) {
+ const llvm::APSInt &newFrom = BV.getValue(- to);
+ newRanges = F.add(newRanges, Range(newFrom, newTo));
+ }
+ if (from.isMinSignedValue()) {
+ newRanges = F.add(newRanges, Range(BV.getMinValue(from),
+ BV.getMinValue(from)));
+ }
+ }
+
+ return newRanges;
+}
+
+void RangeSet::print(raw_ostream &os) const {
+ bool isFirst = true;
+ os << "{ ";
+ for (iterator i = begin(), e = end(); i != e; ++i) {
+ if (isFirst)
+ isFirst = false;
+ else
+ os << ", ";
+
+ os << '[' << i->From().toString(10) << ", " << i->To().toString(10)
+ << ']';
+ }
+ os << " }";
+}
+
+namespace {
+class RangeConstraintManager : public RangedConstraintManager {
+public:
+ RangeConstraintManager(SubEngine *SE, SValBuilder &SVB)
+ : RangedConstraintManager(SE, SVB) {}
+
+ //===------------------------------------------------------------------===//
+ // Implementation for interface from ConstraintManager.
+ //===------------------------------------------------------------------===//
+
+ bool canReasonAbout(SVal X) const override;
+
+ ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override;
+
+ const llvm::APSInt *getSymVal(ProgramStateRef State,
+ SymbolRef Sym) const override;
+
+ ProgramStateRef removeDeadBindings(ProgramStateRef State,
+ SymbolReaper &SymReaper) override;
+
+ void print(ProgramStateRef State, raw_ostream &Out, const char *nl,
+ const char *sep) override;
+
+ //===------------------------------------------------------------------===//
+ // Implementation for interface from RangedConstraintManager.
+ //===------------------------------------------------------------------===//
+
+ ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymWithinInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymOutsideInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
+
+private:
+ RangeSet::Factory F;
+
+ RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
+ const RangeSet* getRangeForMinusSymbol(ProgramStateRef State,
+ SymbolRef Sym);
+
+ RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymGTRange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymLERange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymLERange(llvm::function_ref<RangeSet()> RS,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+
+};
+
+} // end anonymous namespace
+
+std::unique_ptr<ConstraintManager>
+ento::CreateRangeConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) {
+ return llvm::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder());
+}
+
+bool RangeConstraintManager::canReasonAbout(SVal X) const {
+ Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
+ if (SymVal && SymVal->isExpression()) {
+ const SymExpr *SE = SymVal->getSymbol();
+
+ if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(SE)) {
+ switch (SIE->getOpcode()) {
+ // We don't reason yet about bitwise-constraints on symbolic values.
+ case BO_And:
+ case BO_Or:
+ case BO_Xor:
+ return false;
+ // We don't reason yet about these arithmetic constraints on
+ // symbolic values.
+ case BO_Mul:
+ case BO_Div:
+ case BO_Rem:
+ case BO_Shl:
+ case BO_Shr:
+ return false;
+ // All other cases.
+ default:
+ return true;
+ }
+ }
+
+ if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(SE)) {
+ // FIXME: Handle <=> here.
+ if (BinaryOperator::isEqualityOp(SSE->getOpcode()) ||
+ BinaryOperator::isRelationalOp(SSE->getOpcode())) {
+ // We handle Loc <> Loc comparisons, but not (yet) NonLoc <> NonLoc.
+ // We've recently started producing Loc <> NonLoc comparisons (that
+ // result from casts of one of the operands between eg. intptr_t and
+ // void *), but we can't reason about them yet.
+ if (Loc::isLocType(SSE->getLHS()->getType())) {
+ return Loc::isLocType(SSE->getRHS()->getType());
+ }
+ }
+ }
+
+ return false;
+ }
+
+ return true;
+}
+
+ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State,
+ SymbolRef Sym) {
+ const RangeSet *Ranges = State->get<ConstraintRange>(Sym);
+
+ // If we don't have any information about this symbol, it's underconstrained.
+ if (!Ranges)
+ return ConditionTruthVal();
+
+ // If we have a concrete value, see if it's zero.
+ if (const llvm::APSInt *Value = Ranges->getConcreteValue())
+ return *Value == 0;
+
+ BasicValueFactory &BV = getBasicVals();
+ APSIntType IntType = BV.getAPSIntType(Sym->getType());
+ llvm::APSInt Zero = IntType.getZeroValue();
+
+ // Check if zero is in the set of possible values.
+ if (Ranges->Intersect(BV, F, Zero, Zero).isEmpty())
+ return false;
+
+ // Zero is a possible value, but it is not the /only/ possible value.
+ return ConditionTruthVal();
+}
+
+const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St,
+ SymbolRef Sym) const {
+ const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(Sym);
+ return T ? T->getConcreteValue() : nullptr;
+}
+
+/// Scan all symbols referenced by the constraints. If the symbol is not alive
+/// as marked in LSymbols, mark it as dead in DSymbols.
+ProgramStateRef
+RangeConstraintManager::removeDeadBindings(ProgramStateRef State,
+ SymbolReaper &SymReaper) {
+ bool Changed = false;
+ ConstraintRangeTy CR = State->get<ConstraintRange>();
+ ConstraintRangeTy::Factory &CRFactory = State->get_context<ConstraintRange>();
+
+ for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) {
+ SymbolRef Sym = I.getKey();
+ if (SymReaper.isDead(Sym)) {
+ Changed = true;
+ CR = CRFactory.remove(CR, Sym);
+ }
+ }
+
+ return Changed ? State->set<ConstraintRange>(CR) : State;
+}
+
+/// Return a range set subtracting zero from \p Domain.
+static RangeSet assumeNonZero(
+ BasicValueFactory &BV,
+ RangeSet::Factory &F,
+ SymbolRef Sym,
+ RangeSet Domain) {
+ APSIntType IntType = BV.getAPSIntType(Sym->getType());
+ return Domain.Intersect(BV, F, ++IntType.getZeroValue(),
+ --IntType.getZeroValue());
+}
+
+/// Apply implicit constraints for bitwise OR- and AND-.
+/// For unsigned types, bitwise OR with a constant always returns
+/// a value greater-or-equal than the constant, and bitwise AND
+/// returns a value less-or-equal then the constant.
+///
+/// Pattern matches the expression \p Sym against those rule,
+/// and applies the required constraints.
+/// \p Input Previously established expression range set
+static RangeSet applyBitwiseConstraints(
+ BasicValueFactory &BV,
+ RangeSet::Factory &F,
+ RangeSet Input,
+ const SymIntExpr* SIE) {
+ QualType T = SIE->getType();
+ bool IsUnsigned = T->isUnsignedIntegerType();
+ const llvm::APSInt &RHS = SIE->getRHS();
+ const llvm::APSInt &Zero = BV.getAPSIntType(T).getZeroValue();
+ BinaryOperator::Opcode Operator = SIE->getOpcode();
+
+ // For unsigned types, the output of bitwise-or is bigger-or-equal than RHS.
+ if (Operator == BO_Or && IsUnsigned)
+ return Input.Intersect(BV, F, RHS, BV.getMaxValue(T));
+
+ // Bitwise-or with a non-zero constant is always non-zero.
+ if (Operator == BO_Or && RHS != Zero)
+ return assumeNonZero(BV, F, SIE, Input);
+
+ // For unsigned types, or positive RHS,
+ // bitwise-and output is always smaller-or-equal than RHS (assuming two's
+ // complement representation of signed types).
+ if (Operator == BO_And && (IsUnsigned || RHS >= Zero))
+ return Input.Intersect(BV, F, BV.getMinValue(T), RHS);
+
+ return Input;
+}
+
+RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
+ SymbolRef Sym) {
+ if (ConstraintRangeTy::data_type *V = State->get<ConstraintRange>(Sym))
+ return *V;
+
+ BasicValueFactory &BV = getBasicVals();
+
+ // If Sym is a difference of symbols A - B, then maybe we have range set
+ // stored for B - A.
+ if (const RangeSet *R = getRangeForMinusSymbol(State, Sym))
+ return R->Negate(BV, F);
+
+ // Lazily generate a new RangeSet representing all possible values for the
+ // given symbol type.
+ QualType T = Sym->getType();
+
+ RangeSet Result(F, BV.getMinValue(T), BV.getMaxValue(T));
+
+ // References are known to be non-zero.
+ if (T->isReferenceType())
+ return assumeNonZero(BV, F, Sym, Result);
+
+ // Known constraints on ranges of bitwise expressions.
+ if (const SymIntExpr* SIE = dyn_cast<SymIntExpr>(Sym))
+ return applyBitwiseConstraints(BV, F, Result, SIE);
+
+ return Result;
+}
+
+// FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to
+// obtain the negated symbolic expression instead of constructing the
+// symbol manually. This will allow us to support finding ranges of not
+// only negated SymSymExpr-type expressions, but also of other, simpler
+// expressions which we currently do not know how to negate.
+const RangeSet*
+RangeConstraintManager::getRangeForMinusSymbol(ProgramStateRef State,
+ SymbolRef Sym) {
+ if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
+ if (SSE->getOpcode() == BO_Sub) {
+ QualType T = Sym->getType();
+ SymbolManager &SymMgr = State->getSymbolManager();
+ SymbolRef negSym = SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub,
+ SSE->getLHS(), T);
+ if (const RangeSet *negV = State->get<ConstraintRange>(negSym)) {
+ // Unsigned range set cannot be negated, unless it is [0, 0].
+ if ((negV->getConcreteValue() &&
+ (*negV->getConcreteValue() == 0)) ||
+ T->isSignedIntegerOrEnumerationType())
+ return negV;
+ }
+ }
+ }
+ return nullptr;
+}
+
+//===------------------------------------------------------------------------===
+// assumeSymX methods: protected interface for RangeConstraintManager.
+//===------------------------------------------------------------------------===/
+
+// The syntax for ranges below is mathematical, using [x, y] for closed ranges
+// and (x, y) for open ranges. These ranges are modular, corresponding with
+// a common treatment of C integer overflow. This means that these methods
+// do not have to worry about overflow; RangeSet::Intersect can handle such a
+// "wraparound" range.
+// As an example, the range [UINT_MAX-1, 3) contains five values: UINT_MAX-1,
+// UINT_MAX, 0, 1, and 2.
+
+ProgramStateRef
+RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within)
+ return St;
+
+ llvm::APSInt Lower = AdjustmentType.convert(Int) - Adjustment;
+ llvm::APSInt Upper = Lower;
+ --Lower;
+ ++Upper;
+
+ // [Int-Adjustment+1, Int-Adjustment-1]
+ // Notice that the lower bound is greater than the upper bound.
+ RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower);
+ return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+}
+
+ProgramStateRef
+RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within)
+ return nullptr;
+
+ // [Int-Adjustment, Int-Adjustment]
+ llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment;
+ RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt);
+ return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+}
+
+RangeSet RangeConstraintManager::getSymLTRange(ProgramStateRef St,
+ SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int, true)) {
+ case APSIntType::RTR_Below:
+ return F.getEmptySet();
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return getRange(St, Sym);
+ }
+
+ // Special case for Int == Min. This is always false.
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (ComparisonVal == Min)
+ return F.getEmptySet();
+
+ llvm::APSInt Lower = Min - Adjustment;
+ llvm::APSInt Upper = ComparisonVal - Adjustment;
+ --Upper;
+
+ return getRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+}
+
+ProgramStateRef
+RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ RangeSet New = getSymLTRange(St, Sym, Int, Adjustment);
+ return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+}
+
+RangeSet RangeConstraintManager::getSymGTRange(ProgramStateRef St,
+ SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int, true)) {
+ case APSIntType::RTR_Below:
+ return getRange(St, Sym);
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return F.getEmptySet();
+ }
+
+ // Special case for Int == Max. This is always false.
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (ComparisonVal == Max)
+ return F.getEmptySet();
+
+ llvm::APSInt Lower = ComparisonVal - Adjustment;
+ llvm::APSInt Upper = Max - Adjustment;
+ ++Lower;
+
+ return getRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+}
+
+ProgramStateRef
+RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ RangeSet New = getSymGTRange(St, Sym, Int, Adjustment);
+ return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+}
+
+RangeSet RangeConstraintManager::getSymGERange(ProgramStateRef St,
+ SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int, true)) {
+ case APSIntType::RTR_Below:
+ return getRange(St, Sym);
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return F.getEmptySet();
+ }
+
+ // Special case for Int == Min. This is always feasible.
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ if (ComparisonVal == Min)
+ return getRange(St, Sym);
+
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ llvm::APSInt Lower = ComparisonVal - Adjustment;
+ llvm::APSInt Upper = Max - Adjustment;
+
+ return getRange(St, Sym).Intersect(getBasicVals(), F, Lower, Upper);
+}
+
+ProgramStateRef
+RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ RangeSet New = getSymGERange(St, Sym, Int, Adjustment);
+ return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+}
+
+RangeSet RangeConstraintManager::getSymLERange(
+ llvm::function_ref<RangeSet()> RS,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // Before we do any real work, see if the value can even show up.
+ APSIntType AdjustmentType(Adjustment);
+ switch (AdjustmentType.testInRange(Int, true)) {
+ case APSIntType::RTR_Below:
+ return F.getEmptySet();
+ case APSIntType::RTR_Within:
+ break;
+ case APSIntType::RTR_Above:
+ return RS();
+ }
+
+ // Special case for Int == Max. This is always feasible.
+ llvm::APSInt ComparisonVal = AdjustmentType.convert(Int);
+ llvm::APSInt Max = AdjustmentType.getMaxValue();
+ if (ComparisonVal == Max)
+ return RS();
+
+ llvm::APSInt Min = AdjustmentType.getMinValue();
+ llvm::APSInt Lower = Min - Adjustment;
+ llvm::APSInt Upper = ComparisonVal - Adjustment;
+
+ return RS().Intersect(getBasicVals(), F, Lower, Upper);
+}
+
+RangeSet RangeConstraintManager::getSymLERange(ProgramStateRef St,
+ SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ return getSymLERange([&] { return getRange(St, Sym); }, Int, Adjustment);
+}
+
+ProgramStateRef
+RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ RangeSet New = getSymLERange(St, Sym, Int, Adjustment);
+ return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+}
+
+ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, const llvm::APSInt &Adjustment) {
+ RangeSet New = getSymGERange(State, Sym, From, Adjustment);
+ if (New.isEmpty())
+ return nullptr;
+ RangeSet Out = getSymLERange([&] { return New; }, To, Adjustment);
+ return Out.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, Out);
+}
+
+ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, const llvm::APSInt &Adjustment) {
+ RangeSet RangeLT = getSymLTRange(State, Sym, From, Adjustment);
+ RangeSet RangeGT = getSymGTRange(State, Sym, To, Adjustment);
+ RangeSet New(RangeLT.addRange(F, RangeGT));
+ return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New);
+}
+
+//===------------------------------------------------------------------------===
+// Pretty-printing.
+//===------------------------------------------------------------------------===/
+
+void RangeConstraintManager::print(ProgramStateRef St, raw_ostream &Out,
+ const char *nl, const char *sep) {
+
+ ConstraintRangeTy Ranges = St->get<ConstraintRange>();
+
+ if (Ranges.isEmpty()) {
+ Out << nl << sep << "Ranges are empty." << nl;
+ return;
+ }
+
+ Out << nl << sep << "Ranges of symbol values:";
+ for (ConstraintRangeTy::iterator I = Ranges.begin(), E = Ranges.end(); I != E;
+ ++I) {
+ Out << nl << ' ' << I.getKey() << " : ";
+ I.getData().print(Out);
+ }
+ Out << nl;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
new file mode 100644
index 000000000000..146dc20ad021
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
@@ -0,0 +1,210 @@
+//== RangedConstraintManager.cpp --------------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines RangedConstraintManager, a class that provides a
+// range-based constraint manager interface.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/RangedConstraintManager.h"
+
+namespace clang {
+
+namespace ento {
+
+RangedConstraintManager::~RangedConstraintManager() {}
+
+ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State,
+ SymbolRef Sym,
+ bool Assumption) {
+ // Handle SymbolData.
+ if (isa<SymbolData>(Sym)) {
+ return assumeSymUnsupported(State, Sym, Assumption);
+
+ // Handle symbolic expression.
+ } else if (const SymIntExpr *SIE = dyn_cast<SymIntExpr>(Sym)) {
+ // We can only simplify expressions whose RHS is an integer.
+
+ BinaryOperator::Opcode op = SIE->getOpcode();
+ if (BinaryOperator::isComparisonOp(op) && op != BO_Cmp) {
+ if (!Assumption)
+ op = BinaryOperator::negateComparisonOp(op);
+
+ return assumeSymRel(State, SIE->getLHS(), op, SIE->getRHS());
+ }
+
+ } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
+ // Translate "a != b" to "(b - a) != 0".
+ // We invert the order of the operands as a heuristic for how loop
+ // conditions are usually written ("begin != end") as compared to length
+ // calculations ("end - begin"). The more correct thing to do would be to
+ // canonicalize "a - b" and "b - a", which would allow us to treat
+ // "a != b" and "b != a" the same.
+ SymbolManager &SymMgr = getSymbolManager();
+ BinaryOperator::Opcode Op = SSE->getOpcode();
+ assert(BinaryOperator::isComparisonOp(Op));
+
+ // For now, we only support comparing pointers.
+ if (Loc::isLocType(SSE->getLHS()->getType()) &&
+ Loc::isLocType(SSE->getRHS()->getType())) {
+ QualType DiffTy = SymMgr.getContext().getPointerDiffType();
+ SymbolRef Subtraction =
+ SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy);
+
+ const llvm::APSInt &Zero = getBasicVals().getValue(0, DiffTy);
+ Op = BinaryOperator::reverseComparisonOp(Op);
+ if (!Assumption)
+ Op = BinaryOperator::negateComparisonOp(Op);
+ return assumeSymRel(State, Subtraction, Op, Zero);
+ }
+ }
+
+ // If we get here, there's nothing else we can do but treat the symbol as
+ // opaque.
+ return assumeSymUnsupported(State, Sym, Assumption);
+}
+
+ProgramStateRef RangedConstraintManager::assumeSymInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, bool InRange) {
+ // Get the type used for calculating wraparound.
+ BasicValueFactory &BVF = getBasicVals();
+ APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType());
+
+ llvm::APSInt Adjustment = WraparoundType.getZeroValue();
+ SymbolRef AdjustedSym = Sym;
+ computeAdjustment(AdjustedSym, Adjustment);
+
+ // Convert the right-hand side integer as necessary.
+ APSIntType ComparisonType = std::max(WraparoundType, APSIntType(From));
+ llvm::APSInt ConvertedFrom = ComparisonType.convert(From);
+ llvm::APSInt ConvertedTo = ComparisonType.convert(To);
+
+ // Prefer unsigned comparisons.
+ if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() &&
+ ComparisonType.isUnsigned() && !WraparoundType.isUnsigned())
+ Adjustment.setIsSigned(false);
+
+ if (InRange)
+ return assumeSymWithinInclusiveRange(State, AdjustedSym, ConvertedFrom,
+ ConvertedTo, Adjustment);
+ return assumeSymOutsideInclusiveRange(State, AdjustedSym, ConvertedFrom,
+ ConvertedTo, Adjustment);
+}
+
+ProgramStateRef
+RangedConstraintManager::assumeSymUnsupported(ProgramStateRef State,
+ SymbolRef Sym, bool Assumption) {
+ BasicValueFactory &BVF = getBasicVals();
+ QualType T = Sym->getType();
+
+ // Non-integer types are not supported.
+ if (!T->isIntegralOrEnumerationType())
+ return State;
+
+ // Reverse the operation and add directly to state.
+ const llvm::APSInt &Zero = BVF.getValue(0, T);
+ if (Assumption)
+ return assumeSymNE(State, Sym, Zero, Zero);
+ else
+ return assumeSymEQ(State, Sym, Zero, Zero);
+}
+
+ProgramStateRef RangedConstraintManager::assumeSymRel(ProgramStateRef State,
+ SymbolRef Sym,
+ BinaryOperator::Opcode Op,
+ const llvm::APSInt &Int) {
+ assert(BinaryOperator::isComparisonOp(Op) &&
+ "Non-comparison ops should be rewritten as comparisons to zero.");
+
+ // Simplification: translate an assume of a constraint of the form
+ // "(exp comparison_op expr) != 0" to true into an assume of
+ // "exp comparison_op expr" to true. (And similarly, an assume of the form
+ // "(exp comparison_op expr) == 0" to true into an assume of
+ // "exp comparison_op expr" to false.)
+ if (Int == 0 && (Op == BO_EQ || Op == BO_NE)) {
+ if (const BinarySymExpr *SE = dyn_cast<BinarySymExpr>(Sym))
+ if (BinaryOperator::isComparisonOp(SE->getOpcode()))
+ return assumeSym(State, Sym, (Op == BO_NE ? true : false));
+ }
+
+ // Get the type used for calculating wraparound.
+ BasicValueFactory &BVF = getBasicVals();
+ APSIntType WraparoundType = BVF.getAPSIntType(Sym->getType());
+
+ // We only handle simple comparisons of the form "$sym == constant"
+ // or "($sym+constant1) == constant2".
+ // The adjustment is "constant1" in the above expression. It's used to
+ // "slide" the solution range around for modular arithmetic. For example,
+ // x < 4 has the solution [0, 3]. x+2 < 4 has the solution [0-2, 3-2], which
+ // in modular arithmetic is [0, 1] U [UINT_MAX-1, UINT_MAX]. It's up to
+ // the subclasses of SimpleConstraintManager to handle the adjustment.
+ llvm::APSInt Adjustment = WraparoundType.getZeroValue();
+ computeAdjustment(Sym, Adjustment);
+
+ // Convert the right-hand side integer as necessary.
+ APSIntType ComparisonType = std::max(WraparoundType, APSIntType(Int));
+ llvm::APSInt ConvertedInt = ComparisonType.convert(Int);
+
+ // Prefer unsigned comparisons.
+ if (ComparisonType.getBitWidth() == WraparoundType.getBitWidth() &&
+ ComparisonType.isUnsigned() && !WraparoundType.isUnsigned())
+ Adjustment.setIsSigned(false);
+
+ switch (Op) {
+ default:
+ llvm_unreachable("invalid operation not caught by assertion above");
+
+ case BO_EQ:
+ return assumeSymEQ(State, Sym, ConvertedInt, Adjustment);
+
+ case BO_NE:
+ return assumeSymNE(State, Sym, ConvertedInt, Adjustment);
+
+ case BO_GT:
+ return assumeSymGT(State, Sym, ConvertedInt, Adjustment);
+
+ case BO_GE:
+ return assumeSymGE(State, Sym, ConvertedInt, Adjustment);
+
+ case BO_LT:
+ return assumeSymLT(State, Sym, ConvertedInt, Adjustment);
+
+ case BO_LE:
+ return assumeSymLE(State, Sym, ConvertedInt, Adjustment);
+ } // end switch
+}
+
+void RangedConstraintManager::computeAdjustment(SymbolRef &Sym,
+ llvm::APSInt &Adjustment) {
+ // Is it a "($sym+constant1)" expression?
+ if (const SymIntExpr *SE = dyn_cast<SymIntExpr>(Sym)) {
+ BinaryOperator::Opcode Op = SE->getOpcode();
+ if (Op == BO_Add || Op == BO_Sub) {
+ Sym = SE->getLHS();
+ Adjustment = APSIntType(Adjustment).convert(SE->getRHS());
+
+ // Don't forget to negate the adjustment if it's being subtracted.
+ // This should happen /after/ promotion, in case the value being
+ // subtracted is, say, CHAR_MIN, and the promoted type is 'int'.
+ if (Op == BO_Sub)
+ Adjustment = -Adjustment;
+ }
+ }
+}
+
+void *ProgramStateTrait<ConstraintRange>::GDMIndex() {
+ static int Index;
+ return &Index;
+}
+
+} // end of namespace ento
+
+} // end of namespace clang
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
new file mode 100644
index 000000000000..b2339be4f263
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -0,0 +1,2591 @@
+//== RegionStore.cpp - Field-sensitive store model --------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a basic region store model. In this model, we do have field
+// sensitivity. But we assume nothing about the heap shape. So recursive data
+// structures are largely ignored. Basically we do 1-limiting analysis.
+// Parameter pointers are assumed with no aliasing. Pointee objects of
+// parameters are created lazily.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/AST/Attr.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/Analyses/LiveVariables.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
+#include "llvm/ADT/ImmutableMap.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/raw_ostream.h"
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+//===----------------------------------------------------------------------===//
+// Representation of binding keys.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class BindingKey {
+public:
+ enum Kind { Default = 0x0, Direct = 0x1 };
+private:
+ enum { Symbolic = 0x2 };
+
+ llvm::PointerIntPair<const MemRegion *, 2> P;
+ uint64_t Data;
+
+ /// Create a key for a binding to region \p r, which has a symbolic offset
+ /// from region \p Base.
+ explicit BindingKey(const SubRegion *r, const SubRegion *Base, Kind k)
+ : P(r, k | Symbolic), Data(reinterpret_cast<uintptr_t>(Base)) {
+ assert(r && Base && "Must have known regions.");
+ assert(getConcreteOffsetRegion() == Base && "Failed to store base region");
+ }
+
+ /// Create a key for a binding at \p offset from base region \p r.
+ explicit BindingKey(const MemRegion *r, uint64_t offset, Kind k)
+ : P(r, k), Data(offset) {
+ assert(r && "Must have known regions.");
+ assert(getOffset() == offset && "Failed to store offset");
+ assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r) ||
+ isa <CXXDerivedObjectRegion>(r)) &&
+ "Not a base");
+ }
+public:
+
+ bool isDirect() const { return P.getInt() & Direct; }
+ bool hasSymbolicOffset() const { return P.getInt() & Symbolic; }
+
+ const MemRegion *getRegion() const { return P.getPointer(); }
+ uint64_t getOffset() const {
+ assert(!hasSymbolicOffset());
+ return Data;
+ }
+
+ const SubRegion *getConcreteOffsetRegion() const {
+ assert(hasSymbolicOffset());
+ return reinterpret_cast<const SubRegion *>(static_cast<uintptr_t>(Data));
+ }
+
+ const MemRegion *getBaseRegion() const {
+ if (hasSymbolicOffset())
+ return getConcreteOffsetRegion()->getBaseRegion();
+ return getRegion()->getBaseRegion();
+ }
+
+ void Profile(llvm::FoldingSetNodeID& ID) const {
+ ID.AddPointer(P.getOpaqueValue());
+ ID.AddInteger(Data);
+ }
+
+ static BindingKey Make(const MemRegion *R, Kind k);
+
+ bool operator<(const BindingKey &X) const {
+ if (P.getOpaqueValue() < X.P.getOpaqueValue())
+ return true;
+ if (P.getOpaqueValue() > X.P.getOpaqueValue())
+ return false;
+ return Data < X.Data;
+ }
+
+ bool operator==(const BindingKey &X) const {
+ return P.getOpaqueValue() == X.P.getOpaqueValue() &&
+ Data == X.Data;
+ }
+
+ void dump() const;
+};
+} // end anonymous namespace
+
+BindingKey BindingKey::Make(const MemRegion *R, Kind k) {
+ const RegionOffset &RO = R->getAsOffset();
+ if (RO.hasSymbolicOffset())
+ return BindingKey(cast<SubRegion>(R), cast<SubRegion>(RO.getRegion()), k);
+
+ return BindingKey(RO.getRegion(), RO.getOffset(), k);
+}
+
+namespace llvm {
+ static inline
+ raw_ostream &operator<<(raw_ostream &os, BindingKey K) {
+ os << '(' << K.getRegion();
+ if (!K.hasSymbolicOffset())
+ os << ',' << K.getOffset();
+ os << ',' << (K.isDirect() ? "direct" : "default")
+ << ')';
+ return os;
+ }
+
+ template <typename T> struct isPodLike;
+ template <> struct isPodLike<BindingKey> {
+ static const bool value = true;
+ };
+} // end llvm namespace
+
+#ifndef NDEBUG
+LLVM_DUMP_METHOD void BindingKey::dump() const { llvm::errs() << *this; }
+#endif
+
+//===----------------------------------------------------------------------===//
+// Actual Store type.
+//===----------------------------------------------------------------------===//
+
+typedef llvm::ImmutableMap<BindingKey, SVal> ClusterBindings;
+typedef llvm::ImmutableMapRef<BindingKey, SVal> ClusterBindingsRef;
+typedef std::pair<BindingKey, SVal> BindingPair;
+
+typedef llvm::ImmutableMap<const MemRegion *, ClusterBindings>
+ RegionBindings;
+
+namespace {
+class RegionBindingsRef : public llvm::ImmutableMapRef<const MemRegion *,
+ ClusterBindings> {
+ ClusterBindings::Factory *CBFactory;
+
+public:
+ typedef llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>
+ ParentTy;
+
+ RegionBindingsRef(ClusterBindings::Factory &CBFactory,
+ const RegionBindings::TreeTy *T,
+ RegionBindings::TreeTy::Factory *F)
+ : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(T, F),
+ CBFactory(&CBFactory) {}
+
+ RegionBindingsRef(const ParentTy &P, ClusterBindings::Factory &CBFactory)
+ : llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>(P),
+ CBFactory(&CBFactory) {}
+
+ RegionBindingsRef add(key_type_ref K, data_type_ref D) const {
+ return RegionBindingsRef(static_cast<const ParentTy *>(this)->add(K, D),
+ *CBFactory);
+ }
+
+ RegionBindingsRef remove(key_type_ref K) const {
+ return RegionBindingsRef(static_cast<const ParentTy *>(this)->remove(K),
+ *CBFactory);
+ }
+
+ RegionBindingsRef addBinding(BindingKey K, SVal V) const;
+
+ RegionBindingsRef addBinding(const MemRegion *R,
+ BindingKey::Kind k, SVal V) const;
+
+ const SVal *lookup(BindingKey K) const;
+ const SVal *lookup(const MemRegion *R, BindingKey::Kind k) const;
+ using llvm::ImmutableMapRef<const MemRegion *, ClusterBindings>::lookup;
+
+ RegionBindingsRef removeBinding(BindingKey K);
+
+ RegionBindingsRef removeBinding(const MemRegion *R,
+ BindingKey::Kind k);
+
+ RegionBindingsRef removeBinding(const MemRegion *R) {
+ return removeBinding(R, BindingKey::Direct).
+ removeBinding(R, BindingKey::Default);
+ }
+
+ Optional<SVal> getDirectBinding(const MemRegion *R) const;
+
+ /// getDefaultBinding - Returns an SVal* representing an optional default
+ /// binding associated with a region and its subregions.
+ Optional<SVal> getDefaultBinding(const MemRegion *R) const;
+
+ /// Return the internal tree as a Store.
+ Store asStore() const {
+ return asImmutableMap().getRootWithoutRetain();
+ }
+
+ void dump(raw_ostream &OS, const char *nl) const {
+ for (iterator I = begin(), E = end(); I != E; ++I) {
+ const ClusterBindings &Cluster = I.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ OS << ' ' << CI.getKey() << " : " << CI.getData() << nl;
+ }
+ OS << nl;
+ }
+ }
+
+ LLVM_DUMP_METHOD void dump() const { dump(llvm::errs(), "\n"); }
+};
+} // end anonymous namespace
+
+typedef const RegionBindingsRef& RegionBindingsConstRef;
+
+Optional<SVal> RegionBindingsRef::getDirectBinding(const MemRegion *R) const {
+ return Optional<SVal>::create(lookup(R, BindingKey::Direct));
+}
+
+Optional<SVal> RegionBindingsRef::getDefaultBinding(const MemRegion *R) const {
+ return Optional<SVal>::create(lookup(R, BindingKey::Default));
+}
+
+RegionBindingsRef RegionBindingsRef::addBinding(BindingKey K, SVal V) const {
+ const MemRegion *Base = K.getBaseRegion();
+
+ const ClusterBindings *ExistingCluster = lookup(Base);
+ ClusterBindings Cluster =
+ (ExistingCluster ? *ExistingCluster : CBFactory->getEmptyMap());
+
+ ClusterBindings NewCluster = CBFactory->add(Cluster, K, V);
+ return add(Base, NewCluster);
+}
+
+
+RegionBindingsRef RegionBindingsRef::addBinding(const MemRegion *R,
+ BindingKey::Kind k,
+ SVal V) const {
+ return addBinding(BindingKey::Make(R, k), V);
+}
+
+const SVal *RegionBindingsRef::lookup(BindingKey K) const {
+ const ClusterBindings *Cluster = lookup(K.getBaseRegion());
+ if (!Cluster)
+ return nullptr;
+ return Cluster->lookup(K);
+}
+
+const SVal *RegionBindingsRef::lookup(const MemRegion *R,
+ BindingKey::Kind k) const {
+ return lookup(BindingKey::Make(R, k));
+}
+
+RegionBindingsRef RegionBindingsRef::removeBinding(BindingKey K) {
+ const MemRegion *Base = K.getBaseRegion();
+ const ClusterBindings *Cluster = lookup(Base);
+ if (!Cluster)
+ return *this;
+
+ ClusterBindings NewCluster = CBFactory->remove(*Cluster, K);
+ if (NewCluster.isEmpty())
+ return remove(Base);
+ return add(Base, NewCluster);
+}
+
+RegionBindingsRef RegionBindingsRef::removeBinding(const MemRegion *R,
+ BindingKey::Kind k){
+ return removeBinding(BindingKey::Make(R, k));
+}
+
+//===----------------------------------------------------------------------===//
+// Fine-grained control of RegionStoreManager.
+//===----------------------------------------------------------------------===//
+
+namespace {
+struct minimal_features_tag {};
+struct maximal_features_tag {};
+
+class RegionStoreFeatures {
+ bool SupportsFields;
+public:
+ RegionStoreFeatures(minimal_features_tag) :
+ SupportsFields(false) {}
+
+ RegionStoreFeatures(maximal_features_tag) :
+ SupportsFields(true) {}
+
+ void enableFields(bool t) { SupportsFields = t; }
+
+ bool supportsFields() const { return SupportsFields; }
+};
+}
+
+//===----------------------------------------------------------------------===//
+// Main RegionStore logic.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class InvalidateRegionsWorker;
+
+class RegionStoreManager : public StoreManager {
+public:
+ const RegionStoreFeatures Features;
+
+ RegionBindings::Factory RBFactory;
+ mutable ClusterBindings::Factory CBFactory;
+
+ typedef std::vector<SVal> SValListTy;
+private:
+ typedef llvm::DenseMap<const LazyCompoundValData *,
+ SValListTy> LazyBindingsMapTy;
+ LazyBindingsMapTy LazyBindingsMap;
+
+ /// The largest number of fields a struct can have and still be
+ /// considered "small".
+ ///
+ /// This is currently used to decide whether or not it is worth "forcing" a
+ /// LazyCompoundVal on bind.
+ ///
+ /// This is controlled by 'region-store-small-struct-limit' option.
+ /// To disable all small-struct-dependent behavior, set the option to "0".
+ unsigned SmallStructLimit;
+
+ /// A helper used to populate the work list with the given set of
+ /// regions.
+ void populateWorkList(InvalidateRegionsWorker &W,
+ ArrayRef<SVal> Values,
+ InvalidatedRegions *TopLevelRegions);
+
+public:
+ RegionStoreManager(ProgramStateManager& mgr, const RegionStoreFeatures &f)
+ : StoreManager(mgr), Features(f),
+ RBFactory(mgr.getAllocator()), CBFactory(mgr.getAllocator()),
+ SmallStructLimit(0) {
+ SubEngine &Eng = StateMgr.getOwningEngine();
+ AnalyzerOptions &Options = Eng.getAnalysisManager().options;
+ SmallStructLimit = Options.RegionStoreSmallStructLimit;
+ }
+
+
+ /// setImplicitDefaultValue - Set the default binding for the provided
+ /// MemRegion to the value implicitly defined for compound literals when
+ /// the value is not specified.
+ RegionBindingsRef setImplicitDefaultValue(RegionBindingsConstRef B,
+ const MemRegion *R, QualType T);
+
+ /// ArrayToPointer - Emulates the "decay" of an array to a pointer
+ /// type. 'Array' represents the lvalue of the array being decayed
+ /// to a pointer, and the returned SVal represents the decayed
+ /// version of that lvalue (i.e., a pointer to the first element of
+ /// the array). This is called by ExprEngine when evaluating
+ /// casts from arrays to pointers.
+ SVal ArrayToPointer(Loc Array, QualType ElementTy) override;
+
+ StoreRef getInitialStore(const LocationContext *InitLoc) override {
+ return StoreRef(RBFactory.getEmptyMap().getRootWithoutRetain(), *this);
+ }
+
+ //===-------------------------------------------------------------------===//
+ // Binding values to regions.
+ //===-------------------------------------------------------------------===//
+ RegionBindingsRef invalidateGlobalRegion(MemRegion::Kind K,
+ const Expr *Ex,
+ unsigned Count,
+ const LocationContext *LCtx,
+ RegionBindingsRef B,
+ InvalidatedRegions *Invalidated);
+
+ StoreRef invalidateRegions(Store store,
+ ArrayRef<SVal> Values,
+ const Expr *E, unsigned Count,
+ const LocationContext *LCtx,
+ const CallEvent *Call,
+ InvalidatedSymbols &IS,
+ RegionAndSymbolInvalidationTraits &ITraits,
+ InvalidatedRegions *Invalidated,
+ InvalidatedRegions *InvalidatedTopLevel) override;
+
+ bool scanReachableSymbols(Store S, const MemRegion *R,
+ ScanReachableSymbols &Callbacks) override;
+
+ RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B,
+ const SubRegion *R);
+
+public: // Part of public interface to class.
+
+ StoreRef Bind(Store store, Loc LV, SVal V) override {
+ return StoreRef(bind(getRegionBindings(store), LV, V).asStore(), *this);
+ }
+
+ RegionBindingsRef bind(RegionBindingsConstRef B, Loc LV, SVal V);
+
+ // BindDefaultInitial is only used to initialize a region with
+ // a default value.
+ StoreRef BindDefaultInitial(Store store, const MemRegion *R,
+ SVal V) override {
+ RegionBindingsRef B = getRegionBindings(store);
+ // Use other APIs when you have to wipe the region that was initialized
+ // earlier.
+ assert(!(B.getDefaultBinding(R) || B.getDirectBinding(R)) &&
+ "Double initialization!");
+ B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V);
+ return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this);
+ }
+
+ // BindDefaultZero is used for zeroing constructors that may accidentally
+ // overwrite existing bindings.
+ StoreRef BindDefaultZero(Store store, const MemRegion *R) override {
+ // FIXME: The offsets of empty bases can be tricky because of
+ // of the so called "empty base class optimization".
+ // If a base class has been optimized out
+ // we should not try to create a binding, otherwise we should.
+ // Unfortunately, at the moment ASTRecordLayout doesn't expose
+ // the actual sizes of the empty bases
+ // and trying to infer them from offsets/alignments
+ // seems to be error-prone and non-trivial because of the trailing padding.
+ // As a temporary mitigation we don't create bindings for empty bases.
+ if (const auto *BR = dyn_cast<CXXBaseObjectRegion>(R))
+ if (BR->getDecl()->isEmpty())
+ return StoreRef(store, *this);
+
+ RegionBindingsRef B = getRegionBindings(store);
+ SVal V = svalBuilder.makeZeroVal(Ctx.CharTy);
+ B = removeSubRegionBindings(B, cast<SubRegion>(R));
+ B = B.addBinding(BindingKey::Make(R, BindingKey::Default), V);
+ return StoreRef(B.asImmutableMap().getRootWithoutRetain(), *this);
+ }
+
+ /// Attempt to extract the fields of \p LCV and bind them to the struct region
+ /// \p R.
+ ///
+ /// This path is used when it seems advantageous to "force" loading the values
+ /// within a LazyCompoundVal to bind memberwise to the struct region, rather
+ /// than using a Default binding at the base of the entire region. This is a
+ /// heuristic attempting to avoid building long chains of LazyCompoundVals.
+ ///
+ /// \returns The updated store bindings, or \c None if binding non-lazily
+ /// would be too expensive.
+ Optional<RegionBindingsRef> tryBindSmallStruct(RegionBindingsConstRef B,
+ const TypedValueRegion *R,
+ const RecordDecl *RD,
+ nonloc::LazyCompoundVal LCV);
+
+ /// BindStruct - Bind a compound value to a structure.
+ RegionBindingsRef bindStruct(RegionBindingsConstRef B,
+ const TypedValueRegion* R, SVal V);
+
+ /// BindVector - Bind a compound value to a vector.
+ RegionBindingsRef bindVector(RegionBindingsConstRef B,
+ const TypedValueRegion* R, SVal V);
+
+ RegionBindingsRef bindArray(RegionBindingsConstRef B,
+ const TypedValueRegion* R,
+ SVal V);
+
+ /// Clears out all bindings in the given region and assigns a new value
+ /// as a Default binding.
+ RegionBindingsRef bindAggregate(RegionBindingsConstRef B,
+ const TypedRegion *R,
+ SVal DefaultVal);
+
+ /// Create a new store with the specified binding removed.
+ /// \param ST the original store, that is the basis for the new store.
+ /// \param L the location whose binding should be removed.
+ StoreRef killBinding(Store ST, Loc L) override;
+
+ void incrementReferenceCount(Store store) override {
+ getRegionBindings(store).manualRetain();
+ }
+
+ /// If the StoreManager supports it, decrement the reference count of
+ /// the specified Store object. If the reference count hits 0, the memory
+ /// associated with the object is recycled.
+ void decrementReferenceCount(Store store) override {
+ getRegionBindings(store).manualRelease();
+ }
+
+ bool includedInBindings(Store store, const MemRegion *region) const override;
+
+ /// Return the value bound to specified location in a given state.
+ ///
+ /// The high level logic for this method is this:
+ /// getBinding (L)
+ /// if L has binding
+ /// return L's binding
+ /// else if L is in killset
+ /// return unknown
+ /// else
+ /// if L is on stack or heap
+ /// return undefined
+ /// else
+ /// return symbolic
+ SVal getBinding(Store S, Loc L, QualType T) override {
+ return getBinding(getRegionBindings(S), L, T);
+ }
+
+ Optional<SVal> getDefaultBinding(Store S, const MemRegion *R) override {
+ RegionBindingsRef B = getRegionBindings(S);
+ // Default bindings are always applied over a base region so look up the
+ // base region's default binding, otherwise the lookup will fail when R
+ // is at an offset from R->getBaseRegion().
+ return B.getDefaultBinding(R->getBaseRegion());
+ }
+
+ SVal getBinding(RegionBindingsConstRef B, Loc L, QualType T = QualType());
+
+ SVal getBindingForElement(RegionBindingsConstRef B, const ElementRegion *R);
+
+ SVal getBindingForField(RegionBindingsConstRef B, const FieldRegion *R);
+
+ SVal getBindingForObjCIvar(RegionBindingsConstRef B, const ObjCIvarRegion *R);
+
+ SVal getBindingForVar(RegionBindingsConstRef B, const VarRegion *R);
+
+ SVal getBindingForLazySymbol(const TypedValueRegion *R);
+
+ SVal getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
+ const TypedValueRegion *R,
+ QualType Ty);
+
+ SVal getLazyBinding(const SubRegion *LazyBindingRegion,
+ RegionBindingsRef LazyBinding);
+
+ /// Get bindings for the values in a struct and return a CompoundVal, used
+ /// when doing struct copy:
+ /// struct s x, y;
+ /// x = y;
+ /// y's value is retrieved by this method.
+ SVal getBindingForStruct(RegionBindingsConstRef B, const TypedValueRegion *R);
+ SVal getBindingForArray(RegionBindingsConstRef B, const TypedValueRegion *R);
+ NonLoc createLazyBinding(RegionBindingsConstRef B, const TypedValueRegion *R);
+
+ /// Used to lazily generate derived symbols for bindings that are defined
+ /// implicitly by default bindings in a super region.
+ ///
+ /// Note that callers may need to specially handle LazyCompoundVals, which
+ /// are returned as is in case the caller needs to treat them differently.
+ Optional<SVal> getBindingForDerivedDefaultValue(RegionBindingsConstRef B,
+ const MemRegion *superR,
+ const TypedValueRegion *R,
+ QualType Ty);
+
+ /// Get the state and region whose binding this region \p R corresponds to.
+ ///
+ /// If there is no lazy binding for \p R, the returned value will have a null
+ /// \c second. Note that a null pointer can represents a valid Store.
+ std::pair<Store, const SubRegion *>
+ findLazyBinding(RegionBindingsConstRef B, const SubRegion *R,
+ const SubRegion *originalRegion);
+
+ /// Returns the cached set of interesting SVals contained within a lazy
+ /// binding.
+ ///
+ /// The precise value of "interesting" is determined for the purposes of
+ /// RegionStore's internal analysis. It must always contain all regions and
+ /// symbols, but may omit constants and other kinds of SVal.
+ const SValListTy &getInterestingValues(nonloc::LazyCompoundVal LCV);
+
+ //===------------------------------------------------------------------===//
+ // State pruning.
+ //===------------------------------------------------------------------===//
+
+ /// removeDeadBindings - Scans the RegionStore of 'state' for dead values.
+ /// It returns a new Store with these values removed.
+ StoreRef removeDeadBindings(Store store, const StackFrameContext *LCtx,
+ SymbolReaper& SymReaper) override;
+
+ //===------------------------------------------------------------------===//
+ // Region "extents".
+ //===------------------------------------------------------------------===//
+
+ // FIXME: This method will soon be eliminated; see the note in Store.h.
+ DefinedOrUnknownSVal getSizeInElements(ProgramStateRef state,
+ const MemRegion* R,
+ QualType EleTy) override;
+
+ //===------------------------------------------------------------------===//
+ // Utility methods.
+ //===------------------------------------------------------------------===//
+
+ RegionBindingsRef getRegionBindings(Store store) const {
+ return RegionBindingsRef(CBFactory,
+ static_cast<const RegionBindings::TreeTy*>(store),
+ RBFactory.getTreeFactory());
+ }
+
+ void print(Store store, raw_ostream &Out, const char* nl) override;
+
+ void iterBindings(Store store, BindingsHandler& f) override {
+ RegionBindingsRef B = getRegionBindings(store);
+ for (RegionBindingsRef::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ const ClusterBindings &Cluster = I.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ const BindingKey &K = CI.getKey();
+ if (!K.isDirect())
+ continue;
+ if (const SubRegion *R = dyn_cast<SubRegion>(K.getRegion())) {
+ // FIXME: Possibly incorporate the offset?
+ if (!f.HandleBinding(*this, store, R, CI.getData()))
+ return;
+ }
+ }
+ }
+ }
+};
+
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// RegionStore creation.
+//===----------------------------------------------------------------------===//
+
+std::unique_ptr<StoreManager>
+ento::CreateRegionStoreManager(ProgramStateManager &StMgr) {
+ RegionStoreFeatures F = maximal_features_tag();
+ return llvm::make_unique<RegionStoreManager>(StMgr, F);
+}
+
+std::unique_ptr<StoreManager>
+ento::CreateFieldsOnlyRegionStoreManager(ProgramStateManager &StMgr) {
+ RegionStoreFeatures F = minimal_features_tag();
+ F.enableFields(true);
+ return llvm::make_unique<RegionStoreManager>(StMgr, F);
+}
+
+
+//===----------------------------------------------------------------------===//
+// Region Cluster analysis.
+//===----------------------------------------------------------------------===//
+
+namespace {
+/// Used to determine which global regions are automatically included in the
+/// initial worklist of a ClusterAnalysis.
+enum GlobalsFilterKind {
+ /// Don't include any global regions.
+ GFK_None,
+ /// Only include system globals.
+ GFK_SystemOnly,
+ /// Include all global regions.
+ GFK_All
+};
+
+template <typename DERIVED>
+class ClusterAnalysis {
+protected:
+ typedef llvm::DenseMap<const MemRegion *, const ClusterBindings *> ClusterMap;
+ typedef const MemRegion * WorkListElement;
+ typedef SmallVector<WorkListElement, 10> WorkList;
+
+ llvm::SmallPtrSet<const ClusterBindings *, 16> Visited;
+
+ WorkList WL;
+
+ RegionStoreManager &RM;
+ ASTContext &Ctx;
+ SValBuilder &svalBuilder;
+
+ RegionBindingsRef B;
+
+
+protected:
+ const ClusterBindings *getCluster(const MemRegion *R) {
+ return B.lookup(R);
+ }
+
+ /// Returns true if all clusters in the given memspace should be initially
+ /// included in the cluster analysis. Subclasses may provide their
+ /// own implementation.
+ bool includeEntireMemorySpace(const MemRegion *Base) {
+ return false;
+ }
+
+public:
+ ClusterAnalysis(RegionStoreManager &rm, ProgramStateManager &StateMgr,
+ RegionBindingsRef b)
+ : RM(rm), Ctx(StateMgr.getContext()),
+ svalBuilder(StateMgr.getSValBuilder()), B(std::move(b)) {}
+
+ RegionBindingsRef getRegionBindings() const { return B; }
+
+ bool isVisited(const MemRegion *R) {
+ return Visited.count(getCluster(R));
+ }
+
+ void GenerateClusters() {
+ // Scan the entire set of bindings and record the region clusters.
+ for (RegionBindingsRef::iterator RI = B.begin(), RE = B.end();
+ RI != RE; ++RI){
+ const MemRegion *Base = RI.getKey();
+
+ const ClusterBindings &Cluster = RI.getData();
+ assert(!Cluster.isEmpty() && "Empty clusters should be removed");
+ static_cast<DERIVED*>(this)->VisitAddedToCluster(Base, Cluster);
+
+ // If the base's memspace should be entirely invalidated, add the cluster
+ // to the workspace up front.
+ if (static_cast<DERIVED*>(this)->includeEntireMemorySpace(Base))
+ AddToWorkList(WorkListElement(Base), &Cluster);
+ }
+ }
+
+ bool AddToWorkList(WorkListElement E, const ClusterBindings *C) {
+ if (C && !Visited.insert(C).second)
+ return false;
+ WL.push_back(E);
+ return true;
+ }
+
+ bool AddToWorkList(const MemRegion *R) {
+ return static_cast<DERIVED*>(this)->AddToWorkList(R);
+ }
+
+ void RunWorkList() {
+ while (!WL.empty()) {
+ WorkListElement E = WL.pop_back_val();
+ const MemRegion *BaseR = E;
+
+ static_cast<DERIVED*>(this)->VisitCluster(BaseR, getCluster(BaseR));
+ }
+ }
+
+ void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C) {}
+ void VisitCluster(const MemRegion *baseR, const ClusterBindings *C) {}
+
+ void VisitCluster(const MemRegion *BaseR, const ClusterBindings *C,
+ bool Flag) {
+ static_cast<DERIVED*>(this)->VisitCluster(BaseR, C);
+ }
+};
+}
+
+//===----------------------------------------------------------------------===//
+// Binding invalidation.
+//===----------------------------------------------------------------------===//
+
+bool RegionStoreManager::scanReachableSymbols(Store S, const MemRegion *R,
+ ScanReachableSymbols &Callbacks) {
+ assert(R == R->getBaseRegion() && "Should only be called for base regions");
+ RegionBindingsRef B = getRegionBindings(S);
+ const ClusterBindings *Cluster = B.lookup(R);
+
+ if (!Cluster)
+ return true;
+
+ for (ClusterBindings::iterator RI = Cluster->begin(), RE = Cluster->end();
+ RI != RE; ++RI) {
+ if (!Callbacks.scan(RI.getData()))
+ return false;
+ }
+
+ return true;
+}
+
+static inline bool isUnionField(const FieldRegion *FR) {
+ return FR->getDecl()->getParent()->isUnion();
+}
+
+typedef SmallVector<const FieldDecl *, 8> FieldVector;
+
+static void getSymbolicOffsetFields(BindingKey K, FieldVector &Fields) {
+ assert(K.hasSymbolicOffset() && "Not implemented for concrete offset keys");
+
+ const MemRegion *Base = K.getConcreteOffsetRegion();
+ const MemRegion *R = K.getRegion();
+
+ while (R != Base) {
+ if (const FieldRegion *FR = dyn_cast<FieldRegion>(R))
+ if (!isUnionField(FR))
+ Fields.push_back(FR->getDecl());
+
+ R = cast<SubRegion>(R)->getSuperRegion();
+ }
+}
+
+static bool isCompatibleWithFields(BindingKey K, const FieldVector &Fields) {
+ assert(K.hasSymbolicOffset() && "Not implemented for concrete offset keys");
+
+ if (Fields.empty())
+ return true;
+
+ FieldVector FieldsInBindingKey;
+ getSymbolicOffsetFields(K, FieldsInBindingKey);
+
+ ptrdiff_t Delta = FieldsInBindingKey.size() - Fields.size();
+ if (Delta >= 0)
+ return std::equal(FieldsInBindingKey.begin() + Delta,
+ FieldsInBindingKey.end(),
+ Fields.begin());
+ else
+ return std::equal(FieldsInBindingKey.begin(), FieldsInBindingKey.end(),
+ Fields.begin() - Delta);
+}
+
+/// Collects all bindings in \p Cluster that may refer to bindings within
+/// \p Top.
+///
+/// Each binding is a pair whose \c first is the key (a BindingKey) and whose
+/// \c second is the value (an SVal).
+///
+/// The \p IncludeAllDefaultBindings parameter specifies whether to include
+/// default bindings that may extend beyond \p Top itself, e.g. if \p Top is
+/// an aggregate within a larger aggregate with a default binding.
+static void
+collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
+ SValBuilder &SVB, const ClusterBindings &Cluster,
+ const SubRegion *Top, BindingKey TopKey,
+ bool IncludeAllDefaultBindings) {
+ FieldVector FieldsInSymbolicSubregions;
+ if (TopKey.hasSymbolicOffset()) {
+ getSymbolicOffsetFields(TopKey, FieldsInSymbolicSubregions);
+ Top = TopKey.getConcreteOffsetRegion();
+ TopKey = BindingKey::Make(Top, BindingKey::Default);
+ }
+
+ // Find the length (in bits) of the region being invalidated.
+ uint64_t Length = UINT64_MAX;
+ SVal Extent = Top->getExtent(SVB);
+ if (Optional<nonloc::ConcreteInt> ExtentCI =
+ Extent.getAs<nonloc::ConcreteInt>()) {
+ const llvm::APSInt &ExtentInt = ExtentCI->getValue();
+ assert(ExtentInt.isNonNegative() || ExtentInt.isUnsigned());
+ // Extents are in bytes but region offsets are in bits. Be careful!
+ Length = ExtentInt.getLimitedValue() * SVB.getContext().getCharWidth();
+ } else if (const FieldRegion *FR = dyn_cast<FieldRegion>(Top)) {
+ if (FR->getDecl()->isBitField())
+ Length = FR->getDecl()->getBitWidthValue(SVB.getContext());
+ }
+
+ for (ClusterBindings::iterator I = Cluster.begin(), E = Cluster.end();
+ I != E; ++I) {
+ BindingKey NextKey = I.getKey();
+ if (NextKey.getRegion() == TopKey.getRegion()) {
+ // FIXME: This doesn't catch the case where we're really invalidating a
+ // region with a symbolic offset. Example:
+ // R: points[i].y
+ // Next: points[0].x
+
+ if (NextKey.getOffset() > TopKey.getOffset() &&
+ NextKey.getOffset() - TopKey.getOffset() < Length) {
+ // Case 1: The next binding is inside the region we're invalidating.
+ // Include it.
+ Bindings.push_back(*I);
+
+ } else if (NextKey.getOffset() == TopKey.getOffset()) {
+ // Case 2: The next binding is at the same offset as the region we're
+ // invalidating. In this case, we need to leave default bindings alone,
+ // since they may be providing a default value for a regions beyond what
+ // we're invalidating.
+ // FIXME: This is probably incorrect; consider invalidating an outer
+ // struct whose first field is bound to a LazyCompoundVal.
+ if (IncludeAllDefaultBindings || NextKey.isDirect())
+ Bindings.push_back(*I);
+ }
+
+ } else if (NextKey.hasSymbolicOffset()) {
+ const MemRegion *Base = NextKey.getConcreteOffsetRegion();
+ if (Top->isSubRegionOf(Base) && Top != Base) {
+ // Case 3: The next key is symbolic and we just changed something within
+ // its concrete region. We don't know if the binding is still valid, so
+ // we'll be conservative and include it.
+ if (IncludeAllDefaultBindings || NextKey.isDirect())
+ if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions))
+ Bindings.push_back(*I);
+ } else if (const SubRegion *BaseSR = dyn_cast<SubRegion>(Base)) {
+ // Case 4: The next key is symbolic, but we changed a known
+ // super-region. In this case the binding is certainly included.
+ if (BaseSR->isSubRegionOf(Top))
+ if (isCompatibleWithFields(NextKey, FieldsInSymbolicSubregions))
+ Bindings.push_back(*I);
+ }
+ }
+ }
+}
+
+static void
+collectSubRegionBindings(SmallVectorImpl<BindingPair> &Bindings,
+ SValBuilder &SVB, const ClusterBindings &Cluster,
+ const SubRegion *Top, bool IncludeAllDefaultBindings) {
+ collectSubRegionBindings(Bindings, SVB, Cluster, Top,
+ BindingKey::Make(Top, BindingKey::Default),
+ IncludeAllDefaultBindings);
+}
+
+RegionBindingsRef
+RegionStoreManager::removeSubRegionBindings(RegionBindingsConstRef B,
+ const SubRegion *Top) {
+ BindingKey TopKey = BindingKey::Make(Top, BindingKey::Default);
+ const MemRegion *ClusterHead = TopKey.getBaseRegion();
+
+ if (Top == ClusterHead) {
+ // We can remove an entire cluster's bindings all in one go.
+ return B.remove(Top);
+ }
+
+ const ClusterBindings *Cluster = B.lookup(ClusterHead);
+ if (!Cluster) {
+ // If we're invalidating a region with a symbolic offset, we need to make
+ // sure we don't treat the base region as uninitialized anymore.
+ if (TopKey.hasSymbolicOffset()) {
+ const SubRegion *Concrete = TopKey.getConcreteOffsetRegion();
+ return B.addBinding(Concrete, BindingKey::Default, UnknownVal());
+ }
+ return B;
+ }
+
+ SmallVector<BindingPair, 32> Bindings;
+ collectSubRegionBindings(Bindings, svalBuilder, *Cluster, Top, TopKey,
+ /*IncludeAllDefaultBindings=*/false);
+
+ ClusterBindingsRef Result(*Cluster, CBFactory);
+ for (SmallVectorImpl<BindingPair>::const_iterator I = Bindings.begin(),
+ E = Bindings.end();
+ I != E; ++I)
+ Result = Result.remove(I->first);
+
+ // If we're invalidating a region with a symbolic offset, we need to make sure
+ // we don't treat the base region as uninitialized anymore.
+ // FIXME: This isn't very precise; see the example in
+ // collectSubRegionBindings.
+ if (TopKey.hasSymbolicOffset()) {
+ const SubRegion *Concrete = TopKey.getConcreteOffsetRegion();
+ Result = Result.add(BindingKey::Make(Concrete, BindingKey::Default),
+ UnknownVal());
+ }
+
+ if (Result.isEmpty())
+ return B.remove(ClusterHead);
+ return B.add(ClusterHead, Result.asImmutableMap());
+}
+
+namespace {
+class InvalidateRegionsWorker : public ClusterAnalysis<InvalidateRegionsWorker>
+{
+ const Expr *Ex;
+ unsigned Count;
+ const LocationContext *LCtx;
+ InvalidatedSymbols &IS;
+ RegionAndSymbolInvalidationTraits &ITraits;
+ StoreManager::InvalidatedRegions *Regions;
+ GlobalsFilterKind GlobalsFilter;
+public:
+ InvalidateRegionsWorker(RegionStoreManager &rm,
+ ProgramStateManager &stateMgr,
+ RegionBindingsRef b,
+ const Expr *ex, unsigned count,
+ const LocationContext *lctx,
+ InvalidatedSymbols &is,
+ RegionAndSymbolInvalidationTraits &ITraitsIn,
+ StoreManager::InvalidatedRegions *r,
+ GlobalsFilterKind GFK)
+ : ClusterAnalysis<InvalidateRegionsWorker>(rm, stateMgr, b),
+ Ex(ex), Count(count), LCtx(lctx), IS(is), ITraits(ITraitsIn), Regions(r),
+ GlobalsFilter(GFK) {}
+
+ void VisitCluster(const MemRegion *baseR, const ClusterBindings *C);
+ void VisitBinding(SVal V);
+
+ using ClusterAnalysis::AddToWorkList;
+
+ bool AddToWorkList(const MemRegion *R);
+
+ /// Returns true if all clusters in the memory space for \p Base should be
+ /// be invalidated.
+ bool includeEntireMemorySpace(const MemRegion *Base);
+
+ /// Returns true if the memory space of the given region is one of the global
+ /// regions specially included at the start of invalidation.
+ bool isInitiallyIncludedGlobalRegion(const MemRegion *R);
+};
+}
+
+bool InvalidateRegionsWorker::AddToWorkList(const MemRegion *R) {
+ bool doNotInvalidateSuperRegion = ITraits.hasTrait(
+ R, RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
+ const MemRegion *BaseR = doNotInvalidateSuperRegion ? R : R->getBaseRegion();
+ return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR));
+}
+
+void InvalidateRegionsWorker::VisitBinding(SVal V) {
+ // A symbol? Mark it touched by the invalidation.
+ if (SymbolRef Sym = V.getAsSymbol())
+ IS.insert(Sym);
+
+ if (const MemRegion *R = V.getAsRegion()) {
+ AddToWorkList(R);
+ return;
+ }
+
+ // Is it a LazyCompoundVal? All references get invalidated as well.
+ if (Optional<nonloc::LazyCompoundVal> LCS =
+ V.getAs<nonloc::LazyCompoundVal>()) {
+
+ const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS);
+
+ for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(),
+ E = Vals.end();
+ I != E; ++I)
+ VisitBinding(*I);
+
+ return;
+ }
+}
+
+void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
+ const ClusterBindings *C) {
+
+ bool PreserveRegionsContents =
+ ITraits.hasTrait(baseR,
+ RegionAndSymbolInvalidationTraits::TK_PreserveContents);
+
+ if (C) {
+ for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I)
+ VisitBinding(I.getData());
+
+ // Invalidate regions contents.
+ if (!PreserveRegionsContents)
+ B = B.remove(baseR);
+ }
+
+ if (const auto *TO = dyn_cast<TypedValueRegion>(baseR)) {
+ if (const auto *RD = TO->getValueType()->getAsCXXRecordDecl()) {
+
+ // Lambdas can affect all static local variables without explicitly
+ // capturing those.
+ // We invalidate all static locals referenced inside the lambda body.
+ if (RD->isLambda() && RD->getLambdaCallOperator()->getBody()) {
+ using namespace ast_matchers;
+
+ const char *DeclBind = "DeclBind";
+ StatementMatcher RefToStatic = stmt(hasDescendant(declRefExpr(
+ to(varDecl(hasStaticStorageDuration()).bind(DeclBind)))));
+ auto Matches =
+ match(RefToStatic, *RD->getLambdaCallOperator()->getBody(),
+ RD->getASTContext());
+
+ for (BoundNodes &Match : Matches) {
+ auto *VD = Match.getNodeAs<VarDecl>(DeclBind);
+ const VarRegion *ToInvalidate =
+ RM.getRegionManager().getVarRegion(VD, LCtx);
+ AddToWorkList(ToInvalidate);
+ }
+ }
+ }
+ }
+
+ // BlockDataRegion? If so, invalidate captured variables that are passed
+ // by reference.
+ if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(baseR)) {
+ for (BlockDataRegion::referenced_vars_iterator
+ BI = BR->referenced_vars_begin(), BE = BR->referenced_vars_end() ;
+ BI != BE; ++BI) {
+ const VarRegion *VR = BI.getCapturedRegion();
+ const VarDecl *VD = VR->getDecl();
+ if (VD->hasAttr<BlocksAttr>() || !VD->hasLocalStorage()) {
+ AddToWorkList(VR);
+ }
+ else if (Loc::isLocType(VR->getValueType())) {
+ // Map the current bindings to a Store to retrieve the value
+ // of the binding. If that binding itself is a region, we should
+ // invalidate that region. This is because a block may capture
+ // a pointer value, but the thing pointed by that pointer may
+ // get invalidated.
+ SVal V = RM.getBinding(B, loc::MemRegionVal(VR));
+ if (Optional<Loc> L = V.getAs<Loc>()) {
+ if (const MemRegion *LR = L->getAsRegion())
+ AddToWorkList(LR);
+ }
+ }
+ }
+ return;
+ }
+
+ // Symbolic region?
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR))
+ IS.insert(SR->getSymbol());
+
+ // Nothing else should be done in the case when we preserve regions context.
+ if (PreserveRegionsContents)
+ return;
+
+ // Otherwise, we have a normal data region. Record that we touched the region.
+ if (Regions)
+ Regions->push_back(baseR);
+
+ if (isa<AllocaRegion>(baseR) || isa<SymbolicRegion>(baseR)) {
+ // Invalidate the region by setting its default value to
+ // conjured symbol. The type of the symbol is irrelevant.
+ DefinedOrUnknownSVal V =
+ svalBuilder.conjureSymbolVal(baseR, Ex, LCtx, Ctx.IntTy, Count);
+ B = B.addBinding(baseR, BindingKey::Default, V);
+ return;
+ }
+
+ if (!baseR->isBoundable())
+ return;
+
+ const TypedValueRegion *TR = cast<TypedValueRegion>(baseR);
+ QualType T = TR->getValueType();
+
+ if (isInitiallyIncludedGlobalRegion(baseR)) {
+ // If the region is a global and we are invalidating all globals,
+ // erasing the entry is good enough. This causes all globals to be lazily
+ // symbolicated from the same base symbol.
+ return;
+ }
+
+ if (T->isRecordType()) {
+ // Invalidate the region by setting its default value to
+ // conjured symbol. The type of the symbol is irrelevant.
+ DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx,
+ Ctx.IntTy, Count);
+ B = B.addBinding(baseR, BindingKey::Default, V);
+ return;
+ }
+
+ if (const ArrayType *AT = Ctx.getAsArrayType(T)) {
+ bool doNotInvalidateSuperRegion = ITraits.hasTrait(
+ baseR,
+ RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
+
+ if (doNotInvalidateSuperRegion) {
+ // We are not doing blank invalidation of the whole array region so we
+ // have to manually invalidate each elements.
+ Optional<uint64_t> NumElements;
+
+ // Compute lower and upper offsets for region within array.
+ if (const ConstantArrayType *CAT = dyn_cast<ConstantArrayType>(AT))
+ NumElements = CAT->getSize().getZExtValue();
+ if (!NumElements) // We are not dealing with a constant size array
+ goto conjure_default;
+ QualType ElementTy = AT->getElementType();
+ uint64_t ElemSize = Ctx.getTypeSize(ElementTy);
+ const RegionOffset &RO = baseR->getAsOffset();
+ const MemRegion *SuperR = baseR->getBaseRegion();
+ if (RO.hasSymbolicOffset()) {
+ // If base region has a symbolic offset,
+ // we revert to invalidating the super region.
+ if (SuperR)
+ AddToWorkList(SuperR);
+ goto conjure_default;
+ }
+
+ uint64_t LowerOffset = RO.getOffset();
+ uint64_t UpperOffset = LowerOffset + *NumElements * ElemSize;
+ bool UpperOverflow = UpperOffset < LowerOffset;
+
+ // Invalidate regions which are within array boundaries,
+ // or have a symbolic offset.
+ if (!SuperR)
+ goto conjure_default;
+
+ const ClusterBindings *C = B.lookup(SuperR);
+ if (!C)
+ goto conjure_default;
+
+ for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E;
+ ++I) {
+ const BindingKey &BK = I.getKey();
+ Optional<uint64_t> ROffset =
+ BK.hasSymbolicOffset() ? Optional<uint64_t>() : BK.getOffset();
+
+ // Check offset is not symbolic and within array's boundaries.
+ // Handles arrays of 0 elements and of 0-sized elements as well.
+ if (!ROffset ||
+ ((*ROffset >= LowerOffset && *ROffset < UpperOffset) ||
+ (UpperOverflow &&
+ (*ROffset >= LowerOffset || *ROffset < UpperOffset)) ||
+ (LowerOffset == UpperOffset && *ROffset == LowerOffset))) {
+ B = B.removeBinding(I.getKey());
+ // Bound symbolic regions need to be invalidated for dead symbol
+ // detection.
+ SVal V = I.getData();
+ const MemRegion *R = V.getAsRegion();
+ if (R && isa<SymbolicRegion>(R))
+ VisitBinding(V);
+ }
+ }
+ }
+ conjure_default:
+ // Set the default value of the array to conjured symbol.
+ DefinedOrUnknownSVal V =
+ svalBuilder.conjureSymbolVal(baseR, Ex, LCtx,
+ AT->getElementType(), Count);
+ B = B.addBinding(baseR, BindingKey::Default, V);
+ return;
+ }
+
+ DefinedOrUnknownSVal V = svalBuilder.conjureSymbolVal(baseR, Ex, LCtx,
+ T,Count);
+ assert(SymbolManager::canSymbolicate(T) || V.isUnknown());
+ B = B.addBinding(baseR, BindingKey::Direct, V);
+}
+
+bool InvalidateRegionsWorker::isInitiallyIncludedGlobalRegion(
+ const MemRegion *R) {
+ switch (GlobalsFilter) {
+ case GFK_None:
+ return false;
+ case GFK_SystemOnly:
+ return isa<GlobalSystemSpaceRegion>(R->getMemorySpace());
+ case GFK_All:
+ return isa<NonStaticGlobalSpaceRegion>(R->getMemorySpace());
+ }
+
+ llvm_unreachable("unknown globals filter");
+}
+
+bool InvalidateRegionsWorker::includeEntireMemorySpace(const MemRegion *Base) {
+ if (isInitiallyIncludedGlobalRegion(Base))
+ return true;
+
+ const MemSpaceRegion *MemSpace = Base->getMemorySpace();
+ return ITraits.hasTrait(MemSpace,
+ RegionAndSymbolInvalidationTraits::TK_EntireMemSpace);
+}
+
+RegionBindingsRef
+RegionStoreManager::invalidateGlobalRegion(MemRegion::Kind K,
+ const Expr *Ex,
+ unsigned Count,
+ const LocationContext *LCtx,
+ RegionBindingsRef B,
+ InvalidatedRegions *Invalidated) {
+ // Bind the globals memory space to a new symbol that we will use to derive
+ // the bindings for all globals.
+ const GlobalsSpaceRegion *GS = MRMgr.getGlobalsRegion(K);
+ SVal V = svalBuilder.conjureSymbolVal(/* SymbolTag = */ (const void*) GS, Ex, LCtx,
+ /* type does not matter */ Ctx.IntTy,
+ Count);
+
+ B = B.removeBinding(GS)
+ .addBinding(BindingKey::Make(GS, BindingKey::Default), V);
+
+ // Even if there are no bindings in the global scope, we still need to
+ // record that we touched it.
+ if (Invalidated)
+ Invalidated->push_back(GS);
+
+ return B;
+}
+
+void RegionStoreManager::populateWorkList(InvalidateRegionsWorker &W,
+ ArrayRef<SVal> Values,
+ InvalidatedRegions *TopLevelRegions) {
+ for (ArrayRef<SVal>::iterator I = Values.begin(),
+ E = Values.end(); I != E; ++I) {
+ SVal V = *I;
+ if (Optional<nonloc::LazyCompoundVal> LCS =
+ V.getAs<nonloc::LazyCompoundVal>()) {
+
+ const SValListTy &Vals = getInterestingValues(*LCS);
+
+ for (SValListTy::const_iterator I = Vals.begin(),
+ E = Vals.end(); I != E; ++I) {
+ // Note: the last argument is false here because these are
+ // non-top-level regions.
+ if (const MemRegion *R = (*I).getAsRegion())
+ W.AddToWorkList(R);
+ }
+ continue;
+ }
+
+ if (const MemRegion *R = V.getAsRegion()) {
+ if (TopLevelRegions)
+ TopLevelRegions->push_back(R);
+ W.AddToWorkList(R);
+ continue;
+ }
+ }
+}
+
+StoreRef
+RegionStoreManager::invalidateRegions(Store store,
+ ArrayRef<SVal> Values,
+ const Expr *Ex, unsigned Count,
+ const LocationContext *LCtx,
+ const CallEvent *Call,
+ InvalidatedSymbols &IS,
+ RegionAndSymbolInvalidationTraits &ITraits,
+ InvalidatedRegions *TopLevelRegions,
+ InvalidatedRegions *Invalidated) {
+ GlobalsFilterKind GlobalsFilter;
+ if (Call) {
+ if (Call->isInSystemHeader())
+ GlobalsFilter = GFK_SystemOnly;
+ else
+ GlobalsFilter = GFK_All;
+ } else {
+ GlobalsFilter = GFK_None;
+ }
+
+ RegionBindingsRef B = getRegionBindings(store);
+ InvalidateRegionsWorker W(*this, StateMgr, B, Ex, Count, LCtx, IS, ITraits,
+ Invalidated, GlobalsFilter);
+
+ // Scan the bindings and generate the clusters.
+ W.GenerateClusters();
+
+ // Add the regions to the worklist.
+ populateWorkList(W, Values, TopLevelRegions);
+
+ W.RunWorkList();
+
+ // Return the new bindings.
+ B = W.getRegionBindings();
+
+ // For calls, determine which global regions should be invalidated and
+ // invalidate them. (Note that function-static and immutable globals are never
+ // invalidated by this.)
+ // TODO: This could possibly be more precise with modules.
+ switch (GlobalsFilter) {
+ case GFK_All:
+ B = invalidateGlobalRegion(MemRegion::GlobalInternalSpaceRegionKind,
+ Ex, Count, LCtx, B, Invalidated);
+ LLVM_FALLTHROUGH;
+ case GFK_SystemOnly:
+ B = invalidateGlobalRegion(MemRegion::GlobalSystemSpaceRegionKind,
+ Ex, Count, LCtx, B, Invalidated);
+ LLVM_FALLTHROUGH;
+ case GFK_None:
+ break;
+ }
+
+ return StoreRef(B.asStore(), *this);
+}
+
+//===----------------------------------------------------------------------===//
+// Extents for regions.
+//===----------------------------------------------------------------------===//
+
+DefinedOrUnknownSVal
+RegionStoreManager::getSizeInElements(ProgramStateRef state,
+ const MemRegion *R,
+ QualType EleTy) {
+ SVal Size = cast<SubRegion>(R)->getExtent(svalBuilder);
+ const llvm::APSInt *SizeInt = svalBuilder.getKnownValue(state, Size);
+ if (!SizeInt)
+ return UnknownVal();
+
+ CharUnits RegionSize = CharUnits::fromQuantity(SizeInt->getSExtValue());
+
+ if (Ctx.getAsVariableArrayType(EleTy)) {
+ // FIXME: We need to track extra state to properly record the size
+ // of VLAs. Returning UnknownVal here, however, is a stop-gap so that
+ // we don't have a divide-by-zero below.
+ return UnknownVal();
+ }
+
+ CharUnits EleSize = Ctx.getTypeSizeInChars(EleTy);
+
+ // If a variable is reinterpreted as a type that doesn't fit into a larger
+ // type evenly, round it down.
+ // This is a signed value, since it's used in arithmetic with signed indices.
+ return svalBuilder.makeIntVal(RegionSize / EleSize,
+ svalBuilder.getArrayIndexType());
+}
+
+//===----------------------------------------------------------------------===//
+// Location and region casting.
+//===----------------------------------------------------------------------===//
+
+/// ArrayToPointer - Emulates the "decay" of an array to a pointer
+/// type. 'Array' represents the lvalue of the array being decayed
+/// to a pointer, and the returned SVal represents the decayed
+/// version of that lvalue (i.e., a pointer to the first element of
+/// the array). This is called by ExprEngine when evaluating casts
+/// from arrays to pointers.
+SVal RegionStoreManager::ArrayToPointer(Loc Array, QualType T) {
+ if (Array.getAs<loc::ConcreteInt>())
+ return Array;
+
+ if (!Array.getAs<loc::MemRegionVal>())
+ return UnknownVal();
+
+ const SubRegion *R =
+ cast<SubRegion>(Array.castAs<loc::MemRegionVal>().getRegion());
+ NonLoc ZeroIdx = svalBuilder.makeZeroArrayIndex();
+ return loc::MemRegionVal(MRMgr.getElementRegion(T, ZeroIdx, R, Ctx));
+}
+
+//===----------------------------------------------------------------------===//
+// Loading values from regions.
+//===----------------------------------------------------------------------===//
+
+SVal RegionStoreManager::getBinding(RegionBindingsConstRef B, Loc L, QualType T) {
+ assert(!L.getAs<UnknownVal>() && "location unknown");
+ assert(!L.getAs<UndefinedVal>() && "location undefined");
+
+ // For access to concrete addresses, return UnknownVal. Checks
+ // for null dereferences (and similar errors) are done by checkers, not
+ // the Store.
+ // FIXME: We can consider lazily symbolicating such memory, but we really
+ // should defer this when we can reason easily about symbolicating arrays
+ // of bytes.
+ if (L.getAs<loc::ConcreteInt>()) {
+ return UnknownVal();
+ }
+ if (!L.getAs<loc::MemRegionVal>()) {
+ return UnknownVal();
+ }
+
+ const MemRegion *MR = L.castAs<loc::MemRegionVal>().getRegion();
+
+ if (isa<BlockDataRegion>(MR)) {
+ return UnknownVal();
+ }
+
+ if (!isa<TypedValueRegion>(MR)) {
+ if (T.isNull()) {
+ if (const TypedRegion *TR = dyn_cast<TypedRegion>(MR))
+ T = TR->getLocationType()->getPointeeType();
+ else if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(MR))
+ T = SR->getSymbol()->getType()->getPointeeType();
+ }
+ assert(!T.isNull() && "Unable to auto-detect binding type!");
+ assert(!T->isVoidType() && "Attempting to dereference a void pointer!");
+ MR = GetElementZeroRegion(cast<SubRegion>(MR), T);
+ } else {
+ T = cast<TypedValueRegion>(MR)->getValueType();
+ }
+
+ // FIXME: Perhaps this method should just take a 'const MemRegion*' argument
+ // instead of 'Loc', and have the other Loc cases handled at a higher level.
+ const TypedValueRegion *R = cast<TypedValueRegion>(MR);
+ QualType RTy = R->getValueType();
+
+ // FIXME: we do not yet model the parts of a complex type, so treat the
+ // whole thing as "unknown".
+ if (RTy->isAnyComplexType())
+ return UnknownVal();
+
+ // FIXME: We should eventually handle funny addressing. e.g.:
+ //
+ // int x = ...;
+ // int *p = &x;
+ // char *q = (char*) p;
+ // char c = *q; // returns the first byte of 'x'.
+ //
+ // Such funny addressing will occur due to layering of regions.
+ if (RTy->isStructureOrClassType())
+ return getBindingForStruct(B, R);
+
+ // FIXME: Handle unions.
+ if (RTy->isUnionType())
+ return createLazyBinding(B, R);
+
+ if (RTy->isArrayType()) {
+ if (RTy->isConstantArrayType())
+ return getBindingForArray(B, R);
+ else
+ return UnknownVal();
+ }
+
+ // FIXME: handle Vector types.
+ if (RTy->isVectorType())
+ return UnknownVal();
+
+ if (const FieldRegion* FR = dyn_cast<FieldRegion>(R))
+ return CastRetrievedVal(getBindingForField(B, FR), FR, T);
+
+ if (const ElementRegion* ER = dyn_cast<ElementRegion>(R)) {
+ // FIXME: Here we actually perform an implicit conversion from the loaded
+ // value to the element type. Eventually we want to compose these values
+ // more intelligently. For example, an 'element' can encompass multiple
+ // bound regions (e.g., several bound bytes), or could be a subset of
+ // a larger value.
+ return CastRetrievedVal(getBindingForElement(B, ER), ER, T);
+ }
+
+ if (const ObjCIvarRegion *IVR = dyn_cast<ObjCIvarRegion>(R)) {
+ // FIXME: Here we actually perform an implicit conversion from the loaded
+ // value to the ivar type. What we should model is stores to ivars
+ // that blow past the extent of the ivar. If the address of the ivar is
+ // reinterpretted, it is possible we stored a different value that could
+ // fit within the ivar. Either we need to cast these when storing them
+ // or reinterpret them lazily (as we do here).
+ return CastRetrievedVal(getBindingForObjCIvar(B, IVR), IVR, T);
+ }
+
+ if (const VarRegion *VR = dyn_cast<VarRegion>(R)) {
+ // FIXME: Here we actually perform an implicit conversion from the loaded
+ // value to the variable type. What we should model is stores to variables
+ // that blow past the extent of the variable. If the address of the
+ // variable is reinterpretted, it is possible we stored a different value
+ // that could fit within the variable. Either we need to cast these when
+ // storing them or reinterpret them lazily (as we do here).
+ return CastRetrievedVal(getBindingForVar(B, VR), VR, T);
+ }
+
+ const SVal *V = B.lookup(R, BindingKey::Direct);
+
+ // Check if the region has a binding.
+ if (V)
+ return *V;
+
+ // The location does not have a bound value. This means that it has
+ // the value it had upon its creation and/or entry to the analyzed
+ // function/method. These are either symbolic values or 'undefined'.
+ if (R->hasStackNonParametersStorage()) {
+ // All stack variables are considered to have undefined values
+ // upon creation. All heap allocated blocks are considered to
+ // have undefined values as well unless they are explicitly bound
+ // to specific values.
+ return UndefinedVal();
+ }
+
+ // All other values are symbolic.
+ return svalBuilder.getRegionValueSymbolVal(R);
+}
+
+static QualType getUnderlyingType(const SubRegion *R) {
+ QualType RegionTy;
+ if (const TypedValueRegion *TVR = dyn_cast<TypedValueRegion>(R))
+ RegionTy = TVR->getValueType();
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R))
+ RegionTy = SR->getSymbol()->getType();
+
+ return RegionTy;
+}
+
+/// Checks to see if store \p B has a lazy binding for region \p R.
+///
+/// If \p AllowSubregionBindings is \c false, a lazy binding will be rejected
+/// if there are additional bindings within \p R.
+///
+/// Note that unlike RegionStoreManager::findLazyBinding, this will not search
+/// for lazy bindings for super-regions of \p R.
+static Optional<nonloc::LazyCompoundVal>
+getExistingLazyBinding(SValBuilder &SVB, RegionBindingsConstRef B,
+ const SubRegion *R, bool AllowSubregionBindings) {
+ Optional<SVal> V = B.getDefaultBinding(R);
+ if (!V)
+ return None;
+
+ Optional<nonloc::LazyCompoundVal> LCV = V->getAs<nonloc::LazyCompoundVal>();
+ if (!LCV)
+ return None;
+
+ // If the LCV is for a subregion, the types might not match, and we shouldn't
+ // reuse the binding.
+ QualType RegionTy = getUnderlyingType(R);
+ if (!RegionTy.isNull() &&
+ !RegionTy->isVoidPointerType()) {
+ QualType SourceRegionTy = LCV->getRegion()->getValueType();
+ if (!SVB.getContext().hasSameUnqualifiedType(RegionTy, SourceRegionTy))
+ return None;
+ }
+
+ if (!AllowSubregionBindings) {
+ // If there are any other bindings within this region, we shouldn't reuse
+ // the top-level binding.
+ SmallVector<BindingPair, 16> Bindings;
+ collectSubRegionBindings(Bindings, SVB, *B.lookup(R->getBaseRegion()), R,
+ /*IncludeAllDefaultBindings=*/true);
+ if (Bindings.size() > 1)
+ return None;
+ }
+
+ return *LCV;
+}
+
+
+std::pair<Store, const SubRegion *>
+RegionStoreManager::findLazyBinding(RegionBindingsConstRef B,
+ const SubRegion *R,
+ const SubRegion *originalRegion) {
+ if (originalRegion != R) {
+ if (Optional<nonloc::LazyCompoundVal> V =
+ getExistingLazyBinding(svalBuilder, B, R, true))
+ return std::make_pair(V->getStore(), V->getRegion());
+ }
+
+ typedef std::pair<Store, const SubRegion *> StoreRegionPair;
+ StoreRegionPair Result = StoreRegionPair();
+
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(R)) {
+ Result = findLazyBinding(B, cast<SubRegion>(ER->getSuperRegion()),
+ originalRegion);
+
+ if (Result.second)
+ Result.second = MRMgr.getElementRegionWithSuper(ER, Result.second);
+
+ } else if (const FieldRegion *FR = dyn_cast<FieldRegion>(R)) {
+ Result = findLazyBinding(B, cast<SubRegion>(FR->getSuperRegion()),
+ originalRegion);
+
+ if (Result.second)
+ Result.second = MRMgr.getFieldRegionWithSuper(FR, Result.second);
+
+ } else if (const CXXBaseObjectRegion *BaseReg =
+ dyn_cast<CXXBaseObjectRegion>(R)) {
+ // C++ base object region is another kind of region that we should blast
+ // through to look for lazy compound value. It is like a field region.
+ Result = findLazyBinding(B, cast<SubRegion>(BaseReg->getSuperRegion()),
+ originalRegion);
+
+ if (Result.second)
+ Result.second = MRMgr.getCXXBaseObjectRegionWithSuper(BaseReg,
+ Result.second);
+ }
+
+ return Result;
+}
+
+SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
+ const ElementRegion* R) {
+ // We do not currently model bindings of the CompoundLiteralregion.
+ if (isa<CompoundLiteralRegion>(R->getBaseRegion()))
+ return UnknownVal();
+
+ // Check if the region has a binding.
+ if (const Optional<SVal> &V = B.getDirectBinding(R))
+ return *V;
+
+ const MemRegion* superR = R->getSuperRegion();
+
+ // Check if the region is an element region of a string literal.
+ if (const StringRegion *StrR = dyn_cast<StringRegion>(superR)) {
+ // FIXME: Handle loads from strings where the literal is treated as
+ // an integer, e.g., *((unsigned int*)"hello")
+ QualType T = Ctx.getAsArrayType(StrR->getValueType())->getElementType();
+ if (!Ctx.hasSameUnqualifiedType(T, R->getElementType()))
+ return UnknownVal();
+
+ const StringLiteral *Str = StrR->getStringLiteral();
+ SVal Idx = R->getIndex();
+ if (Optional<nonloc::ConcreteInt> CI = Idx.getAs<nonloc::ConcreteInt>()) {
+ int64_t i = CI->getValue().getSExtValue();
+ // Abort on string underrun. This can be possible by arbitrary
+ // clients of getBindingForElement().
+ if (i < 0)
+ return UndefinedVal();
+ int64_t length = Str->getLength();
+ // Technically, only i == length is guaranteed to be null.
+ // However, such overflows should be caught before reaching this point;
+ // the only time such an access would be made is if a string literal was
+ // used to initialize a larger array.
+ char c = (i >= length) ? '\0' : Str->getCodeUnit(i);
+ return svalBuilder.makeIntVal(c, T);
+ }
+ } else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) {
+ // Check if the containing array is const and has an initialized value.
+ const VarDecl *VD = VR->getDecl();
+ // Either the array or the array element has to be const.
+ if (VD->getType().isConstQualified() || R->getElementType().isConstQualified()) {
+ if (const Expr *Init = VD->getInit()) {
+ if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
+ // The array index has to be known.
+ if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
+ int64_t i = CI->getValue().getSExtValue();
+ // If it is known that the index is out of bounds, we can return
+ // an undefined value.
+ if (i < 0)
+ return UndefinedVal();
+
+ if (auto CAT = Ctx.getAsConstantArrayType(VD->getType()))
+ if (CAT->getSize().sle(i))
+ return UndefinedVal();
+
+ // If there is a list, but no init, it must be zero.
+ if (i >= InitList->getNumInits())
+ return svalBuilder.makeZeroVal(R->getElementType());
+
+ if (const Expr *ElemInit = InitList->getInit(i))
+ if (Optional<SVal> V = svalBuilder.getConstantVal(ElemInit))
+ return *V;
+ }
+ }
+ }
+ }
+ }
+
+ // Check for loads from a code text region. For such loads, just give up.
+ if (isa<CodeTextRegion>(superR))
+ return UnknownVal();
+
+ // Handle the case where we are indexing into a larger scalar object.
+ // For example, this handles:
+ // int x = ...
+ // char *y = &x;
+ // return *y;
+ // FIXME: This is a hack, and doesn't do anything really intelligent yet.
+ const RegionRawOffset &O = R->getAsArrayOffset();
+
+ // If we cannot reason about the offset, return an unknown value.
+ if (!O.getRegion())
+ return UnknownVal();
+
+ if (const TypedValueRegion *baseR =
+ dyn_cast_or_null<TypedValueRegion>(O.getRegion())) {
+ QualType baseT = baseR->getValueType();
+ if (baseT->isScalarType()) {
+ QualType elemT = R->getElementType();
+ if (elemT->isScalarType()) {
+ if (Ctx.getTypeSizeInChars(baseT) >= Ctx.getTypeSizeInChars(elemT)) {
+ if (const Optional<SVal> &V = B.getDirectBinding(superR)) {
+ if (SymbolRef parentSym = V->getAsSymbol())
+ return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R);
+
+ if (V->isUnknownOrUndef())
+ return *V;
+ // Other cases: give up. We are indexing into a larger object
+ // that has some value, but we don't know how to handle that yet.
+ return UnknownVal();
+ }
+ }
+ }
+ }
+ }
+ return getBindingForFieldOrElementCommon(B, R, R->getElementType());
+}
+
+SVal RegionStoreManager::getBindingForField(RegionBindingsConstRef B,
+ const FieldRegion* R) {
+
+ // Check if the region has a binding.
+ if (const Optional<SVal> &V = B.getDirectBinding(R))
+ return *V;
+
+ // Is the field declared constant and has an in-class initializer?
+ const FieldDecl *FD = R->getDecl();
+ QualType Ty = FD->getType();
+ if (Ty.isConstQualified())
+ if (const Expr *Init = FD->getInClassInitializer())
+ if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
+ return *V;
+
+ // If the containing record was initialized, try to get its constant value.
+ const MemRegion* superR = R->getSuperRegion();
+ if (const auto *VR = dyn_cast<VarRegion>(superR)) {
+ const VarDecl *VD = VR->getDecl();
+ QualType RecordVarTy = VD->getType();
+ unsigned Index = FD->getFieldIndex();
+ // Either the record variable or the field has to be const qualified.
+ if (RecordVarTy.isConstQualified() || Ty.isConstQualified())
+ if (const Expr *Init = VD->getInit())
+ if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
+ if (Index < InitList->getNumInits()) {
+ if (const Expr *FieldInit = InitList->getInit(Index))
+ if (Optional<SVal> V = svalBuilder.getConstantVal(FieldInit))
+ return *V;
+ } else {
+ return svalBuilder.makeZeroVal(Ty);
+ }
+ }
+ }
+
+ return getBindingForFieldOrElementCommon(B, R, Ty);
+}
+
+Optional<SVal>
+RegionStoreManager::getBindingForDerivedDefaultValue(RegionBindingsConstRef B,
+ const MemRegion *superR,
+ const TypedValueRegion *R,
+ QualType Ty) {
+
+ if (const Optional<SVal> &D = B.getDefaultBinding(superR)) {
+ const SVal &val = D.getValue();
+ if (SymbolRef parentSym = val.getAsSymbol())
+ return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R);
+
+ if (val.isZeroConstant())
+ return svalBuilder.makeZeroVal(Ty);
+
+ if (val.isUnknownOrUndef())
+ return val;
+
+ // Lazy bindings are usually handled through getExistingLazyBinding().
+ // We should unify these two code paths at some point.
+ if (val.getAs<nonloc::LazyCompoundVal>() ||
+ val.getAs<nonloc::CompoundVal>())
+ return val;
+
+ llvm_unreachable("Unknown default value");
+ }
+
+ return None;
+}
+
+SVal RegionStoreManager::getLazyBinding(const SubRegion *LazyBindingRegion,
+ RegionBindingsRef LazyBinding) {
+ SVal Result;
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(LazyBindingRegion))
+ Result = getBindingForElement(LazyBinding, ER);
+ else
+ Result = getBindingForField(LazyBinding,
+ cast<FieldRegion>(LazyBindingRegion));
+
+ // FIXME: This is a hack to deal with RegionStore's inability to distinguish a
+ // default value for /part/ of an aggregate from a default value for the
+ // /entire/ aggregate. The most common case of this is when struct Outer
+ // has as its first member a struct Inner, which is copied in from a stack
+ // variable. In this case, even if the Outer's default value is symbolic, 0,
+ // or unknown, it gets overridden by the Inner's default value of undefined.
+ //
+ // This is a general problem -- if the Inner is zero-initialized, the Outer
+ // will now look zero-initialized. The proper way to solve this is with a
+ // new version of RegionStore that tracks the extent of a binding as well
+ // as the offset.
+ //
+ // This hack only takes care of the undefined case because that can very
+ // quickly result in a warning.
+ if (Result.isUndef())
+ Result = UnknownVal();
+
+ return Result;
+}
+
+SVal
+RegionStoreManager::getBindingForFieldOrElementCommon(RegionBindingsConstRef B,
+ const TypedValueRegion *R,
+ QualType Ty) {
+
+ // At this point we have already checked in either getBindingForElement or
+ // getBindingForField if 'R' has a direct binding.
+
+ // Lazy binding?
+ Store lazyBindingStore = nullptr;
+ const SubRegion *lazyBindingRegion = nullptr;
+ std::tie(lazyBindingStore, lazyBindingRegion) = findLazyBinding(B, R, R);
+ if (lazyBindingRegion)
+ return getLazyBinding(lazyBindingRegion,
+ getRegionBindings(lazyBindingStore));
+
+ // Record whether or not we see a symbolic index. That can completely
+ // be out of scope of our lookup.
+ bool hasSymbolicIndex = false;
+
+ // FIXME: This is a hack to deal with RegionStore's inability to distinguish a
+ // default value for /part/ of an aggregate from a default value for the
+ // /entire/ aggregate. The most common case of this is when struct Outer
+ // has as its first member a struct Inner, which is copied in from a stack
+ // variable. In this case, even if the Outer's default value is symbolic, 0,
+ // or unknown, it gets overridden by the Inner's default value of undefined.
+ //
+ // This is a general problem -- if the Inner is zero-initialized, the Outer
+ // will now look zero-initialized. The proper way to solve this is with a
+ // new version of RegionStore that tracks the extent of a binding as well
+ // as the offset.
+ //
+ // This hack only takes care of the undefined case because that can very
+ // quickly result in a warning.
+ bool hasPartialLazyBinding = false;
+
+ const SubRegion *SR = R;
+ while (SR) {
+ const MemRegion *Base = SR->getSuperRegion();
+ if (Optional<SVal> D = getBindingForDerivedDefaultValue(B, Base, R, Ty)) {
+ if (D->getAs<nonloc::LazyCompoundVal>()) {
+ hasPartialLazyBinding = true;
+ break;
+ }
+
+ return *D;
+ }
+
+ if (const ElementRegion *ER = dyn_cast<ElementRegion>(Base)) {
+ NonLoc index = ER->getIndex();
+ if (!index.isConstant())
+ hasSymbolicIndex = true;
+ }
+
+ // If our super region is a field or element itself, walk up the region
+ // hierarchy to see if there is a default value installed in an ancestor.
+ SR = dyn_cast<SubRegion>(Base);
+ }
+
+ if (R->hasStackNonParametersStorage()) {
+ if (isa<ElementRegion>(R)) {
+ // Currently we don't reason specially about Clang-style vectors. Check
+ // if superR is a vector and if so return Unknown.
+ if (const TypedValueRegion *typedSuperR =
+ dyn_cast<TypedValueRegion>(R->getSuperRegion())) {
+ if (typedSuperR->getValueType()->isVectorType())
+ return UnknownVal();
+ }
+ }
+
+ // FIXME: We also need to take ElementRegions with symbolic indexes into
+ // account. This case handles both directly accessing an ElementRegion
+ // with a symbolic offset, but also fields within an element with
+ // a symbolic offset.
+ if (hasSymbolicIndex)
+ return UnknownVal();
+
+ if (!hasPartialLazyBinding)
+ return UndefinedVal();
+ }
+
+ // All other values are symbolic.
+ return svalBuilder.getRegionValueSymbolVal(R);
+}
+
+SVal RegionStoreManager::getBindingForObjCIvar(RegionBindingsConstRef B,
+ const ObjCIvarRegion* R) {
+ // Check if the region has a binding.
+ if (const Optional<SVal> &V = B.getDirectBinding(R))
+ return *V;
+
+ const MemRegion *superR = R->getSuperRegion();
+
+ // Check if the super region has a default binding.
+ if (const Optional<SVal> &V = B.getDefaultBinding(superR)) {
+ if (SymbolRef parentSym = V->getAsSymbol())
+ return svalBuilder.getDerivedRegionValueSymbolVal(parentSym, R);
+
+ // Other cases: give up.
+ return UnknownVal();
+ }
+
+ return getBindingForLazySymbol(R);
+}
+
+SVal RegionStoreManager::getBindingForVar(RegionBindingsConstRef B,
+ const VarRegion *R) {
+
+ // Check if the region has a binding.
+ if (const Optional<SVal> &V = B.getDirectBinding(R))
+ return *V;
+
+ // Lazily derive a value for the VarRegion.
+ const VarDecl *VD = R->getDecl();
+ const MemSpaceRegion *MS = R->getMemorySpace();
+
+ // Arguments are always symbolic.
+ if (isa<StackArgumentsSpaceRegion>(MS))
+ return svalBuilder.getRegionValueSymbolVal(R);
+
+ // Is 'VD' declared constant? If so, retrieve the constant value.
+ if (VD->getType().isConstQualified()) {
+ if (const Expr *Init = VD->getInit()) {
+ if (Optional<SVal> V = svalBuilder.getConstantVal(Init))
+ return *V;
+
+ // If the variable is const qualified and has an initializer but
+ // we couldn't evaluate initializer to a value, treat the value as
+ // unknown.
+ return UnknownVal();
+ }
+ }
+
+ // This must come after the check for constants because closure-captured
+ // constant variables may appear in UnknownSpaceRegion.
+ if (isa<UnknownSpaceRegion>(MS))
+ return svalBuilder.getRegionValueSymbolVal(R);
+
+ if (isa<GlobalsSpaceRegion>(MS)) {
+ QualType T = VD->getType();
+
+ // Function-scoped static variables are default-initialized to 0; if they
+ // have an initializer, it would have been processed by now.
+ // FIXME: This is only true when we're starting analysis from main().
+ // We're losing a lot of coverage here.
+ if (isa<StaticGlobalSpaceRegion>(MS))
+ return svalBuilder.makeZeroVal(T);
+
+ if (Optional<SVal> V = getBindingForDerivedDefaultValue(B, MS, R, T)) {
+ assert(!V->getAs<nonloc::LazyCompoundVal>());
+ return V.getValue();
+ }
+
+ return svalBuilder.getRegionValueSymbolVal(R);
+ }
+
+ return UndefinedVal();
+}
+
+SVal RegionStoreManager::getBindingForLazySymbol(const TypedValueRegion *R) {
+ // All other values are symbolic.
+ return svalBuilder.getRegionValueSymbolVal(R);
+}
+
+const RegionStoreManager::SValListTy &
+RegionStoreManager::getInterestingValues(nonloc::LazyCompoundVal LCV) {
+ // First, check the cache.
+ LazyBindingsMapTy::iterator I = LazyBindingsMap.find(LCV.getCVData());
+ if (I != LazyBindingsMap.end())
+ return I->second;
+
+ // If we don't have a list of values cached, start constructing it.
+ SValListTy List;
+
+ const SubRegion *LazyR = LCV.getRegion();
+ RegionBindingsRef B = getRegionBindings(LCV.getStore());
+
+ // If this region had /no/ bindings at the time, there are no interesting
+ // values to return.
+ const ClusterBindings *Cluster = B.lookup(LazyR->getBaseRegion());
+ if (!Cluster)
+ return (LazyBindingsMap[LCV.getCVData()] = std::move(List));
+
+ SmallVector<BindingPair, 32> Bindings;
+ collectSubRegionBindings(Bindings, svalBuilder, *Cluster, LazyR,
+ /*IncludeAllDefaultBindings=*/true);
+ for (SmallVectorImpl<BindingPair>::const_iterator I = Bindings.begin(),
+ E = Bindings.end();
+ I != E; ++I) {
+ SVal V = I->second;
+ if (V.isUnknownOrUndef() || V.isConstant())
+ continue;
+
+ if (Optional<nonloc::LazyCompoundVal> InnerLCV =
+ V.getAs<nonloc::LazyCompoundVal>()) {
+ const SValListTy &InnerList = getInterestingValues(*InnerLCV);
+ List.insert(List.end(), InnerList.begin(), InnerList.end());
+ continue;
+ }
+
+ List.push_back(V);
+ }
+
+ return (LazyBindingsMap[LCV.getCVData()] = std::move(List));
+}
+
+NonLoc RegionStoreManager::createLazyBinding(RegionBindingsConstRef B,
+ const TypedValueRegion *R) {
+ if (Optional<nonloc::LazyCompoundVal> V =
+ getExistingLazyBinding(svalBuilder, B, R, false))
+ return *V;
+
+ return svalBuilder.makeLazyCompoundVal(StoreRef(B.asStore(), *this), R);
+}
+
+static bool isRecordEmpty(const RecordDecl *RD) {
+ if (!RD->field_empty())
+ return false;
+ if (const CXXRecordDecl *CRD = dyn_cast<CXXRecordDecl>(RD))
+ return CRD->getNumBases() == 0;
+ return true;
+}
+
+SVal RegionStoreManager::getBindingForStruct(RegionBindingsConstRef B,
+ const TypedValueRegion *R) {
+ const RecordDecl *RD = R->getValueType()->castAs<RecordType>()->getDecl();
+ if (!RD->getDefinition() || isRecordEmpty(RD))
+ return UnknownVal();
+
+ return createLazyBinding(B, R);
+}
+
+SVal RegionStoreManager::getBindingForArray(RegionBindingsConstRef B,
+ const TypedValueRegion *R) {
+ assert(Ctx.getAsConstantArrayType(R->getValueType()) &&
+ "Only constant array types can have compound bindings.");
+
+ return createLazyBinding(B, R);
+}
+
+bool RegionStoreManager::includedInBindings(Store store,
+ const MemRegion *region) const {
+ RegionBindingsRef B = getRegionBindings(store);
+ region = region->getBaseRegion();
+
+ // Quick path: if the base is the head of a cluster, the region is live.
+ if (B.lookup(region))
+ return true;
+
+ // Slow path: if the region is the VALUE of any binding, it is live.
+ for (RegionBindingsRef::iterator RI = B.begin(), RE = B.end(); RI != RE; ++RI) {
+ const ClusterBindings &Cluster = RI.getData();
+ for (ClusterBindings::iterator CI = Cluster.begin(), CE = Cluster.end();
+ CI != CE; ++CI) {
+ const SVal &D = CI.getData();
+ if (const MemRegion *R = D.getAsRegion())
+ if (R->getBaseRegion() == region)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+//===----------------------------------------------------------------------===//
+// Binding values to regions.
+//===----------------------------------------------------------------------===//
+
+StoreRef RegionStoreManager::killBinding(Store ST, Loc L) {
+ if (Optional<loc::MemRegionVal> LV = L.getAs<loc::MemRegionVal>())
+ if (const MemRegion* R = LV->getRegion())
+ return StoreRef(getRegionBindings(ST).removeBinding(R)
+ .asImmutableMap()
+ .getRootWithoutRetain(),
+ *this);
+
+ return StoreRef(ST, *this);
+}
+
+RegionBindingsRef
+RegionStoreManager::bind(RegionBindingsConstRef B, Loc L, SVal V) {
+ if (L.getAs<loc::ConcreteInt>())
+ return B;
+
+ // If we get here, the location should be a region.
+ const MemRegion *R = L.castAs<loc::MemRegionVal>().getRegion();
+
+ // Check if the region is a struct region.
+ if (const TypedValueRegion* TR = dyn_cast<TypedValueRegion>(R)) {
+ QualType Ty = TR->getValueType();
+ if (Ty->isArrayType())
+ return bindArray(B, TR, V);
+ if (Ty->isStructureOrClassType())
+ return bindStruct(B, TR, V);
+ if (Ty->isVectorType())
+ return bindVector(B, TR, V);
+ if (Ty->isUnionType())
+ return bindAggregate(B, TR, V);
+ }
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(R)) {
+ // Binding directly to a symbolic region should be treated as binding
+ // to element 0.
+ QualType T = SR->getSymbol()->getType();
+ if (T->isAnyPointerType() || T->isReferenceType())
+ T = T->getPointeeType();
+
+ R = GetElementZeroRegion(SR, T);
+ }
+
+ assert((!isa<CXXThisRegion>(R) || !B.lookup(R)) &&
+ "'this' pointer is not an l-value and is not assignable");
+
+ // Clear out bindings that may overlap with this binding.
+ RegionBindingsRef NewB = removeSubRegionBindings(B, cast<SubRegion>(R));
+ return NewB.addBinding(BindingKey::Make(R, BindingKey::Direct), V);
+}
+
+RegionBindingsRef
+RegionStoreManager::setImplicitDefaultValue(RegionBindingsConstRef B,
+ const MemRegion *R,
+ QualType T) {
+ SVal V;
+
+ if (Loc::isLocType(T))
+ V = svalBuilder.makeNull();
+ else if (T->isIntegralOrEnumerationType())
+ V = svalBuilder.makeZeroVal(T);
+ else if (T->isStructureOrClassType() || T->isArrayType()) {
+ // Set the default value to a zero constant when it is a structure
+ // or array. The type doesn't really matter.
+ V = svalBuilder.makeZeroVal(Ctx.IntTy);
+ }
+ else {
+ // We can't represent values of this type, but we still need to set a value
+ // to record that the region has been initialized.
+ // If this assertion ever fires, a new case should be added above -- we
+ // should know how to default-initialize any value we can symbolicate.
+ assert(!SymbolManager::canSymbolicate(T) && "This type is representable");
+ V = UnknownVal();
+ }
+
+ return B.addBinding(R, BindingKey::Default, V);
+}
+
+RegionBindingsRef
+RegionStoreManager::bindArray(RegionBindingsConstRef B,
+ const TypedValueRegion* R,
+ SVal Init) {
+
+ const ArrayType *AT =cast<ArrayType>(Ctx.getCanonicalType(R->getValueType()));
+ QualType ElementTy = AT->getElementType();
+ Optional<uint64_t> Size;
+
+ if (const ConstantArrayType* CAT = dyn_cast<ConstantArrayType>(AT))
+ Size = CAT->getSize().getZExtValue();
+
+ // Check if the init expr is a literal. If so, bind the rvalue instead.
+ // FIXME: It's not responsibility of the Store to transform this lvalue
+ // to rvalue. ExprEngine or maybe even CFG should do this before binding.
+ if (Optional<loc::MemRegionVal> MRV = Init.getAs<loc::MemRegionVal>()) {
+ SVal V = getBinding(B.asStore(), *MRV, R->getValueType());
+ return bindAggregate(B, R, V);
+ }
+
+ // Handle lazy compound values.
+ if (Init.getAs<nonloc::LazyCompoundVal>())
+ return bindAggregate(B, R, Init);
+
+ if (Init.isUnknown())
+ return bindAggregate(B, R, UnknownVal());
+
+ // Remaining case: explicit compound values.
+ const nonloc::CompoundVal& CV = Init.castAs<nonloc::CompoundVal>();
+ nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
+ uint64_t i = 0;
+
+ RegionBindingsRef NewB(B);
+
+ for (; Size.hasValue() ? i < Size.getValue() : true ; ++i, ++VI) {
+ // The init list might be shorter than the array length.
+ if (VI == VE)
+ break;
+
+ const NonLoc &Idx = svalBuilder.makeArrayIndex(i);
+ const ElementRegion *ER = MRMgr.getElementRegion(ElementTy, Idx, R, Ctx);
+
+ if (ElementTy->isStructureOrClassType())
+ NewB = bindStruct(NewB, ER, *VI);
+ else if (ElementTy->isArrayType())
+ NewB = bindArray(NewB, ER, *VI);
+ else
+ NewB = bind(NewB, loc::MemRegionVal(ER), *VI);
+ }
+
+ // If the init list is shorter than the array length (or the array has
+ // variable length), set the array default value. Values that are already set
+ // are not overwritten.
+ if (!Size.hasValue() || i < Size.getValue())
+ NewB = setImplicitDefaultValue(NewB, R, ElementTy);
+
+ return NewB;
+}
+
+RegionBindingsRef RegionStoreManager::bindVector(RegionBindingsConstRef B,
+ const TypedValueRegion* R,
+ SVal V) {
+ QualType T = R->getValueType();
+ assert(T->isVectorType());
+ const VectorType *VT = T->getAs<VectorType>(); // Use getAs for typedefs.
+
+ // Handle lazy compound values and symbolic values.
+ if (V.getAs<nonloc::LazyCompoundVal>() || V.getAs<nonloc::SymbolVal>())
+ return bindAggregate(B, R, V);
+
+ // We may get non-CompoundVal accidentally due to imprecise cast logic or
+ // that we are binding symbolic struct value. Kill the field values, and if
+ // the value is symbolic go and bind it as a "default" binding.
+ if (!V.getAs<nonloc::CompoundVal>()) {
+ return bindAggregate(B, R, UnknownVal());
+ }
+
+ QualType ElemType = VT->getElementType();
+ nonloc::CompoundVal CV = V.castAs<nonloc::CompoundVal>();
+ nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
+ unsigned index = 0, numElements = VT->getNumElements();
+ RegionBindingsRef NewB(B);
+
+ for ( ; index != numElements ; ++index) {
+ if (VI == VE)
+ break;
+
+ NonLoc Idx = svalBuilder.makeArrayIndex(index);
+ const ElementRegion *ER = MRMgr.getElementRegion(ElemType, Idx, R, Ctx);
+
+ if (ElemType->isArrayType())
+ NewB = bindArray(NewB, ER, *VI);
+ else if (ElemType->isStructureOrClassType())
+ NewB = bindStruct(NewB, ER, *VI);
+ else
+ NewB = bind(NewB, loc::MemRegionVal(ER), *VI);
+ }
+ return NewB;
+}
+
+Optional<RegionBindingsRef>
+RegionStoreManager::tryBindSmallStruct(RegionBindingsConstRef B,
+ const TypedValueRegion *R,
+ const RecordDecl *RD,
+ nonloc::LazyCompoundVal LCV) {
+ FieldVector Fields;
+
+ if (const CXXRecordDecl *Class = dyn_cast<CXXRecordDecl>(RD))
+ if (Class->getNumBases() != 0 || Class->getNumVBases() != 0)
+ return None;
+
+ for (const auto *FD : RD->fields()) {
+ if (FD->isUnnamedBitfield())
+ continue;
+
+ // If there are too many fields, or if any of the fields are aggregates,
+ // just use the LCV as a default binding.
+ if (Fields.size() == SmallStructLimit)
+ return None;
+
+ QualType Ty = FD->getType();
+ if (!(Ty->isScalarType() || Ty->isReferenceType()))
+ return None;
+
+ Fields.push_back(FD);
+ }
+
+ RegionBindingsRef NewB = B;
+
+ for (FieldVector::iterator I = Fields.begin(), E = Fields.end(); I != E; ++I){
+ const FieldRegion *SourceFR = MRMgr.getFieldRegion(*I, LCV.getRegion());
+ SVal V = getBindingForField(getRegionBindings(LCV.getStore()), SourceFR);
+
+ const FieldRegion *DestFR = MRMgr.getFieldRegion(*I, R);
+ NewB = bind(NewB, loc::MemRegionVal(DestFR), V);
+ }
+
+ return NewB;
+}
+
+RegionBindingsRef RegionStoreManager::bindStruct(RegionBindingsConstRef B,
+ const TypedValueRegion* R,
+ SVal V) {
+ if (!Features.supportsFields())
+ return B;
+
+ QualType T = R->getValueType();
+ assert(T->isStructureOrClassType());
+
+ const RecordType* RT = T->getAs<RecordType>();
+ const RecordDecl *RD = RT->getDecl();
+
+ if (!RD->isCompleteDefinition())
+ return B;
+
+ // Handle lazy compound values and symbolic values.
+ if (Optional<nonloc::LazyCompoundVal> LCV =
+ V.getAs<nonloc::LazyCompoundVal>()) {
+ if (Optional<RegionBindingsRef> NewB = tryBindSmallStruct(B, R, RD, *LCV))
+ return *NewB;
+ return bindAggregate(B, R, V);
+ }
+ if (V.getAs<nonloc::SymbolVal>())
+ return bindAggregate(B, R, V);
+
+ // We may get non-CompoundVal accidentally due to imprecise cast logic or
+ // that we are binding symbolic struct value. Kill the field values, and if
+ // the value is symbolic go and bind it as a "default" binding.
+ if (V.isUnknown() || !V.getAs<nonloc::CompoundVal>())
+ return bindAggregate(B, R, UnknownVal());
+
+ const nonloc::CompoundVal& CV = V.castAs<nonloc::CompoundVal>();
+ nonloc::CompoundVal::iterator VI = CV.begin(), VE = CV.end();
+
+ RecordDecl::field_iterator FI, FE;
+ RegionBindingsRef NewB(B);
+
+ for (FI = RD->field_begin(), FE = RD->field_end(); FI != FE; ++FI) {
+
+ if (VI == VE)
+ break;
+
+ // Skip any unnamed bitfields to stay in sync with the initializers.
+ if (FI->isUnnamedBitfield())
+ continue;
+
+ QualType FTy = FI->getType();
+ const FieldRegion* FR = MRMgr.getFieldRegion(*FI, R);
+
+ if (FTy->isArrayType())
+ NewB = bindArray(NewB, FR, *VI);
+ else if (FTy->isStructureOrClassType())
+ NewB = bindStruct(NewB, FR, *VI);
+ else
+ NewB = bind(NewB, loc::MemRegionVal(FR), *VI);
+ ++VI;
+ }
+
+ // There may be fewer values in the initialize list than the fields of struct.
+ if (FI != FE) {
+ NewB = NewB.addBinding(R, BindingKey::Default,
+ svalBuilder.makeIntVal(0, false));
+ }
+
+ return NewB;
+}
+
+RegionBindingsRef
+RegionStoreManager::bindAggregate(RegionBindingsConstRef B,
+ const TypedRegion *R,
+ SVal Val) {
+ // Remove the old bindings, using 'R' as the root of all regions
+ // we will invalidate. Then add the new binding.
+ return removeSubRegionBindings(B, R).addBinding(R, BindingKey::Default, Val);
+}
+
+//===----------------------------------------------------------------------===//
+// State pruning.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class RemoveDeadBindingsWorker
+ : public ClusterAnalysis<RemoveDeadBindingsWorker> {
+ using ChildrenListTy = SmallVector<const SymbolDerived *, 4>;
+ using MapParentsToDerivedTy = llvm::DenseMap<SymbolRef, ChildrenListTy>;
+
+ MapParentsToDerivedTy ParentsToDerived;
+ SymbolReaper &SymReaper;
+ const StackFrameContext *CurrentLCtx;
+
+public:
+ RemoveDeadBindingsWorker(RegionStoreManager &rm,
+ ProgramStateManager &stateMgr,
+ RegionBindingsRef b, SymbolReaper &symReaper,
+ const StackFrameContext *LCtx)
+ : ClusterAnalysis<RemoveDeadBindingsWorker>(rm, stateMgr, b),
+ SymReaper(symReaper), CurrentLCtx(LCtx) {}
+
+ // Called by ClusterAnalysis.
+ void VisitAddedToCluster(const MemRegion *baseR, const ClusterBindings &C);
+ void VisitCluster(const MemRegion *baseR, const ClusterBindings *C);
+ using ClusterAnalysis<RemoveDeadBindingsWorker>::VisitCluster;
+
+ using ClusterAnalysis::AddToWorkList;
+
+ bool AddToWorkList(const MemRegion *R);
+
+ void VisitBinding(SVal V);
+
+private:
+ void populateWorklistFromSymbol(SymbolRef s);
+};
+}
+
+bool RemoveDeadBindingsWorker::AddToWorkList(const MemRegion *R) {
+ const MemRegion *BaseR = R->getBaseRegion();
+ return AddToWorkList(WorkListElement(BaseR), getCluster(BaseR));
+}
+
+void RemoveDeadBindingsWorker::VisitAddedToCluster(const MemRegion *baseR,
+ const ClusterBindings &C) {
+
+ if (const VarRegion *VR = dyn_cast<VarRegion>(baseR)) {
+ if (SymReaper.isLive(VR))
+ AddToWorkList(baseR, &C);
+
+ return;
+ }
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(baseR)) {
+ if (SymReaper.isLive(SR->getSymbol())) {
+ AddToWorkList(SR, &C);
+ } else if (const auto *SD = dyn_cast<SymbolDerived>(SR->getSymbol())) {
+ ParentsToDerived[SD->getParentSymbol()].push_back(SD);
+ }
+
+ return;
+ }
+
+ if (isa<NonStaticGlobalSpaceRegion>(baseR)) {
+ AddToWorkList(baseR, &C);
+ return;
+ }
+
+ // CXXThisRegion in the current or parent location context is live.
+ if (const CXXThisRegion *TR = dyn_cast<CXXThisRegion>(baseR)) {
+ const auto *StackReg =
+ cast<StackArgumentsSpaceRegion>(TR->getSuperRegion());
+ const StackFrameContext *RegCtx = StackReg->getStackFrame();
+ if (CurrentLCtx &&
+ (RegCtx == CurrentLCtx || RegCtx->isParentOf(CurrentLCtx)))
+ AddToWorkList(TR, &C);
+ }
+}
+
+void RemoveDeadBindingsWorker::VisitCluster(const MemRegion *baseR,
+ const ClusterBindings *C) {
+ if (!C)
+ return;
+
+ // Mark the symbol for any SymbolicRegion with live bindings as live itself.
+ // This means we should continue to track that symbol.
+ if (const SymbolicRegion *SymR = dyn_cast<SymbolicRegion>(baseR))
+ SymReaper.markLive(SymR->getSymbol());
+
+ for (ClusterBindings::iterator I = C->begin(), E = C->end(); I != E; ++I) {
+ // Element index of a binding key is live.
+ SymReaper.markElementIndicesLive(I.getKey().getRegion());
+
+ VisitBinding(I.getData());
+ }
+}
+
+void RemoveDeadBindingsWorker::VisitBinding(SVal V) {
+ // Is it a LazyCompoundVal? All referenced regions are live as well.
+ if (Optional<nonloc::LazyCompoundVal> LCS =
+ V.getAs<nonloc::LazyCompoundVal>()) {
+
+ const RegionStoreManager::SValListTy &Vals = RM.getInterestingValues(*LCS);
+
+ for (RegionStoreManager::SValListTy::const_iterator I = Vals.begin(),
+ E = Vals.end();
+ I != E; ++I)
+ VisitBinding(*I);
+
+ return;
+ }
+
+ // If V is a region, then add it to the worklist.
+ if (const MemRegion *R = V.getAsRegion()) {
+ AddToWorkList(R);
+
+ if (const auto *TVR = dyn_cast<TypedValueRegion>(R)) {
+ DefinedOrUnknownSVal RVS =
+ RM.getSValBuilder().getRegionValueSymbolVal(TVR);
+ if (const MemRegion *SR = RVS.getAsRegion()) {
+ AddToWorkList(SR);
+ }
+ }
+
+ SymReaper.markLive(R);
+
+ // All regions captured by a block are also live.
+ if (const BlockDataRegion *BR = dyn_cast<BlockDataRegion>(R)) {
+ BlockDataRegion::referenced_vars_iterator I = BR->referenced_vars_begin(),
+ E = BR->referenced_vars_end();
+ for ( ; I != E; ++I)
+ AddToWorkList(I.getCapturedRegion());
+ }
+ }
+
+
+ // Update the set of live symbols.
+ for (auto SI = V.symbol_begin(), SE = V.symbol_end(); SI != SE; ++SI) {
+ populateWorklistFromSymbol(*SI);
+
+ for (const auto *SD : ParentsToDerived[*SI])
+ populateWorklistFromSymbol(SD);
+
+ SymReaper.markLive(*SI);
+ }
+}
+
+void RemoveDeadBindingsWorker::populateWorklistFromSymbol(SymbolRef S) {
+ if (const auto *SD = dyn_cast<SymbolData>(S)) {
+ if (Loc::isLocType(SD->getType()) && !SymReaper.isLive(SD)) {
+ const SymbolicRegion *SR = RM.getRegionManager().getSymbolicRegion(SD);
+
+ if (B.contains(SR))
+ AddToWorkList(SR);
+
+ const SymbolicRegion *SHR =
+ RM.getRegionManager().getSymbolicHeapRegion(SD);
+ if (B.contains(SHR))
+ AddToWorkList(SHR);
+ }
+ }
+}
+
+StoreRef RegionStoreManager::removeDeadBindings(Store store,
+ const StackFrameContext *LCtx,
+ SymbolReaper& SymReaper) {
+ RegionBindingsRef B = getRegionBindings(store);
+ RemoveDeadBindingsWorker W(*this, StateMgr, B, SymReaper, LCtx);
+ W.GenerateClusters();
+
+ // Enqueue the region roots onto the worklist.
+ for (SymbolReaper::region_iterator I = SymReaper.region_begin(),
+ E = SymReaper.region_end(); I != E; ++I) {
+ W.AddToWorkList(*I);
+ }
+
+ W.RunWorkList();
+
+ // We have now scanned the store, marking reachable regions and symbols
+ // as live. We now remove all the regions that are dead from the store
+ // as well as update DSymbols with the set symbols that are now dead.
+ for (RegionBindingsRef::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ const MemRegion *Base = I.getKey();
+
+ // If the cluster has been visited, we know the region has been marked.
+ // Otherwise, remove the dead entry.
+ if (!W.isVisited(Base))
+ B = B.remove(Base);
+ }
+
+ return StoreRef(B.asStore(), *this);
+}
+
+//===----------------------------------------------------------------------===//
+// Utility methods.
+//===----------------------------------------------------------------------===//
+
+void RegionStoreManager::print(Store store, raw_ostream &OS,
+ const char* nl) {
+ RegionBindingsRef B = getRegionBindings(store);
+ OS << "Store (direct and default bindings), "
+ << B.asStore()
+ << " :" << nl;
+ B.dump(OS, nl);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp
new file mode 100644
index 000000000000..2e40cc33381c
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/RetainSummaryManager.cpp
@@ -0,0 +1,1229 @@
+//== RetainSummaryManager.cpp - Summaries for reference counting --*- C++ -*--//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines summaries implementation for retain counting, which
+// implements a reference count checker for Core Foundation, Cocoa
+// and OSObject (on Mac OS X).
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/RetainSummaryManager.h"
+#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
+#include "clang/AST/Attr.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/ParentMap.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang;
+using namespace ento;
+
+template <class T>
+constexpr static bool isOneOf() {
+ return false;
+}
+
+/// Helper function to check whether the class is one of the
+/// rest of varargs.
+template <class T, class P, class... ToCompare>
+constexpr static bool isOneOf() {
+ return std::is_same<T, P>::value || isOneOf<T, ToCompare...>();
+}
+
+namespace {
+
+/// Fake attribute class for RC* attributes.
+struct GeneralizedReturnsRetainedAttr {
+ static bool classof(const Attr *A) {
+ if (auto AA = dyn_cast<AnnotateAttr>(A))
+ return AA->getAnnotation() == "rc_ownership_returns_retained";
+ return false;
+ }
+};
+
+struct GeneralizedReturnsNotRetainedAttr {
+ static bool classof(const Attr *A) {
+ if (auto AA = dyn_cast<AnnotateAttr>(A))
+ return AA->getAnnotation() == "rc_ownership_returns_not_retained";
+ return false;
+ }
+};
+
+struct GeneralizedConsumedAttr {
+ static bool classof(const Attr *A) {
+ if (auto AA = dyn_cast<AnnotateAttr>(A))
+ return AA->getAnnotation() == "rc_ownership_consumed";
+ return false;
+ }
+};
+
+}
+
+template <class T>
+Optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D,
+ QualType QT) {
+ ObjKind K;
+ if (isOneOf<T, CFConsumedAttr, CFReturnsRetainedAttr,
+ CFReturnsNotRetainedAttr>()) {
+ if (!TrackObjCAndCFObjects)
+ return None;
+
+ K = ObjKind::CF;
+ } else if (isOneOf<T, NSConsumedAttr, NSConsumesSelfAttr,
+ NSReturnsAutoreleasedAttr, NSReturnsRetainedAttr,
+ NSReturnsNotRetainedAttr, NSConsumesSelfAttr>()) {
+
+ if (!TrackObjCAndCFObjects)
+ return None;
+
+ if (isOneOf<T, NSReturnsRetainedAttr, NSReturnsAutoreleasedAttr,
+ NSReturnsNotRetainedAttr>() &&
+ !cocoa::isCocoaObjectRef(QT))
+ return None;
+ K = ObjKind::ObjC;
+ } else if (isOneOf<T, OSConsumedAttr, OSConsumesThisAttr,
+ OSReturnsNotRetainedAttr, OSReturnsRetainedAttr,
+ OSReturnsRetainedOnZeroAttr,
+ OSReturnsRetainedOnNonZeroAttr>()) {
+ if (!TrackOSObjects)
+ return None;
+ K = ObjKind::OS;
+ } else if (isOneOf<T, GeneralizedReturnsNotRetainedAttr,
+ GeneralizedReturnsRetainedAttr,
+ GeneralizedConsumedAttr>()) {
+ K = ObjKind::Generalized;
+ } else {
+ llvm_unreachable("Unexpected attribute");
+ }
+ if (D->hasAttr<T>())
+ return K;
+ return None;
+}
+
+template <class T1, class T2, class... Others>
+Optional<ObjKind> RetainSummaryManager::hasAnyEnabledAttrOf(const Decl *D,
+ QualType QT) {
+ if (auto Out = hasAnyEnabledAttrOf<T1>(D, QT))
+ return Out;
+ return hasAnyEnabledAttrOf<T2, Others...>(D, QT);
+}
+
+const RetainSummary *
+RetainSummaryManager::getPersistentSummary(const RetainSummary &OldSumm) {
+ // Unique "simple" summaries -- those without ArgEffects.
+ if (OldSumm.isSimple()) {
+ ::llvm::FoldingSetNodeID ID;
+ OldSumm.Profile(ID);
+
+ void *Pos;
+ CachedSummaryNode *N = SimpleSummaries.FindNodeOrInsertPos(ID, Pos);
+
+ if (!N) {
+ N = (CachedSummaryNode *) BPAlloc.Allocate<CachedSummaryNode>();
+ new (N) CachedSummaryNode(OldSumm);
+ SimpleSummaries.InsertNode(N, Pos);
+ }
+
+ return &N->getValue();
+ }
+
+ RetainSummary *Summ = (RetainSummary *) BPAlloc.Allocate<RetainSummary>();
+ new (Summ) RetainSummary(OldSumm);
+ return Summ;
+}
+
+static bool isSubclass(const Decl *D,
+ StringRef ClassName) {
+ using namespace ast_matchers;
+ DeclarationMatcher SubclassM = cxxRecordDecl(isSameOrDerivedFrom(ClassName));
+ return !(match(SubclassM, *D, D->getASTContext()).empty());
+}
+
+static bool isOSObjectSubclass(const Decl *D) {
+ return isSubclass(D, "OSObject");
+}
+
+static bool isOSObjectDynamicCast(StringRef S) {
+ return S == "safeMetaCast";
+}
+
+static bool isOSIteratorSubclass(const Decl *D) {
+ return isSubclass(D, "OSIterator");
+}
+
+static bool hasRCAnnotation(const Decl *D, StringRef rcAnnotation) {
+ for (const auto *Ann : D->specific_attrs<AnnotateAttr>()) {
+ if (Ann->getAnnotation() == rcAnnotation)
+ return true;
+ }
+ return false;
+}
+
+static bool isRetain(const FunctionDecl *FD, StringRef FName) {
+ return FName.startswith_lower("retain") || FName.endswith_lower("retain");
+}
+
+static bool isRelease(const FunctionDecl *FD, StringRef FName) {
+ return FName.startswith_lower("release") || FName.endswith_lower("release");
+}
+
+static bool isAutorelease(const FunctionDecl *FD, StringRef FName) {
+ return FName.startswith_lower("autorelease") ||
+ FName.endswith_lower("autorelease");
+}
+
+static bool isMakeCollectable(StringRef FName) {
+ return FName.contains_lower("MakeCollectable");
+}
+
+/// A function is OSObject related if it is declared on a subclass
+/// of OSObject, or any of the parameters is a subclass of an OSObject.
+static bool isOSObjectRelated(const CXXMethodDecl *MD) {
+ if (isOSObjectSubclass(MD->getParent()))
+ return true;
+
+ for (ParmVarDecl *Param : MD->parameters()) {
+ QualType PT = Param->getType()->getPointeeType();
+ if (!PT.isNull())
+ if (CXXRecordDecl *RD = PT->getAsCXXRecordDecl())
+ if (isOSObjectSubclass(RD))
+ return true;
+ }
+
+ return false;
+}
+
+const RetainSummary *
+RetainSummaryManager::getSummaryForOSObject(const FunctionDecl *FD,
+ StringRef FName, QualType RetTy) {
+ if (RetTy->isPointerType()) {
+ const CXXRecordDecl *PD = RetTy->getPointeeType()->getAsCXXRecordDecl();
+ if (PD && isOSObjectSubclass(PD)) {
+ if (const IdentifierInfo *II = FD->getIdentifier()) {
+ if (isOSObjectDynamicCast(II->getName()))
+ return getDefaultSummary();
+
+ // All objects returned with functions *not* starting with
+ // get, or iterators, are returned at +1.
+ if ((!II->getName().startswith("get") &&
+ !II->getName().startswith("Get")) ||
+ isOSIteratorSubclass(PD)) {
+ return getOSSummaryCreateRule(FD);
+ } else {
+ return getOSSummaryGetRule(FD);
+ }
+ }
+ }
+ }
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ const CXXRecordDecl *Parent = MD->getParent();
+ if (TrackOSObjects && Parent && isOSObjectSubclass(Parent)) {
+ if (FName == "release")
+ return getOSSummaryReleaseRule(FD);
+
+ if (FName == "retain")
+ return getOSSummaryRetainRule(FD);
+
+ if (FName == "free")
+ return getOSSummaryFreeRule(FD);
+
+ if (MD->getOverloadedOperator() == OO_New)
+ return getOSSummaryCreateRule(MD);
+ }
+ }
+
+ return nullptr;
+}
+
+const RetainSummary *RetainSummaryManager::getSummaryForObjCOrCFObject(
+ const FunctionDecl *FD,
+ StringRef FName,
+ QualType RetTy,
+ const FunctionType *FT,
+ bool &AllowAnnotations) {
+
+ ArgEffects ScratchArgs(AF.getEmptyMap());
+
+ std::string RetTyName = RetTy.getAsString();
+ if (FName == "pthread_create" || FName == "pthread_setspecific") {
+ // Part of: <rdar://problem/7299394> and <rdar://problem/11282706>.
+ // This will be addressed better with IPA.
+ return getPersistentStopSummary();
+ } else if(FName == "NSMakeCollectable") {
+ // Handle: id NSMakeCollectable(CFTypeRef)
+ AllowAnnotations = false;
+ return RetTy->isObjCIdType() ? getUnarySummary(FT, DoNothing)
+ : getPersistentStopSummary();
+ } else if (FName == "CMBufferQueueDequeueAndRetain" ||
+ FName == "CMBufferQueueDequeueIfDataReadyAndRetain") {
+ // Part of: <rdar://problem/39390714>.
+ return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF),
+ ScratchArgs,
+ ArgEffect(DoNothing),
+ ArgEffect(DoNothing));
+ } else if (FName == "CFPlugInInstanceCreate") {
+ return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs);
+ } else if (FName == "IORegistryEntrySearchCFProperty" ||
+ (RetTyName == "CFMutableDictionaryRef" &&
+ (FName == "IOBSDNameMatching" || FName == "IOServiceMatching" ||
+ FName == "IOServiceNameMatching" ||
+ FName == "IORegistryEntryIDMatching" ||
+ FName == "IOOpenFirmwarePathMatching"))) {
+ // Part of <rdar://problem/6961230>. (IOKit)
+ // This should be addressed using a API table.
+ return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "IOServiceGetMatchingService" ||
+ FName == "IOServiceGetMatchingServices") {
+ // FIXES: <rdar://problem/6326900>
+ // This should be addressed using a API table. This strcmp is also
+ // a little gross, but there is no need to super optimize here.
+ ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(DecRef, ObjKind::CF));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "IOServiceAddNotification" ||
+ FName == "IOServiceAddMatchingNotification") {
+ // Part of <rdar://problem/6961230>. (IOKit)
+ // This should be addressed using a API table.
+ ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(DecRef, ObjKind::CF));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "CVPixelBufferCreateWithBytes") {
+ // FIXES: <rdar://problem/7283567>
+ // Eventually this can be improved by recognizing that the pixel
+ // buffer passed to CVPixelBufferCreateWithBytes is released via
+ // a callback and doing full IPA to make sure this is done correctly.
+ // FIXME: This function has an out parameter that returns an
+ // allocated object.
+ ScratchArgs = AF.add(ScratchArgs, 7, ArgEffect(StopTracking));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "CGBitmapContextCreateWithData") {
+ // FIXES: <rdar://problem/7358899>
+ // Eventually this can be improved by recognizing that 'releaseInfo'
+ // passed to CGBitmapContextCreateWithData is released via
+ // a callback and doing full IPA to make sure this is done correctly.
+ ScratchArgs = AF.add(ScratchArgs, 8, ArgEffect(ArgEffect(StopTracking)));
+ return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "CVPixelBufferCreateWithPlanarBytes") {
+ // FIXES: <rdar://problem/7283567>
+ // Eventually this can be improved by recognizing that the pixel
+ // buffer passed to CVPixelBufferCreateWithPlanarBytes is released
+ // via a callback and doing full IPA to make sure this is done
+ // correctly.
+ ScratchArgs = AF.add(ScratchArgs, 12, ArgEffect(StopTracking));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "VTCompressionSessionEncodeFrame") {
+ // The context argument passed to VTCompressionSessionEncodeFrame()
+ // is passed to the callback specified when creating the session
+ // (e.g. with VTCompressionSessionCreate()) which can release it.
+ // To account for this possibility, conservatively stop tracking
+ // the context.
+ ScratchArgs = AF.add(ScratchArgs, 5, ArgEffect(StopTracking));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName == "dispatch_set_context" ||
+ FName == "xpc_connection_set_context") {
+ // <rdar://problem/11059275> - The analyzer currently doesn't have
+ // a good way to reason about the finalizer function for libdispatch.
+ // If we pass a context object that is memory managed, stop tracking it.
+ // <rdar://problem/13783514> - Same problem, but for XPC.
+ // FIXME: this hack should possibly go away once we can handle
+ // libdispatch and XPC finalizers.
+ ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+ } else if (FName.startswith("NSLog")) {
+ return getDoNothingSummary();
+ } else if (FName.startswith("NS") &&
+ (FName.find("Insert") != StringRef::npos)) {
+ // Whitelist NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
+ // be deallocated by NSMapRemove. (radar://11152419)
+ ScratchArgs = AF.add(ScratchArgs, 1, ArgEffect(StopTracking));
+ ScratchArgs = AF.add(ScratchArgs, 2, ArgEffect(StopTracking));
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs, ArgEffect(DoNothing),
+ ArgEffect(DoNothing));
+ }
+
+ if (RetTy->isPointerType()) {
+
+ // For CoreFoundation ('CF') types.
+ if (cocoa::isRefType(RetTy, "CF", FName)) {
+ if (isRetain(FD, FName)) {
+ // CFRetain isn't supposed to be annotated. However, this may as
+ // well be a user-made "safe" CFRetain function that is incorrectly
+ // annotated as cf_returns_retained due to lack of better options.
+ // We want to ignore such annotation.
+ AllowAnnotations = false;
+
+ return getUnarySummary(FT, IncRef);
+ } else if (isAutorelease(FD, FName)) {
+ // The headers use cf_consumed, but we can fully model CFAutorelease
+ // ourselves.
+ AllowAnnotations = false;
+
+ return getUnarySummary(FT, Autorelease);
+ } else if (isMakeCollectable(FName)) {
+ AllowAnnotations = false;
+ return getUnarySummary(FT, DoNothing);
+ } else {
+ return getCFCreateGetRuleSummary(FD);
+ }
+ }
+
+ // For CoreGraphics ('CG') and CoreVideo ('CV') types.
+ if (cocoa::isRefType(RetTy, "CG", FName) ||
+ cocoa::isRefType(RetTy, "CV", FName)) {
+ if (isRetain(FD, FName))
+ return getUnarySummary(FT, IncRef);
+ else
+ return getCFCreateGetRuleSummary(FD);
+ }
+
+ // For all other CF-style types, use the Create/Get
+ // rule for summaries but don't support Retain functions
+ // with framework-specific prefixes.
+ if (coreFoundation::isCFObjectRef(RetTy)) {
+ return getCFCreateGetRuleSummary(FD);
+ }
+
+ if (FD->hasAttr<CFAuditedTransferAttr>()) {
+ return getCFCreateGetRuleSummary(FD);
+ }
+ }
+
+ // Check for release functions, the only kind of functions that we care
+ // about that don't return a pointer type.
+ if (FName.startswith("CG") || FName.startswith("CF")) {
+ // Test for 'CGCF'.
+ FName = FName.substr(FName.startswith("CGCF") ? 4 : 2);
+
+ if (isRelease(FD, FName))
+ return getUnarySummary(FT, DecRef);
+ else {
+ assert(ScratchArgs.isEmpty());
+ // Remaining CoreFoundation and CoreGraphics functions.
+ // We use to assume that they all strictly followed the ownership idiom
+ // and that ownership cannot be transferred. While this is technically
+ // correct, many methods allow a tracked object to escape. For example:
+ //
+ // CFMutableDictionaryRef x = CFDictionaryCreateMutable(...);
+ // CFDictionaryAddValue(y, key, x);
+ // CFRelease(x);
+ // ... it is okay to use 'x' since 'y' has a reference to it
+ //
+ // We handle this and similar cases with the follow heuristic. If the
+ // function name contains "InsertValue", "SetValue", "AddValue",
+ // "AppendValue", or "SetAttribute", then we assume that arguments may
+ // "escape." This means that something else holds on to the object,
+ // allowing it be used even after its local retain count drops to 0.
+ ArgEffectKind E =
+ (StrInStrNoCase(FName, "InsertValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "AddValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "SetValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "AppendValue") != StringRef::npos ||
+ StrInStrNoCase(FName, "SetAttribute") != StringRef::npos)
+ ? MayEscape
+ : DoNothing;
+
+ return getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(E, ObjKind::CF));
+ }
+ }
+
+ return nullptr;
+}
+
+const RetainSummary *
+RetainSummaryManager::generateSummary(const FunctionDecl *FD,
+ bool &AllowAnnotations) {
+ // We generate "stop" summaries for implicitly defined functions.
+ if (FD->isImplicit())
+ return getPersistentStopSummary();
+
+ const IdentifierInfo *II = FD->getIdentifier();
+
+ StringRef FName = II ? II->getName() : "";
+
+ // Strip away preceding '_'. Doing this here will effect all the checks
+ // down below.
+ FName = FName.substr(FName.find_first_not_of('_'));
+
+ // Inspect the result type. Strip away any typedefs.
+ const auto *FT = FD->getType()->getAs<FunctionType>();
+ QualType RetTy = FT->getReturnType();
+
+ if (TrackOSObjects)
+ if (const RetainSummary *S = getSummaryForOSObject(FD, FName, RetTy))
+ return S;
+
+ if (TrackObjCAndCFObjects)
+ if (const RetainSummary *S =
+ getSummaryForObjCOrCFObject(FD, FName, RetTy, FT, AllowAnnotations))
+ return S;
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD))
+ if (!(TrackOSObjects && isOSObjectRelated(MD)))
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ArgEffects(AF.getEmptyMap()),
+ ArgEffect(DoNothing),
+ ArgEffect(StopTracking),
+ ArgEffect(DoNothing));
+
+ return getDefaultSummary();
+}
+
+const RetainSummary *
+RetainSummaryManager::getFunctionSummary(const FunctionDecl *FD) {
+ // If we don't know what function we're calling, use our default summary.
+ if (!FD)
+ return getDefaultSummary();
+
+ // Look up a summary in our cache of FunctionDecls -> Summaries.
+ FuncSummariesTy::iterator I = FuncSummaries.find(FD);
+ if (I != FuncSummaries.end())
+ return I->second;
+
+ // No summary? Generate one.
+ bool AllowAnnotations = true;
+ const RetainSummary *S = generateSummary(FD, AllowAnnotations);
+
+ // Annotations override defaults.
+ if (AllowAnnotations)
+ updateSummaryFromAnnotations(S, FD);
+
+ FuncSummaries[FD] = S;
+ return S;
+}
+
+//===----------------------------------------------------------------------===//
+// Summary creation for functions (largely uses of Core Foundation).
+//===----------------------------------------------------------------------===//
+
+static ArgEffect getStopTrackingHardEquivalent(ArgEffect E) {
+ switch (E.getKind()) {
+ case DoNothing:
+ case Autorelease:
+ case DecRefBridgedTransferred:
+ case IncRef:
+ case UnretainedOutParameter:
+ case RetainedOutParameter:
+ case RetainedOutParameterOnZero:
+ case RetainedOutParameterOnNonZero:
+ case MayEscape:
+ case StopTracking:
+ case StopTrackingHard:
+ return E.withKind(StopTrackingHard);
+ case DecRef:
+ case DecRefAndStopTrackingHard:
+ return E.withKind(DecRefAndStopTrackingHard);
+ case Dealloc:
+ return E.withKind(Dealloc);
+ }
+
+ llvm_unreachable("Unknown ArgEffect kind");
+}
+
+void RetainSummaryManager::updateSummaryForCall(const RetainSummary *&S,
+ const CallEvent &Call) {
+ if (Call.hasNonZeroCallbackArg()) {
+ ArgEffect RecEffect =
+ getStopTrackingHardEquivalent(S->getReceiverEffect());
+ ArgEffect DefEffect =
+ getStopTrackingHardEquivalent(S->getDefaultArgEffect());
+
+ ArgEffects ScratchArgs(AF.getEmptyMap());
+ ArgEffects CustomArgEffects = S->getArgEffects();
+ for (ArgEffects::iterator I = CustomArgEffects.begin(),
+ E = CustomArgEffects.end();
+ I != E; ++I) {
+ ArgEffect Translated = getStopTrackingHardEquivalent(I->second);
+ if (Translated.getKind() != DefEffect.getKind())
+ ScratchArgs = AF.add(ScratchArgs, I->first, Translated);
+ }
+
+ RetEffect RE = RetEffect::MakeNoRetHard();
+
+ // Special cases where the callback argument CANNOT free the return value.
+ // This can generally only happen if we know that the callback will only be
+ // called when the return value is already being deallocated.
+ if (const SimpleFunctionCall *FC = dyn_cast<SimpleFunctionCall>(&Call)) {
+ if (IdentifierInfo *Name = FC->getDecl()->getIdentifier()) {
+ // When the CGBitmapContext is deallocated, the callback here will free
+ // the associated data buffer.
+ // The callback in dispatch_data_create frees the buffer, but not
+ // the data object.
+ if (Name->isStr("CGBitmapContextCreateWithData") ||
+ Name->isStr("dispatch_data_create"))
+ RE = S->getRetEffect();
+ }
+ }
+
+ S = getPersistentSummary(RE, ScratchArgs, RecEffect, DefEffect);
+ }
+
+ // Special case '[super init];' and '[self init];'
+ //
+ // Even though calling '[super init]' without assigning the result to self
+ // and checking if the parent returns 'nil' is a bad pattern, it is common.
+ // Additionally, our Self Init checker already warns about it. To avoid
+ // overwhelming the user with messages from both checkers, we model the case
+ // of '[super init]' in cases when it is not consumed by another expression
+ // as if the call preserves the value of 'self'; essentially, assuming it can
+ // never fail and return 'nil'.
+ // Note, we don't want to just stop tracking the value since we want the
+ // RetainCount checker to report leaks and use-after-free if SelfInit checker
+ // is turned off.
+ if (const ObjCMethodCall *MC = dyn_cast<ObjCMethodCall>(&Call)) {
+ if (MC->getMethodFamily() == OMF_init && MC->isReceiverSelfOrSuper()) {
+
+ // Check if the message is not consumed, we know it will not be used in
+ // an assignment, ex: "self = [super init]".
+ const Expr *ME = MC->getOriginExpr();
+ const LocationContext *LCtx = MC->getLocationContext();
+ ParentMap &PM = LCtx->getAnalysisDeclContext()->getParentMap();
+ if (!PM.isConsumedExpr(ME)) {
+ RetainSummaryTemplate ModifiableSummaryTemplate(S, *this);
+ ModifiableSummaryTemplate->setReceiverEffect(ArgEffect(DoNothing));
+ ModifiableSummaryTemplate->setRetEffect(RetEffect::MakeNoRet());
+ }
+ }
+ }
+}
+
+const RetainSummary *
+RetainSummaryManager::getSummary(const CallEvent &Call,
+ QualType ReceiverType) {
+ const RetainSummary *Summ;
+ switch (Call.getKind()) {
+ case CE_Function:
+ case CE_CXXMember:
+ case CE_CXXMemberOperator:
+ case CE_CXXConstructor:
+ case CE_CXXAllocator:
+ Summ = getFunctionSummary(cast_or_null<FunctionDecl>(Call.getDecl()));
+ break;
+ case CE_Block:
+ case CE_CXXDestructor:
+ // FIXME: These calls are currently unsupported.
+ return getPersistentStopSummary();
+ case CE_ObjCMessage: {
+ const ObjCMethodCall &Msg = cast<ObjCMethodCall>(Call);
+ if (Msg.isInstanceMessage())
+ Summ = getInstanceMethodSummary(Msg, ReceiverType);
+ else
+ Summ = getClassMethodSummary(Msg);
+ break;
+ }
+ }
+
+ updateSummaryForCall(Summ, Call);
+
+ assert(Summ && "Unknown call type?");
+ return Summ;
+}
+
+
+const RetainSummary *
+RetainSummaryManager::getCFCreateGetRuleSummary(const FunctionDecl *FD) {
+ if (coreFoundation::followsCreateRule(FD))
+ return getCFSummaryCreateRule(FD);
+
+ return getCFSummaryGetRule(FD);
+}
+
+bool RetainSummaryManager::isTrustedReferenceCountImplementation(
+ const FunctionDecl *FD) {
+ return hasRCAnnotation(FD, "rc_ownership_trusted_implementation");
+}
+
+Optional<RetainSummaryManager::BehaviorSummary>
+RetainSummaryManager::canEval(const CallExpr *CE, const FunctionDecl *FD,
+ bool &hasTrustedImplementationAnnotation) {
+
+ IdentifierInfo *II = FD->getIdentifier();
+ if (!II)
+ return None;
+
+ StringRef FName = II->getName();
+ FName = FName.substr(FName.find_first_not_of('_'));
+
+ QualType ResultTy = CE->getCallReturnType(Ctx);
+ if (ResultTy->isObjCIdType()) {
+ if (II->isStr("NSMakeCollectable"))
+ return BehaviorSummary::Identity;
+ } else if (ResultTy->isPointerType()) {
+ // Handle: (CF|CG|CV)Retain
+ // CFAutorelease
+ // It's okay to be a little sloppy here.
+ if (FName == "CMBufferQueueDequeueAndRetain" ||
+ FName == "CMBufferQueueDequeueIfDataReadyAndRetain") {
+ // Part of: <rdar://problem/39390714>.
+ // These are not retain. They just return something and retain it.
+ return None;
+ }
+ if (cocoa::isRefType(ResultTy, "CF", FName) ||
+ cocoa::isRefType(ResultTy, "CG", FName) ||
+ cocoa::isRefType(ResultTy, "CV", FName))
+ if (isRetain(FD, FName) || isAutorelease(FD, FName) ||
+ isMakeCollectable(FName))
+ return BehaviorSummary::Identity;
+
+ // safeMetaCast is called by OSDynamicCast.
+ // We assume that OSDynamicCast is either an identity (cast is OK,
+ // the input was non-zero),
+ // or that it returns zero (when the cast failed, or the input
+ // was zero).
+ if (TrackOSObjects && isOSObjectDynamicCast(FName)) {
+ return BehaviorSummary::IdentityOrZero;
+ }
+
+ const FunctionDecl* FDD = FD->getDefinition();
+ if (FDD && isTrustedReferenceCountImplementation(FDD)) {
+ hasTrustedImplementationAnnotation = true;
+ return BehaviorSummary::Identity;
+ }
+ }
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ const CXXRecordDecl *Parent = MD->getParent();
+ if (TrackOSObjects && Parent && isOSObjectSubclass(Parent))
+ if (FName == "release" || FName == "retain")
+ return BehaviorSummary::NoOp;
+ }
+
+ return None;
+}
+
+const RetainSummary *
+RetainSummaryManager::getUnarySummary(const FunctionType* FT,
+ ArgEffectKind AE) {
+
+ // Unary functions have no arg effects by definition.
+ ArgEffects ScratchArgs(AF.getEmptyMap());
+
+ // Sanity check that this is *really* a unary function. This can
+ // happen if people do weird things.
+ const FunctionProtoType* FTP = dyn_cast<FunctionProtoType>(FT);
+ if (!FTP || FTP->getNumParams() != 1)
+ return getPersistentStopSummary();
+
+ ArgEffect Effect(AE, ObjKind::CF);
+
+ ScratchArgs = AF.add(ScratchArgs, 0, Effect);
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ ScratchArgs,
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryRetainRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ AF.getEmptyMap(),
+ /*ReceiverEff=*/ArgEffect(DoNothing),
+ /*DefaultEff=*/ArgEffect(DoNothing),
+ /*ThisEff=*/ArgEffect(IncRef, ObjKind::OS));
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryReleaseRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ AF.getEmptyMap(),
+ /*ReceiverEff=*/ArgEffect(DoNothing),
+ /*DefaultEff=*/ArgEffect(DoNothing),
+ /*ThisEff=*/ArgEffect(DecRef, ObjKind::OS));
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryFreeRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNoRet(),
+ AF.getEmptyMap(),
+ /*ReceiverEff=*/ArgEffect(DoNothing),
+ /*DefaultEff=*/ArgEffect(DoNothing),
+ /*ThisEff=*/ArgEffect(Dealloc, ObjKind::OS));
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryCreateRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeOwned(ObjKind::OS),
+ AF.getEmptyMap());
+}
+
+const RetainSummary *
+RetainSummaryManager::getOSSummaryGetRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::OS),
+ AF.getEmptyMap());
+}
+
+const RetainSummary *
+RetainSummaryManager::getCFSummaryCreateRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF),
+ ArgEffects(AF.getEmptyMap()));
+}
+
+const RetainSummary *
+RetainSummaryManager::getCFSummaryGetRule(const FunctionDecl *FD) {
+ return getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::CF),
+ ArgEffects(AF.getEmptyMap()),
+ ArgEffect(DoNothing), ArgEffect(DoNothing));
+}
+
+
+
+
+//===----------------------------------------------------------------------===//
+// Summary creation for Selectors.
+//===----------------------------------------------------------------------===//
+
+Optional<RetEffect>
+RetainSummaryManager::getRetEffectFromAnnotations(QualType RetTy,
+ const Decl *D) {
+ if (hasAnyEnabledAttrOf<NSReturnsRetainedAttr>(D, RetTy))
+ return ObjCAllocRetE;
+
+ if (auto K = hasAnyEnabledAttrOf<CFReturnsRetainedAttr, OSReturnsRetainedAttr,
+ GeneralizedReturnsRetainedAttr>(D, RetTy))
+ return RetEffect::MakeOwned(*K);
+
+ if (auto K = hasAnyEnabledAttrOf<
+ CFReturnsNotRetainedAttr, OSReturnsNotRetainedAttr,
+ GeneralizedReturnsNotRetainedAttr, NSReturnsNotRetainedAttr,
+ NSReturnsAutoreleasedAttr>(D, RetTy))
+ return RetEffect::MakeNotOwned(*K);
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D))
+ for (const auto *PD : MD->overridden_methods())
+ if (auto RE = getRetEffectFromAnnotations(RetTy, PD))
+ return RE;
+
+ return None;
+}
+
+/// \return Whether the chain of typedefs starting from {@code QT}
+/// has a typedef with a given name {@code Name}.
+static bool hasTypedefNamed(QualType QT,
+ StringRef Name) {
+ while (auto *T = dyn_cast<TypedefType>(QT)) {
+ const auto &Context = T->getDecl()->getASTContext();
+ if (T->getDecl()->getIdentifier() == &Context.Idents.get(Name))
+ return true;
+ QT = T->getDecl()->getUnderlyingType();
+ }
+ return false;
+}
+
+static QualType getCallableReturnType(const NamedDecl *ND) {
+ if (const auto *FD = dyn_cast<FunctionDecl>(ND)) {
+ return FD->getReturnType();
+ } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(ND)) {
+ return MD->getReturnType();
+ } else {
+ llvm_unreachable("Unexpected decl");
+ }
+}
+
+bool RetainSummaryManager::applyParamAnnotationEffect(
+ const ParmVarDecl *pd, unsigned parm_idx, const NamedDecl *FD,
+ RetainSummaryTemplate &Template) {
+ QualType QT = pd->getType();
+ if (auto K =
+ hasAnyEnabledAttrOf<NSConsumedAttr, CFConsumedAttr, OSConsumedAttr,
+ GeneralizedConsumedAttr>(pd, QT)) {
+ Template->addArg(AF, parm_idx, ArgEffect(DecRef, *K));
+ return true;
+ } else if (auto K = hasAnyEnabledAttrOf<
+ CFReturnsRetainedAttr, OSReturnsRetainedAttr,
+ OSReturnsRetainedOnNonZeroAttr, OSReturnsRetainedOnZeroAttr,
+ GeneralizedReturnsRetainedAttr>(pd, QT)) {
+
+ // For OSObjects, we try to guess whether the object is created based
+ // on the return value.
+ if (K == ObjKind::OS) {
+ QualType QT = getCallableReturnType(FD);
+
+ bool HasRetainedOnZero = pd->hasAttr<OSReturnsRetainedOnZeroAttr>();
+ bool HasRetainedOnNonZero = pd->hasAttr<OSReturnsRetainedOnNonZeroAttr>();
+
+ // The usual convention is to create an object on non-zero return, but
+ // it's reverted if the typedef chain has a typedef kern_return_t,
+ // because kReturnSuccess constant is defined as zero.
+ // The convention can be overwritten by custom attributes.
+ bool SuccessOnZero =
+ HasRetainedOnZero ||
+ (hasTypedefNamed(QT, "kern_return_t") && !HasRetainedOnNonZero);
+ bool ShouldSplit = !QT.isNull() && !QT->isVoidType();
+ ArgEffectKind AK = RetainedOutParameter;
+ if (ShouldSplit && SuccessOnZero) {
+ AK = RetainedOutParameterOnZero;
+ } else if (ShouldSplit && (!SuccessOnZero || HasRetainedOnNonZero)) {
+ AK = RetainedOutParameterOnNonZero;
+ }
+ Template->addArg(AF, parm_idx, ArgEffect(AK, ObjKind::OS));
+ }
+
+ // For others:
+ // Do nothing. Retained out parameters will either point to a +1 reference
+ // or NULL, but the way you check for failure differs depending on the
+ // API. Consequently, we don't have a good way to track them yet.
+ return true;
+ } else if (auto K = hasAnyEnabledAttrOf<CFReturnsNotRetainedAttr,
+ OSReturnsNotRetainedAttr,
+ GeneralizedReturnsNotRetainedAttr>(
+ pd, QT)) {
+ Template->addArg(AF, parm_idx, ArgEffect(UnretainedOutParameter, *K));
+ return true;
+ }
+
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(FD)) {
+ for (const auto *OD : MD->overridden_methods()) {
+ const ParmVarDecl *OP = OD->parameters()[parm_idx];
+ if (applyParamAnnotationEffect(OP, parm_idx, OD, Template))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void
+RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
+ const FunctionDecl *FD) {
+ if (!FD)
+ return;
+
+ assert(Summ && "Must have a summary to add annotations to.");
+ RetainSummaryTemplate Template(Summ, *this);
+
+ // Effects on the parameters.
+ unsigned parm_idx = 0;
+ for (auto pi = FD->param_begin(),
+ pe = FD->param_end(); pi != pe; ++pi, ++parm_idx)
+ applyParamAnnotationEffect(*pi, parm_idx, FD, Template);
+
+ QualType RetTy = FD->getReturnType();
+ if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, FD))
+ Template->setRetEffect(*RetE);
+
+ if (hasAnyEnabledAttrOf<OSConsumesThisAttr>(FD, RetTy))
+ Template->setThisEffect(ArgEffect(DecRef, ObjKind::OS));
+}
+
+void
+RetainSummaryManager::updateSummaryFromAnnotations(const RetainSummary *&Summ,
+ const ObjCMethodDecl *MD) {
+ if (!MD)
+ return;
+
+ assert(Summ && "Must have a valid summary to add annotations to");
+ RetainSummaryTemplate Template(Summ, *this);
+
+ // Effects on the receiver.
+ if (hasAnyEnabledAttrOf<NSConsumesSelfAttr>(MD, MD->getReturnType()))
+ Template->setReceiverEffect(ArgEffect(DecRef, ObjKind::ObjC));
+
+ // Effects on the parameters.
+ unsigned parm_idx = 0;
+ for (auto pi = MD->param_begin(), pe = MD->param_end(); pi != pe;
+ ++pi, ++parm_idx)
+ applyParamAnnotationEffect(*pi, parm_idx, MD, Template);
+
+ QualType RetTy = MD->getReturnType();
+ if (Optional<RetEffect> RetE = getRetEffectFromAnnotations(RetTy, MD))
+ Template->setRetEffect(*RetE);
+}
+
+const RetainSummary *
+RetainSummaryManager::getStandardMethodSummary(const ObjCMethodDecl *MD,
+ Selector S, QualType RetTy) {
+ // Any special effects?
+ ArgEffect ReceiverEff = ArgEffect(DoNothing, ObjKind::ObjC);
+ RetEffect ResultEff = RetEffect::MakeNoRet();
+
+ // Check the method family, and apply any default annotations.
+ switch (MD ? MD->getMethodFamily() : S.getMethodFamily()) {
+ case OMF_None:
+ case OMF_initialize:
+ case OMF_performSelector:
+ // Assume all Objective-C methods follow Cocoa Memory Management rules.
+ // FIXME: Does the non-threaded performSelector family really belong here?
+ // The selector could be, say, @selector(copy).
+ if (cocoa::isCocoaObjectRef(RetTy))
+ ResultEff = RetEffect::MakeNotOwned(ObjKind::ObjC);
+ else if (coreFoundation::isCFObjectRef(RetTy)) {
+ // ObjCMethodDecl currently doesn't consider CF objects as valid return
+ // values for alloc, new, copy, or mutableCopy, so we have to
+ // double-check with the selector. This is ugly, but there aren't that
+ // many Objective-C methods that return CF objects, right?
+ if (MD) {
+ switch (S.getMethodFamily()) {
+ case OMF_alloc:
+ case OMF_new:
+ case OMF_copy:
+ case OMF_mutableCopy:
+ ResultEff = RetEffect::MakeOwned(ObjKind::CF);
+ break;
+ default:
+ ResultEff = RetEffect::MakeNotOwned(ObjKind::CF);
+ break;
+ }
+ } else {
+ ResultEff = RetEffect::MakeNotOwned(ObjKind::CF);
+ }
+ }
+ break;
+ case OMF_init:
+ ResultEff = ObjCInitRetE;
+ ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC);
+ break;
+ case OMF_alloc:
+ case OMF_new:
+ case OMF_copy:
+ case OMF_mutableCopy:
+ if (cocoa::isCocoaObjectRef(RetTy))
+ ResultEff = ObjCAllocRetE;
+ else if (coreFoundation::isCFObjectRef(RetTy))
+ ResultEff = RetEffect::MakeOwned(ObjKind::CF);
+ break;
+ case OMF_autorelease:
+ ReceiverEff = ArgEffect(Autorelease, ObjKind::ObjC);
+ break;
+ case OMF_retain:
+ ReceiverEff = ArgEffect(IncRef, ObjKind::ObjC);
+ break;
+ case OMF_release:
+ ReceiverEff = ArgEffect(DecRef, ObjKind::ObjC);
+ break;
+ case OMF_dealloc:
+ ReceiverEff = ArgEffect(Dealloc, ObjKind::ObjC);
+ break;
+ case OMF_self:
+ // -self is handled specially by the ExprEngine to propagate the receiver.
+ break;
+ case OMF_retainCount:
+ case OMF_finalize:
+ // These methods don't return objects.
+ break;
+ }
+
+ // If one of the arguments in the selector has the keyword 'delegate' we
+ // should stop tracking the reference count for the receiver. This is
+ // because the reference count is quite possibly handled by a delegate
+ // method.
+ if (S.isKeywordSelector()) {
+ for (unsigned i = 0, e = S.getNumArgs(); i != e; ++i) {
+ StringRef Slot = S.getNameForSlot(i);
+ if (Slot.substr(Slot.size() - 8).equals_lower("delegate")) {
+ if (ResultEff == ObjCInitRetE)
+ ResultEff = RetEffect::MakeNoRetHard();
+ else
+ ReceiverEff = ArgEffect(StopTrackingHard, ObjKind::ObjC);
+ }
+ }
+ }
+
+ if (ReceiverEff.getKind() == DoNothing &&
+ ResultEff.getKind() == RetEffect::NoRet)
+ return getDefaultSummary();
+
+ return getPersistentSummary(ResultEff, ArgEffects(AF.getEmptyMap()),
+ ArgEffect(ReceiverEff), ArgEffect(MayEscape));
+}
+
+const RetainSummary *RetainSummaryManager::getInstanceMethodSummary(
+ const ObjCMethodCall &Msg,
+ QualType ReceiverType) {
+ const ObjCInterfaceDecl *ReceiverClass = nullptr;
+
+ // We do better tracking of the type of the object than the core ExprEngine.
+ // See if we have its type in our private state.
+ if (!ReceiverType.isNull())
+ if (const auto *PT = ReceiverType->getAs<ObjCObjectPointerType>())
+ ReceiverClass = PT->getInterfaceDecl();
+
+ // If we don't know what kind of object this is, fall back to its static type.
+ if (!ReceiverClass)
+ ReceiverClass = Msg.getReceiverInterface();
+
+ // FIXME: The receiver could be a reference to a class, meaning that
+ // we should use the class method.
+ // id x = [NSObject class];
+ // [x performSelector:... withObject:... afterDelay:...];
+ Selector S = Msg.getSelector();
+ const ObjCMethodDecl *Method = Msg.getDecl();
+ if (!Method && ReceiverClass)
+ Method = ReceiverClass->getInstanceMethod(S);
+
+ return getMethodSummary(S, ReceiverClass, Method, Msg.getResultType(),
+ ObjCMethodSummaries);
+}
+
+const RetainSummary *
+RetainSummaryManager::getMethodSummary(Selector S,
+ const ObjCInterfaceDecl *ID,
+ const ObjCMethodDecl *MD, QualType RetTy,
+ ObjCMethodSummariesTy &CachedSummaries) {
+
+ // Objective-C method summaries are only applicable to ObjC and CF objects.
+ if (!TrackObjCAndCFObjects)
+ return getDefaultSummary();
+
+ // Look up a summary in our summary cache.
+ const RetainSummary *Summ = CachedSummaries.find(ID, S);
+
+ if (!Summ) {
+ Summ = getStandardMethodSummary(MD, S, RetTy);
+
+ // Annotations override defaults.
+ updateSummaryFromAnnotations(Summ, MD);
+
+ // Memoize the summary.
+ CachedSummaries[ObjCSummaryKey(ID, S)] = Summ;
+ }
+
+ return Summ;
+}
+
+void RetainSummaryManager::InitializeClassMethodSummaries() {
+ ArgEffects ScratchArgs = AF.getEmptyMap();
+
+ // Create the [NSAssertionHandler currentHander] summary.
+ addClassMethSummary("NSAssertionHandler", "currentHandler",
+ getPersistentSummary(RetEffect::MakeNotOwned(ObjKind::ObjC),
+ ScratchArgs));
+
+ // Create the [NSAutoreleasePool addObject:] summary.
+ ScratchArgs = AF.add(ScratchArgs, 0, ArgEffect(Autorelease));
+ addClassMethSummary("NSAutoreleasePool", "addObject",
+ getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs,
+ ArgEffect(DoNothing),
+ ArgEffect(Autorelease)));
+}
+
+void RetainSummaryManager::InitializeMethodSummaries() {
+
+ ArgEffects ScratchArgs = AF.getEmptyMap();
+ // Create the "init" selector. It just acts as a pass-through for the
+ // receiver.
+ const RetainSummary *InitSumm = getPersistentSummary(
+ ObjCInitRetE, ScratchArgs, ArgEffect(DecRef, ObjKind::ObjC));
+ addNSObjectMethSummary(GetNullarySelector("init", Ctx), InitSumm);
+
+ // awakeAfterUsingCoder: behaves basically like an 'init' method. It
+ // claims the receiver and returns a retained object.
+ addNSObjectMethSummary(GetUnarySelector("awakeAfterUsingCoder", Ctx),
+ InitSumm);
+
+ // The next methods are allocators.
+ const RetainSummary *AllocSumm = getPersistentSummary(ObjCAllocRetE,
+ ScratchArgs);
+ const RetainSummary *CFAllocSumm =
+ getPersistentSummary(RetEffect::MakeOwned(ObjKind::CF), ScratchArgs);
+
+ // Create the "retain" selector.
+ RetEffect NoRet = RetEffect::MakeNoRet();
+ const RetainSummary *Summ = getPersistentSummary(
+ NoRet, ScratchArgs, ArgEffect(IncRef, ObjKind::ObjC));
+ addNSObjectMethSummary(GetNullarySelector("retain", Ctx), Summ);
+
+ // Create the "release" selector.
+ Summ = getPersistentSummary(NoRet, ScratchArgs,
+ ArgEffect(DecRef, ObjKind::ObjC));
+ addNSObjectMethSummary(GetNullarySelector("release", Ctx), Summ);
+
+ // Create the -dealloc summary.
+ Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Dealloc,
+ ObjKind::ObjC));
+ addNSObjectMethSummary(GetNullarySelector("dealloc", Ctx), Summ);
+
+ // Create the "autorelease" selector.
+ Summ = getPersistentSummary(NoRet, ScratchArgs, ArgEffect(Autorelease,
+ ObjKind::ObjC));
+ addNSObjectMethSummary(GetNullarySelector("autorelease", Ctx), Summ);
+
+ // For NSWindow, allocated objects are (initially) self-owned.
+ // FIXME: For now we opt for false negatives with NSWindow, as these objects
+ // self-own themselves. However, they only do this once they are displayed.
+ // Thus, we need to track an NSWindow's display status.
+ // This is tracked in <rdar://problem/6062711>.
+ // See also http://llvm.org/bugs/show_bug.cgi?id=3714.
+ const RetainSummary *NoTrackYet =
+ getPersistentSummary(RetEffect::MakeNoRet(), ScratchArgs,
+ ArgEffect(StopTracking), ArgEffect(StopTracking));
+
+ addClassMethSummary("NSWindow", "alloc", NoTrackYet);
+
+ // For NSPanel (which subclasses NSWindow), allocated objects are not
+ // self-owned.
+ // FIXME: For now we don't track NSPanels. object for the same reason
+ // as for NSWindow objects.
+ addClassMethSummary("NSPanel", "alloc", NoTrackYet);
+
+ // For NSNull, objects returned by +null are singletons that ignore
+ // retain/release semantics. Just don't track them.
+ // <rdar://problem/12858915>
+ addClassMethSummary("NSNull", "null", NoTrackYet);
+
+ // Don't track allocated autorelease pools, as it is okay to prematurely
+ // exit a method.
+ addClassMethSummary("NSAutoreleasePool", "alloc", NoTrackYet);
+ addClassMethSummary("NSAutoreleasePool", "allocWithZone", NoTrackYet, false);
+ addClassMethSummary("NSAutoreleasePool", "new", NoTrackYet);
+
+ // Create summaries QCRenderer/QCView -createSnapShotImageOfType:
+ addInstMethSummary("QCRenderer", AllocSumm, "createSnapshotImageOfType");
+ addInstMethSummary("QCView", AllocSumm, "createSnapshotImageOfType");
+
+ // Create summaries for CIContext, 'createCGImage' and
+ // 'createCGLayerWithSize'. These objects are CF objects, and are not
+ // automatically garbage collected.
+ addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect");
+ addInstMethSummary("CIContext", CFAllocSumm, "createCGImage", "fromRect",
+ "format", "colorSpace");
+ addInstMethSummary("CIContext", CFAllocSumm, "createCGLayerWithSize", "info");
+}
+
+CallEffects CallEffects::getEffect(const ObjCMethodDecl *MD) {
+ ASTContext &Ctx = MD->getASTContext();
+ LangOptions L = Ctx.getLangOpts();
+ RetainSummaryManager M(Ctx, L.ObjCAutoRefCount,
+ /*TrackNSAndCFObjects=*/true,
+ /*TrackOSObjects=*/false);
+ const RetainSummary *S = M.getMethodSummary(MD);
+ CallEffects CE(S->getRetEffect(), S->getReceiverEffect());
+ unsigned N = MD->param_size();
+ for (unsigned i = 0; i < N; ++i) {
+ CE.Args.push_back(S->getArg(i));
+ }
+ return CE;
+}
+
+CallEffects CallEffects::getEffect(const FunctionDecl *FD) {
+ ASTContext &Ctx = FD->getASTContext();
+ LangOptions L = Ctx.getLangOpts();
+ RetainSummaryManager M(Ctx, L.ObjCAutoRefCount,
+ /*TrackNSAndCFObjects=*/true,
+ /*TrackOSObjects=*/false);
+ const RetainSummary *S = M.getFunctionSummary(FD);
+ CallEffects CE(S->getRetEffect());
+ unsigned N = FD->param_size();
+ for (unsigned i = 0; i < N; ++i) {
+ CE.Args.push_back(S->getArg(i));
+ }
+ return CE;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
new file mode 100644
index 000000000000..6c0d487c8a87
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -0,0 +1,655 @@
+//===- SValBuilder.cpp - Basic class for all SValBuilder implementations --===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines SValBuilder, the base class for all (complete) SValBuilder
+// implementations.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/ExprCXX.h"
+#include "clang/AST/ExprObjC.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/None.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include <cassert>
+#include <tuple>
+
+using namespace clang;
+using namespace ento;
+
+//===----------------------------------------------------------------------===//
+// Basic SVal creation.
+//===----------------------------------------------------------------------===//
+
+void SValBuilder::anchor() {}
+
+DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) {
+ if (Loc::isLocType(type))
+ return makeNull();
+
+ if (type->isIntegralOrEnumerationType())
+ return makeIntVal(0, type);
+
+ if (type->isArrayType() || type->isRecordType() || type->isVectorType() ||
+ type->isAnyComplexType())
+ return makeCompoundVal(type, BasicVals.getEmptySValList());
+
+ // FIXME: Handle floats.
+ return UnknownVal();
+}
+
+NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op,
+ const llvm::APSInt& rhs, QualType type) {
+ // The Environment ensures we always get a persistent APSInt in
+ // BasicValueFactory, so we don't need to get the APSInt from
+ // BasicValueFactory again.
+ assert(lhs);
+ assert(!Loc::isLocType(type));
+ return nonloc::SymbolVal(SymMgr.getSymIntExpr(lhs, op, rhs, type));
+}
+
+NonLoc SValBuilder::makeNonLoc(const llvm::APSInt& lhs,
+ BinaryOperator::Opcode op, const SymExpr *rhs,
+ QualType type) {
+ assert(rhs);
+ assert(!Loc::isLocType(type));
+ return nonloc::SymbolVal(SymMgr.getIntSymExpr(lhs, op, rhs, type));
+}
+
+NonLoc SValBuilder::makeNonLoc(const SymExpr *lhs, BinaryOperator::Opcode op,
+ const SymExpr *rhs, QualType type) {
+ assert(lhs && rhs);
+ assert(!Loc::isLocType(type));
+ return nonloc::SymbolVal(SymMgr.getSymSymExpr(lhs, op, rhs, type));
+}
+
+NonLoc SValBuilder::makeNonLoc(const SymExpr *operand,
+ QualType fromTy, QualType toTy) {
+ assert(operand);
+ assert(!Loc::isLocType(toTy));
+ return nonloc::SymbolVal(SymMgr.getCastSymbol(operand, fromTy, toTy));
+}
+
+SVal SValBuilder::convertToArrayIndex(SVal val) {
+ if (val.isUnknownOrUndef())
+ return val;
+
+ // Common case: we have an appropriately sized integer.
+ if (Optional<nonloc::ConcreteInt> CI = val.getAs<nonloc::ConcreteInt>()) {
+ const llvm::APSInt& I = CI->getValue();
+ if (I.getBitWidth() == ArrayIndexWidth && I.isSigned())
+ return val;
+ }
+
+ return evalCastFromNonLoc(val.castAs<NonLoc>(), ArrayIndexTy);
+}
+
+nonloc::ConcreteInt SValBuilder::makeBoolVal(const CXXBoolLiteralExpr *boolean){
+ return makeTruthVal(boolean->getValue());
+}
+
+DefinedOrUnknownSVal
+SValBuilder::getRegionValueSymbolVal(const TypedValueRegion *region) {
+ QualType T = region->getValueType();
+
+ if (T->isNullPtrType())
+ return makeZeroVal(T);
+
+ if (!SymbolManager::canSymbolicate(T))
+ return UnknownVal();
+
+ SymbolRef sym = SymMgr.getRegionValueSymbol(region);
+
+ if (Loc::isLocType(T))
+ return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
+
+ return nonloc::SymbolVal(sym);
+}
+
+DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const void *SymbolTag,
+ const Expr *Ex,
+ const LocationContext *LCtx,
+ unsigned Count) {
+ QualType T = Ex->getType();
+
+ if (T->isNullPtrType())
+ return makeZeroVal(T);
+
+ // Compute the type of the result. If the expression is not an R-value, the
+ // result should be a location.
+ QualType ExType = Ex->getType();
+ if (Ex->isGLValue())
+ T = LCtx->getAnalysisDeclContext()->getASTContext().getPointerType(ExType);
+
+ return conjureSymbolVal(SymbolTag, Ex, LCtx, T, Count);
+}
+
+DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const void *symbolTag,
+ const Expr *expr,
+ const LocationContext *LCtx,
+ QualType type,
+ unsigned count) {
+ if (type->isNullPtrType())
+ return makeZeroVal(type);
+
+ if (!SymbolManager::canSymbolicate(type))
+ return UnknownVal();
+
+ SymbolRef sym = SymMgr.conjureSymbol(expr, LCtx, type, count, symbolTag);
+
+ if (Loc::isLocType(type))
+ return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
+
+ return nonloc::SymbolVal(sym);
+}
+
+DefinedOrUnknownSVal SValBuilder::conjureSymbolVal(const Stmt *stmt,
+ const LocationContext *LCtx,
+ QualType type,
+ unsigned visitCount) {
+ if (type->isNullPtrType())
+ return makeZeroVal(type);
+
+ if (!SymbolManager::canSymbolicate(type))
+ return UnknownVal();
+
+ SymbolRef sym = SymMgr.conjureSymbol(stmt, LCtx, type, visitCount);
+
+ if (Loc::isLocType(type))
+ return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
+
+ return nonloc::SymbolVal(sym);
+}
+
+DefinedOrUnknownSVal
+SValBuilder::getConjuredHeapSymbolVal(const Expr *E,
+ const LocationContext *LCtx,
+ unsigned VisitCount) {
+ QualType T = E->getType();
+ assert(Loc::isLocType(T));
+ assert(SymbolManager::canSymbolicate(T));
+ if (T->isNullPtrType())
+ return makeZeroVal(T);
+
+ SymbolRef sym = SymMgr.conjureSymbol(E, LCtx, T, VisitCount);
+ return loc::MemRegionVal(MemMgr.getSymbolicHeapRegion(sym));
+}
+
+DefinedSVal SValBuilder::getMetadataSymbolVal(const void *symbolTag,
+ const MemRegion *region,
+ const Expr *expr, QualType type,
+ const LocationContext *LCtx,
+ unsigned count) {
+ assert(SymbolManager::canSymbolicate(type) && "Invalid metadata symbol type");
+
+ SymbolRef sym =
+ SymMgr.getMetadataSymbol(region, expr, type, LCtx, count, symbolTag);
+
+ if (Loc::isLocType(type))
+ return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
+
+ return nonloc::SymbolVal(sym);
+}
+
+DefinedOrUnknownSVal
+SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol,
+ const TypedValueRegion *region) {
+ QualType T = region->getValueType();
+
+ if (T->isNullPtrType())
+ return makeZeroVal(T);
+
+ if (!SymbolManager::canSymbolicate(T))
+ return UnknownVal();
+
+ SymbolRef sym = SymMgr.getDerivedSymbol(parentSymbol, region);
+
+ if (Loc::isLocType(T))
+ return loc::MemRegionVal(MemMgr.getSymbolicRegion(sym));
+
+ return nonloc::SymbolVal(sym);
+}
+
+DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) {
+ assert(!DD || isa<CXXMethodDecl>(DD) || isa<FieldDecl>(DD));
+
+ if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(DD)) {
+ // Sema treats pointers to static member functions as have function pointer
+ // type, so return a function pointer for the method.
+ // We don't need to play a similar trick for static member fields
+ // because these are represented as plain VarDecls and not FieldDecls
+ // in the AST.
+ if (MD->isStatic())
+ return getFunctionPointer(MD);
+ }
+
+ return nonloc::PointerToMember(DD);
+}
+
+DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) {
+ return loc::MemRegionVal(MemMgr.getFunctionCodeRegion(func));
+}
+
+DefinedSVal SValBuilder::getBlockPointer(const BlockDecl *block,
+ CanQualType locTy,
+ const LocationContext *locContext,
+ unsigned blockCount) {
+ const BlockCodeRegion *BC =
+ MemMgr.getBlockCodeRegion(block, locTy, locContext->getAnalysisDeclContext());
+ const BlockDataRegion *BD = MemMgr.getBlockDataRegion(BC, locContext,
+ blockCount);
+ return loc::MemRegionVal(BD);
+}
+
+/// Return a memory region for the 'this' object reference.
+loc::MemRegionVal SValBuilder::getCXXThis(const CXXMethodDecl *D,
+ const StackFrameContext *SFC) {
+ return loc::MemRegionVal(
+ getRegionManager().getCXXThisRegion(D->getThisType(), SFC));
+}
+
+/// Return a memory region for the 'this' object reference.
+loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D,
+ const StackFrameContext *SFC) {
+ const Type *T = D->getTypeForDecl();
+ QualType PT = getContext().getPointerType(QualType(T, 0));
+ return loc::MemRegionVal(getRegionManager().getCXXThisRegion(PT, SFC));
+}
+
+Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
+ E = E->IgnoreParens();
+
+ switch (E->getStmtClass()) {
+ // Handle expressions that we treat differently from the AST's constant
+ // evaluator.
+ case Stmt::AddrLabelExprClass:
+ return makeLoc(cast<AddrLabelExpr>(E));
+
+ case Stmt::CXXScalarValueInitExprClass:
+ case Stmt::ImplicitValueInitExprClass:
+ return makeZeroVal(E->getType());
+
+ case Stmt::ObjCStringLiteralClass: {
+ const auto *SL = cast<ObjCStringLiteral>(E);
+ return makeLoc(getRegionManager().getObjCStringRegion(SL));
+ }
+
+ case Stmt::StringLiteralClass: {
+ const auto *SL = cast<StringLiteral>(E);
+ return makeLoc(getRegionManager().getStringRegion(SL));
+ }
+
+ // Fast-path some expressions to avoid the overhead of going through the AST's
+ // constant evaluator
+ case Stmt::CharacterLiteralClass: {
+ const auto *C = cast<CharacterLiteral>(E);
+ return makeIntVal(C->getValue(), C->getType());
+ }
+
+ case Stmt::CXXBoolLiteralExprClass:
+ return makeBoolVal(cast<CXXBoolLiteralExpr>(E));
+
+ case Stmt::TypeTraitExprClass: {
+ const auto *TE = cast<TypeTraitExpr>(E);
+ return makeTruthVal(TE->getValue(), TE->getType());
+ }
+
+ case Stmt::IntegerLiteralClass:
+ return makeIntVal(cast<IntegerLiteral>(E));
+
+ case Stmt::ObjCBoolLiteralExprClass:
+ return makeBoolVal(cast<ObjCBoolLiteralExpr>(E));
+
+ case Stmt::CXXNullPtrLiteralExprClass:
+ return makeNull();
+
+ case Stmt::CStyleCastExprClass:
+ case Stmt::CXXFunctionalCastExprClass:
+ case Stmt::CXXConstCastExprClass:
+ case Stmt::CXXReinterpretCastExprClass:
+ case Stmt::CXXStaticCastExprClass:
+ case Stmt::ImplicitCastExprClass: {
+ const auto *CE = cast<CastExpr>(E);
+ switch (CE->getCastKind()) {
+ default:
+ break;
+ case CK_ArrayToPointerDecay:
+ case CK_IntegralToPointer:
+ case CK_NoOp:
+ case CK_BitCast: {
+ const Expr *SE = CE->getSubExpr();
+ Optional<SVal> Val = getConstantVal(SE);
+ if (!Val)
+ return None;
+ return evalCast(*Val, CE->getType(), SE->getType());
+ }
+ }
+ // FALLTHROUGH
+ LLVM_FALLTHROUGH;
+ }
+
+ // If we don't have a special case, fall back to the AST's constant evaluator.
+ default: {
+ // Don't try to come up with a value for materialized temporaries.
+ if (E->isGLValue())
+ return None;
+
+ ASTContext &Ctx = getContext();
+ Expr::EvalResult Result;
+ if (E->EvaluateAsInt(Result, Ctx))
+ return makeIntVal(Result.Val.getInt());
+
+ if (Loc::isLocType(E->getType()))
+ if (E->isNullPointerConstant(Ctx, Expr::NPC_ValueDependentIsNotNull))
+ return makeNull();
+
+ return None;
+ }
+ }
+}
+
+SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op,
+ NonLoc LHS, NonLoc RHS,
+ QualType ResultTy) {
+ const SymExpr *symLHS = LHS.getAsSymExpr();
+ const SymExpr *symRHS = RHS.getAsSymExpr();
+
+ // TODO: When the Max Complexity is reached, we should conjure a symbol
+ // instead of generating an Unknown value and propagate the taint info to it.
+ const unsigned MaxComp = StateMgr.getOwningEngine()
+ .getAnalysisManager()
+ .options.MaxSymbolComplexity;
+
+ if (symLHS && symRHS &&
+ (symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp)
+ return makeNonLoc(symLHS, Op, symRHS, ResultTy);
+
+ if (symLHS && symLHS->computeComplexity() < MaxComp)
+ if (Optional<nonloc::ConcreteInt> rInt = RHS.getAs<nonloc::ConcreteInt>())
+ return makeNonLoc(symLHS, Op, rInt->getValue(), ResultTy);
+
+ if (symRHS && symRHS->computeComplexity() < MaxComp)
+ if (Optional<nonloc::ConcreteInt> lInt = LHS.getAs<nonloc::ConcreteInt>())
+ return makeNonLoc(lInt->getValue(), Op, symRHS, ResultTy);
+
+ return UnknownVal();
+}
+
+SVal SValBuilder::evalBinOp(ProgramStateRef state, BinaryOperator::Opcode op,
+ SVal lhs, SVal rhs, QualType type) {
+ if (lhs.isUndef() || rhs.isUndef())
+ return UndefinedVal();
+
+ if (lhs.isUnknown() || rhs.isUnknown())
+ return UnknownVal();
+
+ if (lhs.getAs<nonloc::LazyCompoundVal>() ||
+ rhs.getAs<nonloc::LazyCompoundVal>()) {
+ return UnknownVal();
+ }
+
+ if (Optional<Loc> LV = lhs.getAs<Loc>()) {
+ if (Optional<Loc> RV = rhs.getAs<Loc>())
+ return evalBinOpLL(state, op, *LV, *RV, type);
+
+ return evalBinOpLN(state, op, *LV, rhs.castAs<NonLoc>(), type);
+ }
+
+ if (Optional<Loc> RV = rhs.getAs<Loc>()) {
+ // Support pointer arithmetic where the addend is on the left
+ // and the pointer on the right.
+ assert(op == BO_Add);
+
+ // Commute the operands.
+ return evalBinOpLN(state, op, *RV, lhs.castAs<NonLoc>(), type);
+ }
+
+ return evalBinOpNN(state, op, lhs.castAs<NonLoc>(), rhs.castAs<NonLoc>(),
+ type);
+}
+
+ConditionTruthVal SValBuilder::areEqual(ProgramStateRef state, SVal lhs,
+ SVal rhs) {
+ return state->isNonNull(evalEQ(state, lhs, rhs));
+}
+
+SVal SValBuilder::evalEQ(ProgramStateRef state, SVal lhs, SVal rhs) {
+ return evalBinOp(state, BO_EQ, lhs, rhs, getConditionType());
+}
+
+DefinedOrUnknownSVal SValBuilder::evalEQ(ProgramStateRef state,
+ DefinedOrUnknownSVal lhs,
+ DefinedOrUnknownSVal rhs) {
+ return evalEQ(state, static_cast<SVal>(lhs), static_cast<SVal>(rhs))
+ .castAs<DefinedOrUnknownSVal>();
+}
+
+/// Recursively check if the pointer types are equal modulo const, volatile,
+/// and restrict qualifiers. Also, assume that all types are similar to 'void'.
+/// Assumes the input types are canonical.
+static bool shouldBeModeledWithNoOp(ASTContext &Context, QualType ToTy,
+ QualType FromTy) {
+ while (Context.UnwrapSimilarTypes(ToTy, FromTy)) {
+ Qualifiers Quals1, Quals2;
+ ToTy = Context.getUnqualifiedArrayType(ToTy, Quals1);
+ FromTy = Context.getUnqualifiedArrayType(FromTy, Quals2);
+
+ // Make sure that non-cvr-qualifiers the other qualifiers (e.g., address
+ // spaces) are identical.
+ Quals1.removeCVRQualifiers();
+ Quals2.removeCVRQualifiers();
+ if (Quals1 != Quals2)
+ return false;
+ }
+
+ // If we are casting to void, the 'From' value can be used to represent the
+ // 'To' value.
+ //
+ // FIXME: Doing this after unwrapping the types doesn't make any sense. A
+ // cast from 'int**' to 'void**' is not special in the way that a cast from
+ // 'int*' to 'void*' is.
+ if (ToTy->isVoidType())
+ return true;
+
+ if (ToTy != FromTy)
+ return false;
+
+ return true;
+}
+
+// Handles casts of type CK_IntegralCast.
+// At the moment, this function will redirect to evalCast, except when the range
+// of the original value is known to be greater than the max of the target type.
+SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val,
+ QualType castTy, QualType originalTy) {
+ // No truncations if target type is big enough.
+ if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy))
+ return evalCast(val, castTy, originalTy);
+
+ const SymExpr *se = val.getAsSymbolicExpression();
+ if (!se) // Let evalCast handle non symbolic expressions.
+ return evalCast(val, castTy, originalTy);
+
+ // Find the maximum value of the target type.
+ APSIntType ToType(getContext().getTypeSize(castTy),
+ castTy->isUnsignedIntegerType());
+ llvm::APSInt ToTypeMax = ToType.getMaxValue();
+ NonLoc ToTypeMaxVal =
+ makeIntVal(ToTypeMax.isUnsigned() ? ToTypeMax.getZExtValue()
+ : ToTypeMax.getSExtValue(),
+ castTy)
+ .castAs<NonLoc>();
+ // Check the range of the symbol being casted against the maximum value of the
+ // target type.
+ NonLoc FromVal = val.castAs<NonLoc>();
+ QualType CmpTy = getConditionType();
+ NonLoc CompVal =
+ evalBinOpNN(state, BO_LE, FromVal, ToTypeMaxVal, CmpTy).castAs<NonLoc>();
+ ProgramStateRef IsNotTruncated, IsTruncated;
+ std::tie(IsNotTruncated, IsTruncated) = state->assume(CompVal);
+ if (!IsNotTruncated && IsTruncated) {
+ // Symbol is truncated so we evaluate it as a cast.
+ NonLoc CastVal = makeNonLoc(se, originalTy, castTy);
+ return CastVal;
+ }
+ return evalCast(val, castTy, originalTy);
+}
+
+// FIXME: should rewrite according to the cast kind.
+SVal SValBuilder::evalCast(SVal val, QualType castTy, QualType originalTy) {
+ castTy = Context.getCanonicalType(castTy);
+ originalTy = Context.getCanonicalType(originalTy);
+ if (val.isUnknownOrUndef() || castTy == originalTy)
+ return val;
+
+ if (castTy->isBooleanType()) {
+ if (val.isUnknownOrUndef())
+ return val;
+ if (val.isConstant())
+ return makeTruthVal(!val.isZeroConstant(), castTy);
+ if (!Loc::isLocType(originalTy) &&
+ !originalTy->isIntegralOrEnumerationType() &&
+ !originalTy->isMemberPointerType())
+ return UnknownVal();
+ if (SymbolRef Sym = val.getAsSymbol(true)) {
+ BasicValueFactory &BVF = getBasicValueFactory();
+ // FIXME: If we had a state here, we could see if the symbol is known to
+ // be zero, but we don't.
+ return makeNonLoc(Sym, BO_NE, BVF.getValue(0, Sym->getType()), castTy);
+ }
+ // Loc values are not always true, they could be weakly linked functions.
+ if (Optional<Loc> L = val.getAs<Loc>())
+ return evalCastFromLoc(*L, castTy);
+
+ Loc L = val.castAs<nonloc::LocAsInteger>().getLoc();
+ return evalCastFromLoc(L, castTy);
+ }
+
+ // For const casts, casts to void, just propagate the value.
+ if (!castTy->isVariableArrayType() && !originalTy->isVariableArrayType())
+ if (shouldBeModeledWithNoOp(Context, Context.getPointerType(castTy),
+ Context.getPointerType(originalTy)))
+ return val;
+
+ // Check for casts from pointers to integers.
+ if (castTy->isIntegralOrEnumerationType() && Loc::isLocType(originalTy))
+ return evalCastFromLoc(val.castAs<Loc>(), castTy);
+
+ // Check for casts from integers to pointers.
+ if (Loc::isLocType(castTy) && originalTy->isIntegralOrEnumerationType()) {
+ if (Optional<nonloc::LocAsInteger> LV = val.getAs<nonloc::LocAsInteger>()) {
+ if (const MemRegion *R = LV->getLoc().getAsRegion()) {
+ StoreManager &storeMgr = StateMgr.getStoreManager();
+ R = storeMgr.castRegion(R, castTy);
+ return R ? SVal(loc::MemRegionVal(R)) : UnknownVal();
+ }
+ return LV->getLoc();
+ }
+ return dispatchCast(val, castTy);
+ }
+
+ // Just pass through function and block pointers.
+ if (originalTy->isBlockPointerType() || originalTy->isFunctionPointerType()) {
+ assert(Loc::isLocType(castTy));
+ return val;
+ }
+
+ // Check for casts from array type to another type.
+ if (const auto *arrayT =
+ dyn_cast<ArrayType>(originalTy.getCanonicalType())) {
+ // We will always decay to a pointer.
+ QualType elemTy = arrayT->getElementType();
+ val = StateMgr.ArrayToPointer(val.castAs<Loc>(), elemTy);
+
+ // Are we casting from an array to a pointer? If so just pass on
+ // the decayed value.
+ if (castTy->isPointerType() || castTy->isReferenceType())
+ return val;
+
+ // Are we casting from an array to an integer? If so, cast the decayed
+ // pointer value to an integer.
+ assert(castTy->isIntegralOrEnumerationType());
+
+ // FIXME: Keep these here for now in case we decide soon that we
+ // need the original decayed type.
+ // QualType elemTy = cast<ArrayType>(originalTy)->getElementType();
+ // QualType pointerTy = C.getPointerType(elemTy);
+ return evalCastFromLoc(val.castAs<Loc>(), castTy);
+ }
+
+ // Check for casts from a region to a specific type.
+ if (const MemRegion *R = val.getAsRegion()) {
+ // Handle other casts of locations to integers.
+ if (castTy->isIntegralOrEnumerationType())
+ return evalCastFromLoc(loc::MemRegionVal(R), castTy);
+
+ // FIXME: We should handle the case where we strip off view layers to get
+ // to a desugared type.
+ if (!Loc::isLocType(castTy)) {
+ // FIXME: There can be gross cases where one casts the result of a function
+ // (that returns a pointer) to some other value that happens to fit
+ // within that pointer value. We currently have no good way to
+ // model such operations. When this happens, the underlying operation
+ // is that the caller is reasoning about bits. Conceptually we are
+ // layering a "view" of a location on top of those bits. Perhaps
+ // we need to be more lazy about mutual possible views, even on an
+ // SVal? This may be necessary for bit-level reasoning as well.
+ return UnknownVal();
+ }
+
+ // We get a symbolic function pointer for a dereference of a function
+ // pointer, but it is of function type. Example:
+
+ // struct FPRec {
+ // void (*my_func)(int * x);
+ // };
+ //
+ // int bar(int x);
+ //
+ // int f1_a(struct FPRec* foo) {
+ // int x;
+ // (*foo->my_func)(&x);
+ // return bar(x)+1; // no-warning
+ // }
+
+ assert(Loc::isLocType(originalTy) || originalTy->isFunctionType() ||
+ originalTy->isBlockPointerType() || castTy->isReferenceType());
+
+ StoreManager &storeMgr = StateMgr.getStoreManager();
+
+ // Delegate to store manager to get the result of casting a region to a
+ // different type. If the MemRegion* returned is NULL, this expression
+ // Evaluates to UnknownVal.
+ R = storeMgr.castRegion(R, castTy);
+ return R ? SVal(loc::MemRegionVal(R)) : UnknownVal();
+ }
+
+ return dispatchCast(val, castTy);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp
new file mode 100644
index 000000000000..933c5c330072
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SVals.cpp
@@ -0,0 +1,384 @@
+//===- RValues.cpp - Abstract RValues for Path-Sens. Value Tracking -------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines SVal, Loc, and NonLoc, classes that represent
+// abstract r-values for use with path-sensitive value tracking.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+
+using namespace clang;
+using namespace ento;
+
+//===----------------------------------------------------------------------===//
+// Symbol iteration within an SVal.
+//===----------------------------------------------------------------------===//
+
+//===----------------------------------------------------------------------===//
+// Utility methods.
+//===----------------------------------------------------------------------===//
+
+bool SVal::hasConjuredSymbol() const {
+ if (Optional<nonloc::SymbolVal> SV = getAs<nonloc::SymbolVal>()) {
+ SymbolRef sym = SV->getSymbol();
+ if (isa<SymbolConjured>(sym))
+ return true;
+ }
+
+ if (Optional<loc::MemRegionVal> RV = getAs<loc::MemRegionVal>()) {
+ const MemRegion *R = RV->getRegion();
+ if (const auto *SR = dyn_cast<SymbolicRegion>(R)) {
+ SymbolRef sym = SR->getSymbol();
+ if (isa<SymbolConjured>(sym))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+const FunctionDecl *SVal::getAsFunctionDecl() const {
+ if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) {
+ const MemRegion* R = X->getRegion();
+ if (const FunctionCodeRegion *CTR = R->getAs<FunctionCodeRegion>())
+ if (const auto *FD = dyn_cast<FunctionDecl>(CTR->getDecl()))
+ return FD;
+ }
+
+ if (auto X = getAs<nonloc::PointerToMember>()) {
+ if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(X->getDecl()))
+ return MD;
+ }
+ return nullptr;
+}
+
+/// If this SVal is a location (subclasses Loc) and wraps a symbol,
+/// return that SymbolRef. Otherwise return 0.
+///
+/// Implicit casts (ex: void* -> char*) can turn Symbolic region into Element
+/// region. If that is the case, gets the underlining region.
+/// When IncludeBaseRegions is set to true and the SubRegion is non-symbolic,
+/// the first symbolic parent region is returned.
+SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const {
+ // FIXME: should we consider SymbolRef wrapped in CodeTextRegion?
+ if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>())
+ return X->getLoc().getAsLocSymbol(IncludeBaseRegions);
+
+ if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) {
+ const MemRegion *R = X->getRegion();
+ if (const SymbolicRegion *SymR = IncludeBaseRegions ?
+ R->getSymbolicBase() :
+ dyn_cast<SymbolicRegion>(R->StripCasts()))
+ return SymR->getSymbol();
+ }
+ return nullptr;
+}
+
+/// Get the symbol in the SVal or its base region.
+SymbolRef SVal::getLocSymbolInBase() const {
+ Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>();
+
+ if (!X)
+ return nullptr;
+
+ const MemRegion *R = X->getRegion();
+
+ while (const auto *SR = dyn_cast<SubRegion>(R)) {
+ if (const auto *SymR = dyn_cast<SymbolicRegion>(SR))
+ return SymR->getSymbol();
+ else
+ R = SR->getSuperRegion();
+ }
+
+ return nullptr;
+}
+
+// TODO: The next 3 functions have to be simplified.
+
+/// If this SVal wraps a symbol return that SymbolRef.
+/// Otherwise, return 0.
+///
+/// Casts are ignored during lookup.
+/// \param IncludeBaseRegions The boolean that controls whether the search
+/// should continue to the base regions if the region is not symbolic.
+SymbolRef SVal::getAsSymbol(bool IncludeBaseRegions) const {
+ // FIXME: should we consider SymbolRef wrapped in CodeTextRegion?
+ if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>())
+ return X->getSymbol();
+
+ return getAsLocSymbol(IncludeBaseRegions);
+}
+
+/// getAsSymbolicExpression - If this Sval wraps a symbolic expression then
+/// return that expression. Otherwise return NULL.
+const SymExpr *SVal::getAsSymbolicExpression() const {
+ if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>())
+ return X->getSymbol();
+
+ return getAsSymbol();
+}
+
+const SymExpr* SVal::getAsSymExpr() const {
+ const SymExpr* Sym = getAsSymbol();
+ if (!Sym)
+ Sym = getAsSymbolicExpression();
+ return Sym;
+}
+
+const MemRegion *SVal::getAsRegion() const {
+ if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>())
+ return X->getRegion();
+
+ if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>())
+ return X->getLoc().getAsRegion();
+
+ return nullptr;
+}
+
+const MemRegion *loc::MemRegionVal::stripCasts(bool StripBaseCasts) const {
+ const MemRegion *R = getRegion();
+ return R ? R->StripCasts(StripBaseCasts) : nullptr;
+}
+
+const void *nonloc::LazyCompoundVal::getStore() const {
+ return static_cast<const LazyCompoundValData*>(Data)->getStore();
+}
+
+const TypedValueRegion *nonloc::LazyCompoundVal::getRegion() const {
+ return static_cast<const LazyCompoundValData*>(Data)->getRegion();
+}
+
+bool nonloc::PointerToMember::isNullMemberPointer() const {
+ return getPTMData().isNull();
+}
+
+const DeclaratorDecl *nonloc::PointerToMember::getDecl() const {
+ const auto PTMD = this->getPTMData();
+ if (PTMD.isNull())
+ return nullptr;
+
+ const DeclaratorDecl *DD = nullptr;
+ if (PTMD.is<const DeclaratorDecl *>())
+ DD = PTMD.get<const DeclaratorDecl *>();
+ else
+ DD = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl();
+
+ return DD;
+}
+
+//===----------------------------------------------------------------------===//
+// Other Iterators.
+//===----------------------------------------------------------------------===//
+
+nonloc::CompoundVal::iterator nonloc::CompoundVal::begin() const {
+ return getValue()->begin();
+}
+
+nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const {
+ return getValue()->end();
+}
+
+nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const {
+ const PTMDataType PTMD = getPTMData();
+ if (PTMD.is<const DeclaratorDecl *>())
+ return {};
+ return PTMD.get<const PointerToMemberData *>()->begin();
+}
+
+nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const {
+ const PTMDataType PTMD = getPTMData();
+ if (PTMD.is<const DeclaratorDecl *>())
+ return {};
+ return PTMD.get<const PointerToMemberData *>()->end();
+}
+
+//===----------------------------------------------------------------------===//
+// Useful predicates.
+//===----------------------------------------------------------------------===//
+
+bool SVal::isConstant() const {
+ return getAs<nonloc::ConcreteInt>() || getAs<loc::ConcreteInt>();
+}
+
+bool SVal::isConstant(int I) const {
+ if (Optional<loc::ConcreteInt> LV = getAs<loc::ConcreteInt>())
+ return LV->getValue() == I;
+ if (Optional<nonloc::ConcreteInt> NV = getAs<nonloc::ConcreteInt>())
+ return NV->getValue() == I;
+ return false;
+}
+
+bool SVal::isZeroConstant() const {
+ return isConstant(0);
+}
+
+//===----------------------------------------------------------------------===//
+// Transfer function dispatch for Non-Locs.
+//===----------------------------------------------------------------------===//
+
+SVal nonloc::ConcreteInt::evalBinOp(SValBuilder &svalBuilder,
+ BinaryOperator::Opcode Op,
+ const nonloc::ConcreteInt& R) const {
+ const llvm::APSInt* X =
+ svalBuilder.getBasicValueFactory().evalAPSInt(Op, getValue(), R.getValue());
+
+ if (X)
+ return nonloc::ConcreteInt(*X);
+ else
+ return UndefinedVal();
+}
+
+nonloc::ConcreteInt
+nonloc::ConcreteInt::evalComplement(SValBuilder &svalBuilder) const {
+ return svalBuilder.makeIntVal(~getValue());
+}
+
+nonloc::ConcreteInt
+nonloc::ConcreteInt::evalMinus(SValBuilder &svalBuilder) const {
+ return svalBuilder.makeIntVal(-getValue());
+}
+
+//===----------------------------------------------------------------------===//
+// Transfer function dispatch for Locs.
+//===----------------------------------------------------------------------===//
+
+SVal loc::ConcreteInt::evalBinOp(BasicValueFactory& BasicVals,
+ BinaryOperator::Opcode Op,
+ const loc::ConcreteInt& R) const {
+ assert(BinaryOperator::isComparisonOp(Op) || Op == BO_Sub);
+
+ const llvm::APSInt *X = BasicVals.evalAPSInt(Op, getValue(), R.getValue());
+
+ if (X)
+ return nonloc::ConcreteInt(*X);
+ else
+ return UndefinedVal();
+}
+
+//===----------------------------------------------------------------------===//
+// Pretty-Printing.
+//===----------------------------------------------------------------------===//
+
+LLVM_DUMP_METHOD void SVal::dump() const { dumpToStream(llvm::errs()); }
+
+void SVal::dumpToStream(raw_ostream &os) const {
+ switch (getBaseKind()) {
+ case UnknownValKind:
+ os << "Unknown";
+ break;
+ case NonLocKind:
+ castAs<NonLoc>().dumpToStream(os);
+ break;
+ case LocKind:
+ castAs<Loc>().dumpToStream(os);
+ break;
+ case UndefinedValKind:
+ os << "Undefined";
+ break;
+ }
+}
+
+void NonLoc::dumpToStream(raw_ostream &os) const {
+ switch (getSubKind()) {
+ case nonloc::ConcreteIntKind: {
+ const auto &Value = castAs<nonloc::ConcreteInt>().getValue();
+ os << Value << ' ' << (Value.isSigned() ? 'S' : 'U')
+ << Value.getBitWidth() << 'b';
+ break;
+ }
+ case nonloc::SymbolValKind:
+ os << castAs<nonloc::SymbolVal>().getSymbol();
+ break;
+
+ case nonloc::LocAsIntegerKind: {
+ const nonloc::LocAsInteger& C = castAs<nonloc::LocAsInteger>();
+ os << C.getLoc() << " [as " << C.getNumBits() << " bit integer]";
+ break;
+ }
+ case nonloc::CompoundValKind: {
+ const nonloc::CompoundVal& C = castAs<nonloc::CompoundVal>();
+ os << "compoundVal{";
+ bool first = true;
+ for (const auto &I : C) {
+ if (first) {
+ os << ' '; first = false;
+ }
+ else
+ os << ", ";
+
+ I.dumpToStream(os);
+ }
+ os << "}";
+ break;
+ }
+ case nonloc::LazyCompoundValKind: {
+ const nonloc::LazyCompoundVal &C = castAs<nonloc::LazyCompoundVal>();
+ os << "lazyCompoundVal{" << const_cast<void *>(C.getStore())
+ << ',' << C.getRegion()
+ << '}';
+ break;
+ }
+ case nonloc::PointerToMemberKind: {
+ os << "pointerToMember{";
+ const nonloc::PointerToMember &CastRes =
+ castAs<nonloc::PointerToMember>();
+ if (CastRes.getDecl())
+ os << "|" << CastRes.getDecl()->getQualifiedNameAsString() << "|";
+ bool first = true;
+ for (const auto &I : CastRes) {
+ if (first) {
+ os << ' '; first = false;
+ }
+ else
+ os << ", ";
+
+ os << (*I).getType().getAsString();
+ }
+
+ os << '}';
+ break;
+ }
+ default:
+ assert(false && "Pretty-printed not implemented for this NonLoc.");
+ break;
+ }
+}
+
+void Loc::dumpToStream(raw_ostream &os) const {
+ switch (getSubKind()) {
+ case loc::ConcreteIntKind:
+ os << castAs<loc::ConcreteInt>().getValue().getZExtValue() << " (Loc)";
+ break;
+ case loc::GotoLabelKind:
+ os << "&&" << castAs<loc::GotoLabel>().getLabel()->getName();
+ break;
+ case loc::MemRegionValKind:
+ os << '&' << castAs<loc::MemRegionVal>().getRegion()->getString();
+ break;
+ default:
+ llvm_unreachable("Pretty-printing not implemented for this Loc.");
+ }
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
new file mode 100644
index 000000000000..fecbc0001079
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
@@ -0,0 +1,349 @@
+//===--- SarifDiagnostics.cpp - Sarif Diagnostics for Paths -----*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines the SarifDiagnostics object.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/Version.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Path.h"
+
+using namespace llvm;
+using namespace clang;
+using namespace ento;
+
+namespace {
+class SarifDiagnostics : public PathDiagnosticConsumer {
+ std::string OutputFile;
+
+public:
+ SarifDiagnostics(AnalyzerOptions &, const std::string &Output)
+ : OutputFile(Output) {}
+ ~SarifDiagnostics() override = default;
+
+ void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
+ FilesMade *FM) override;
+
+ StringRef getName() const override { return "SarifDiagnostics"; }
+ PathGenerationScheme getGenerationScheme() const override { return Minimal; }
+ bool supportsLogicalOpControlFlow() const override { return true; }
+ bool supportsCrossFileDiagnostics() const override { return true; }
+};
+} // end anonymous namespace
+
+void ento::createSarifDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
+ PathDiagnosticConsumers &C,
+ const std::string &Output,
+ const Preprocessor &) {
+ C.push_back(new SarifDiagnostics(AnalyzerOpts, Output));
+}
+
+static StringRef getFileName(const FileEntry &FE) {
+ StringRef Filename = FE.tryGetRealPathName();
+ if (Filename.empty())
+ Filename = FE.getName();
+ return Filename;
+}
+
+static std::string percentEncodeURICharacter(char C) {
+ // RFC 3986 claims alpha, numeric, and this handful of
+ // characters are not reserved for the path component and
+ // should be written out directly. Otherwise, percent
+ // encode the character and write that out instead of the
+ // reserved character.
+ if (llvm::isAlnum(C) ||
+ StringRef::npos != StringRef("-._~:@!$&'()*+,;=").find(C))
+ return std::string(&C, 1);
+ return "%" + llvm::toHex(StringRef(&C, 1));
+}
+
+static std::string fileNameToURI(StringRef Filename) {
+ llvm::SmallString<32> Ret = StringRef("file://");
+
+ // Get the root name to see if it has a URI authority.
+ StringRef Root = sys::path::root_name(Filename);
+ if (Root.startswith("//")) {
+ // There is an authority, so add it to the URI.
+ Ret += Root.drop_front(2).str();
+ } else if (!Root.empty()) {
+ // There is no authority, so end the component and add the root to the URI.
+ Ret += Twine("/" + Root).str();
+ }
+
+ auto Iter = sys::path::begin(Filename), End = sys::path::end(Filename);
+ assert(Iter != End && "Expected there to be a non-root path component.");
+ // Add the rest of the path components, encoding any reserved characters;
+ // we skip past the first path component, as it was handled it above.
+ std::for_each(++Iter, End, [&Ret](StringRef Component) {
+ // For reasons unknown to me, we may get a backslash with Windows native
+ // paths for the initial backslash following the drive component, which
+ // we need to ignore as a URI path part.
+ if (Component == "\\")
+ return;
+
+ // Add the separator between the previous path part and the one being
+ // currently processed.
+ Ret += "/";
+
+ // URI encode the part.
+ for (char C : Component) {
+ Ret += percentEncodeURICharacter(C);
+ }
+ });
+
+ return Ret.str().str();
+}
+
+static json::Object createFileLocation(const FileEntry &FE) {
+ return json::Object{{"uri", fileNameToURI(getFileName(FE))}};
+}
+
+static json::Object createFile(const FileEntry &FE) {
+ return json::Object{{"fileLocation", createFileLocation(FE)},
+ {"roles", json::Array{"resultFile"}},
+ {"length", FE.getSize()},
+ {"mimeType", "text/plain"}};
+}
+
+static json::Object createFileLocation(const FileEntry &FE,
+ json::Array &Files) {
+ std::string FileURI = fileNameToURI(getFileName(FE));
+
+ // See if the Files array contains this URI already. If it does not, create
+ // a new file object to add to the array.
+ auto I = llvm::find_if(Files, [&](const json::Value &File) {
+ if (const json::Object *Obj = File.getAsObject()) {
+ if (const json::Object *FileLoc = Obj->getObject("fileLocation")) {
+ Optional<StringRef> URI = FileLoc->getString("uri");
+ return URI && URI->equals(FileURI);
+ }
+ }
+ return false;
+ });
+
+ // Calculate the index within the file location array so it can be stored in
+ // the JSON object.
+ auto Index = static_cast<unsigned>(std::distance(Files.begin(), I));
+ if (I == Files.end())
+ Files.push_back(createFile(FE));
+
+ return json::Object{{"uri", FileURI}, {"fileIndex", Index}};
+}
+
+static json::Object createTextRegion(SourceRange R, const SourceManager &SM) {
+ return json::Object{
+ {"startLine", SM.getExpansionLineNumber(R.getBegin())},
+ {"endLine", SM.getExpansionLineNumber(R.getEnd())},
+ {"startColumn", SM.getExpansionColumnNumber(R.getBegin())},
+ {"endColumn", SM.getExpansionColumnNumber(R.getEnd())}};
+}
+
+static json::Object createPhysicalLocation(SourceRange R, const FileEntry &FE,
+ const SourceManager &SMgr,
+ json::Array &Files) {
+ return json::Object{{{"fileLocation", createFileLocation(FE, Files)},
+ {"region", createTextRegion(R, SMgr)}}};
+}
+
+enum class Importance { Important, Essential, Unimportant };
+
+static StringRef importanceToStr(Importance I) {
+ switch (I) {
+ case Importance::Important:
+ return "important";
+ case Importance::Essential:
+ return "essential";
+ case Importance::Unimportant:
+ return "unimportant";
+ }
+ llvm_unreachable("Fully covered switch is not so fully covered");
+}
+
+static json::Object createThreadFlowLocation(json::Object &&Location,
+ Importance I) {
+ return json::Object{{"location", std::move(Location)},
+ {"importance", importanceToStr(I)}};
+}
+
+static json::Object createMessage(StringRef Text) {
+ return json::Object{{"text", Text.str()}};
+}
+
+static json::Object createLocation(json::Object &&PhysicalLocation,
+ StringRef Message = "") {
+ json::Object Ret{{"physicalLocation", std::move(PhysicalLocation)}};
+ if (!Message.empty())
+ Ret.insert({"message", createMessage(Message)});
+ return Ret;
+}
+
+static Importance calculateImportance(const PathDiagnosticPiece &Piece) {
+ switch (Piece.getKind()) {
+ case PathDiagnosticPiece::Kind::Call:
+ case PathDiagnosticPiece::Kind::Macro:
+ case PathDiagnosticPiece::Kind::Note:
+ // FIXME: What should be reported here?
+ break;
+ case PathDiagnosticPiece::Kind::Event:
+ return Piece.getTagStr() == "ConditionBRVisitor" ? Importance::Important
+ : Importance::Essential;
+ case PathDiagnosticPiece::Kind::ControlFlow:
+ return Importance::Unimportant;
+ }
+ return Importance::Unimportant;
+}
+
+static json::Object createThreadFlow(const PathPieces &Pieces,
+ json::Array &Files) {
+ const SourceManager &SMgr = Pieces.front()->getLocation().getManager();
+ json::Array Locations;
+ for (const auto &Piece : Pieces) {
+ const PathDiagnosticLocation &P = Piece->getLocation();
+ Locations.push_back(createThreadFlowLocation(
+ createLocation(createPhysicalLocation(P.asRange(),
+ *P.asLocation().getFileEntry(),
+ SMgr, Files),
+ Piece->getString()),
+ calculateImportance(*Piece)));
+ }
+ return json::Object{{"locations", std::move(Locations)}};
+}
+
+static json::Object createCodeFlow(const PathPieces &Pieces,
+ json::Array &Files) {
+ return json::Object{
+ {"threadFlows", json::Array{createThreadFlow(Pieces, Files)}}};
+}
+
+static json::Object createTool() {
+ return json::Object{{"name", "clang"},
+ {"fullName", "clang static analyzer"},
+ {"language", "en-US"},
+ {"version", getClangFullVersion()}};
+}
+
+static json::Object createResult(const PathDiagnostic &Diag, json::Array &Files,
+ const StringMap<unsigned> &RuleMapping) {
+ const PathPieces &Path = Diag.path.flatten(false);
+ const SourceManager &SMgr = Path.front()->getLocation().getManager();
+
+ auto Iter = RuleMapping.find(Diag.getCheckName());
+ assert(Iter != RuleMapping.end() && "Rule ID is not in the array index map?");
+
+ return json::Object{
+ {"message", createMessage(Diag.getVerboseDescription())},
+ {"codeFlows", json::Array{createCodeFlow(Path, Files)}},
+ {"locations",
+ json::Array{createLocation(createPhysicalLocation(
+ Diag.getLocation().asRange(),
+ *Diag.getLocation().asLocation().getFileEntry(), SMgr, Files))}},
+ {"ruleIndex", Iter->getValue()},
+ {"ruleId", Diag.getCheckName()}};
+}
+
+static StringRef getRuleDescription(StringRef CheckName) {
+ return llvm::StringSwitch<StringRef>(CheckName)
+#define GET_CHECKERS
+#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \
+ .Case(FULLNAME, HELPTEXT)
+#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
+#undef CHECKER
+#undef GET_CHECKERS
+ ;
+}
+
+static StringRef getRuleHelpURIStr(StringRef CheckName) {
+ return llvm::StringSwitch<StringRef>(CheckName)
+#define GET_CHECKERS
+#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \
+ .Case(FULLNAME, DOC_URI)
+#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
+#undef CHECKER
+#undef GET_CHECKERS
+ ;
+}
+
+static json::Object createRule(const PathDiagnostic &Diag) {
+ StringRef CheckName = Diag.getCheckName();
+ json::Object Ret{
+ {"fullDescription", createMessage(getRuleDescription(CheckName))},
+ {"name", createMessage(CheckName)},
+ {"id", CheckName}};
+
+ std::string RuleURI = getRuleHelpURIStr(CheckName);
+ if (!RuleURI.empty())
+ Ret["helpUri"] = RuleURI;
+
+ return Ret;
+}
+
+static json::Array createRules(std::vector<const PathDiagnostic *> &Diags,
+ StringMap<unsigned> &RuleMapping) {
+ json::Array Rules;
+ llvm::StringSet<> Seen;
+
+ llvm::for_each(Diags, [&](const PathDiagnostic *D) {
+ StringRef RuleID = D->getCheckName();
+ std::pair<llvm::StringSet<>::iterator, bool> P = Seen.insert(RuleID);
+ if (P.second) {
+ RuleMapping[RuleID] = Rules.size(); // Maps RuleID to an Array Index.
+ Rules.push_back(createRule(*D));
+ }
+ });
+
+ return Rules;
+}
+
+static json::Object createResources(std::vector<const PathDiagnostic *> &Diags,
+ StringMap<unsigned> &RuleMapping) {
+ return json::Object{{"rules", createRules(Diags, RuleMapping)}};
+}
+
+static json::Object createRun(std::vector<const PathDiagnostic *> &Diags) {
+ json::Array Results, Files;
+ StringMap<unsigned> RuleMapping;
+ json::Object Resources = createResources(Diags, RuleMapping);
+
+ llvm::for_each(Diags, [&](const PathDiagnostic *D) {
+ Results.push_back(createResult(*D, Files, RuleMapping));
+ });
+
+ return json::Object{{"tool", createTool()},
+ {"resources", std::move(Resources)},
+ {"results", std::move(Results)},
+ {"files", std::move(Files)}};
+}
+
+void SarifDiagnostics::FlushDiagnosticsImpl(
+ std::vector<const PathDiagnostic *> &Diags, FilesMade *) {
+ // We currently overwrite the file if it already exists. However, it may be
+ // useful to add a feature someday that allows the user to append a run to an
+ // existing SARIF file. One danger from that approach is that the size of the
+ // file can become large very quickly, so decoding into JSON to append a run
+ // may be an expensive operation.
+ std::error_code EC;
+ llvm::raw_fd_ostream OS(OutputFile, EC, llvm::sys::fs::F_Text);
+ if (EC) {
+ llvm::errs() << "warning: could not create file: " << EC.message() << '\n';
+ return;
+ }
+ json::Object Sarif{
+ {"$schema",
+ "http://json.schemastore.org/sarif-2.0.0-csd.2.beta.2018-11-28"},
+ {"version", "2.0.0-csd.2.beta.2018-11-28"},
+ {"runs", json::Array{createRun(Diags)}}};
+ OS << llvm::formatv("{0:2}", json::Value(std::move(Sarif)));
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
new file mode 100644
index 000000000000..adb40178f5b1
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -0,0 +1,133 @@
+//== SimpleConstraintManager.cpp --------------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines SimpleConstraintManager, a class that provides a
+// simplified constraint manager interface, compared to ConstraintManager.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+
+namespace clang {
+
+namespace ento {
+
+SimpleConstraintManager::~SimpleConstraintManager() {}
+
+ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
+ DefinedSVal Cond,
+ bool Assumption) {
+ // If we have a Loc value, cast it to a bool NonLoc first.
+ if (Optional<Loc> LV = Cond.getAs<Loc>()) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ QualType T;
+ const MemRegion *MR = LV->getAsRegion();
+ if (const TypedRegion *TR = dyn_cast_or_null<TypedRegion>(MR))
+ T = TR->getLocationType();
+ else
+ T = SVB.getContext().VoidPtrTy;
+
+ Cond = SVB.evalCast(*LV, SVB.getContext().BoolTy, T).castAs<DefinedSVal>();
+ }
+
+ return assume(State, Cond.castAs<NonLoc>(), Assumption);
+}
+
+ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
+ NonLoc Cond, bool Assumption) {
+ State = assumeAux(State, Cond, Assumption);
+ if (NotifyAssumeClients && SU)
+ return SU->processAssume(State, Cond, Assumption);
+ return State;
+}
+
+ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State,
+ NonLoc Cond,
+ bool Assumption) {
+
+ // We cannot reason about SymSymExprs, and can only reason about some
+ // SymIntExprs.
+ if (!canReasonAbout(Cond)) {
+ // Just add the constraint to the expression without trying to simplify.
+ SymbolRef Sym = Cond.getAsSymExpr();
+ assert(Sym);
+ return assumeSymUnsupported(State, Sym, Assumption);
+ }
+
+ switch (Cond.getSubKind()) {
+ default:
+ llvm_unreachable("'Assume' not implemented for this NonLoc");
+
+ case nonloc::SymbolValKind: {
+ nonloc::SymbolVal SV = Cond.castAs<nonloc::SymbolVal>();
+ SymbolRef Sym = SV.getSymbol();
+ assert(Sym);
+ return assumeSym(State, Sym, Assumption);
+ }
+
+ case nonloc::ConcreteIntKind: {
+ bool b = Cond.castAs<nonloc::ConcreteInt>().getValue() != 0;
+ bool isFeasible = b ? Assumption : !Assumption;
+ return isFeasible ? State : nullptr;
+ }
+
+ case nonloc::PointerToMemberKind: {
+ bool IsNull = !Cond.castAs<nonloc::PointerToMember>().isNullMemberPointer();
+ bool IsFeasible = IsNull ? Assumption : !Assumption;
+ return IsFeasible ? State : nullptr;
+ }
+
+ case nonloc::LocAsIntegerKind:
+ return assume(State, Cond.castAs<nonloc::LocAsInteger>().getLoc(),
+ Assumption);
+ } // end switch
+}
+
+ProgramStateRef SimpleConstraintManager::assumeInclusiveRange(
+ ProgramStateRef State, NonLoc Value, const llvm::APSInt &From,
+ const llvm::APSInt &To, bool InRange) {
+
+ assert(From.isUnsigned() == To.isUnsigned() &&
+ From.getBitWidth() == To.getBitWidth() &&
+ "Values should have same types!");
+
+ if (!canReasonAbout(Value)) {
+ // Just add the constraint to the expression without trying to simplify.
+ SymbolRef Sym = Value.getAsSymExpr();
+ assert(Sym);
+ return assumeSymInclusiveRange(State, Sym, From, To, InRange);
+ }
+
+ switch (Value.getSubKind()) {
+ default:
+ llvm_unreachable("'assumeInclusiveRange' is not implemented"
+ "for this NonLoc");
+
+ case nonloc::LocAsIntegerKind:
+ case nonloc::SymbolValKind: {
+ if (SymbolRef Sym = Value.getAsSymbol())
+ return assumeSymInclusiveRange(State, Sym, From, To, InRange);
+ return State;
+ } // end switch
+
+ case nonloc::ConcreteIntKind: {
+ const llvm::APSInt &IntVal = Value.castAs<nonloc::ConcreteInt>().getValue();
+ bool IsInRange = IntVal >= From && IntVal <= To;
+ bool isFeasible = (IsInRange == InRange);
+ return isFeasible ? State : nullptr;
+ }
+ } // end switch
+}
+
+} // end of namespace ento
+
+} // end of namespace clang
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
new file mode 100644
index 000000000000..fc57cecac9cb
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -0,0 +1,1343 @@
+// SimpleSValBuilder.cpp - A basic SValBuilder -----------------------*- C++ -*-
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines SimpleSValBuilder, a basic implementation of SValBuilder.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class SimpleSValBuilder : public SValBuilder {
+protected:
+ SVal dispatchCast(SVal val, QualType castTy) override;
+ SVal evalCastFromNonLoc(NonLoc val, QualType castTy) override;
+ SVal evalCastFromLoc(Loc val, QualType castTy) override;
+
+public:
+ SimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context,
+ ProgramStateManager &stateMgr)
+ : SValBuilder(alloc, context, stateMgr) {}
+ ~SimpleSValBuilder() override {}
+
+ SVal evalMinus(NonLoc val) override;
+ SVal evalComplement(NonLoc val) override;
+ SVal evalBinOpNN(ProgramStateRef state, BinaryOperator::Opcode op,
+ NonLoc lhs, NonLoc rhs, QualType resultTy) override;
+ SVal evalBinOpLL(ProgramStateRef state, BinaryOperator::Opcode op,
+ Loc lhs, Loc rhs, QualType resultTy) override;
+ SVal evalBinOpLN(ProgramStateRef state, BinaryOperator::Opcode op,
+ Loc lhs, NonLoc rhs, QualType resultTy) override;
+
+ /// getKnownValue - evaluates a given SVal. If the SVal has only one possible
+ /// (integer) value, that value is returned. Otherwise, returns NULL.
+ const llvm::APSInt *getKnownValue(ProgramStateRef state, SVal V) override;
+
+ /// Recursively descends into symbolic expressions and replaces symbols
+ /// with their known values (in the sense of the getKnownValue() method).
+ SVal simplifySVal(ProgramStateRef State, SVal V) override;
+
+ SVal MakeSymIntVal(const SymExpr *LHS, BinaryOperator::Opcode op,
+ const llvm::APSInt &RHS, QualType resultTy);
+};
+} // end anonymous namespace
+
+SValBuilder *ento::createSimpleSValBuilder(llvm::BumpPtrAllocator &alloc,
+ ASTContext &context,
+ ProgramStateManager &stateMgr) {
+ return new SimpleSValBuilder(alloc, context, stateMgr);
+}
+
+//===----------------------------------------------------------------------===//
+// Transfer function for Casts.
+//===----------------------------------------------------------------------===//
+
+SVal SimpleSValBuilder::dispatchCast(SVal Val, QualType CastTy) {
+ assert(Val.getAs<Loc>() || Val.getAs<NonLoc>());
+ return Val.getAs<Loc>() ? evalCastFromLoc(Val.castAs<Loc>(), CastTy)
+ : evalCastFromNonLoc(Val.castAs<NonLoc>(), CastTy);
+}
+
+SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) {
+ bool isLocType = Loc::isLocType(castTy);
+ if (val.getAs<nonloc::PointerToMember>())
+ return val;
+
+ if (Optional<nonloc::LocAsInteger> LI = val.getAs<nonloc::LocAsInteger>()) {
+ if (isLocType)
+ return LI->getLoc();
+ // FIXME: Correctly support promotions/truncations.
+ unsigned castSize = Context.getIntWidth(castTy);
+ if (castSize == LI->getNumBits())
+ return val;
+ return makeLocAsInteger(LI->getLoc(), castSize);
+ }
+
+ if (const SymExpr *se = val.getAsSymbolicExpression()) {
+ QualType T = Context.getCanonicalType(se->getType());
+ // If types are the same or both are integers, ignore the cast.
+ // FIXME: Remove this hack when we support symbolic truncation/extension.
+ // HACK: If both castTy and T are integers, ignore the cast. This is
+ // not a permanent solution. Eventually we want to precisely handle
+ // extension/truncation of symbolic integers. This prevents us from losing
+ // precision when we assign 'x = y' and 'y' is symbolic and x and y are
+ // different integer types.
+ if (haveSameType(T, castTy))
+ return val;
+
+ if (!isLocType)
+ return makeNonLoc(se, T, castTy);
+ return UnknownVal();
+ }
+
+ // If value is a non-integer constant, produce unknown.
+ if (!val.getAs<nonloc::ConcreteInt>())
+ return UnknownVal();
+
+ // Handle casts to a boolean type.
+ if (castTy->isBooleanType()) {
+ bool b = val.castAs<nonloc::ConcreteInt>().getValue().getBoolValue();
+ return makeTruthVal(b, castTy);
+ }
+
+ // Only handle casts from integers to integers - if val is an integer constant
+ // being cast to a non-integer type, produce unknown.
+ if (!isLocType && !castTy->isIntegralOrEnumerationType())
+ return UnknownVal();
+
+ llvm::APSInt i = val.castAs<nonloc::ConcreteInt>().getValue();
+ BasicVals.getAPSIntType(castTy).apply(i);
+
+ if (isLocType)
+ return makeIntLocVal(i);
+ else
+ return makeIntVal(i);
+}
+
+SVal SimpleSValBuilder::evalCastFromLoc(Loc val, QualType castTy) {
+
+ // Casts from pointers -> pointers, just return the lval.
+ //
+ // Casts from pointers -> references, just return the lval. These
+ // can be introduced by the frontend for corner cases, e.g
+ // casting from va_list* to __builtin_va_list&.
+ //
+ if (Loc::isLocType(castTy) || castTy->isReferenceType())
+ return val;
+
+ // FIXME: Handle transparent unions where a value can be "transparently"
+ // lifted into a union type.
+ if (castTy->isUnionType())
+ return UnknownVal();
+
+ // Casting a Loc to a bool will almost always be true,
+ // unless this is a weak function or a symbolic region.
+ if (castTy->isBooleanType()) {
+ switch (val.getSubKind()) {
+ case loc::MemRegionValKind: {
+ const MemRegion *R = val.castAs<loc::MemRegionVal>().getRegion();
+ if (const FunctionCodeRegion *FTR = dyn_cast<FunctionCodeRegion>(R))
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(FTR->getDecl()))
+ if (FD->isWeak())
+ // FIXME: Currently we are using an extent symbol here,
+ // because there are no generic region address metadata
+ // symbols to use, only content metadata.
+ return nonloc::SymbolVal(SymMgr.getExtentSymbol(FTR));
+
+ if (const SymbolicRegion *SymR = R->getSymbolicBase())
+ return makeNonLoc(SymR->getSymbol(), BO_NE,
+ BasicVals.getZeroWithPtrWidth(), castTy);
+
+ // FALL-THROUGH
+ LLVM_FALLTHROUGH;
+ }
+
+ case loc::GotoLabelKind:
+ // Labels and non-symbolic memory regions are always true.
+ return makeTruthVal(true, castTy);
+ }
+ }
+
+ if (castTy->isIntegralOrEnumerationType()) {
+ unsigned BitWidth = Context.getIntWidth(castTy);
+
+ if (!val.getAs<loc::ConcreteInt>())
+ return makeLocAsInteger(val, BitWidth);
+
+ llvm::APSInt i = val.castAs<loc::ConcreteInt>().getValue();
+ BasicVals.getAPSIntType(castTy).apply(i);
+ return makeIntVal(i);
+ }
+
+ // All other cases: return 'UnknownVal'. This includes casting pointers
+ // to floats, which is probably badness it itself, but this is a good
+ // intermediate solution until we do something better.
+ return UnknownVal();
+}
+
+//===----------------------------------------------------------------------===//
+// Transfer function for unary operators.
+//===----------------------------------------------------------------------===//
+
+SVal SimpleSValBuilder::evalMinus(NonLoc val) {
+ switch (val.getSubKind()) {
+ case nonloc::ConcreteIntKind:
+ return val.castAs<nonloc::ConcreteInt>().evalMinus(*this);
+ default:
+ return UnknownVal();
+ }
+}
+
+SVal SimpleSValBuilder::evalComplement(NonLoc X) {
+ switch (X.getSubKind()) {
+ case nonloc::ConcreteIntKind:
+ return X.castAs<nonloc::ConcreteInt>().evalComplement(*this);
+ default:
+ return UnknownVal();
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Transfer function for binary operators.
+//===----------------------------------------------------------------------===//
+
+SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS,
+ BinaryOperator::Opcode op,
+ const llvm::APSInt &RHS,
+ QualType resultTy) {
+ bool isIdempotent = false;
+
+ // Check for a few special cases with known reductions first.
+ switch (op) {
+ default:
+ // We can't reduce this case; just treat it normally.
+ break;
+ case BO_Mul:
+ // a*0 and a*1
+ if (RHS == 0)
+ return makeIntVal(0, resultTy);
+ else if (RHS == 1)
+ isIdempotent = true;
+ break;
+ case BO_Div:
+ // a/0 and a/1
+ if (RHS == 0)
+ // This is also handled elsewhere.
+ return UndefinedVal();
+ else if (RHS == 1)
+ isIdempotent = true;
+ break;
+ case BO_Rem:
+ // a%0 and a%1
+ if (RHS == 0)
+ // This is also handled elsewhere.
+ return UndefinedVal();
+ else if (RHS == 1)
+ return makeIntVal(0, resultTy);
+ break;
+ case BO_Add:
+ case BO_Sub:
+ case BO_Shl:
+ case BO_Shr:
+ case BO_Xor:
+ // a+0, a-0, a<<0, a>>0, a^0
+ if (RHS == 0)
+ isIdempotent = true;
+ break;
+ case BO_And:
+ // a&0 and a&(~0)
+ if (RHS == 0)
+ return makeIntVal(0, resultTy);
+ else if (RHS.isAllOnesValue())
+ isIdempotent = true;
+ break;
+ case BO_Or:
+ // a|0 and a|(~0)
+ if (RHS == 0)
+ isIdempotent = true;
+ else if (RHS.isAllOnesValue()) {
+ const llvm::APSInt &Result = BasicVals.Convert(resultTy, RHS);
+ return nonloc::ConcreteInt(Result);
+ }
+ break;
+ }
+
+ // Idempotent ops (like a*1) can still change the type of an expression.
+ // Wrap the LHS up in a NonLoc again and let evalCastFromNonLoc do the
+ // dirty work.
+ if (isIdempotent)
+ return evalCastFromNonLoc(nonloc::SymbolVal(LHS), resultTy);
+
+ // If we reach this point, the expression cannot be simplified.
+ // Make a SymbolVal for the entire expression, after converting the RHS.
+ const llvm::APSInt *ConvertedRHS = &RHS;
+ if (BinaryOperator::isComparisonOp(op)) {
+ // We're looking for a type big enough to compare the symbolic value
+ // with the given constant.
+ // FIXME: This is an approximation of Sema::UsualArithmeticConversions.
+ ASTContext &Ctx = getContext();
+ QualType SymbolType = LHS->getType();
+ uint64_t ValWidth = RHS.getBitWidth();
+ uint64_t TypeWidth = Ctx.getTypeSize(SymbolType);
+
+ if (ValWidth < TypeWidth) {
+ // If the value is too small, extend it.
+ ConvertedRHS = &BasicVals.Convert(SymbolType, RHS);
+ } else if (ValWidth == TypeWidth) {
+ // If the value is signed but the symbol is unsigned, do the comparison
+ // in unsigned space. [C99 6.3.1.8]
+ // (For the opposite case, the value is already unsigned.)
+ if (RHS.isSigned() && !SymbolType->isSignedIntegerOrEnumerationType())
+ ConvertedRHS = &BasicVals.Convert(SymbolType, RHS);
+ }
+ } else
+ ConvertedRHS = &BasicVals.Convert(resultTy, RHS);
+
+ return makeNonLoc(LHS, op, *ConvertedRHS, resultTy);
+}
+
+// See if Sym is known to be a relation Rel with Bound.
+static bool isInRelation(BinaryOperator::Opcode Rel, SymbolRef Sym,
+ llvm::APSInt Bound, ProgramStateRef State) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ SVal Result =
+ SVB.evalBinOpNN(State, Rel, nonloc::SymbolVal(Sym),
+ nonloc::ConcreteInt(Bound), SVB.getConditionType());
+ if (auto DV = Result.getAs<DefinedSVal>()) {
+ return !State->assume(*DV, false);
+ }
+ return false;
+}
+
+// See if Sym is known to be within [min/4, max/4], where min and max
+// are the bounds of the symbol's integral type. With such symbols,
+// some manipulations can be performed without the risk of overflow.
+// assume() doesn't cause infinite recursion because we should be dealing
+// with simpler symbols on every recursive call.
+static bool isWithinConstantOverflowBounds(SymbolRef Sym,
+ ProgramStateRef State) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ BasicValueFactory &BV = SVB.getBasicValueFactory();
+
+ QualType T = Sym->getType();
+ assert(T->isSignedIntegerOrEnumerationType() &&
+ "This only works with signed integers!");
+ APSIntType AT = BV.getAPSIntType(T);
+
+ llvm::APSInt Max = AT.getMaxValue() / AT.getValue(4), Min = -Max;
+ return isInRelation(BO_LE, Sym, Max, State) &&
+ isInRelation(BO_GE, Sym, Min, State);
+}
+
+// Same for the concrete integers: see if I is within [min/4, max/4].
+static bool isWithinConstantOverflowBounds(llvm::APSInt I) {
+ APSIntType AT(I);
+ assert(!AT.isUnsigned() &&
+ "This only works with signed integers!");
+
+ llvm::APSInt Max = AT.getMaxValue() / AT.getValue(4), Min = -Max;
+ return (I <= Max) && (I >= -Max);
+}
+
+static std::pair<SymbolRef, llvm::APSInt>
+decomposeSymbol(SymbolRef Sym, BasicValueFactory &BV) {
+ if (const auto *SymInt = dyn_cast<SymIntExpr>(Sym))
+ if (BinaryOperator::isAdditiveOp(SymInt->getOpcode()))
+ return std::make_pair(SymInt->getLHS(),
+ (SymInt->getOpcode() == BO_Add) ?
+ (SymInt->getRHS()) :
+ (-SymInt->getRHS()));
+
+ // Fail to decompose: "reduce" the problem to the "$x + 0" case.
+ return std::make_pair(Sym, BV.getValue(0, Sym->getType()));
+}
+
+// Simplify "(LSym + LInt) Op (RSym + RInt)" assuming all values are of the
+// same signed integral type and no overflows occur (which should be checked
+// by the caller).
+static NonLoc doRearrangeUnchecked(ProgramStateRef State,
+ BinaryOperator::Opcode Op,
+ SymbolRef LSym, llvm::APSInt LInt,
+ SymbolRef RSym, llvm::APSInt RInt) {
+ SValBuilder &SVB = State->getStateManager().getSValBuilder();
+ BasicValueFactory &BV = SVB.getBasicValueFactory();
+ SymbolManager &SymMgr = SVB.getSymbolManager();
+
+ QualType SymTy = LSym->getType();
+ assert(SymTy == RSym->getType() &&
+ "Symbols are not of the same type!");
+ assert(APSIntType(LInt) == BV.getAPSIntType(SymTy) &&
+ "Integers are not of the same type as symbols!");
+ assert(APSIntType(RInt) == BV.getAPSIntType(SymTy) &&
+ "Integers are not of the same type as symbols!");
+
+ QualType ResultTy;
+ if (BinaryOperator::isComparisonOp(Op))
+ ResultTy = SVB.getConditionType();
+ else if (BinaryOperator::isAdditiveOp(Op))
+ ResultTy = SymTy;
+ else
+ llvm_unreachable("Operation not suitable for unchecked rearrangement!");
+
+ // FIXME: Can we use assume() without getting into an infinite recursion?
+ if (LSym == RSym)
+ return SVB.evalBinOpNN(State, Op, nonloc::ConcreteInt(LInt),
+ nonloc::ConcreteInt(RInt), ResultTy)
+ .castAs<NonLoc>();
+
+ SymbolRef ResultSym = nullptr;
+ BinaryOperator::Opcode ResultOp;
+ llvm::APSInt ResultInt;
+ if (BinaryOperator::isComparisonOp(Op)) {
+ // Prefer comparing to a non-negative number.
+ // FIXME: Maybe it'd be better to have consistency in
+ // "$x - $y" vs. "$y - $x" because those are solver's keys.
+ if (LInt > RInt) {
+ ResultSym = SymMgr.getSymSymExpr(RSym, BO_Sub, LSym, SymTy);
+ ResultOp = BinaryOperator::reverseComparisonOp(Op);
+ ResultInt = LInt - RInt; // Opposite order!
+ } else {
+ ResultSym = SymMgr.getSymSymExpr(LSym, BO_Sub, RSym, SymTy);
+ ResultOp = Op;
+ ResultInt = RInt - LInt; // Opposite order!
+ }
+ } else {
+ ResultSym = SymMgr.getSymSymExpr(LSym, Op, RSym, SymTy);
+ ResultInt = (Op == BO_Add) ? (LInt + RInt) : (LInt - RInt);
+ ResultOp = BO_Add;
+ // Bring back the cosmetic difference.
+ if (ResultInt < 0) {
+ ResultInt = -ResultInt;
+ ResultOp = BO_Sub;
+ } else if (ResultInt == 0) {
+ // Shortcut: Simplify "$x + 0" to "$x".
+ return nonloc::SymbolVal(ResultSym);
+ }
+ }
+ const llvm::APSInt &PersistentResultInt = BV.getValue(ResultInt);
+ return nonloc::SymbolVal(
+ SymMgr.getSymIntExpr(ResultSym, ResultOp, PersistentResultInt, ResultTy));
+}
+
+// Rearrange if symbol type matches the result type and if the operator is a
+// comparison operator, both symbol and constant must be within constant
+// overflow bounds.
+static bool shouldRearrange(ProgramStateRef State, BinaryOperator::Opcode Op,
+ SymbolRef Sym, llvm::APSInt Int, QualType Ty) {
+ return Sym->getType() == Ty &&
+ (!BinaryOperator::isComparisonOp(Op) ||
+ (isWithinConstantOverflowBounds(Sym, State) &&
+ isWithinConstantOverflowBounds(Int)));
+}
+
+static Optional<NonLoc> tryRearrange(ProgramStateRef State,
+ BinaryOperator::Opcode Op, NonLoc Lhs,
+ NonLoc Rhs, QualType ResultTy) {
+ ProgramStateManager &StateMgr = State->getStateManager();
+ SValBuilder &SVB = StateMgr.getSValBuilder();
+
+ // We expect everything to be of the same type - this type.
+ QualType SingleTy;
+
+ auto &Opts =
+ StateMgr.getOwningEngine().getAnalysisManager().getAnalyzerOptions();
+
+ // FIXME: After putting complexity threshold to the symbols we can always
+ // rearrange additive operations but rearrange comparisons only if
+ // option is set.
+ if(!Opts.ShouldAggressivelySimplifyBinaryOperation)
+ return None;
+
+ SymbolRef LSym = Lhs.getAsSymbol();
+ if (!LSym)
+ return None;
+
+ if (BinaryOperator::isComparisonOp(Op)) {
+ SingleTy = LSym->getType();
+ if (ResultTy != SVB.getConditionType())
+ return None;
+ // Initialize SingleTy later with a symbol's type.
+ } else if (BinaryOperator::isAdditiveOp(Op)) {
+ SingleTy = ResultTy;
+ if (LSym->getType() != SingleTy)
+ return None;
+ } else {
+ // Don't rearrange other operations.
+ return None;
+ }
+
+ assert(!SingleTy.isNull() && "We should have figured out the type by now!");
+
+ // Rearrange signed symbolic expressions only
+ if (!SingleTy->isSignedIntegerOrEnumerationType())
+ return None;
+
+ SymbolRef RSym = Rhs.getAsSymbol();
+ if (!RSym || RSym->getType() != SingleTy)
+ return None;
+
+ BasicValueFactory &BV = State->getBasicVals();
+ llvm::APSInt LInt, RInt;
+ std::tie(LSym, LInt) = decomposeSymbol(LSym, BV);
+ std::tie(RSym, RInt) = decomposeSymbol(RSym, BV);
+ if (!shouldRearrange(State, Op, LSym, LInt, SingleTy) ||
+ !shouldRearrange(State, Op, RSym, RInt, SingleTy))
+ return None;
+
+ // We know that no overflows can occur anymore.
+ return doRearrangeUnchecked(State, Op, LSym, LInt, RSym, RInt);
+}
+
+SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
+ BinaryOperator::Opcode op,
+ NonLoc lhs, NonLoc rhs,
+ QualType resultTy) {
+ NonLoc InputLHS = lhs;
+ NonLoc InputRHS = rhs;
+
+ // Handle trivial case where left-side and right-side are the same.
+ if (lhs == rhs)
+ switch (op) {
+ default:
+ break;
+ case BO_EQ:
+ case BO_LE:
+ case BO_GE:
+ return makeTruthVal(true, resultTy);
+ case BO_LT:
+ case BO_GT:
+ case BO_NE:
+ return makeTruthVal(false, resultTy);
+ case BO_Xor:
+ case BO_Sub:
+ if (resultTy->isIntegralOrEnumerationType())
+ return makeIntVal(0, resultTy);
+ return evalCastFromNonLoc(makeIntVal(0, /*Unsigned=*/false), resultTy);
+ case BO_Or:
+ case BO_And:
+ return evalCastFromNonLoc(lhs, resultTy);
+ }
+
+ while (1) {
+ switch (lhs.getSubKind()) {
+ default:
+ return makeSymExprValNN(op, lhs, rhs, resultTy);
+ case nonloc::PointerToMemberKind: {
+ assert(rhs.getSubKind() == nonloc::PointerToMemberKind &&
+ "Both SVals should have pointer-to-member-type");
+ auto LPTM = lhs.castAs<nonloc::PointerToMember>(),
+ RPTM = rhs.castAs<nonloc::PointerToMember>();
+ auto LPTMD = LPTM.getPTMData(), RPTMD = RPTM.getPTMData();
+ switch (op) {
+ case BO_EQ:
+ return makeTruthVal(LPTMD == RPTMD, resultTy);
+ case BO_NE:
+ return makeTruthVal(LPTMD != RPTMD, resultTy);
+ default:
+ return UnknownVal();
+ }
+ }
+ case nonloc::LocAsIntegerKind: {
+ Loc lhsL = lhs.castAs<nonloc::LocAsInteger>().getLoc();
+ switch (rhs.getSubKind()) {
+ case nonloc::LocAsIntegerKind:
+ // FIXME: at the moment the implementation
+ // of modeling "pointers as integers" is not complete.
+ if (!BinaryOperator::isComparisonOp(op))
+ return UnknownVal();
+ return evalBinOpLL(state, op, lhsL,
+ rhs.castAs<nonloc::LocAsInteger>().getLoc(),
+ resultTy);
+ case nonloc::ConcreteIntKind: {
+ // FIXME: at the moment the implementation
+ // of modeling "pointers as integers" is not complete.
+ if (!BinaryOperator::isComparisonOp(op))
+ return UnknownVal();
+ // Transform the integer into a location and compare.
+ // FIXME: This only makes sense for comparisons. If we want to, say,
+ // add 1 to a LocAsInteger, we'd better unpack the Loc and add to it,
+ // then pack it back into a LocAsInteger.
+ llvm::APSInt i = rhs.castAs<nonloc::ConcreteInt>().getValue();
+ BasicVals.getAPSIntType(Context.VoidPtrTy).apply(i);
+ return evalBinOpLL(state, op, lhsL, makeLoc(i), resultTy);
+ }
+ default:
+ switch (op) {
+ case BO_EQ:
+ return makeTruthVal(false, resultTy);
+ case BO_NE:
+ return makeTruthVal(true, resultTy);
+ default:
+ // This case also handles pointer arithmetic.
+ return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
+ }
+ }
+ }
+ case nonloc::ConcreteIntKind: {
+ llvm::APSInt LHSValue = lhs.castAs<nonloc::ConcreteInt>().getValue();
+
+ // If we're dealing with two known constants, just perform the operation.
+ if (const llvm::APSInt *KnownRHSValue = getKnownValue(state, rhs)) {
+ llvm::APSInt RHSValue = *KnownRHSValue;
+ if (BinaryOperator::isComparisonOp(op)) {
+ // We're looking for a type big enough to compare the two values.
+ // FIXME: This is not correct. char + short will result in a promotion
+ // to int. Unfortunately we have lost types by this point.
+ APSIntType CompareType = std::max(APSIntType(LHSValue),
+ APSIntType(RHSValue));
+ CompareType.apply(LHSValue);
+ CompareType.apply(RHSValue);
+ } else if (!BinaryOperator::isShiftOp(op)) {
+ APSIntType IntType = BasicVals.getAPSIntType(resultTy);
+ IntType.apply(LHSValue);
+ IntType.apply(RHSValue);
+ }
+
+ const llvm::APSInt *Result =
+ BasicVals.evalAPSInt(op, LHSValue, RHSValue);
+ if (!Result)
+ return UndefinedVal();
+
+ return nonloc::ConcreteInt(*Result);
+ }
+
+ // Swap the left and right sides and flip the operator if doing so
+ // allows us to better reason about the expression (this is a form
+ // of expression canonicalization).
+ // While we're at it, catch some special cases for non-commutative ops.
+ switch (op) {
+ case BO_LT:
+ case BO_GT:
+ case BO_LE:
+ case BO_GE:
+ op = BinaryOperator::reverseComparisonOp(op);
+ LLVM_FALLTHROUGH;
+ case BO_EQ:
+ case BO_NE:
+ case BO_Add:
+ case BO_Mul:
+ case BO_And:
+ case BO_Xor:
+ case BO_Or:
+ std::swap(lhs, rhs);
+ continue;
+ case BO_Shr:
+ // (~0)>>a
+ if (LHSValue.isAllOnesValue() && LHSValue.isSigned())
+ return evalCastFromNonLoc(lhs, resultTy);
+ LLVM_FALLTHROUGH;
+ case BO_Shl:
+ // 0<<a and 0>>a
+ if (LHSValue == 0)
+ return evalCastFromNonLoc(lhs, resultTy);
+ return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
+ default:
+ return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
+ }
+ }
+ case nonloc::SymbolValKind: {
+ // We only handle LHS as simple symbols or SymIntExprs.
+ SymbolRef Sym = lhs.castAs<nonloc::SymbolVal>().getSymbol();
+
+ // LHS is a symbolic expression.
+ if (const SymIntExpr *symIntExpr = dyn_cast<SymIntExpr>(Sym)) {
+
+ // Is this a logical not? (!x is represented as x == 0.)
+ if (op == BO_EQ && rhs.isZeroConstant()) {
+ // We know how to negate certain expressions. Simplify them here.
+
+ BinaryOperator::Opcode opc = symIntExpr->getOpcode();
+ switch (opc) {
+ default:
+ // We don't know how to negate this operation.
+ // Just handle it as if it were a normal comparison to 0.
+ break;
+ case BO_LAnd:
+ case BO_LOr:
+ llvm_unreachable("Logical operators handled by branching logic.");
+ case BO_Assign:
+ case BO_MulAssign:
+ case BO_DivAssign:
+ case BO_RemAssign:
+ case BO_AddAssign:
+ case BO_SubAssign:
+ case BO_ShlAssign:
+ case BO_ShrAssign:
+ case BO_AndAssign:
+ case BO_XorAssign:
+ case BO_OrAssign:
+ case BO_Comma:
+ llvm_unreachable("'=' and ',' operators handled by ExprEngine.");
+ case BO_PtrMemD:
+ case BO_PtrMemI:
+ llvm_unreachable("Pointer arithmetic not handled here.");
+ case BO_LT:
+ case BO_GT:
+ case BO_LE:
+ case BO_GE:
+ case BO_EQ:
+ case BO_NE:
+ assert(resultTy->isBooleanType() ||
+ resultTy == getConditionType());
+ assert(symIntExpr->getType()->isBooleanType() ||
+ getContext().hasSameUnqualifiedType(symIntExpr->getType(),
+ getConditionType()));
+ // Negate the comparison and make a value.
+ opc = BinaryOperator::negateComparisonOp(opc);
+ return makeNonLoc(symIntExpr->getLHS(), opc,
+ symIntExpr->getRHS(), resultTy);
+ }
+ }
+
+ // For now, only handle expressions whose RHS is a constant.
+ if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs)) {
+ // If both the LHS and the current expression are additive,
+ // fold their constants and try again.
+ if (BinaryOperator::isAdditiveOp(op)) {
+ BinaryOperator::Opcode lop = symIntExpr->getOpcode();
+ if (BinaryOperator::isAdditiveOp(lop)) {
+ // Convert the two constants to a common type, then combine them.
+
+ // resultTy may not be the best type to convert to, but it's
+ // probably the best choice in expressions with mixed type
+ // (such as x+1U+2LL). The rules for implicit conversions should
+ // choose a reasonable type to preserve the expression, and will
+ // at least match how the value is going to be used.
+ APSIntType IntType = BasicVals.getAPSIntType(resultTy);
+ const llvm::APSInt &first = IntType.convert(symIntExpr->getRHS());
+ const llvm::APSInt &second = IntType.convert(*RHSValue);
+
+ const llvm::APSInt *newRHS;
+ if (lop == op)
+ newRHS = BasicVals.evalAPSInt(BO_Add, first, second);
+ else
+ newRHS = BasicVals.evalAPSInt(BO_Sub, first, second);
+
+ assert(newRHS && "Invalid operation despite common type!");
+ rhs = nonloc::ConcreteInt(*newRHS);
+ lhs = nonloc::SymbolVal(symIntExpr->getLHS());
+ op = lop;
+ continue;
+ }
+ }
+
+ // Otherwise, make a SymIntExpr out of the expression.
+ return MakeSymIntVal(symIntExpr, op, *RHSValue, resultTy);
+ }
+ }
+
+ // Does the symbolic expression simplify to a constant?
+ // If so, "fold" the constant by setting 'lhs' to a ConcreteInt
+ // and try again.
+ SVal simplifiedLhs = simplifySVal(state, lhs);
+ if (simplifiedLhs != lhs)
+ if (auto simplifiedLhsAsNonLoc = simplifiedLhs.getAs<NonLoc>()) {
+ lhs = *simplifiedLhsAsNonLoc;
+ continue;
+ }
+
+ // Is the RHS a constant?
+ if (const llvm::APSInt *RHSValue = getKnownValue(state, rhs))
+ return MakeSymIntVal(Sym, op, *RHSValue, resultTy);
+
+ if (Optional<NonLoc> V = tryRearrange(state, op, lhs, rhs, resultTy))
+ return *V;
+
+ // Give up -- this is not a symbolic expression we can handle.
+ return makeSymExprValNN(op, InputLHS, InputRHS, resultTy);
+ }
+ }
+ }
+}
+
+static SVal evalBinOpFieldRegionFieldRegion(const FieldRegion *LeftFR,
+ const FieldRegion *RightFR,
+ BinaryOperator::Opcode op,
+ QualType resultTy,
+ SimpleSValBuilder &SVB) {
+ // Only comparisons are meaningful here!
+ if (!BinaryOperator::isComparisonOp(op))
+ return UnknownVal();
+
+ // Next, see if the two FRs have the same super-region.
+ // FIXME: This doesn't handle casts yet, and simply stripping the casts
+ // doesn't help.
+ if (LeftFR->getSuperRegion() != RightFR->getSuperRegion())
+ return UnknownVal();
+
+ const FieldDecl *LeftFD = LeftFR->getDecl();
+ const FieldDecl *RightFD = RightFR->getDecl();
+ const RecordDecl *RD = LeftFD->getParent();
+
+ // Make sure the two FRs are from the same kind of record. Just in case!
+ // FIXME: This is probably where inheritance would be a problem.
+ if (RD != RightFD->getParent())
+ return UnknownVal();
+
+ // We know for sure that the two fields are not the same, since that
+ // would have given us the same SVal.
+ if (op == BO_EQ)
+ return SVB.makeTruthVal(false, resultTy);
+ if (op == BO_NE)
+ return SVB.makeTruthVal(true, resultTy);
+
+ // Iterate through the fields and see which one comes first.
+ // [C99 6.7.2.1.13] "Within a structure object, the non-bit-field
+ // members and the units in which bit-fields reside have addresses that
+ // increase in the order in which they are declared."
+ bool leftFirst = (op == BO_LT || op == BO_LE);
+ for (const auto *I : RD->fields()) {
+ if (I == LeftFD)
+ return SVB.makeTruthVal(leftFirst, resultTy);
+ if (I == RightFD)
+ return SVB.makeTruthVal(!leftFirst, resultTy);
+ }
+
+ llvm_unreachable("Fields not found in parent record's definition");
+}
+
+// FIXME: all this logic will change if/when we have MemRegion::getLocation().
+SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
+ BinaryOperator::Opcode op,
+ Loc lhs, Loc rhs,
+ QualType resultTy) {
+ // Only comparisons and subtractions are valid operations on two pointers.
+ // See [C99 6.5.5 through 6.5.14] or [C++0x 5.6 through 5.15].
+ // However, if a pointer is casted to an integer, evalBinOpNN may end up
+ // calling this function with another operation (PR7527). We don't attempt to
+ // model this for now, but it could be useful, particularly when the
+ // "location" is actually an integer value that's been passed through a void*.
+ if (!(BinaryOperator::isComparisonOp(op) || op == BO_Sub))
+ return UnknownVal();
+
+ // Special cases for when both sides are identical.
+ if (lhs == rhs) {
+ switch (op) {
+ default:
+ llvm_unreachable("Unimplemented operation for two identical values");
+ case BO_Sub:
+ return makeZeroVal(resultTy);
+ case BO_EQ:
+ case BO_LE:
+ case BO_GE:
+ return makeTruthVal(true, resultTy);
+ case BO_NE:
+ case BO_LT:
+ case BO_GT:
+ return makeTruthVal(false, resultTy);
+ }
+ }
+
+ switch (lhs.getSubKind()) {
+ default:
+ llvm_unreachable("Ordering not implemented for this Loc.");
+
+ case loc::GotoLabelKind:
+ // The only thing we know about labels is that they're non-null.
+ if (rhs.isZeroConstant()) {
+ switch (op) {
+ default:
+ break;
+ case BO_Sub:
+ return evalCastFromLoc(lhs, resultTy);
+ case BO_EQ:
+ case BO_LE:
+ case BO_LT:
+ return makeTruthVal(false, resultTy);
+ case BO_NE:
+ case BO_GT:
+ case BO_GE:
+ return makeTruthVal(true, resultTy);
+ }
+ }
+ // There may be two labels for the same location, and a function region may
+ // have the same address as a label at the start of the function (depending
+ // on the ABI).
+ // FIXME: we can probably do a comparison against other MemRegions, though.
+ // FIXME: is there a way to tell if two labels refer to the same location?
+ return UnknownVal();
+
+ case loc::ConcreteIntKind: {
+ // If one of the operands is a symbol and the other is a constant,
+ // build an expression for use by the constraint manager.
+ if (SymbolRef rSym = rhs.getAsLocSymbol()) {
+ // We can only build expressions with symbols on the left,
+ // so we need a reversible operator.
+ if (!BinaryOperator::isComparisonOp(op) || op == BO_Cmp)
+ return UnknownVal();
+
+ const llvm::APSInt &lVal = lhs.castAs<loc::ConcreteInt>().getValue();
+ op = BinaryOperator::reverseComparisonOp(op);
+ return makeNonLoc(rSym, op, lVal, resultTy);
+ }
+
+ // If both operands are constants, just perform the operation.
+ if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) {
+ SVal ResultVal =
+ lhs.castAs<loc::ConcreteInt>().evalBinOp(BasicVals, op, *rInt);
+ if (Optional<NonLoc> Result = ResultVal.getAs<NonLoc>())
+ return evalCastFromNonLoc(*Result, resultTy);
+
+ assert(!ResultVal.getAs<Loc>() && "Loc-Loc ops should not produce Locs");
+ return UnknownVal();
+ }
+
+ // Special case comparisons against NULL.
+ // This must come after the test if the RHS is a symbol, which is used to
+ // build constraints. The address of any non-symbolic region is guaranteed
+ // to be non-NULL, as is any label.
+ assert(rhs.getAs<loc::MemRegionVal>() || rhs.getAs<loc::GotoLabel>());
+ if (lhs.isZeroConstant()) {
+ switch (op) {
+ default:
+ break;
+ case BO_EQ:
+ case BO_GT:
+ case BO_GE:
+ return makeTruthVal(false, resultTy);
+ case BO_NE:
+ case BO_LT:
+ case BO_LE:
+ return makeTruthVal(true, resultTy);
+ }
+ }
+
+ // Comparing an arbitrary integer to a region or label address is
+ // completely unknowable.
+ return UnknownVal();
+ }
+ case loc::MemRegionValKind: {
+ if (Optional<loc::ConcreteInt> rInt = rhs.getAs<loc::ConcreteInt>()) {
+ // If one of the operands is a symbol and the other is a constant,
+ // build an expression for use by the constraint manager.
+ if (SymbolRef lSym = lhs.getAsLocSymbol(true)) {
+ if (BinaryOperator::isComparisonOp(op))
+ return MakeSymIntVal(lSym, op, rInt->getValue(), resultTy);
+ return UnknownVal();
+ }
+ // Special case comparisons to NULL.
+ // This must come after the test if the LHS is a symbol, which is used to
+ // build constraints. The address of any non-symbolic region is guaranteed
+ // to be non-NULL.
+ if (rInt->isZeroConstant()) {
+ if (op == BO_Sub)
+ return evalCastFromLoc(lhs, resultTy);
+
+ if (BinaryOperator::isComparisonOp(op)) {
+ QualType boolType = getContext().BoolTy;
+ NonLoc l = evalCastFromLoc(lhs, boolType).castAs<NonLoc>();
+ NonLoc r = makeTruthVal(false, boolType).castAs<NonLoc>();
+ return evalBinOpNN(state, op, l, r, resultTy);
+ }
+ }
+
+ // Comparing a region to an arbitrary integer is completely unknowable.
+ return UnknownVal();
+ }
+
+ // Get both values as regions, if possible.
+ const MemRegion *LeftMR = lhs.getAsRegion();
+ assert(LeftMR && "MemRegionValKind SVal doesn't have a region!");
+
+ const MemRegion *RightMR = rhs.getAsRegion();
+ if (!RightMR)
+ // The RHS is probably a label, which in theory could address a region.
+ // FIXME: we can probably make a more useful statement about non-code
+ // regions, though.
+ return UnknownVal();
+
+ const MemRegion *LeftBase = LeftMR->getBaseRegion();
+ const MemRegion *RightBase = RightMR->getBaseRegion();
+ const MemSpaceRegion *LeftMS = LeftBase->getMemorySpace();
+ const MemSpaceRegion *RightMS = RightBase->getMemorySpace();
+ const MemSpaceRegion *UnknownMS = MemMgr.getUnknownRegion();
+
+ // If the two regions are from different known memory spaces they cannot be
+ // equal. Also, assume that no symbolic region (whose memory space is
+ // unknown) is on the stack.
+ if (LeftMS != RightMS &&
+ ((LeftMS != UnknownMS && RightMS != UnknownMS) ||
+ (isa<StackSpaceRegion>(LeftMS) || isa<StackSpaceRegion>(RightMS)))) {
+ switch (op) {
+ default:
+ return UnknownVal();
+ case BO_EQ:
+ return makeTruthVal(false, resultTy);
+ case BO_NE:
+ return makeTruthVal(true, resultTy);
+ }
+ }
+
+ // If both values wrap regions, see if they're from different base regions.
+ // Note, heap base symbolic regions are assumed to not alias with
+ // each other; for example, we assume that malloc returns different address
+ // on each invocation.
+ // FIXME: ObjC object pointers always reside on the heap, but currently
+ // we treat their memory space as unknown, because symbolic pointers
+ // to ObjC objects may alias. There should be a way to construct
+ // possibly-aliasing heap-based regions. For instance, MacOSXApiChecker
+ // guesses memory space for ObjC object pointers manually instead of
+ // relying on us.
+ if (LeftBase != RightBase &&
+ ((!isa<SymbolicRegion>(LeftBase) && !isa<SymbolicRegion>(RightBase)) ||
+ (isa<HeapSpaceRegion>(LeftMS) || isa<HeapSpaceRegion>(RightMS))) ){
+ switch (op) {
+ default:
+ return UnknownVal();
+ case BO_EQ:
+ return makeTruthVal(false, resultTy);
+ case BO_NE:
+ return makeTruthVal(true, resultTy);
+ }
+ }
+
+ // Handle special cases for when both regions are element regions.
+ const ElementRegion *RightER = dyn_cast<ElementRegion>(RightMR);
+ const ElementRegion *LeftER = dyn_cast<ElementRegion>(LeftMR);
+ if (RightER && LeftER) {
+ // Next, see if the two ERs have the same super-region and matching types.
+ // FIXME: This should do something useful even if the types don't match,
+ // though if both indexes are constant the RegionRawOffset path will
+ // give the correct answer.
+ if (LeftER->getSuperRegion() == RightER->getSuperRegion() &&
+ LeftER->getElementType() == RightER->getElementType()) {
+ // Get the left index and cast it to the correct type.
+ // If the index is unknown or undefined, bail out here.
+ SVal LeftIndexVal = LeftER->getIndex();
+ Optional<NonLoc> LeftIndex = LeftIndexVal.getAs<NonLoc>();
+ if (!LeftIndex)
+ return UnknownVal();
+ LeftIndexVal = evalCastFromNonLoc(*LeftIndex, ArrayIndexTy);
+ LeftIndex = LeftIndexVal.getAs<NonLoc>();
+ if (!LeftIndex)
+ return UnknownVal();
+
+ // Do the same for the right index.
+ SVal RightIndexVal = RightER->getIndex();
+ Optional<NonLoc> RightIndex = RightIndexVal.getAs<NonLoc>();
+ if (!RightIndex)
+ return UnknownVal();
+ RightIndexVal = evalCastFromNonLoc(*RightIndex, ArrayIndexTy);
+ RightIndex = RightIndexVal.getAs<NonLoc>();
+ if (!RightIndex)
+ return UnknownVal();
+
+ // Actually perform the operation.
+ // evalBinOpNN expects the two indexes to already be the right type.
+ return evalBinOpNN(state, op, *LeftIndex, *RightIndex, resultTy);
+ }
+ }
+
+ // Special handling of the FieldRegions, even with symbolic offsets.
+ const FieldRegion *RightFR = dyn_cast<FieldRegion>(RightMR);
+ const FieldRegion *LeftFR = dyn_cast<FieldRegion>(LeftMR);
+ if (RightFR && LeftFR) {
+ SVal R = evalBinOpFieldRegionFieldRegion(LeftFR, RightFR, op, resultTy,
+ *this);
+ if (!R.isUnknown())
+ return R;
+ }
+
+ // Compare the regions using the raw offsets.
+ RegionOffset LeftOffset = LeftMR->getAsOffset();
+ RegionOffset RightOffset = RightMR->getAsOffset();
+
+ if (LeftOffset.getRegion() != nullptr &&
+ LeftOffset.getRegion() == RightOffset.getRegion() &&
+ !LeftOffset.hasSymbolicOffset() && !RightOffset.hasSymbolicOffset()) {
+ int64_t left = LeftOffset.getOffset();
+ int64_t right = RightOffset.getOffset();
+
+ switch (op) {
+ default:
+ return UnknownVal();
+ case BO_LT:
+ return makeTruthVal(left < right, resultTy);
+ case BO_GT:
+ return makeTruthVal(left > right, resultTy);
+ case BO_LE:
+ return makeTruthVal(left <= right, resultTy);
+ case BO_GE:
+ return makeTruthVal(left >= right, resultTy);
+ case BO_EQ:
+ return makeTruthVal(left == right, resultTy);
+ case BO_NE:
+ return makeTruthVal(left != right, resultTy);
+ }
+ }
+
+ // At this point we're not going to get a good answer, but we can try
+ // conjuring an expression instead.
+ SymbolRef LHSSym = lhs.getAsLocSymbol();
+ SymbolRef RHSSym = rhs.getAsLocSymbol();
+ if (LHSSym && RHSSym)
+ return makeNonLoc(LHSSym, op, RHSSym, resultTy);
+
+ // If we get here, we have no way of comparing the regions.
+ return UnknownVal();
+ }
+ }
+}
+
+SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
+ BinaryOperator::Opcode op,
+ Loc lhs, NonLoc rhs, QualType resultTy) {
+ if (op >= BO_PtrMemD && op <= BO_PtrMemI) {
+ if (auto PTMSV = rhs.getAs<nonloc::PointerToMember>()) {
+ if (PTMSV->isNullMemberPointer())
+ return UndefinedVal();
+ if (const FieldDecl *FD = PTMSV->getDeclAs<FieldDecl>()) {
+ SVal Result = lhs;
+
+ for (const auto &I : *PTMSV)
+ Result = StateMgr.getStoreManager().evalDerivedToBase(
+ Result, I->getType(),I->isVirtual());
+ return state->getLValue(FD, Result);
+ }
+ }
+
+ return rhs;
+ }
+
+ assert(!BinaryOperator::isComparisonOp(op) &&
+ "arguments to comparison ops must be of the same type");
+
+ // Special case: rhs is a zero constant.
+ if (rhs.isZeroConstant())
+ return lhs;
+
+ // Perserve the null pointer so that it can be found by the DerefChecker.
+ if (lhs.isZeroConstant())
+ return lhs;
+
+ // We are dealing with pointer arithmetic.
+
+ // Handle pointer arithmetic on constant values.
+ if (Optional<nonloc::ConcreteInt> rhsInt = rhs.getAs<nonloc::ConcreteInt>()) {
+ if (Optional<loc::ConcreteInt> lhsInt = lhs.getAs<loc::ConcreteInt>()) {
+ const llvm::APSInt &leftI = lhsInt->getValue();
+ assert(leftI.isUnsigned());
+ llvm::APSInt rightI(rhsInt->getValue(), /* isUnsigned */ true);
+
+ // Convert the bitwidth of rightI. This should deal with overflow
+ // since we are dealing with concrete values.
+ rightI = rightI.extOrTrunc(leftI.getBitWidth());
+
+ // Offset the increment by the pointer size.
+ llvm::APSInt Multiplicand(rightI.getBitWidth(), /* isUnsigned */ true);
+ QualType pointeeType = resultTy->getPointeeType();
+ Multiplicand = getContext().getTypeSizeInChars(pointeeType).getQuantity();
+ rightI *= Multiplicand;
+
+ // Compute the adjusted pointer.
+ switch (op) {
+ case BO_Add:
+ rightI = leftI + rightI;
+ break;
+ case BO_Sub:
+ rightI = leftI - rightI;
+ break;
+ default:
+ llvm_unreachable("Invalid pointer arithmetic operation");
+ }
+ return loc::ConcreteInt(getBasicValueFactory().getValue(rightI));
+ }
+ }
+
+ // Handle cases where 'lhs' is a region.
+ if (const MemRegion *region = lhs.getAsRegion()) {
+ rhs = convertToArrayIndex(rhs).castAs<NonLoc>();
+ SVal index = UnknownVal();
+ const SubRegion *superR = nullptr;
+ // We need to know the type of the pointer in order to add an integer to it.
+ // Depending on the type, different amount of bytes is added.
+ QualType elementType;
+
+ if (const ElementRegion *elemReg = dyn_cast<ElementRegion>(region)) {
+ assert(op == BO_Add || op == BO_Sub);
+ index = evalBinOpNN(state, op, elemReg->getIndex(), rhs,
+ getArrayIndexType());
+ superR = cast<SubRegion>(elemReg->getSuperRegion());
+ elementType = elemReg->getElementType();
+ }
+ else if (isa<SubRegion>(region)) {
+ assert(op == BO_Add || op == BO_Sub);
+ index = (op == BO_Add) ? rhs : evalMinus(rhs);
+ superR = cast<SubRegion>(region);
+ // TODO: Is this actually reliable? Maybe improving our MemRegion
+ // hierarchy to provide typed regions for all non-void pointers would be
+ // better. For instance, we cannot extend this towards LocAsInteger
+ // operations, where result type of the expression is integer.
+ if (resultTy->isAnyPointerType())
+ elementType = resultTy->getPointeeType();
+ }
+
+ // Represent arithmetic on void pointers as arithmetic on char pointers.
+ // It is fine when a TypedValueRegion of char value type represents
+ // a void pointer. Note that arithmetic on void pointers is a GCC extension.
+ if (elementType->isVoidType())
+ elementType = getContext().CharTy;
+
+ if (Optional<NonLoc> indexV = index.getAs<NonLoc>()) {
+ return loc::MemRegionVal(MemMgr.getElementRegion(elementType, *indexV,
+ superR, getContext()));
+ }
+ }
+ return UnknownVal();
+}
+
+const llvm::APSInt *SimpleSValBuilder::getKnownValue(ProgramStateRef state,
+ SVal V) {
+ V = simplifySVal(state, V);
+ if (V.isUnknownOrUndef())
+ return nullptr;
+
+ if (Optional<loc::ConcreteInt> X = V.getAs<loc::ConcreteInt>())
+ return &X->getValue();
+
+ if (Optional<nonloc::ConcreteInt> X = V.getAs<nonloc::ConcreteInt>())
+ return &X->getValue();
+
+ if (SymbolRef Sym = V.getAsSymbol())
+ return state->getConstraintManager().getSymVal(state, Sym);
+
+ // FIXME: Add support for SymExprs.
+ return nullptr;
+}
+
+SVal SimpleSValBuilder::simplifySVal(ProgramStateRef State, SVal V) {
+ // For now, this function tries to constant-fold symbols inside a
+ // nonloc::SymbolVal, and does nothing else. More simplifications should
+ // be possible, such as constant-folding an index in an ElementRegion.
+
+ class Simplifier : public FullSValVisitor<Simplifier, SVal> {
+ ProgramStateRef State;
+ SValBuilder &SVB;
+
+ // Cache results for the lifetime of the Simplifier. Results change every
+ // time new constraints are added to the program state, which is the whole
+ // point of simplifying, and for that very reason it's pointless to maintain
+ // the same cache for the duration of the whole analysis.
+ llvm::DenseMap<SymbolRef, SVal> Cached;
+
+ static bool isUnchanged(SymbolRef Sym, SVal Val) {
+ return Sym == Val.getAsSymbol();
+ }
+
+ SVal cache(SymbolRef Sym, SVal V) {
+ Cached[Sym] = V;
+ return V;
+ }
+
+ SVal skip(SymbolRef Sym) {
+ return cache(Sym, SVB.makeSymbolVal(Sym));
+ }
+
+ public:
+ Simplifier(ProgramStateRef State)
+ : State(State), SVB(State->getStateManager().getSValBuilder()) {}
+
+ SVal VisitSymbolData(const SymbolData *S) {
+ // No cache here.
+ if (const llvm::APSInt *I =
+ SVB.getKnownValue(State, SVB.makeSymbolVal(S)))
+ return Loc::isLocType(S->getType()) ? (SVal)SVB.makeIntLocVal(*I)
+ : (SVal)SVB.makeIntVal(*I);
+ return SVB.makeSymbolVal(S);
+ }
+
+ // TODO: Support SymbolCast. Support IntSymExpr when/if we actually
+ // start producing them.
+
+ SVal VisitSymIntExpr(const SymIntExpr *S) {
+ auto I = Cached.find(S);
+ if (I != Cached.end())
+ return I->second;
+
+ SVal LHS = Visit(S->getLHS());
+ if (isUnchanged(S->getLHS(), LHS))
+ return skip(S);
+
+ SVal RHS;
+ // By looking at the APSInt in the right-hand side of S, we cannot
+ // figure out if it should be treated as a Loc or as a NonLoc.
+ // So make our guess by recalling that we cannot multiply pointers
+ // or compare a pointer to an integer.
+ if (Loc::isLocType(S->getLHS()->getType()) &&
+ BinaryOperator::isComparisonOp(S->getOpcode())) {
+ // The usual conversion of $sym to &SymRegion{$sym}, as they have
+ // the same meaning for Loc-type symbols, but the latter form
+ // is preferred in SVal computations for being Loc itself.
+ if (SymbolRef Sym = LHS.getAsSymbol()) {
+ assert(Loc::isLocType(Sym->getType()));
+ LHS = SVB.makeLoc(Sym);
+ }
+ RHS = SVB.makeIntLocVal(S->getRHS());
+ } else {
+ RHS = SVB.makeIntVal(S->getRHS());
+ }
+
+ return cache(
+ S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()));
+ }
+
+ SVal VisitSymSymExpr(const SymSymExpr *S) {
+ auto I = Cached.find(S);
+ if (I != Cached.end())
+ return I->second;
+
+ // For now don't try to simplify mixed Loc/NonLoc expressions
+ // because they often appear from LocAsInteger operations
+ // and we don't know how to combine a LocAsInteger
+ // with a concrete value.
+ if (Loc::isLocType(S->getLHS()->getType()) !=
+ Loc::isLocType(S->getRHS()->getType()))
+ return skip(S);
+
+ SVal LHS = Visit(S->getLHS());
+ SVal RHS = Visit(S->getRHS());
+ if (isUnchanged(S->getLHS(), LHS) && isUnchanged(S->getRHS(), RHS))
+ return skip(S);
+
+ return cache(
+ S, SVB.evalBinOp(State, S->getOpcode(), LHS, RHS, S->getType()));
+ }
+
+ SVal VisitSymExpr(SymbolRef S) { return nonloc::SymbolVal(S); }
+
+ SVal VisitMemRegion(const MemRegion *R) { return loc::MemRegionVal(R); }
+
+ SVal VisitNonLocSymbolVal(nonloc::SymbolVal V) {
+ // Simplification is much more costly than computing complexity.
+ // For high complexity, it may be not worth it.
+ return Visit(V.getSymbol());
+ }
+
+ SVal VisitSVal(SVal V) { return V; }
+ };
+
+ // A crude way of preventing this function from calling itself from evalBinOp.
+ static bool isReentering = false;
+ if (isReentering)
+ return V;
+
+ isReentering = true;
+ SVal SimplifiedV = Simplifier(State).Visit(V);
+ isReentering = false;
+
+ return SimplifiedV;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp
new file mode 100644
index 000000000000..4fa937d9658d
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -0,0 +1,561 @@
+//===- Store.cpp - Interface for maps from Locations to Values ------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defined the types Store and StoreManager.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/CXXInheritance.h"
+#include "clang/AST/CharUnits.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/Expr.h"
+#include "clang/AST/Type.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/BasicValueFactory.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "llvm/ADT/APSInt.h"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/ErrorHandling.h"
+#include <cassert>
+#include <cstdint>
+
+using namespace clang;
+using namespace ento;
+
+StoreManager::StoreManager(ProgramStateManager &stateMgr)
+ : svalBuilder(stateMgr.getSValBuilder()), StateMgr(stateMgr),
+ MRMgr(svalBuilder.getRegionManager()), Ctx(stateMgr.getContext()) {}
+
+StoreRef StoreManager::enterStackFrame(Store OldStore,
+ const CallEvent &Call,
+ const StackFrameContext *LCtx) {
+ StoreRef Store = StoreRef(OldStore, *this);
+
+ SmallVector<CallEvent::FrameBindingTy, 16> InitialBindings;
+ Call.getInitialStackFrameContents(LCtx, InitialBindings);
+
+ for (const auto &I : InitialBindings)
+ Store = Bind(Store.getStore(), I.first, I.second);
+
+ return Store;
+}
+
+const ElementRegion *StoreManager::MakeElementRegion(const SubRegion *Base,
+ QualType EleTy,
+ uint64_t index) {
+ NonLoc idx = svalBuilder.makeArrayIndex(index);
+ return MRMgr.getElementRegion(EleTy, idx, Base, svalBuilder.getContext());
+}
+
+const ElementRegion *StoreManager::GetElementZeroRegion(const SubRegion *R,
+ QualType T) {
+ NonLoc idx = svalBuilder.makeZeroArrayIndex();
+ assert(!T.isNull());
+ return MRMgr.getElementRegion(T, idx, R, Ctx);
+}
+
+const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy) {
+ ASTContext &Ctx = StateMgr.getContext();
+
+ // Handle casts to Objective-C objects.
+ if (CastToTy->isObjCObjectPointerType())
+ return R->StripCasts();
+
+ if (CastToTy->isBlockPointerType()) {
+ // FIXME: We may need different solutions, depending on the symbol
+ // involved. Blocks can be casted to/from 'id', as they can be treated
+ // as Objective-C objects. This could possibly be handled by enhancing
+ // our reasoning of downcasts of symbolic objects.
+ if (isa<CodeTextRegion>(R) || isa<SymbolicRegion>(R))
+ return R;
+
+ // We don't know what to make of it. Return a NULL region, which
+ // will be interpreted as UnknownVal.
+ return nullptr;
+ }
+
+ // Now assume we are casting from pointer to pointer. Other cases should
+ // already be handled.
+ QualType PointeeTy = CastToTy->getPointeeType();
+ QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
+
+ // Handle casts to void*. We just pass the region through.
+ if (CanonPointeeTy.getLocalUnqualifiedType() == Ctx.VoidTy)
+ return R;
+
+ // Handle casts from compatible types.
+ if (R->isBoundable())
+ if (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
+ QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
+ if (CanonPointeeTy == ObjTy)
+ return R;
+ }
+
+ // Process region cast according to the kind of the region being cast.
+ switch (R->getKind()) {
+ case MemRegion::CXXThisRegionKind:
+ case MemRegion::CodeSpaceRegionKind:
+ case MemRegion::StackLocalsSpaceRegionKind:
+ case MemRegion::StackArgumentsSpaceRegionKind:
+ case MemRegion::HeapSpaceRegionKind:
+ case MemRegion::UnknownSpaceRegionKind:
+ case MemRegion::StaticGlobalSpaceRegionKind:
+ case MemRegion::GlobalInternalSpaceRegionKind:
+ case MemRegion::GlobalSystemSpaceRegionKind:
+ case MemRegion::GlobalImmutableSpaceRegionKind: {
+ llvm_unreachable("Invalid region cast");
+ }
+
+ case MemRegion::FunctionCodeRegionKind:
+ case MemRegion::BlockCodeRegionKind:
+ case MemRegion::BlockDataRegionKind:
+ case MemRegion::StringRegionKind:
+ // FIXME: Need to handle arbitrary downcasts.
+ case MemRegion::SymbolicRegionKind:
+ case MemRegion::AllocaRegionKind:
+ case MemRegion::CompoundLiteralRegionKind:
+ case MemRegion::FieldRegionKind:
+ case MemRegion::ObjCIvarRegionKind:
+ case MemRegion::ObjCStringRegionKind:
+ case MemRegion::VarRegionKind:
+ case MemRegion::CXXTempObjectRegionKind:
+ case MemRegion::CXXBaseObjectRegionKind:
+ case MemRegion::CXXDerivedObjectRegionKind:
+ return MakeElementRegion(cast<SubRegion>(R), PointeeTy);
+
+ case MemRegion::ElementRegionKind: {
+ // If we are casting from an ElementRegion to another type, the
+ // algorithm is as follows:
+ //
+ // (1) Compute the "raw offset" of the ElementRegion from the
+ // base region. This is done by calling 'getAsRawOffset()'.
+ //
+ // (2a) If we get a 'RegionRawOffset' after calling
+ // 'getAsRawOffset()', determine if the absolute offset
+ // can be exactly divided into chunks of the size of the
+ // casted-pointee type. If so, create a new ElementRegion with
+ // the pointee-cast type as the new ElementType and the index
+ // being the offset divded by the chunk size. If not, create
+ // a new ElementRegion at offset 0 off the raw offset region.
+ //
+ // (2b) If we don't a get a 'RegionRawOffset' after calling
+ // 'getAsRawOffset()', it means that we are at offset 0.
+ //
+ // FIXME: Handle symbolic raw offsets.
+
+ const ElementRegion *elementR = cast<ElementRegion>(R);
+ const RegionRawOffset &rawOff = elementR->getAsArrayOffset();
+ const MemRegion *baseR = rawOff.getRegion();
+
+ // If we cannot compute a raw offset, throw up our hands and return
+ // a NULL MemRegion*.
+ if (!baseR)
+ return nullptr;
+
+ CharUnits off = rawOff.getOffset();
+
+ if (off.isZero()) {
+ // Edge case: we are at 0 bytes off the beginning of baseR. We
+ // check to see if type we are casting to is the same as the base
+ // region. If so, just return the base region.
+ if (const auto *TR = dyn_cast<TypedValueRegion>(baseR)) {
+ QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
+ QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
+ if (CanonPointeeTy == ObjTy)
+ return baseR;
+ }
+
+ // Otherwise, create a new ElementRegion at offset 0.
+ return MakeElementRegion(cast<SubRegion>(baseR), PointeeTy);
+ }
+
+ // We have a non-zero offset from the base region. We want to determine
+ // if the offset can be evenly divided by sizeof(PointeeTy). If so,
+ // we create an ElementRegion whose index is that value. Otherwise, we
+ // create two ElementRegions, one that reflects a raw offset and the other
+ // that reflects the cast.
+
+ // Compute the index for the new ElementRegion.
+ int64_t newIndex = 0;
+ const MemRegion *newSuperR = nullptr;
+
+ // We can only compute sizeof(PointeeTy) if it is a complete type.
+ if (!PointeeTy->isIncompleteType()) {
+ // Compute the size in **bytes**.
+ CharUnits pointeeTySize = Ctx.getTypeSizeInChars(PointeeTy);
+ if (!pointeeTySize.isZero()) {
+ // Is the offset a multiple of the size? If so, we can layer the
+ // ElementRegion (with elementType == PointeeTy) directly on top of
+ // the base region.
+ if (off % pointeeTySize == 0) {
+ newIndex = off / pointeeTySize;
+ newSuperR = baseR;
+ }
+ }
+ }
+
+ if (!newSuperR) {
+ // Create an intermediate ElementRegion to represent the raw byte.
+ // This will be the super region of the final ElementRegion.
+ newSuperR = MakeElementRegion(cast<SubRegion>(baseR), Ctx.CharTy,
+ off.getQuantity());
+ }
+
+ return MakeElementRegion(cast<SubRegion>(newSuperR), PointeeTy, newIndex);
+ }
+ }
+
+ llvm_unreachable("unreachable");
+}
+
+static bool regionMatchesCXXRecordType(SVal V, QualType Ty) {
+ const MemRegion *MR = V.getAsRegion();
+ if (!MR)
+ return true;
+
+ const auto *TVR = dyn_cast<TypedValueRegion>(MR);
+ if (!TVR)
+ return true;
+
+ const CXXRecordDecl *RD = TVR->getValueType()->getAsCXXRecordDecl();
+ if (!RD)
+ return true;
+
+ const CXXRecordDecl *Expected = Ty->getPointeeCXXRecordDecl();
+ if (!Expected)
+ Expected = Ty->getAsCXXRecordDecl();
+
+ return Expected->getCanonicalDecl() == RD->getCanonicalDecl();
+}
+
+SVal StoreManager::evalDerivedToBase(SVal Derived, const CastExpr *Cast) {
+ // Sanity check to avoid doing the wrong thing in the face of
+ // reinterpret_cast.
+ if (!regionMatchesCXXRecordType(Derived, Cast->getSubExpr()->getType()))
+ return UnknownVal();
+
+ // Walk through the cast path to create nested CXXBaseRegions.
+ SVal Result = Derived;
+ for (CastExpr::path_const_iterator I = Cast->path_begin(),
+ E = Cast->path_end();
+ I != E; ++I) {
+ Result = evalDerivedToBase(Result, (*I)->getType(), (*I)->isVirtual());
+ }
+ return Result;
+}
+
+SVal StoreManager::evalDerivedToBase(SVal Derived, const CXXBasePath &Path) {
+ // Walk through the path to create nested CXXBaseRegions.
+ SVal Result = Derived;
+ for (const auto &I : Path)
+ Result = evalDerivedToBase(Result, I.Base->getType(),
+ I.Base->isVirtual());
+ return Result;
+}
+
+SVal StoreManager::evalDerivedToBase(SVal Derived, QualType BaseType,
+ bool IsVirtual) {
+ const MemRegion *DerivedReg = Derived.getAsRegion();
+ if (!DerivedReg)
+ return Derived;
+
+ const CXXRecordDecl *BaseDecl = BaseType->getPointeeCXXRecordDecl();
+ if (!BaseDecl)
+ BaseDecl = BaseType->getAsCXXRecordDecl();
+ assert(BaseDecl && "not a C++ object?");
+
+ if (const auto *AlreadyDerivedReg =
+ dyn_cast<CXXDerivedObjectRegion>(DerivedReg)) {
+ if (const auto *SR =
+ dyn_cast<SymbolicRegion>(AlreadyDerivedReg->getSuperRegion()))
+ if (SR->getSymbol()->getType()->getPointeeCXXRecordDecl() == BaseDecl)
+ return loc::MemRegionVal(SR);
+
+ DerivedReg = AlreadyDerivedReg->getSuperRegion();
+ }
+
+ const MemRegion *BaseReg = MRMgr.getCXXBaseObjectRegion(
+ BaseDecl, cast<SubRegion>(DerivedReg), IsVirtual);
+
+ return loc::MemRegionVal(BaseReg);
+}
+
+/// Returns the static type of the given region, if it represents a C++ class
+/// object.
+///
+/// This handles both fully-typed regions, where the dynamic type is known, and
+/// symbolic regions, where the dynamic type is merely bounded (and even then,
+/// only ostensibly!), but does not take advantage of any dynamic type info.
+static const CXXRecordDecl *getCXXRecordType(const MemRegion *MR) {
+ if (const auto *TVR = dyn_cast<TypedValueRegion>(MR))
+ return TVR->getValueType()->getAsCXXRecordDecl();
+ if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
+ return SR->getSymbol()->getType()->getPointeeCXXRecordDecl();
+ return nullptr;
+}
+
+SVal StoreManager::attemptDownCast(SVal Base, QualType TargetType,
+ bool &Failed) {
+ Failed = false;
+
+ const MemRegion *MR = Base.getAsRegion();
+ if (!MR)
+ return UnknownVal();
+
+ // Assume the derived class is a pointer or a reference to a CXX record.
+ TargetType = TargetType->getPointeeType();
+ assert(!TargetType.isNull());
+ const CXXRecordDecl *TargetClass = TargetType->getAsCXXRecordDecl();
+ if (!TargetClass && !TargetType->isVoidType())
+ return UnknownVal();
+
+ // Drill down the CXXBaseObject chains, which represent upcasts (casts from
+ // derived to base).
+ while (const CXXRecordDecl *MRClass = getCXXRecordType(MR)) {
+ // If found the derived class, the cast succeeds.
+ if (MRClass == TargetClass)
+ return loc::MemRegionVal(MR);
+
+ // We skip over incomplete types. They must be the result of an earlier
+ // reinterpret_cast, as one can only dynamic_cast between types in the same
+ // class hierarchy.
+ if (!TargetType->isVoidType() && MRClass->hasDefinition()) {
+ // Static upcasts are marked as DerivedToBase casts by Sema, so this will
+ // only happen when multiple or virtual inheritance is involved.
+ CXXBasePaths Paths(/*FindAmbiguities=*/false, /*RecordPaths=*/true,
+ /*DetectVirtual=*/false);
+ if (MRClass->isDerivedFrom(TargetClass, Paths))
+ return evalDerivedToBase(loc::MemRegionVal(MR), Paths.front());
+ }
+
+ if (const auto *BaseR = dyn_cast<CXXBaseObjectRegion>(MR)) {
+ // Drill down the chain to get the derived classes.
+ MR = BaseR->getSuperRegion();
+ continue;
+ }
+
+ // If this is a cast to void*, return the region.
+ if (TargetType->isVoidType())
+ return loc::MemRegionVal(MR);
+
+ // Strange use of reinterpret_cast can give us paths we don't reason
+ // about well, by putting in ElementRegions where we'd expect
+ // CXXBaseObjectRegions. If it's a valid reinterpret_cast (i.e. if the
+ // derived class has a zero offset from the base class), then it's safe
+ // to strip the cast; if it's invalid, -Wreinterpret-base-class should
+ // catch it. In the interest of performance, the analyzer will silently
+ // do the wrong thing in the invalid case (because offsets for subregions
+ // will be wrong).
+ const MemRegion *Uncasted = MR->StripCasts(/*IncludeBaseCasts=*/false);
+ if (Uncasted == MR) {
+ // We reached the bottom of the hierarchy and did not find the derived
+ // class. We must be casting the base to derived, so the cast should
+ // fail.
+ break;
+ }
+
+ MR = Uncasted;
+ }
+
+ // If we're casting a symbolic base pointer to a derived class, use
+ // CXXDerivedObjectRegion to represent the cast. If it's a pointer to an
+ // unrelated type, it must be a weird reinterpret_cast and we have to
+ // be fine with ElementRegion. TODO: Should we instead make
+ // Derived{TargetClass, Element{SourceClass, SR}}?
+ if (const auto *SR = dyn_cast<SymbolicRegion>(MR)) {
+ QualType T = SR->getSymbol()->getType();
+ const CXXRecordDecl *SourceClass = T->getPointeeCXXRecordDecl();
+ if (TargetClass && SourceClass && TargetClass->isDerivedFrom(SourceClass))
+ return loc::MemRegionVal(
+ MRMgr.getCXXDerivedObjectRegion(TargetClass, SR));
+ return loc::MemRegionVal(GetElementZeroRegion(SR, TargetType));
+ }
+
+ // We failed if the region we ended up with has perfect type info.
+ Failed = isa<TypedValueRegion>(MR);
+ return UnknownVal();
+}
+
+/// CastRetrievedVal - Used by subclasses of StoreManager to implement
+/// implicit casts that arise from loads from regions that are reinterpreted
+/// as another region.
+SVal StoreManager::CastRetrievedVal(SVal V, const TypedValueRegion *R,
+ QualType castTy) {
+ if (castTy.isNull() || V.isUnknownOrUndef())
+ return V;
+
+ // The dispatchCast() call below would convert the int into a float.
+ // What we want, however, is a bit-by-bit reinterpretation of the int
+ // as a float, which usually yields nothing garbage. For now skip casts
+ // from ints to floats.
+ // TODO: What other combinations of types are affected?
+ if (castTy->isFloatingType()) {
+ SymbolRef Sym = V.getAsSymbol();
+ if (Sym && !Sym->getType()->isFloatingType())
+ return UnknownVal();
+ }
+
+ // When retrieving symbolic pointer and expecting a non-void pointer,
+ // wrap them into element regions of the expected type if necessary.
+ // SValBuilder::dispatchCast() doesn't do that, but it is necessary to
+ // make sure that the retrieved value makes sense, because there's no other
+ // cast in the AST that would tell us to cast it to the correct pointer type.
+ // We might need to do that for non-void pointers as well.
+ // FIXME: We really need a single good function to perform casts for us
+ // correctly every time we need it.
+ if (castTy->isPointerType() && !castTy->isVoidPointerType())
+ if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(V.getAsRegion()))
+ if (SR->getSymbol()->getType().getCanonicalType() !=
+ castTy.getCanonicalType())
+ return loc::MemRegionVal(castRegion(SR, castTy));
+
+ return svalBuilder.dispatchCast(V, castTy);
+}
+
+SVal StoreManager::getLValueFieldOrIvar(const Decl *D, SVal Base) {
+ if (Base.isUnknownOrUndef())
+ return Base;
+
+ Loc BaseL = Base.castAs<Loc>();
+ const SubRegion* BaseR = nullptr;
+
+ switch (BaseL.getSubKind()) {
+ case loc::MemRegionValKind:
+ BaseR = cast<SubRegion>(BaseL.castAs<loc::MemRegionVal>().getRegion());
+ break;
+
+ case loc::GotoLabelKind:
+ // These are anormal cases. Flag an undefined value.
+ return UndefinedVal();
+
+ case loc::ConcreteIntKind:
+ // While these seem funny, this can happen through casts.
+ // FIXME: What we should return is the field offset, not base. For example,
+ // add the field offset to the integer value. That way things
+ // like this work properly: &(((struct foo *) 0xa)->f)
+ // However, that's not easy to fix without reducing our abilities
+ // to catch null pointer dereference. Eg., ((struct foo *)0x0)->f = 7
+ // is a null dereference even though we're dereferencing offset of f
+ // rather than null. Coming up with an approach that computes offsets
+ // over null pointers properly while still being able to catch null
+ // dereferences might be worth it.
+ return Base;
+
+ default:
+ llvm_unreachable("Unhandled Base.");
+ }
+
+ // NOTE: We must have this check first because ObjCIvarDecl is a subclass
+ // of FieldDecl.
+ if (const auto *ID = dyn_cast<ObjCIvarDecl>(D))
+ return loc::MemRegionVal(MRMgr.getObjCIvarRegion(ID, BaseR));
+
+ return loc::MemRegionVal(MRMgr.getFieldRegion(cast<FieldDecl>(D), BaseR));
+}
+
+SVal StoreManager::getLValueIvar(const ObjCIvarDecl *decl, SVal base) {
+ return getLValueFieldOrIvar(decl, base);
+}
+
+SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
+ SVal Base) {
+ // If the base is an unknown or undefined value, just return it back.
+ // FIXME: For absolute pointer addresses, we just return that value back as
+ // well, although in reality we should return the offset added to that
+ // value. See also the similar FIXME in getLValueFieldOrIvar().
+ if (Base.isUnknownOrUndef() || Base.getAs<loc::ConcreteInt>())
+ return Base;
+
+ if (Base.getAs<loc::GotoLabel>())
+ return UnknownVal();
+
+ const SubRegion *BaseRegion =
+ Base.castAs<loc::MemRegionVal>().getRegionAs<SubRegion>();
+
+ // Pointer of any type can be cast and used as array base.
+ const auto *ElemR = dyn_cast<ElementRegion>(BaseRegion);
+
+ // Convert the offset to the appropriate size and signedness.
+ Offset = svalBuilder.convertToArrayIndex(Offset).castAs<NonLoc>();
+
+ if (!ElemR) {
+ // If the base region is not an ElementRegion, create one.
+ // This can happen in the following example:
+ //
+ // char *p = __builtin_alloc(10);
+ // p[1] = 8;
+ //
+ // Observe that 'p' binds to an AllocaRegion.
+ return loc::MemRegionVal(MRMgr.getElementRegion(elementType, Offset,
+ BaseRegion, Ctx));
+ }
+
+ SVal BaseIdx = ElemR->getIndex();
+
+ if (!BaseIdx.getAs<nonloc::ConcreteInt>())
+ return UnknownVal();
+
+ const llvm::APSInt &BaseIdxI =
+ BaseIdx.castAs<nonloc::ConcreteInt>().getValue();
+
+ // Only allow non-integer offsets if the base region has no offset itself.
+ // FIXME: This is a somewhat arbitrary restriction. We should be using
+ // SValBuilder here to add the two offsets without checking their types.
+ if (!Offset.getAs<nonloc::ConcreteInt>()) {
+ if (isa<ElementRegion>(BaseRegion->StripCasts()))
+ return UnknownVal();
+
+ return loc::MemRegionVal(MRMgr.getElementRegion(
+ elementType, Offset, cast<SubRegion>(ElemR->getSuperRegion()), Ctx));
+ }
+
+ const llvm::APSInt& OffI = Offset.castAs<nonloc::ConcreteInt>().getValue();
+ assert(BaseIdxI.isSigned());
+
+ // Compute the new index.
+ nonloc::ConcreteInt NewIdx(svalBuilder.getBasicValueFactory().getValue(BaseIdxI +
+ OffI));
+
+ // Construct the new ElementRegion.
+ const SubRegion *ArrayR = cast<SubRegion>(ElemR->getSuperRegion());
+ return loc::MemRegionVal(MRMgr.getElementRegion(elementType, NewIdx, ArrayR,
+ Ctx));
+}
+
+StoreManager::BindingsHandler::~BindingsHandler() = default;
+
+bool StoreManager::FindUniqueBinding::HandleBinding(StoreManager& SMgr,
+ Store store,
+ const MemRegion* R,
+ SVal val) {
+ SymbolRef SymV = val.getAsLocSymbol();
+ if (!SymV || SymV != Sym)
+ return true;
+
+ if (Binding) {
+ First = false;
+ return false;
+ }
+ else
+ Binding = R;
+
+ return true;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp
new file mode 100644
index 000000000000..350f4b8bb3a2
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SubEngine.cpp
@@ -0,0 +1,14 @@
+//== SubEngine.cpp - Interface of the subengine of CoreEngine ------*- C++ -*-//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/SubEngine.h"
+
+using namespace clang::ento;
+
+void SubEngine::anchor() { }
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
new file mode 100644
index 000000000000..66273f099a38
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -0,0 +1,567 @@
+//===- SymbolManager.h - Management of Symbolic Values --------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines SymbolManager, a class that manages symbolic values
+// created for use by ExprEngine and related classes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Expr.h"
+#include "clang/Analysis/Analyses/LiveVariables.h"
+#include "clang/Analysis/AnalysisDeclContext.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/Store.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SymExpr.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/Casting.h"
+#include "llvm/Support/Compiler.h"
+#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+
+using namespace clang;
+using namespace ento;
+
+void SymExpr::anchor() {}
+
+LLVM_DUMP_METHOD void SymExpr::dump() const {
+ dumpToStream(llvm::errs());
+}
+
+void SymIntExpr::dumpToStream(raw_ostream &os) const {
+ os << '(';
+ getLHS()->dumpToStream(os);
+ os << ") "
+ << BinaryOperator::getOpcodeStr(getOpcode()) << ' ';
+ if (getRHS().isUnsigned())
+ os << getRHS().getZExtValue();
+ else
+ os << getRHS().getSExtValue();
+ if (getRHS().isUnsigned())
+ os << 'U';
+}
+
+void IntSymExpr::dumpToStream(raw_ostream &os) const {
+ if (getLHS().isUnsigned())
+ os << getLHS().getZExtValue();
+ else
+ os << getLHS().getSExtValue();
+ if (getLHS().isUnsigned())
+ os << 'U';
+ os << ' '
+ << BinaryOperator::getOpcodeStr(getOpcode())
+ << " (";
+ getRHS()->dumpToStream(os);
+ os << ')';
+}
+
+void SymSymExpr::dumpToStream(raw_ostream &os) const {
+ os << '(';
+ getLHS()->dumpToStream(os);
+ os << ") "
+ << BinaryOperator::getOpcodeStr(getOpcode())
+ << " (";
+ getRHS()->dumpToStream(os);
+ os << ')';
+}
+
+void SymbolCast::dumpToStream(raw_ostream &os) const {
+ os << '(' << ToTy.getAsString() << ") (";
+ Operand->dumpToStream(os);
+ os << ')';
+}
+
+void SymbolConjured::dumpToStream(raw_ostream &os) const {
+ os << "conj_$" << getSymbolID() << '{' << T.getAsString() << ", LC"
+ << LCtx->getID();
+ if (S)
+ os << ", S" << S->getID(LCtx->getDecl()->getASTContext());
+ else
+ os << ", no stmt";
+ os << ", #" << Count << '}';
+}
+
+void SymbolDerived::dumpToStream(raw_ostream &os) const {
+ os << "derived_$" << getSymbolID() << '{'
+ << getParentSymbol() << ',' << getRegion() << '}';
+}
+
+void SymbolExtent::dumpToStream(raw_ostream &os) const {
+ os << "extent_$" << getSymbolID() << '{' << getRegion() << '}';
+}
+
+void SymbolMetadata::dumpToStream(raw_ostream &os) const {
+ os << "meta_$" << getSymbolID() << '{'
+ << getRegion() << ',' << T.getAsString() << '}';
+}
+
+void SymbolData::anchor() {}
+
+void SymbolRegionValue::dumpToStream(raw_ostream &os) const {
+ os << "reg_$" << getSymbolID()
+ << '<' << getType().getAsString() << ' ' << R << '>';
+}
+
+bool SymExpr::symbol_iterator::operator==(const symbol_iterator &X) const {
+ return itr == X.itr;
+}
+
+bool SymExpr::symbol_iterator::operator!=(const symbol_iterator &X) const {
+ return itr != X.itr;
+}
+
+SymExpr::symbol_iterator::symbol_iterator(const SymExpr *SE) {
+ itr.push_back(SE);
+}
+
+SymExpr::symbol_iterator &SymExpr::symbol_iterator::operator++() {
+ assert(!itr.empty() && "attempting to iterate on an 'end' iterator");
+ expand();
+ return *this;
+}
+
+SymbolRef SymExpr::symbol_iterator::operator*() {
+ assert(!itr.empty() && "attempting to dereference an 'end' iterator");
+ return itr.back();
+}
+
+void SymExpr::symbol_iterator::expand() {
+ const SymExpr *SE = itr.pop_back_val();
+
+ switch (SE->getKind()) {
+ case SymExpr::SymbolRegionValueKind:
+ case SymExpr::SymbolConjuredKind:
+ case SymExpr::SymbolDerivedKind:
+ case SymExpr::SymbolExtentKind:
+ case SymExpr::SymbolMetadataKind:
+ return;
+ case SymExpr::SymbolCastKind:
+ itr.push_back(cast<SymbolCast>(SE)->getOperand());
+ return;
+ case SymExpr::SymIntExprKind:
+ itr.push_back(cast<SymIntExpr>(SE)->getLHS());
+ return;
+ case SymExpr::IntSymExprKind:
+ itr.push_back(cast<IntSymExpr>(SE)->getRHS());
+ return;
+ case SymExpr::SymSymExprKind: {
+ const auto *x = cast<SymSymExpr>(SE);
+ itr.push_back(x->getLHS());
+ itr.push_back(x->getRHS());
+ return;
+ }
+ }
+ llvm_unreachable("unhandled expansion case");
+}
+
+const SymbolRegionValue*
+SymbolManager::getRegionValueSymbol(const TypedValueRegion* R) {
+ llvm::FoldingSetNodeID profile;
+ SymbolRegionValue::Profile(profile, R);
+ void *InsertPos;
+ SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
+ if (!SD) {
+ SD = (SymExpr*) BPAlloc.Allocate<SymbolRegionValue>();
+ new (SD) SymbolRegionValue(SymbolCounter, R);
+ DataSet.InsertNode(SD, InsertPos);
+ ++SymbolCounter;
+ }
+
+ return cast<SymbolRegionValue>(SD);
+}
+
+const SymbolConjured* SymbolManager::conjureSymbol(const Stmt *E,
+ const LocationContext *LCtx,
+ QualType T,
+ unsigned Count,
+ const void *SymbolTag) {
+ llvm::FoldingSetNodeID profile;
+ SymbolConjured::Profile(profile, E, T, Count, LCtx, SymbolTag);
+ void *InsertPos;
+ SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
+ if (!SD) {
+ SD = (SymExpr*) BPAlloc.Allocate<SymbolConjured>();
+ new (SD) SymbolConjured(SymbolCounter, E, LCtx, T, Count, SymbolTag);
+ DataSet.InsertNode(SD, InsertPos);
+ ++SymbolCounter;
+ }
+
+ return cast<SymbolConjured>(SD);
+}
+
+const SymbolDerived*
+SymbolManager::getDerivedSymbol(SymbolRef parentSymbol,
+ const TypedValueRegion *R) {
+ llvm::FoldingSetNodeID profile;
+ SymbolDerived::Profile(profile, parentSymbol, R);
+ void *InsertPos;
+ SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
+ if (!SD) {
+ SD = (SymExpr*) BPAlloc.Allocate<SymbolDerived>();
+ new (SD) SymbolDerived(SymbolCounter, parentSymbol, R);
+ DataSet.InsertNode(SD, InsertPos);
+ ++SymbolCounter;
+ }
+
+ return cast<SymbolDerived>(SD);
+}
+
+const SymbolExtent*
+SymbolManager::getExtentSymbol(const SubRegion *R) {
+ llvm::FoldingSetNodeID profile;
+ SymbolExtent::Profile(profile, R);
+ void *InsertPos;
+ SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
+ if (!SD) {
+ SD = (SymExpr*) BPAlloc.Allocate<SymbolExtent>();
+ new (SD) SymbolExtent(SymbolCounter, R);
+ DataSet.InsertNode(SD, InsertPos);
+ ++SymbolCounter;
+ }
+
+ return cast<SymbolExtent>(SD);
+}
+
+const SymbolMetadata *
+SymbolManager::getMetadataSymbol(const MemRegion* R, const Stmt *S, QualType T,
+ const LocationContext *LCtx,
+ unsigned Count, const void *SymbolTag) {
+ llvm::FoldingSetNodeID profile;
+ SymbolMetadata::Profile(profile, R, S, T, LCtx, Count, SymbolTag);
+ void *InsertPos;
+ SymExpr *SD = DataSet.FindNodeOrInsertPos(profile, InsertPos);
+ if (!SD) {
+ SD = (SymExpr*) BPAlloc.Allocate<SymbolMetadata>();
+ new (SD) SymbolMetadata(SymbolCounter, R, S, T, LCtx, Count, SymbolTag);
+ DataSet.InsertNode(SD, InsertPos);
+ ++SymbolCounter;
+ }
+
+ return cast<SymbolMetadata>(SD);
+}
+
+const SymbolCast*
+SymbolManager::getCastSymbol(const SymExpr *Op,
+ QualType From, QualType To) {
+ llvm::FoldingSetNodeID ID;
+ SymbolCast::Profile(ID, Op, From, To);
+ void *InsertPos;
+ SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
+ if (!data) {
+ data = (SymbolCast*) BPAlloc.Allocate<SymbolCast>();
+ new (data) SymbolCast(Op, From, To);
+ DataSet.InsertNode(data, InsertPos);
+ }
+
+ return cast<SymbolCast>(data);
+}
+
+const SymIntExpr *SymbolManager::getSymIntExpr(const SymExpr *lhs,
+ BinaryOperator::Opcode op,
+ const llvm::APSInt& v,
+ QualType t) {
+ llvm::FoldingSetNodeID ID;
+ SymIntExpr::Profile(ID, lhs, op, v, t);
+ void *InsertPos;
+ SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
+
+ if (!data) {
+ data = (SymIntExpr*) BPAlloc.Allocate<SymIntExpr>();
+ new (data) SymIntExpr(lhs, op, v, t);
+ DataSet.InsertNode(data, InsertPos);
+ }
+
+ return cast<SymIntExpr>(data);
+}
+
+const IntSymExpr *SymbolManager::getIntSymExpr(const llvm::APSInt& lhs,
+ BinaryOperator::Opcode op,
+ const SymExpr *rhs,
+ QualType t) {
+ llvm::FoldingSetNodeID ID;
+ IntSymExpr::Profile(ID, lhs, op, rhs, t);
+ void *InsertPos;
+ SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
+
+ if (!data) {
+ data = (IntSymExpr*) BPAlloc.Allocate<IntSymExpr>();
+ new (data) IntSymExpr(lhs, op, rhs, t);
+ DataSet.InsertNode(data, InsertPos);
+ }
+
+ return cast<IntSymExpr>(data);
+}
+
+const SymSymExpr *SymbolManager::getSymSymExpr(const SymExpr *lhs,
+ BinaryOperator::Opcode op,
+ const SymExpr *rhs,
+ QualType t) {
+ llvm::FoldingSetNodeID ID;
+ SymSymExpr::Profile(ID, lhs, op, rhs, t);
+ void *InsertPos;
+ SymExpr *data = DataSet.FindNodeOrInsertPos(ID, InsertPos);
+
+ if (!data) {
+ data = (SymSymExpr*) BPAlloc.Allocate<SymSymExpr>();
+ new (data) SymSymExpr(lhs, op, rhs, t);
+ DataSet.InsertNode(data, InsertPos);
+ }
+
+ return cast<SymSymExpr>(data);
+}
+
+QualType SymbolConjured::getType() const {
+ return T;
+}
+
+QualType SymbolDerived::getType() const {
+ return R->getValueType();
+}
+
+QualType SymbolExtent::getType() const {
+ ASTContext &Ctx = R->getMemRegionManager()->getContext();
+ return Ctx.getSizeType();
+}
+
+QualType SymbolMetadata::getType() const {
+ return T;
+}
+
+QualType SymbolRegionValue::getType() const {
+ return R->getValueType();
+}
+
+SymbolManager::~SymbolManager() {
+ llvm::DeleteContainerSeconds(SymbolDependencies);
+}
+
+bool SymbolManager::canSymbolicate(QualType T) {
+ T = T.getCanonicalType();
+
+ if (Loc::isLocType(T))
+ return true;
+
+ if (T->isIntegralOrEnumerationType())
+ return true;
+
+ if (T->isRecordType() && !T->isUnionType())
+ return true;
+
+ return false;
+}
+
+void SymbolManager::addSymbolDependency(const SymbolRef Primary,
+ const SymbolRef Dependent) {
+ SymbolDependTy::iterator I = SymbolDependencies.find(Primary);
+ SymbolRefSmallVectorTy *dependencies = nullptr;
+ if (I == SymbolDependencies.end()) {
+ dependencies = new SymbolRefSmallVectorTy();
+ SymbolDependencies[Primary] = dependencies;
+ } else {
+ dependencies = I->second;
+ }
+ dependencies->push_back(Dependent);
+}
+
+const SymbolRefSmallVectorTy *SymbolManager::getDependentSymbols(
+ const SymbolRef Primary) {
+ SymbolDependTy::const_iterator I = SymbolDependencies.find(Primary);
+ if (I == SymbolDependencies.end())
+ return nullptr;
+ return I->second;
+}
+
+void SymbolReaper::markDependentsLive(SymbolRef sym) {
+ // Do not mark dependents more then once.
+ SymbolMapTy::iterator LI = TheLiving.find(sym);
+ assert(LI != TheLiving.end() && "The primary symbol is not live.");
+ if (LI->second == HaveMarkedDependents)
+ return;
+ LI->second = HaveMarkedDependents;
+
+ if (const SymbolRefSmallVectorTy *Deps = SymMgr.getDependentSymbols(sym)) {
+ for (const auto I : *Deps) {
+ if (TheLiving.find(I) != TheLiving.end())
+ continue;
+ markLive(I);
+ }
+ }
+}
+
+void SymbolReaper::markLive(SymbolRef sym) {
+ TheLiving[sym] = NotProcessed;
+ markDependentsLive(sym);
+}
+
+void SymbolReaper::markLive(const MemRegion *region) {
+ RegionRoots.insert(region);
+ markElementIndicesLive(region);
+}
+
+void SymbolReaper::markElementIndicesLive(const MemRegion *region) {
+ for (auto SR = dyn_cast<SubRegion>(region); SR;
+ SR = dyn_cast<SubRegion>(SR->getSuperRegion())) {
+ if (const auto ER = dyn_cast<ElementRegion>(SR)) {
+ SVal Idx = ER->getIndex();
+ for (auto SI = Idx.symbol_begin(), SE = Idx.symbol_end(); SI != SE; ++SI)
+ markLive(*SI);
+ }
+ }
+}
+
+void SymbolReaper::markInUse(SymbolRef sym) {
+ if (isa<SymbolMetadata>(sym))
+ MetadataInUse.insert(sym);
+}
+
+bool SymbolReaper::isLiveRegion(const MemRegion *MR) {
+ if (RegionRoots.count(MR))
+ return true;
+
+ MR = MR->getBaseRegion();
+
+ if (const auto *SR = dyn_cast<SymbolicRegion>(MR))
+ return isLive(SR->getSymbol());
+
+ if (const auto *VR = dyn_cast<VarRegion>(MR))
+ return isLive(VR, true);
+
+ // FIXME: This is a gross over-approximation. What we really need is a way to
+ // tell if anything still refers to this region. Unlike SymbolicRegions,
+ // AllocaRegions don't have associated symbols, though, so we don't actually
+ // have a way to track their liveness.
+ if (isa<AllocaRegion>(MR))
+ return true;
+
+ if (isa<CXXThisRegion>(MR))
+ return true;
+
+ if (isa<MemSpaceRegion>(MR))
+ return true;
+
+ if (isa<CodeTextRegion>(MR))
+ return true;
+
+ return false;
+}
+
+bool SymbolReaper::isLive(SymbolRef sym) {
+ if (TheLiving.count(sym)) {
+ markDependentsLive(sym);
+ return true;
+ }
+
+ bool KnownLive;
+
+ switch (sym->getKind()) {
+ case SymExpr::SymbolRegionValueKind:
+ KnownLive = isLiveRegion(cast<SymbolRegionValue>(sym)->getRegion());
+ break;
+ case SymExpr::SymbolConjuredKind:
+ KnownLive = false;
+ break;
+ case SymExpr::SymbolDerivedKind:
+ KnownLive = isLive(cast<SymbolDerived>(sym)->getParentSymbol());
+ break;
+ case SymExpr::SymbolExtentKind:
+ KnownLive = isLiveRegion(cast<SymbolExtent>(sym)->getRegion());
+ break;
+ case SymExpr::SymbolMetadataKind:
+ KnownLive = MetadataInUse.count(sym) &&
+ isLiveRegion(cast<SymbolMetadata>(sym)->getRegion());
+ if (KnownLive)
+ MetadataInUse.erase(sym);
+ break;
+ case SymExpr::SymIntExprKind:
+ KnownLive = isLive(cast<SymIntExpr>(sym)->getLHS());
+ break;
+ case SymExpr::IntSymExprKind:
+ KnownLive = isLive(cast<IntSymExpr>(sym)->getRHS());
+ break;
+ case SymExpr::SymSymExprKind:
+ KnownLive = isLive(cast<SymSymExpr>(sym)->getLHS()) &&
+ isLive(cast<SymSymExpr>(sym)->getRHS());
+ break;
+ case SymExpr::SymbolCastKind:
+ KnownLive = isLive(cast<SymbolCast>(sym)->getOperand());
+ break;
+ }
+
+ if (KnownLive)
+ markLive(sym);
+
+ return KnownLive;
+}
+
+bool
+SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
+ if (LCtx == nullptr)
+ return false;
+
+ if (LCtx != ELCtx) {
+ // If the reaper's location context is a parent of the expression's
+ // location context, then the expression value is now "out of scope".
+ if (LCtx->isParentOf(ELCtx))
+ return false;
+ return true;
+ }
+
+ // If no statement is provided, everything is this and parent contexts is live.
+ if (!Loc)
+ return true;
+
+ return LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, ExprVal);
+}
+
+bool SymbolReaper::isLive(const VarRegion *VR, bool includeStoreBindings) const{
+ const StackFrameContext *VarContext = VR->getStackFrame();
+
+ if (!VarContext)
+ return true;
+
+ if (!LCtx)
+ return false;
+ const StackFrameContext *CurrentContext = LCtx->getStackFrame();
+
+ if (VarContext == CurrentContext) {
+ // If no statement is provided, everything is live.
+ if (!Loc)
+ return true;
+
+ if (LCtx->getAnalysis<RelaxedLiveVariables>()->isLive(Loc, VR->getDecl()))
+ return true;
+
+ if (!includeStoreBindings)
+ return false;
+
+ unsigned &cachedQuery =
+ const_cast<SymbolReaper *>(this)->includedRegionCache[VR];
+
+ if (cachedQuery) {
+ return cachedQuery == 1;
+ }
+
+ // Query the store to see if the region occurs in any live bindings.
+ if (Store store = reapedStore.getStore()) {
+ bool hasRegion =
+ reapedStore.getStoreManager().includedInBindings(store, VR);
+ cachedQuery = hasRegion ? 1 : 2;
+ return hasRegion;
+ }
+
+ return false;
+ }
+
+ return VarContext->isParentOf(CurrentContext);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TaintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TaintManager.cpp
new file mode 100644
index 000000000000..c34b0ca1839d
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/TaintManager.cpp
@@ -0,0 +1,23 @@
+//== TaintManager.cpp ------------------------------------------ -*- C++ -*--=//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/TaintManager.h"
+
+using namespace clang;
+using namespace ento;
+
+void *ProgramStateTrait<TaintMap>::GDMIndex() {
+ static int index = 0;
+ return &index;
+}
+
+void *ProgramStateTrait<DerivedSymTaint>::GDMIndex() {
+ static int index;
+ return &index;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/WorkList.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/WorkList.cpp
new file mode 100644
index 000000000000..e705393cb83a
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/WorkList.cpp
@@ -0,0 +1,314 @@
+//===- WorkList.cpp - Analyzer work-list implementation--------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines different worklist implementations for the static analyzer.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/WorkList.h"
+#include "llvm/ADT/PriorityQueue.h"
+#include "llvm/ADT/DenseSet.h"
+#include "llvm/ADT/DenseMap.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Statistic.h"
+#include <deque>
+#include <vector>
+
+using namespace clang;
+using namespace ento;
+
+#define DEBUG_TYPE "WorkList"
+
+STATISTIC(MaxQueueSize, "Maximum size of the worklist");
+STATISTIC(MaxReachableSize, "Maximum size of auxiliary worklist set");
+
+//===----------------------------------------------------------------------===//
+// Worklist classes for exploration of reachable states.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class DFS : public WorkList {
+ SmallVector<WorkListUnit, 20> Stack;
+
+public:
+ bool hasWork() const override {
+ return !Stack.empty();
+ }
+
+ void enqueue(const WorkListUnit& U) override {
+ Stack.push_back(U);
+ }
+
+ WorkListUnit dequeue() override {
+ assert(!Stack.empty());
+ const WorkListUnit& U = Stack.back();
+ Stack.pop_back(); // This technically "invalidates" U, but we are fine.
+ return U;
+ }
+};
+
+class BFS : public WorkList {
+ std::deque<WorkListUnit> Queue;
+
+public:
+ bool hasWork() const override {
+ return !Queue.empty();
+ }
+
+ void enqueue(const WorkListUnit& U) override {
+ Queue.push_back(U);
+ }
+
+ WorkListUnit dequeue() override {
+ WorkListUnit U = Queue.front();
+ Queue.pop_front();
+ return U;
+ }
+};
+
+} // namespace
+
+// Place the dstor for WorkList here because it contains virtual member
+// functions, and we the code for the dstor generated in one compilation unit.
+WorkList::~WorkList() = default;
+
+std::unique_ptr<WorkList> WorkList::makeDFS() {
+ return llvm::make_unique<DFS>();
+}
+
+std::unique_ptr<WorkList> WorkList::makeBFS() {
+ return llvm::make_unique<BFS>();
+}
+
+namespace {
+
+ class BFSBlockDFSContents : public WorkList {
+ std::deque<WorkListUnit> Queue;
+ SmallVector<WorkListUnit, 20> Stack;
+
+ public:
+ bool hasWork() const override {
+ return !Queue.empty() || !Stack.empty();
+ }
+
+ void enqueue(const WorkListUnit& U) override {
+ if (U.getNode()->getLocation().getAs<BlockEntrance>())
+ Queue.push_front(U);
+ else
+ Stack.push_back(U);
+ }
+
+ WorkListUnit dequeue() override {
+ // Process all basic blocks to completion.
+ if (!Stack.empty()) {
+ const WorkListUnit& U = Stack.back();
+ Stack.pop_back(); // This technically "invalidates" U, but we are fine.
+ return U;
+ }
+
+ assert(!Queue.empty());
+ // Don't use const reference. The subsequent pop_back() might make it
+ // unsafe.
+ WorkListUnit U = Queue.front();
+ Queue.pop_front();
+ return U;
+ }
+ };
+
+} // namespace
+
+std::unique_ptr<WorkList> WorkList::makeBFSBlockDFSContents() {
+ return llvm::make_unique<BFSBlockDFSContents>();
+}
+
+namespace {
+
+class UnexploredFirstStack : public WorkList {
+ /// Stack of nodes known to have statements we have not traversed yet.
+ SmallVector<WorkListUnit, 20> StackUnexplored;
+
+ /// Stack of all other nodes.
+ SmallVector<WorkListUnit, 20> StackOthers;
+
+ using BlockID = unsigned;
+ using LocIdentifier = std::pair<BlockID, const StackFrameContext *>;
+
+ llvm::DenseSet<LocIdentifier> Reachable;
+
+public:
+ bool hasWork() const override {
+ return !(StackUnexplored.empty() && StackOthers.empty());
+ }
+
+ void enqueue(const WorkListUnit &U) override {
+ const ExplodedNode *N = U.getNode();
+ auto BE = N->getLocation().getAs<BlockEntrance>();
+
+ if (!BE) {
+ // Assume the choice of the order of the preceding block entrance was
+ // correct.
+ StackUnexplored.push_back(U);
+ } else {
+ LocIdentifier LocId = std::make_pair(
+ BE->getBlock()->getBlockID(),
+ N->getLocationContext()->getStackFrame());
+ auto InsertInfo = Reachable.insert(LocId);
+
+ if (InsertInfo.second) {
+ StackUnexplored.push_back(U);
+ } else {
+ StackOthers.push_back(U);
+ }
+ }
+ MaxReachableSize.updateMax(Reachable.size());
+ MaxQueueSize.updateMax(StackUnexplored.size() + StackOthers.size());
+ }
+
+ WorkListUnit dequeue() override {
+ if (!StackUnexplored.empty()) {
+ WorkListUnit &U = StackUnexplored.back();
+ StackUnexplored.pop_back();
+ return U;
+ } else {
+ WorkListUnit &U = StackOthers.back();
+ StackOthers.pop_back();
+ return U;
+ }
+ }
+};
+
+} // namespace
+
+std::unique_ptr<WorkList> WorkList::makeUnexploredFirst() {
+ return llvm::make_unique<UnexploredFirstStack>();
+}
+
+namespace {
+class UnexploredFirstPriorityQueue : public WorkList {
+ using BlockID = unsigned;
+ using LocIdentifier = std::pair<BlockID, const StackFrameContext *>;
+
+ // How many times each location was visited.
+ // Is signed because we negate it later in order to have a reversed
+ // comparison.
+ using VisitedTimesMap = llvm::DenseMap<LocIdentifier, int>;
+
+ // Compare by number of times the location was visited first (negated
+ // to prefer less often visited locations), then by insertion time (prefer
+ // expanding nodes inserted sooner first).
+ using QueuePriority = std::pair<int, unsigned long>;
+ using QueueItem = std::pair<WorkListUnit, QueuePriority>;
+
+ struct ExplorationComparator {
+ bool operator() (const QueueItem &LHS, const QueueItem &RHS) {
+ return LHS.second < RHS.second;
+ }
+ };
+
+ // Number of inserted nodes, used to emulate DFS ordering in the priority
+ // queue when insertions are equal.
+ unsigned long Counter = 0;
+
+ // Number of times a current location was reached.
+ VisitedTimesMap NumReached;
+
+ // The top item is the largest one.
+ llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, ExplorationComparator>
+ queue;
+
+public:
+ bool hasWork() const override {
+ return !queue.empty();
+ }
+
+ void enqueue(const WorkListUnit &U) override {
+ const ExplodedNode *N = U.getNode();
+ unsigned NumVisited = 0;
+ if (auto BE = N->getLocation().getAs<BlockEntrance>()) {
+ LocIdentifier LocId = std::make_pair(
+ BE->getBlock()->getBlockID(),
+ N->getLocationContext()->getStackFrame());
+ NumVisited = NumReached[LocId]++;
+ }
+
+ queue.push(std::make_pair(U, std::make_pair(-NumVisited, ++Counter)));
+ }
+
+ WorkListUnit dequeue() override {
+ QueueItem U = queue.top();
+ queue.pop();
+ return U.first;
+ }
+};
+} // namespace
+
+std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityQueue() {
+ return llvm::make_unique<UnexploredFirstPriorityQueue>();
+}
+
+namespace {
+class UnexploredFirstPriorityLocationQueue : public WorkList {
+ using LocIdentifier = const CFGBlock *;
+
+ // How many times each location was visited.
+ // Is signed because we negate it later in order to have a reversed
+ // comparison.
+ using VisitedTimesMap = llvm::DenseMap<LocIdentifier, int>;
+
+ // Compare by number of times the location was visited first (negated
+ // to prefer less often visited locations), then by insertion time (prefer
+ // expanding nodes inserted sooner first).
+ using QueuePriority = std::pair<int, unsigned long>;
+ using QueueItem = std::pair<WorkListUnit, QueuePriority>;
+
+ struct ExplorationComparator {
+ bool operator() (const QueueItem &LHS, const QueueItem &RHS) {
+ return LHS.second < RHS.second;
+ }
+ };
+
+ // Number of inserted nodes, used to emulate DFS ordering in the priority
+ // queue when insertions are equal.
+ unsigned long Counter = 0;
+
+ // Number of times a current location was reached.
+ VisitedTimesMap NumReached;
+
+ // The top item is the largest one.
+ llvm::PriorityQueue<QueueItem, std::vector<QueueItem>, ExplorationComparator>
+ queue;
+
+public:
+ bool hasWork() const override {
+ return !queue.empty();
+ }
+
+ void enqueue(const WorkListUnit &U) override {
+ const ExplodedNode *N = U.getNode();
+ unsigned NumVisited = 0;
+ if (auto BE = N->getLocation().getAs<BlockEntrance>())
+ NumVisited = NumReached[BE->getBlock()]++;
+
+ queue.push(std::make_pair(U, std::make_pair(-NumVisited, ++Counter)));
+ }
+
+ WorkListUnit dequeue() override {
+ QueueItem U = queue.top();
+ queue.pop();
+ return U.first;
+ }
+
+};
+
+}
+
+std::unique_ptr<WorkList> WorkList::makeUnexploredFirstPriorityLocationQueue() {
+ return llvm::make_unique<UnexploredFirstPriorityLocationQueue>();
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
new file mode 100644
index 000000000000..c4729f969f33
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp
@@ -0,0 +1,841 @@
+//== Z3ConstraintManager.cpp --------------------------------*- C++ -*--==//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Basic/TargetInfo.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConstraintManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/SMTConv.h"
+
+#include "clang/Config/config.h"
+
+using namespace clang;
+using namespace ento;
+
+#if CLANG_ANALYZER_WITH_Z3
+
+#include <z3.h>
+
+namespace {
+
+/// Configuration class for Z3
+class Z3Config {
+ friend class Z3Context;
+
+ Z3_config Config;
+
+public:
+ Z3Config() : Config(Z3_mk_config()) {
+ // Enable model finding
+ Z3_set_param_value(Config, "model", "true");
+ // Disable proof generation
+ Z3_set_param_value(Config, "proof", "false");
+ // Set timeout to 15000ms = 15s
+ Z3_set_param_value(Config, "timeout", "15000");
+ }
+
+ ~Z3Config() { Z3_del_config(Config); }
+}; // end class Z3Config
+
+// Function used to report errors
+void Z3ErrorHandler(Z3_context Context, Z3_error_code Error) {
+ llvm::report_fatal_error("Z3 error: " +
+ llvm::Twine(Z3_get_error_msg(Context, Error)));
+}
+
+/// Wrapper for Z3 context
+class Z3Context {
+public:
+ Z3_context Context;
+
+ Z3Context() {
+ Context = Z3_mk_context_rc(Z3Config().Config);
+ // The error function is set here because the context is the first object
+ // created by the backend
+ Z3_set_error_handler(Context, Z3ErrorHandler);
+ }
+
+ virtual ~Z3Context() {
+ Z3_del_context(Context);
+ Context = nullptr;
+ }
+}; // end class Z3Context
+
+/// Wrapper for Z3 Sort
+class Z3Sort : public SMTSort {
+ friend class Z3Solver;
+
+ Z3Context &Context;
+
+ Z3_sort Sort;
+
+public:
+ /// Default constructor, mainly used by make_shared
+ Z3Sort(Z3Context &C, Z3_sort ZS) : Context(C), Sort(ZS) {
+ Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort));
+ }
+
+ /// Override implicit copy constructor for correct reference counting.
+ Z3Sort(const Z3Sort &Other) : Context(Other.Context), Sort(Other.Sort) {
+ Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort));
+ }
+
+ /// Override implicit copy assignment constructor for correct reference
+ /// counting.
+ Z3Sort &operator=(const Z3Sort &Other) {
+ Z3_inc_ref(Context.Context, reinterpret_cast<Z3_ast>(Other.Sort));
+ Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort));
+ Sort = Other.Sort;
+ return *this;
+ }
+
+ Z3Sort(Z3Sort &&Other) = delete;
+ Z3Sort &operator=(Z3Sort &&Other) = delete;
+
+ ~Z3Sort() {
+ if (Sort)
+ Z3_dec_ref(Context.Context, reinterpret_cast<Z3_ast>(Sort));
+ }
+
+ bool isBitvectorSortImpl() const override {
+ return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BV_SORT);
+ }
+
+ bool isFloatSortImpl() const override {
+ return (Z3_get_sort_kind(Context.Context, Sort) == Z3_FLOATING_POINT_SORT);
+ }
+
+ bool isBooleanSortImpl() const override {
+ return (Z3_get_sort_kind(Context.Context, Sort) == Z3_BOOL_SORT);
+ }
+
+ unsigned getBitvectorSortSizeImpl() const override {
+ return Z3_get_bv_sort_size(Context.Context, Sort);
+ }
+
+ unsigned getFloatSortSizeImpl() const override {
+ return Z3_fpa_get_ebits(Context.Context, Sort) +
+ Z3_fpa_get_sbits(Context.Context, Sort);
+ }
+
+ bool equal_to(SMTSort const &Other) const override {
+ return Z3_is_eq_sort(Context.Context, Sort,
+ static_cast<const Z3Sort &>(Other).Sort);
+ }
+
+ void print(raw_ostream &OS) const override {
+ OS << Z3_sort_to_string(Context.Context, Sort);
+ }
+}; // end class Z3Sort
+
+static const Z3Sort &toZ3Sort(const SMTSort &S) {
+ return static_cast<const Z3Sort &>(S);
+}
+
+class Z3Expr : public SMTExpr {
+ friend class Z3Solver;
+
+ Z3Context &Context;
+
+ Z3_ast AST;
+
+public:
+ Z3Expr(Z3Context &C, Z3_ast ZA) : SMTExpr(), Context(C), AST(ZA) {
+ Z3_inc_ref(Context.Context, AST);
+ }
+
+ /// Override implicit copy constructor for correct reference counting.
+ Z3Expr(const Z3Expr &Copy) : SMTExpr(), Context(Copy.Context), AST(Copy.AST) {
+ Z3_inc_ref(Context.Context, AST);
+ }
+
+ /// Override implicit copy assignment constructor for correct reference
+ /// counting.
+ Z3Expr &operator=(const Z3Expr &Other) {
+ Z3_inc_ref(Context.Context, Other.AST);
+ Z3_dec_ref(Context.Context, AST);
+ AST = Other.AST;
+ return *this;
+ }
+
+ Z3Expr(Z3Expr &&Other) = delete;
+ Z3Expr &operator=(Z3Expr &&Other) = delete;
+
+ ~Z3Expr() {
+ if (AST)
+ Z3_dec_ref(Context.Context, AST);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ ID.AddInteger(Z3_get_ast_hash(Context.Context, AST));
+ }
+
+ /// Comparison of AST equality, not model equivalence.
+ bool equal_to(SMTExpr const &Other) const override {
+ assert(Z3_is_eq_sort(Context.Context, Z3_get_sort(Context.Context, AST),
+ Z3_get_sort(Context.Context,
+ static_cast<const Z3Expr &>(Other).AST)) &&
+ "AST's must have the same sort");
+ return Z3_is_eq_ast(Context.Context, AST,
+ static_cast<const Z3Expr &>(Other).AST);
+ }
+
+ void print(raw_ostream &OS) const override {
+ OS << Z3_ast_to_string(Context.Context, AST);
+ }
+}; // end class Z3Expr
+
+static const Z3Expr &toZ3Expr(const SMTExpr &E) {
+ return static_cast<const Z3Expr &>(E);
+}
+
+class Z3Model {
+ friend class Z3Solver;
+
+ Z3Context &Context;
+
+ Z3_model Model;
+
+public:
+ Z3Model(Z3Context &C, Z3_model ZM) : Context(C), Model(ZM) {
+ Z3_model_inc_ref(Context.Context, Model);
+ }
+
+ Z3Model(const Z3Model &Other) = delete;
+ Z3Model(Z3Model &&Other) = delete;
+ Z3Model &operator=(Z3Model &Other) = delete;
+ Z3Model &operator=(Z3Model &&Other) = delete;
+
+ ~Z3Model() {
+ if (Model)
+ Z3_model_dec_ref(Context.Context, Model);
+ }
+
+ void print(raw_ostream &OS) const {
+ OS << Z3_model_to_string(Context.Context, Model);
+ }
+
+ LLVM_DUMP_METHOD void dump() const { print(llvm::errs()); }
+}; // end class Z3Model
+
+/// Get the corresponding IEEE floating-point type for a given bitwidth.
+static const llvm::fltSemantics &getFloatSemantics(unsigned BitWidth) {
+ switch (BitWidth) {
+ default:
+ llvm_unreachable("Unsupported floating-point semantics!");
+ break;
+ case 16:
+ return llvm::APFloat::IEEEhalf();
+ case 32:
+ return llvm::APFloat::IEEEsingle();
+ case 64:
+ return llvm::APFloat::IEEEdouble();
+ case 128:
+ return llvm::APFloat::IEEEquad();
+ }
+}
+
+// Determine whether two float semantics are equivalent
+static bool areEquivalent(const llvm::fltSemantics &LHS,
+ const llvm::fltSemantics &RHS) {
+ return (llvm::APFloat::semanticsPrecision(LHS) ==
+ llvm::APFloat::semanticsPrecision(RHS)) &&
+ (llvm::APFloat::semanticsMinExponent(LHS) ==
+ llvm::APFloat::semanticsMinExponent(RHS)) &&
+ (llvm::APFloat::semanticsMaxExponent(LHS) ==
+ llvm::APFloat::semanticsMaxExponent(RHS)) &&
+ (llvm::APFloat::semanticsSizeInBits(LHS) ==
+ llvm::APFloat::semanticsSizeInBits(RHS));
+}
+
+} // end anonymous namespace
+
+typedef llvm::ImmutableSet<std::pair<SymbolRef, Z3Expr>> ConstraintZ3Ty;
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ConstraintZ3, ConstraintZ3Ty)
+
+namespace {
+
+class Z3Solver : public SMTSolver {
+ friend class Z3ConstraintManager;
+
+ Z3Context Context;
+
+ Z3_solver Solver;
+
+public:
+ Z3Solver() : Solver(Z3_mk_simple_solver(Context.Context)) {
+ Z3_solver_inc_ref(Context.Context, Solver);
+ }
+
+ Z3Solver(const Z3Solver &Other) = delete;
+ Z3Solver(Z3Solver &&Other) = delete;
+ Z3Solver &operator=(Z3Solver &Other) = delete;
+ Z3Solver &operator=(Z3Solver &&Other) = delete;
+
+ ~Z3Solver() {
+ if (Solver)
+ Z3_solver_dec_ref(Context.Context, Solver);
+ }
+
+ void addConstraint(const SMTExprRef &Exp) const override {
+ Z3_solver_assert(Context.Context, Solver, toZ3Expr(*Exp).AST);
+ }
+
+ SMTSortRef getBoolSort() override {
+ return std::make_shared<Z3Sort>(Context, Z3_mk_bool_sort(Context.Context));
+ }
+
+ SMTSortRef getBitvectorSort(unsigned BitWidth) override {
+ return std::make_shared<Z3Sort>(Context,
+ Z3_mk_bv_sort(Context.Context, BitWidth));
+ }
+
+ SMTSortRef getSort(const SMTExprRef &Exp) override {
+ return std::make_shared<Z3Sort>(
+ Context, Z3_get_sort(Context.Context, toZ3Expr(*Exp).AST));
+ }
+
+ SMTSortRef getFloat16Sort() override {
+ return std::make_shared<Z3Sort>(Context,
+ Z3_mk_fpa_sort_16(Context.Context));
+ }
+
+ SMTSortRef getFloat32Sort() override {
+ return std::make_shared<Z3Sort>(Context,
+ Z3_mk_fpa_sort_32(Context.Context));
+ }
+
+ SMTSortRef getFloat64Sort() override {
+ return std::make_shared<Z3Sort>(Context,
+ Z3_mk_fpa_sort_64(Context.Context));
+ }
+
+ SMTSortRef getFloat128Sort() override {
+ return std::make_shared<Z3Sort>(Context,
+ Z3_mk_fpa_sort_128(Context.Context));
+ }
+
+ SMTExprRef newExprRef(const SMTExpr &E) const override {
+ return std::make_shared<Z3Expr>(toZ3Expr(E));
+ }
+
+ SMTExprRef mkBVNeg(const SMTExprRef &Exp) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvneg(Context.Context, toZ3Expr(*Exp).AST)));
+ }
+
+ SMTExprRef mkBVNot(const SMTExprRef &Exp) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvnot(Context.Context, toZ3Expr(*Exp).AST)));
+ }
+
+ SMTExprRef mkNot(const SMTExprRef &Exp) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_not(Context.Context, toZ3Expr(*Exp).AST)));
+ }
+
+ SMTExprRef mkBVAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvadd(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsub(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVMul(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvmul(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVSRem(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsrem(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVURem(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvurem(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVSDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsdiv(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVUDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvudiv(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVShl(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvshl(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVAshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvashr(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVLshr(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvlshr(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVXor(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvxor(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVOr(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvor(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvand(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVUlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvult(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVSlt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvslt(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVUgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvugt(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVSgt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsgt(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVUle(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvule(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVSle(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsle(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVUge(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvuge(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkBVSge(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_bvsge(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkAnd(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ Z3_ast Args[2] = {toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST};
+ return newExprRef(Z3Expr(Context, Z3_mk_and(Context.Context, 2, Args)));
+ }
+
+ SMTExprRef mkOr(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ Z3_ast Args[2] = {toZ3Expr(*LHS).AST, toZ3Expr(*RHS).AST};
+ return newExprRef(Z3Expr(Context, Z3_mk_or(Context.Context, 2, Args)));
+ }
+
+ SMTExprRef mkEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_eq(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkFPNeg(const SMTExprRef &Exp) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_neg(Context.Context, toZ3Expr(*Exp).AST)));
+ }
+
+ SMTExprRef mkFPIsInfinite(const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_is_infinite(Context.Context, toZ3Expr(*Exp).AST)));
+ }
+
+ SMTExprRef mkFPIsNaN(const SMTExprRef &Exp) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_is_nan(Context.Context, toZ3Expr(*Exp).AST)));
+ }
+
+ SMTExprRef mkFPIsNormal(const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_is_normal(Context.Context, toZ3Expr(*Exp).AST)));
+ }
+
+ SMTExprRef mkFPIsZero(const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_is_zero(Context.Context, toZ3Expr(*Exp).AST)));
+ }
+
+ SMTExprRef mkFPMul(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(
+ Z3Expr(Context,
+ Z3_mk_fpa_mul(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST)));
+ }
+
+ SMTExprRef mkFPDiv(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(
+ Z3Expr(Context,
+ Z3_mk_fpa_div(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST)));
+ }
+
+ SMTExprRef mkFPRem(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_rem(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkFPAdd(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(
+ Z3Expr(Context,
+ Z3_mk_fpa_add(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST)));
+ }
+
+ SMTExprRef mkFPSub(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(
+ Z3Expr(Context,
+ Z3_mk_fpa_sub(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST, toZ3Expr(*RoundingMode).AST)));
+ }
+
+ SMTExprRef mkFPLt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_lt(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkFPGt(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_gt(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkFPLe(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_leq(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkFPGe(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_geq(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkFPEqual(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_fpa_eq(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkIte(const SMTExprRef &Cond, const SMTExprRef &T,
+ const SMTExprRef &F) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_ite(Context.Context, toZ3Expr(*Cond).AST,
+ toZ3Expr(*T).AST, toZ3Expr(*F).AST)));
+ }
+
+ SMTExprRef mkBVSignExt(unsigned i, const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_sign_ext(Context.Context, i, toZ3Expr(*Exp).AST)));
+ }
+
+ SMTExprRef mkBVZeroExt(unsigned i, const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_zero_ext(Context.Context, i, toZ3Expr(*Exp).AST)));
+ }
+
+ SMTExprRef mkBVExtract(unsigned High, unsigned Low,
+ const SMTExprRef &Exp) override {
+ return newExprRef(Z3Expr(Context, Z3_mk_extract(Context.Context, High, Low,
+ toZ3Expr(*Exp).AST)));
+ }
+
+ SMTExprRef mkBVConcat(const SMTExprRef &LHS, const SMTExprRef &RHS) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_concat(Context.Context, toZ3Expr(*LHS).AST,
+ toZ3Expr(*RHS).AST)));
+ }
+
+ SMTExprRef mkFPtoFP(const SMTExprRef &From, const SMTSortRef &To) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(Z3Expr(
+ Context,
+ Z3_mk_fpa_to_fp_float(Context.Context, toZ3Expr(*RoundingMode).AST,
+ toZ3Expr(*From).AST, toZ3Sort(*To).Sort)));
+ }
+
+ SMTExprRef mkSBVtoFP(const SMTExprRef &From, const SMTSortRef &To) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(Z3Expr(
+ Context,
+ Z3_mk_fpa_to_fp_signed(Context.Context, toZ3Expr(*RoundingMode).AST,
+ toZ3Expr(*From).AST, toZ3Sort(*To).Sort)));
+ }
+
+ SMTExprRef mkUBVtoFP(const SMTExprRef &From, const SMTSortRef &To) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(Z3Expr(
+ Context,
+ Z3_mk_fpa_to_fp_unsigned(Context.Context, toZ3Expr(*RoundingMode).AST,
+ toZ3Expr(*From).AST, toZ3Sort(*To).Sort)));
+ }
+
+ SMTExprRef mkFPtoSBV(const SMTExprRef &From, unsigned ToWidth) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_to_sbv(Context.Context, toZ3Expr(*RoundingMode).AST,
+ toZ3Expr(*From).AST, ToWidth)));
+ }
+
+ SMTExprRef mkFPtoUBV(const SMTExprRef &From, unsigned ToWidth) override {
+ SMTExprRef RoundingMode = getFloatRoundingMode();
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_to_ubv(Context.Context, toZ3Expr(*RoundingMode).AST,
+ toZ3Expr(*From).AST, ToWidth)));
+ }
+
+ SMTExprRef mkBoolean(const bool b) override {
+ return newExprRef(Z3Expr(Context, b ? Z3_mk_true(Context.Context)
+ : Z3_mk_false(Context.Context)));
+ }
+
+ SMTExprRef mkBitvector(const llvm::APSInt Int, unsigned BitWidth) override {
+ const SMTSortRef Sort = getBitvectorSort(BitWidth);
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_numeral(Context.Context, Int.toString(10).c_str(),
+ toZ3Sort(*Sort).Sort)));
+ }
+
+ SMTExprRef mkFloat(const llvm::APFloat Float) override {
+ SMTSortRef Sort =
+ getFloatSort(llvm::APFloat::semanticsSizeInBits(Float.getSemantics()));
+
+ llvm::APSInt Int = llvm::APSInt(Float.bitcastToAPInt(), false);
+ SMTExprRef Z3Int = mkBitvector(Int, Int.getBitWidth());
+ return newExprRef(Z3Expr(
+ Context, Z3_mk_fpa_to_fp_bv(Context.Context, toZ3Expr(*Z3Int).AST,
+ toZ3Sort(*Sort).Sort)));
+ }
+
+ SMTExprRef mkSymbol(const char *Name, SMTSortRef Sort) override {
+ return newExprRef(
+ Z3Expr(Context, Z3_mk_const(Context.Context,
+ Z3_mk_string_symbol(Context.Context, Name),
+ toZ3Sort(*Sort).Sort)));
+ }
+
+ llvm::APSInt getBitvector(const SMTExprRef &Exp, unsigned BitWidth,
+ bool isUnsigned) override {
+ return llvm::APSInt(
+ llvm::APInt(BitWidth,
+ Z3_get_numeral_string(Context.Context, toZ3Expr(*Exp).AST),
+ 10),
+ isUnsigned);
+ }
+
+ bool getBoolean(const SMTExprRef &Exp) override {
+ return Z3_get_bool_value(Context.Context, toZ3Expr(*Exp).AST) == Z3_L_TRUE;
+ }
+
+ SMTExprRef getFloatRoundingMode() override {
+ // TODO: Don't assume nearest ties to even rounding mode
+ return newExprRef(Z3Expr(Context, Z3_mk_fpa_rne(Context.Context)));
+ }
+
+ bool toAPFloat(const SMTSortRef &Sort, const SMTExprRef &AST,
+ llvm::APFloat &Float, bool useSemantics) {
+ assert(Sort->isFloatSort() && "Unsupported sort to floating-point!");
+
+ llvm::APSInt Int(Sort->getFloatSortSize(), true);
+ const llvm::fltSemantics &Semantics =
+ getFloatSemantics(Sort->getFloatSortSize());
+ SMTSortRef BVSort = getBitvectorSort(Sort->getFloatSortSize());
+ if (!toAPSInt(BVSort, AST, Int, true)) {
+ return false;
+ }
+
+ if (useSemantics && !areEquivalent(Float.getSemantics(), Semantics)) {
+ assert(false && "Floating-point types don't match!");
+ return false;
+ }
+
+ Float = llvm::APFloat(Semantics, Int);
+ return true;
+ }
+
+ bool toAPSInt(const SMTSortRef &Sort, const SMTExprRef &AST,
+ llvm::APSInt &Int, bool useSemantics) {
+ if (Sort->isBitvectorSort()) {
+ if (useSemantics && Int.getBitWidth() != Sort->getBitvectorSortSize()) {
+ assert(false && "Bitvector types don't match!");
+ return false;
+ }
+
+ // FIXME: This function is also used to retrieve floating-point values,
+ // which can be 16, 32, 64 or 128 bits long. Bitvectors can be anything
+ // between 1 and 64 bits long, which is the reason we have this weird
+ // guard. In the future, we need proper calls in the backend to retrieve
+ // floating-points and its special values (NaN, +/-infinity, +/-zero),
+ // then we can drop this weird condition.
+ if (Sort->getBitvectorSortSize() <= 64 ||
+ Sort->getBitvectorSortSize() == 128) {
+ Int = getBitvector(AST, Int.getBitWidth(), Int.isUnsigned());
+ return true;
+ }
+
+ assert(false && "Bitwidth not supported!");
+ return false;
+ }
+
+ if (Sort->isBooleanSort()) {
+ if (useSemantics && Int.getBitWidth() < 1) {
+ assert(false && "Boolean type doesn't match!");
+ return false;
+ }
+
+ Int = llvm::APSInt(llvm::APInt(Int.getBitWidth(), getBoolean(AST)),
+ Int.isUnsigned());
+ return true;
+ }
+
+ llvm_unreachable("Unsupported sort to integer!");
+ }
+
+ bool getInterpretation(const SMTExprRef &Exp, llvm::APSInt &Int) override {
+ Z3Model Model(Context, Z3_solver_get_model(Context.Context, Solver));
+ Z3_func_decl Func = Z3_get_app_decl(
+ Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST));
+ if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE)
+ return false;
+
+ SMTExprRef Assign = newExprRef(
+ Z3Expr(Context,
+ Z3_model_get_const_interp(Context.Context, Model.Model, Func)));
+ SMTSortRef Sort = getSort(Assign);
+ return toAPSInt(Sort, Assign, Int, true);
+ }
+
+ bool getInterpretation(const SMTExprRef &Exp, llvm::APFloat &Float) override {
+ Z3Model Model(Context, Z3_solver_get_model(Context.Context, Solver));
+ Z3_func_decl Func = Z3_get_app_decl(
+ Context.Context, Z3_to_app(Context.Context, toZ3Expr(*Exp).AST));
+ if (Z3_model_has_interp(Context.Context, Model.Model, Func) != Z3_L_TRUE)
+ return false;
+
+ SMTExprRef Assign = newExprRef(
+ Z3Expr(Context,
+ Z3_model_get_const_interp(Context.Context, Model.Model, Func)));
+ SMTSortRef Sort = getSort(Assign);
+ return toAPFloat(Sort, Assign, Float, true);
+ }
+
+ Optional<bool> check() const override {
+ Z3_lbool res = Z3_solver_check(Context.Context, Solver);
+ if (res == Z3_L_TRUE)
+ return true;
+
+ if (res == Z3_L_FALSE)
+ return false;
+
+ return Optional<bool>();
+ }
+
+ void push() override { return Z3_solver_push(Context.Context, Solver); }
+
+ void pop(unsigned NumStates = 1) override {
+ assert(Z3_solver_get_num_scopes(Context.Context, Solver) >= NumStates);
+ return Z3_solver_pop(Context.Context, Solver, NumStates);
+ }
+
+ bool isFPSupported() override { return true; }
+
+ /// Reset the solver and remove all constraints.
+ void reset() override { Z3_solver_reset(Context.Context, Solver); }
+
+ void print(raw_ostream &OS) const override {
+ OS << Z3_solver_to_string(Context.Context, Solver);
+ }
+}; // end class Z3Solver
+
+class Z3ConstraintManager : public SMTConstraintManager<ConstraintZ3, Z3Expr> {
+ SMTSolverRef Solver = CreateZ3Solver();
+
+public:
+ Z3ConstraintManager(SubEngine *SE, SValBuilder &SB)
+ : SMTConstraintManager(SE, SB, Solver) {}
+}; // end class Z3ConstraintManager
+
+} // end anonymous namespace
+
+#endif
+
+SMTSolverRef clang::ento::CreateZ3Solver() {
+#if CLANG_ANALYZER_WITH_Z3
+ return llvm::make_unique<Z3Solver>();
+#else
+ llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild "
+ "with -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON",
+ false);
+ return nullptr;
+#endif
+}
+
+std::unique_ptr<ConstraintManager>
+ento::CreateZ3ConstraintManager(ProgramStateManager &StMgr, SubEngine *Eng) {
+#if CLANG_ANALYZER_WITH_Z3
+ return llvm::make_unique<Z3ConstraintManager>(Eng, StMgr.getSValBuilder());
+#else
+ llvm::report_fatal_error("Clang was not compiled with Z3 support, rebuild "
+ "with -DCLANG_ANALYZER_ENABLE_Z3_SOLVER=ON",
+ false);
+ return nullptr;
+#endif
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
new file mode 100644
index 000000000000..d87937d9b63d
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp
@@ -0,0 +1,771 @@
+//===--- AnalysisConsumer.cpp - ASTConsumer for running Analyses ----------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// "Meta" ASTConsumer for running different source analyses.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
+#include "ModelInjector.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Analysis/Analyses/LiveVariables.h"
+#include "clang/Analysis/CFG.h"
+#include "clang/Analysis/CallGraph.h"
+#include "clang/Analysis/CodeInjector.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/CrossTU/CrossTranslationUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/StaticAnalyzer/Checkers/LocalCheckers.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/PathDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
+#include "llvm/ADT/PostOrderIterator.h"
+#include "llvm/ADT/Statistic.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/Program.h"
+#include "llvm/Support/Timer.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+#include <queue>
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+#define DEBUG_TYPE "AnalysisConsumer"
+
+STATISTIC(NumFunctionTopLevel, "The # of functions at top level.");
+STATISTIC(NumFunctionsAnalyzed,
+ "The # of functions and blocks analyzed (as top level "
+ "with inlining turned on).");
+STATISTIC(NumBlocksInAnalyzedFunctions,
+ "The # of basic blocks in the analyzed functions.");
+STATISTIC(NumVisitedBlocksInAnalyzedFunctions,
+ "The # of visited basic blocks in the analyzed functions.");
+STATISTIC(PercentReachableBlocks, "The % of reachable basic blocks.");
+STATISTIC(MaxCFGSize, "The maximum number of basic blocks in a function.");
+
+//===----------------------------------------------------------------------===//
+// Special PathDiagnosticConsumers.
+//===----------------------------------------------------------------------===//
+
+void ento::createPlistHTMLDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
+ PathDiagnosticConsumers &C,
+ const std::string &prefix,
+ const Preprocessor &PP) {
+ createHTMLDiagnosticConsumer(AnalyzerOpts, C,
+ llvm::sys::path::parent_path(prefix), PP);
+ createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP);
+}
+
+void ento::createTextPathDiagnosticConsumer(AnalyzerOptions &AnalyzerOpts,
+ PathDiagnosticConsumers &C,
+ const std::string &Prefix,
+ const clang::Preprocessor &PP) {
+ llvm_unreachable("'text' consumer should be enabled on ClangDiags");
+}
+
+namespace {
+class ClangDiagPathDiagConsumer : public PathDiagnosticConsumer {
+ DiagnosticsEngine &Diag;
+ bool IncludePath;
+public:
+ ClangDiagPathDiagConsumer(DiagnosticsEngine &Diag)
+ : Diag(Diag), IncludePath(false) {}
+ ~ClangDiagPathDiagConsumer() override {}
+ StringRef getName() const override { return "ClangDiags"; }
+
+ bool supportsLogicalOpControlFlow() const override { return true; }
+ bool supportsCrossFileDiagnostics() const override { return true; }
+
+ PathGenerationScheme getGenerationScheme() const override {
+ return IncludePath ? Minimal : None;
+ }
+
+ void enablePaths() {
+ IncludePath = true;
+ }
+
+ void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
+ FilesMade *filesMade) override {
+ unsigned WarnID = Diag.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
+ unsigned NoteID = Diag.getCustomDiagID(DiagnosticsEngine::Note, "%0");
+
+ for (std::vector<const PathDiagnostic*>::iterator I = Diags.begin(),
+ E = Diags.end(); I != E; ++I) {
+ const PathDiagnostic *PD = *I;
+ SourceLocation WarnLoc = PD->getLocation().asLocation();
+ Diag.Report(WarnLoc, WarnID) << PD->getShortDescription()
+ << PD->path.back()->getRanges();
+
+ // First, add extra notes, even if paths should not be included.
+ for (const auto &Piece : PD->path) {
+ if (!isa<PathDiagnosticNotePiece>(Piece.get()))
+ continue;
+
+ SourceLocation NoteLoc = Piece->getLocation().asLocation();
+ Diag.Report(NoteLoc, NoteID) << Piece->getString()
+ << Piece->getRanges();
+ }
+
+ if (!IncludePath)
+ continue;
+
+ // Then, add the path notes if necessary.
+ PathPieces FlatPath = PD->path.flatten(/*ShouldFlattenMacros=*/true);
+ for (const auto &Piece : FlatPath) {
+ if (isa<PathDiagnosticNotePiece>(Piece.get()))
+ continue;
+
+ SourceLocation NoteLoc = Piece->getLocation().asLocation();
+ Diag.Report(NoteLoc, NoteID) << Piece->getString()
+ << Piece->getRanges();
+ }
+ }
+ }
+};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// AnalysisConsumer declaration.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class AnalysisConsumer : public AnalysisASTConsumer,
+ public RecursiveASTVisitor<AnalysisConsumer> {
+ enum {
+ AM_None = 0,
+ AM_Syntax = 0x1,
+ AM_Path = 0x2
+ };
+ typedef unsigned AnalysisMode;
+
+ /// Mode of the analyzes while recursively visiting Decls.
+ AnalysisMode RecVisitorMode;
+ /// Bug Reporter to use while recursively visiting Decls.
+ BugReporter *RecVisitorBR;
+
+ std::vector<std::function<void(CheckerRegistry &)>> CheckerRegistrationFns;
+
+public:
+ ASTContext *Ctx;
+ const Preprocessor &PP;
+ const std::string OutDir;
+ AnalyzerOptionsRef Opts;
+ ArrayRef<std::string> Plugins;
+ CodeInjector *Injector;
+ cross_tu::CrossTranslationUnitContext CTU;
+
+ /// Stores the declarations from the local translation unit.
+ /// Note, we pre-compute the local declarations at parse time as an
+ /// optimization to make sure we do not deserialize everything from disk.
+ /// The local declaration to all declarations ratio might be very small when
+ /// working with a PCH file.
+ SetOfDecls LocalTUDecls;
+
+ // Set of PathDiagnosticConsumers. Owned by AnalysisManager.
+ PathDiagnosticConsumers PathConsumers;
+
+ StoreManagerCreator CreateStoreMgr;
+ ConstraintManagerCreator CreateConstraintMgr;
+
+ std::unique_ptr<CheckerManager> checkerMgr;
+ std::unique_ptr<AnalysisManager> Mgr;
+
+ /// Time the analyzes time of each translation unit.
+ std::unique_ptr<llvm::TimerGroup> AnalyzerTimers;
+ std::unique_ptr<llvm::Timer> TUTotalTimer;
+
+ /// The information about analyzed functions shared throughout the
+ /// translation unit.
+ FunctionSummariesTy FunctionSummaries;
+
+ AnalysisConsumer(CompilerInstance &CI, const std::string &outdir,
+ AnalyzerOptionsRef opts, ArrayRef<std::string> plugins,
+ CodeInjector *injector)
+ : RecVisitorMode(0), RecVisitorBR(nullptr), Ctx(nullptr),
+ PP(CI.getPreprocessor()), OutDir(outdir), Opts(std::move(opts)),
+ Plugins(plugins), Injector(injector), CTU(CI) {
+ DigestAnalyzerOptions();
+ if (Opts->PrintStats || Opts->ShouldSerializeStats) {
+ AnalyzerTimers = llvm::make_unique<llvm::TimerGroup>(
+ "analyzer", "Analyzer timers");
+ TUTotalTimer = llvm::make_unique<llvm::Timer>(
+ "time", "Analyzer total time", *AnalyzerTimers);
+ llvm::EnableStatistics(/* PrintOnExit= */ false);
+ }
+ }
+
+ ~AnalysisConsumer() override {
+ if (Opts->PrintStats) {
+ llvm::PrintStatistics();
+ }
+ }
+
+ void DigestAnalyzerOptions() {
+ if (Opts->AnalysisDiagOpt != PD_NONE) {
+ // Create the PathDiagnosticConsumer.
+ ClangDiagPathDiagConsumer *clangDiags =
+ new ClangDiagPathDiagConsumer(PP.getDiagnostics());
+ PathConsumers.push_back(clangDiags);
+
+ if (Opts->AnalysisDiagOpt == PD_TEXT) {
+ clangDiags->enablePaths();
+
+ } else if (!OutDir.empty()) {
+ switch (Opts->AnalysisDiagOpt) {
+ default:
+#define ANALYSIS_DIAGNOSTICS(NAME, CMDFLAG, DESC, CREATEFN) \
+ case PD_##NAME: \
+ CREATEFN(*Opts.get(), PathConsumers, OutDir, PP); \
+ break;
+#include "clang/StaticAnalyzer/Core/Analyses.def"
+ }
+ }
+ }
+
+ // Create the analyzer component creators.
+ switch (Opts->AnalysisStoreOpt) {
+ default:
+ llvm_unreachable("Unknown store manager.");
+#define ANALYSIS_STORE(NAME, CMDFLAG, DESC, CREATEFN) \
+ case NAME##Model: CreateStoreMgr = CREATEFN; break;
+#include "clang/StaticAnalyzer/Core/Analyses.def"
+ }
+
+ switch (Opts->AnalysisConstraintsOpt) {
+ default:
+ llvm_unreachable("Unknown constraint manager.");
+#define ANALYSIS_CONSTRAINTS(NAME, CMDFLAG, DESC, CREATEFN) \
+ case NAME##Model: CreateConstraintMgr = CREATEFN; break;
+#include "clang/StaticAnalyzer/Core/Analyses.def"
+ }
+ }
+
+ void DisplayFunction(const Decl *D, AnalysisMode Mode,
+ ExprEngine::InliningModes IMode) {
+ if (!Opts->AnalyzerDisplayProgress)
+ return;
+
+ SourceManager &SM = Mgr->getASTContext().getSourceManager();
+ PresumedLoc Loc = SM.getPresumedLoc(D->getLocation());
+ if (Loc.isValid()) {
+ llvm::errs() << "ANALYZE";
+
+ if (Mode == AM_Syntax)
+ llvm::errs() << " (Syntax)";
+ else if (Mode == AM_Path) {
+ llvm::errs() << " (Path, ";
+ switch (IMode) {
+ case ExprEngine::Inline_Minimal:
+ llvm::errs() << " Inline_Minimal";
+ break;
+ case ExprEngine::Inline_Regular:
+ llvm::errs() << " Inline_Regular";
+ break;
+ }
+ llvm::errs() << ")";
+ }
+ else
+ assert(Mode == (AM_Syntax | AM_Path) && "Unexpected mode!");
+
+ llvm::errs() << ": " << Loc.getFilename() << ' '
+ << getFunctionName(D) << '\n';
+ }
+ }
+
+ void Initialize(ASTContext &Context) override {
+ Ctx = &Context;
+ checkerMgr = createCheckerManager(
+ *Ctx, *Opts, Plugins, CheckerRegistrationFns, PP.getDiagnostics());
+
+ Mgr = llvm::make_unique<AnalysisManager>(
+ *Ctx, PP.getDiagnostics(), PathConsumers, CreateStoreMgr,
+ CreateConstraintMgr, checkerMgr.get(), *Opts, Injector);
+ }
+
+ /// Store the top level decls in the set to be processed later on.
+ /// (Doing this pre-processing avoids deserialization of data from PCH.)
+ bool HandleTopLevelDecl(DeclGroupRef D) override;
+ void HandleTopLevelDeclInObjCContainer(DeclGroupRef D) override;
+
+ void HandleTranslationUnit(ASTContext &C) override;
+
+ /// Determine which inlining mode should be used when this function is
+ /// analyzed. This allows to redefine the default inlining policies when
+ /// analyzing a given function.
+ ExprEngine::InliningModes
+ getInliningModeForFunction(const Decl *D, const SetOfConstDecls &Visited);
+
+ /// Build the call graph for all the top level decls of this TU and
+ /// use it to define the order in which the functions should be visited.
+ void HandleDeclsCallGraph(const unsigned LocalTUDeclsSize);
+
+ /// Run analyzes(syntax or path sensitive) on the given function.
+ /// \param Mode - determines if we are requesting syntax only or path
+ /// sensitive only analysis.
+ /// \param VisitedCallees - The output parameter, which is populated with the
+ /// set of functions which should be considered analyzed after analyzing the
+ /// given root function.
+ void HandleCode(Decl *D, AnalysisMode Mode,
+ ExprEngine::InliningModes IMode = ExprEngine::Inline_Minimal,
+ SetOfConstDecls *VisitedCallees = nullptr);
+
+ void RunPathSensitiveChecks(Decl *D,
+ ExprEngine::InliningModes IMode,
+ SetOfConstDecls *VisitedCallees);
+
+ /// Visitors for the RecursiveASTVisitor.
+ bool shouldWalkTypesOfTypeLocs() const { return false; }
+
+ /// Handle callbacks for arbitrary Decls.
+ bool VisitDecl(Decl *D) {
+ AnalysisMode Mode = getModeForDecl(D, RecVisitorMode);
+ if (Mode & AM_Syntax)
+ checkerMgr->runCheckersOnASTDecl(D, *Mgr, *RecVisitorBR);
+ return true;
+ }
+
+ bool VisitFunctionDecl(FunctionDecl *FD) {
+ IdentifierInfo *II = FD->getIdentifier();
+ if (II && II->getName().startswith("__inline"))
+ return true;
+
+ // We skip function template definitions, as their semantics is
+ // only determined when they are instantiated.
+ if (FD->isThisDeclarationADefinition() &&
+ !FD->isDependentContext()) {
+ assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
+ HandleCode(FD, RecVisitorMode);
+ }
+ return true;
+ }
+
+ bool VisitObjCMethodDecl(ObjCMethodDecl *MD) {
+ if (MD->isThisDeclarationADefinition()) {
+ assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
+ HandleCode(MD, RecVisitorMode);
+ }
+ return true;
+ }
+
+ bool VisitBlockDecl(BlockDecl *BD) {
+ if (BD->hasBody()) {
+ assert(RecVisitorMode == AM_Syntax || Mgr->shouldInlineCall() == false);
+ // Since we skip function template definitions, we should skip blocks
+ // declared in those functions as well.
+ if (!BD->isDependentContext()) {
+ HandleCode(BD, RecVisitorMode);
+ }
+ }
+ return true;
+ }
+
+ void AddDiagnosticConsumer(PathDiagnosticConsumer *Consumer) override {
+ PathConsumers.push_back(Consumer);
+ }
+
+ void AddCheckerRegistrationFn(std::function<void(CheckerRegistry&)> Fn) override {
+ CheckerRegistrationFns.push_back(std::move(Fn));
+ }
+
+private:
+ void storeTopLevelDecls(DeclGroupRef DG);
+ std::string getFunctionName(const Decl *D);
+
+ /// Check if we should skip (not analyze) the given function.
+ AnalysisMode getModeForDecl(Decl *D, AnalysisMode Mode);
+ void runAnalysisOnTranslationUnit(ASTContext &C);
+
+ /// Print \p S to stderr if \c Opts->AnalyzerDisplayProgress is set.
+ void reportAnalyzerProgress(StringRef S);
+};
+} // end anonymous namespace
+
+
+//===----------------------------------------------------------------------===//
+// AnalysisConsumer implementation.
+//===----------------------------------------------------------------------===//
+bool AnalysisConsumer::HandleTopLevelDecl(DeclGroupRef DG) {
+ storeTopLevelDecls(DG);
+ return true;
+}
+
+void AnalysisConsumer::HandleTopLevelDeclInObjCContainer(DeclGroupRef DG) {
+ storeTopLevelDecls(DG);
+}
+
+void AnalysisConsumer::storeTopLevelDecls(DeclGroupRef DG) {
+ for (DeclGroupRef::iterator I = DG.begin(), E = DG.end(); I != E; ++I) {
+
+ // Skip ObjCMethodDecl, wait for the objc container to avoid
+ // analyzing twice.
+ if (isa<ObjCMethodDecl>(*I))
+ continue;
+
+ LocalTUDecls.push_back(*I);
+ }
+}
+
+static bool shouldSkipFunction(const Decl *D,
+ const SetOfConstDecls &Visited,
+ const SetOfConstDecls &VisitedAsTopLevel) {
+ if (VisitedAsTopLevel.count(D))
+ return true;
+
+ // We want to re-analyse the functions as top level in the following cases:
+ // - The 'init' methods should be reanalyzed because
+ // ObjCNonNilReturnValueChecker assumes that '[super init]' never returns
+ // 'nil' and unless we analyze the 'init' functions as top level, we will
+ // not catch errors within defensive code.
+ // - We want to reanalyze all ObjC methods as top level to report Retain
+ // Count naming convention errors more aggressively.
+ if (isa<ObjCMethodDecl>(D))
+ return false;
+ // We also want to reanalyze all C++ copy and move assignment operators to
+ // separately check the two cases where 'this' aliases with the parameter and
+ // where it may not. (cplusplus.SelfAssignmentChecker)
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
+ if (MD->isCopyAssignmentOperator() || MD->isMoveAssignmentOperator())
+ return false;
+ }
+
+ // Otherwise, if we visited the function before, do not reanalyze it.
+ return Visited.count(D);
+}
+
+ExprEngine::InliningModes
+AnalysisConsumer::getInliningModeForFunction(const Decl *D,
+ const SetOfConstDecls &Visited) {
+ // We want to reanalyze all ObjC methods as top level to report Retain
+ // Count naming convention errors more aggressively. But we should tune down
+ // inlining when reanalyzing an already inlined function.
+ if (Visited.count(D) && isa<ObjCMethodDecl>(D)) {
+ const ObjCMethodDecl *ObjCM = cast<ObjCMethodDecl>(D);
+ if (ObjCM->getMethodFamily() != OMF_init)
+ return ExprEngine::Inline_Minimal;
+ }
+
+ return ExprEngine::Inline_Regular;
+}
+
+void AnalysisConsumer::HandleDeclsCallGraph(const unsigned LocalTUDeclsSize) {
+ // Build the Call Graph by adding all the top level declarations to the graph.
+ // Note: CallGraph can trigger deserialization of more items from a pch
+ // (though HandleInterestingDecl); triggering additions to LocalTUDecls.
+ // We rely on random access to add the initially processed Decls to CG.
+ CallGraph CG;
+ for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
+ CG.addToCallGraph(LocalTUDecls[i]);
+ }
+
+ // Walk over all of the call graph nodes in topological order, so that we
+ // analyze parents before the children. Skip the functions inlined into
+ // the previously processed functions. Use external Visited set to identify
+ // inlined functions. The topological order allows the "do not reanalyze
+ // previously inlined function" performance heuristic to be triggered more
+ // often.
+ SetOfConstDecls Visited;
+ SetOfConstDecls VisitedAsTopLevel;
+ llvm::ReversePostOrderTraversal<clang::CallGraph*> RPOT(&CG);
+ for (llvm::ReversePostOrderTraversal<clang::CallGraph*>::rpo_iterator
+ I = RPOT.begin(), E = RPOT.end(); I != E; ++I) {
+ NumFunctionTopLevel++;
+
+ CallGraphNode *N = *I;
+ Decl *D = N->getDecl();
+
+ // Skip the abstract root node.
+ if (!D)
+ continue;
+
+ // Skip the functions which have been processed already or previously
+ // inlined.
+ if (shouldSkipFunction(D, Visited, VisitedAsTopLevel))
+ continue;
+
+ // Analyze the function.
+ SetOfConstDecls VisitedCallees;
+
+ HandleCode(D, AM_Path, getInliningModeForFunction(D, Visited),
+ (Mgr->options.InliningMode == All ? nullptr : &VisitedCallees));
+
+ // Add the visited callees to the global visited set.
+ for (const Decl *Callee : VisitedCallees)
+ // Decls from CallGraph are already canonical. But Decls coming from
+ // CallExprs may be not. We should canonicalize them manually.
+ Visited.insert(isa<ObjCMethodDecl>(Callee) ? Callee
+ : Callee->getCanonicalDecl());
+ VisitedAsTopLevel.insert(D);
+ }
+}
+
+static bool isBisonFile(ASTContext &C) {
+ const SourceManager &SM = C.getSourceManager();
+ FileID FID = SM.getMainFileID();
+ StringRef Buffer = SM.getBuffer(FID)->getBuffer();
+ if (Buffer.startswith("/* A Bison parser, made by"))
+ return true;
+ return false;
+}
+
+void AnalysisConsumer::runAnalysisOnTranslationUnit(ASTContext &C) {
+ BugReporter BR(*Mgr);
+ TranslationUnitDecl *TU = C.getTranslationUnitDecl();
+ checkerMgr->runCheckersOnASTDecl(TU, *Mgr, BR);
+
+ // Run the AST-only checks using the order in which functions are defined.
+ // If inlining is not turned on, use the simplest function order for path
+ // sensitive analyzes as well.
+ RecVisitorMode = AM_Syntax;
+ if (!Mgr->shouldInlineCall())
+ RecVisitorMode |= AM_Path;
+ RecVisitorBR = &BR;
+
+ // Process all the top level declarations.
+ //
+ // Note: TraverseDecl may modify LocalTUDecls, but only by appending more
+ // entries. Thus we don't use an iterator, but rely on LocalTUDecls
+ // random access. By doing so, we automatically compensate for iterators
+ // possibly being invalidated, although this is a bit slower.
+ const unsigned LocalTUDeclsSize = LocalTUDecls.size();
+ for (unsigned i = 0 ; i < LocalTUDeclsSize ; ++i) {
+ TraverseDecl(LocalTUDecls[i]);
+ }
+
+ if (Mgr->shouldInlineCall())
+ HandleDeclsCallGraph(LocalTUDeclsSize);
+
+ // After all decls handled, run checkers on the entire TranslationUnit.
+ checkerMgr->runCheckersOnEndOfTranslationUnit(TU, *Mgr, BR);
+
+ RecVisitorBR = nullptr;
+}
+
+void AnalysisConsumer::reportAnalyzerProgress(StringRef S) {
+ if (Opts->AnalyzerDisplayProgress)
+ llvm::errs() << S;
+}
+
+void AnalysisConsumer::HandleTranslationUnit(ASTContext &C) {
+
+ // Don't run the actions if an error has occurred with parsing the file.
+ DiagnosticsEngine &Diags = PP.getDiagnostics();
+ if (Diags.hasErrorOccurred() || Diags.hasFatalErrorOccurred())
+ return;
+
+ if (TUTotalTimer) TUTotalTimer->startTimer();
+
+ if (isBisonFile(C)) {
+ reportAnalyzerProgress("Skipping bison-generated file\n");
+ } else if (Opts->DisableAllChecks) {
+
+ // Don't analyze if the user explicitly asked for no checks to be performed
+ // on this file.
+ reportAnalyzerProgress("All checks are disabled using a supplied option\n");
+ } else {
+ // Otherwise, just run the analysis.
+ runAnalysisOnTranslationUnit(C);
+ }
+
+ if (TUTotalTimer) TUTotalTimer->stopTimer();
+
+ // Count how many basic blocks we have not covered.
+ NumBlocksInAnalyzedFunctions = FunctionSummaries.getTotalNumBasicBlocks();
+ NumVisitedBlocksInAnalyzedFunctions =
+ FunctionSummaries.getTotalNumVisitedBasicBlocks();
+ if (NumBlocksInAnalyzedFunctions > 0)
+ PercentReachableBlocks =
+ (FunctionSummaries.getTotalNumVisitedBasicBlocks() * 100) /
+ NumBlocksInAnalyzedFunctions;
+
+ // Explicitly destroy the PathDiagnosticConsumer. This will flush its output.
+ // FIXME: This should be replaced with something that doesn't rely on
+ // side-effects in PathDiagnosticConsumer's destructor. This is required when
+ // used with option -disable-free.
+ Mgr.reset();
+}
+
+std::string AnalysisConsumer::getFunctionName(const Decl *D) {
+ std::string Str;
+ llvm::raw_string_ostream OS(Str);
+
+ if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) {
+ OS << FD->getQualifiedNameAsString();
+
+ // In C++, there are overloads.
+ if (Ctx->getLangOpts().CPlusPlus) {
+ OS << '(';
+ for (const auto &P : FD->parameters()) {
+ if (P != *FD->param_begin())
+ OS << ", ";
+ OS << P->getType().getAsString();
+ }
+ OS << ')';
+ }
+
+ } else if (isa<BlockDecl>(D)) {
+ PresumedLoc Loc = Ctx->getSourceManager().getPresumedLoc(D->getLocation());
+
+ if (Loc.isValid()) {
+ OS << "block (line: " << Loc.getLine() << ", col: " << Loc.getColumn()
+ << ')';
+ }
+
+ } else if (const ObjCMethodDecl *OMD = dyn_cast<ObjCMethodDecl>(D)) {
+
+ // FIXME: copy-pasted from CGDebugInfo.cpp.
+ OS << (OMD->isInstanceMethod() ? '-' : '+') << '[';
+ const DeclContext *DC = OMD->getDeclContext();
+ if (const auto *OID = dyn_cast<ObjCImplementationDecl>(DC)) {
+ OS << OID->getName();
+ } else if (const auto *OID = dyn_cast<ObjCInterfaceDecl>(DC)) {
+ OS << OID->getName();
+ } else if (const auto *OC = dyn_cast<ObjCCategoryDecl>(DC)) {
+ if (OC->IsClassExtension()) {
+ OS << OC->getClassInterface()->getName();
+ } else {
+ OS << OC->getIdentifier()->getNameStart() << '('
+ << OC->getIdentifier()->getNameStart() << ')';
+ }
+ } else if (const auto *OCD = dyn_cast<ObjCCategoryImplDecl>(DC)) {
+ OS << OCD->getClassInterface()->getName() << '('
+ << OCD->getName() << ')';
+ } else if (isa<ObjCProtocolDecl>(DC)) {
+ // We can extract the type of the class from the self pointer.
+ if (ImplicitParamDecl *SelfDecl = OMD->getSelfDecl()) {
+ QualType ClassTy =
+ cast<ObjCObjectPointerType>(SelfDecl->getType())->getPointeeType();
+ ClassTy.print(OS, PrintingPolicy(LangOptions()));
+ }
+ }
+ OS << ' ' << OMD->getSelector().getAsString() << ']';
+
+ }
+
+ return OS.str();
+}
+
+AnalysisConsumer::AnalysisMode
+AnalysisConsumer::getModeForDecl(Decl *D, AnalysisMode Mode) {
+ if (!Opts->AnalyzeSpecificFunction.empty() &&
+ getFunctionName(D) != Opts->AnalyzeSpecificFunction)
+ return AM_None;
+
+ // Unless -analyze-all is specified, treat decls differently depending on
+ // where they came from:
+ // - Main source file: run both path-sensitive and non-path-sensitive checks.
+ // - Header files: run non-path-sensitive checks only.
+ // - System headers: don't run any checks.
+ SourceManager &SM = Ctx->getSourceManager();
+ const Stmt *Body = D->getBody();
+ SourceLocation SL = Body ? Body->getBeginLoc() : D->getLocation();
+ SL = SM.getExpansionLoc(SL);
+
+ if (!Opts->AnalyzeAll && !Mgr->isInCodeFile(SL)) {
+ if (SL.isInvalid() || SM.isInSystemHeader(SL))
+ return AM_None;
+ return Mode & ~AM_Path;
+ }
+
+ return Mode;
+}
+
+void AnalysisConsumer::HandleCode(Decl *D, AnalysisMode Mode,
+ ExprEngine::InliningModes IMode,
+ SetOfConstDecls *VisitedCallees) {
+ if (!D->hasBody())
+ return;
+ Mode = getModeForDecl(D, Mode);
+ if (Mode == AM_None)
+ return;
+
+ // Clear the AnalysisManager of old AnalysisDeclContexts.
+ Mgr->ClearContexts();
+ // Ignore autosynthesized code.
+ if (Mgr->getAnalysisDeclContext(D)->isBodyAutosynthesized())
+ return;
+
+ DisplayFunction(D, Mode, IMode);
+ CFG *DeclCFG = Mgr->getCFG(D);
+ if (DeclCFG)
+ MaxCFGSize.updateMax(DeclCFG->size());
+
+ BugReporter BR(*Mgr);
+
+ if (Mode & AM_Syntax)
+ checkerMgr->runCheckersOnASTBody(D, *Mgr, BR);
+ if ((Mode & AM_Path) && checkerMgr->hasPathSensitiveCheckers()) {
+ RunPathSensitiveChecks(D, IMode, VisitedCallees);
+ if (IMode != ExprEngine::Inline_Minimal)
+ NumFunctionsAnalyzed++;
+ }
+}
+
+//===----------------------------------------------------------------------===//
+// Path-sensitive checking.
+//===----------------------------------------------------------------------===//
+
+void AnalysisConsumer::RunPathSensitiveChecks(Decl *D,
+ ExprEngine::InliningModes IMode,
+ SetOfConstDecls *VisitedCallees) {
+ // Construct the analysis engine. First check if the CFG is valid.
+ // FIXME: Inter-procedural analysis will need to handle invalid CFGs.
+ if (!Mgr->getCFG(D))
+ return;
+
+ // See if the LiveVariables analysis scales.
+ if (!Mgr->getAnalysisDeclContext(D)->getAnalysis<RelaxedLiveVariables>())
+ return;
+
+ ExprEngine Eng(CTU, *Mgr, VisitedCallees, &FunctionSummaries, IMode);
+
+ // Execute the worklist algorithm.
+ Eng.ExecuteWorkList(Mgr->getAnalysisDeclContextManager().getStackFrame(D),
+ Mgr->options.MaxNodesPerTopLevelFunction);
+
+ if (!Mgr->options.DumpExplodedGraphTo.empty())
+ Eng.DumpGraph(Mgr->options.TrimGraph, Mgr->options.DumpExplodedGraphTo);
+
+ // Visualize the exploded graph.
+ if (Mgr->options.visualizeExplodedGraphWithGraphViz)
+ Eng.ViewGraph(Mgr->options.TrimGraph);
+
+ // Display warnings.
+ Eng.getBugReporter().FlushReports();
+}
+
+//===----------------------------------------------------------------------===//
+// AnalysisConsumer creation.
+//===----------------------------------------------------------------------===//
+
+std::unique_ptr<AnalysisASTConsumer>
+ento::CreateAnalysisConsumer(CompilerInstance &CI) {
+ // Disable the effects of '-Werror' when using the AnalysisConsumer.
+ CI.getPreprocessor().getDiagnostics().setWarningsAsErrors(false);
+
+ AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts();
+ bool hasModelPath = analyzerOpts->Config.count("model-path") > 0;
+
+ return llvm::make_unique<AnalysisConsumer>(
+ CI, CI.getFrontendOpts().OutputFile, analyzerOpts,
+ CI.getFrontendOpts().Plugins,
+ hasModelPath ? new ModelInjector(CI) : nullptr);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
new file mode 100644
index 000000000000..1c31c35b75e4
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp
@@ -0,0 +1,137 @@
+//===--- CheckerRegistration.cpp - Registration for the Analyzer Checkers -===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines the registration function for the analyzer checkers.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistration.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
+#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/FormattedStream.h"
+#include "llvm/Support/raw_ostream.h"
+#include <memory>
+
+using namespace clang;
+using namespace ento;
+
+std::unique_ptr<CheckerManager> ento::createCheckerManager(
+ ASTContext &context,
+ AnalyzerOptions &opts,
+ ArrayRef<std::string> plugins,
+ ArrayRef<std::function<void(CheckerRegistry &)>> checkerRegistrationFns,
+ DiagnosticsEngine &diags) {
+ auto checkerMgr = llvm::make_unique<CheckerManager>(context, opts);
+
+ CheckerRegistry allCheckers(plugins, diags);
+
+ for (const auto &Fn : checkerRegistrationFns)
+ Fn(allCheckers);
+
+ allCheckers.initializeManager(*checkerMgr, opts);
+ allCheckers.validateCheckerOptions(opts);
+ checkerMgr->finishedCheckerRegistration();
+
+ return checkerMgr;
+}
+
+void ento::printCheckerHelp(raw_ostream &out, ArrayRef<std::string> plugins,
+ DiagnosticsEngine &diags) {
+ out << "OVERVIEW: Clang Static Analyzer Checkers List\n\n";
+ out << "USAGE: -analyzer-checker <CHECKER or PACKAGE,...>\n\n";
+
+ CheckerRegistry(plugins, diags).printHelp(out);
+}
+
+void ento::printEnabledCheckerList(raw_ostream &out,
+ ArrayRef<std::string> plugins,
+ const AnalyzerOptions &opts,
+ DiagnosticsEngine &diags) {
+ out << "OVERVIEW: Clang Static Analyzer Enabled Checkers List\n\n";
+
+ CheckerRegistry(plugins, diags).printList(out, opts);
+}
+
+void ento::printAnalyzerConfigList(raw_ostream &out) {
+ out << "OVERVIEW: Clang Static Analyzer -analyzer-config Option List\n\n";
+ out << "USAGE: clang -cc1 [CLANG_OPTIONS] -analyzer-config "
+ "<OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
+ out << " clang -cc1 [CLANG_OPTIONS] -analyzer-config OPTION1=VALUE, "
+ "-analyzer-config OPTION2=VALUE, ...\n\n";
+ out << " clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang"
+ "<OPTION1=VALUE,OPTION2=VALUE,...>\n\n";
+ out << " clang [CLANG_OPTIONS] -Xclang -analyzer-config -Xclang "
+ "OPTION1=VALUE, -Xclang -analyzer-config -Xclang "
+ "OPTION2=VALUE, ...\n\n";
+ out << "OPTIONS:\n\n";
+
+ using OptionAndDescriptionTy = std::pair<StringRef, std::string>;
+ OptionAndDescriptionTy PrintableOptions[] = {
+#define ANALYZER_OPTION(TYPE, NAME, CMDFLAG, DESC, DEFAULT_VAL) \
+ { \
+ CMDFLAG, \
+ llvm::Twine(llvm::Twine() + "(" + \
+ (StringRef(#TYPE) == "StringRef" ? "string" : #TYPE ) + \
+ ") " DESC \
+ " (default: " #DEFAULT_VAL ")").str() \
+ },
+
+#define ANALYZER_OPTION_DEPENDS_ON_USER_MODE(TYPE, NAME, CMDFLAG, DESC, \
+ SHALLOW_VAL, DEEP_VAL) \
+ { \
+ CMDFLAG, \
+ llvm::Twine(llvm::Twine() + "(" + \
+ (StringRef(#TYPE) == "StringRef" ? "string" : #TYPE ) + \
+ ") " DESC \
+ " (default: " #SHALLOW_VAL " in shallow mode, " #DEEP_VAL \
+ " in deep mode)").str() \
+ },
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.def"
+#undef ANALYZER_OPTION
+#undef ANALYZER_OPTION_DEPENDS_ON_USER_MODE
+ };
+
+ llvm::sort(PrintableOptions, [](const OptionAndDescriptionTy &LHS,
+ const OptionAndDescriptionTy &RHS) {
+ return LHS.first < RHS.first;
+ });
+
+ constexpr size_t MinLineWidth = 70;
+ constexpr size_t PadForOpt = 2;
+ constexpr size_t OptionWidth = 30;
+ constexpr size_t PadForDesc = PadForOpt + OptionWidth;
+ static_assert(MinLineWidth > PadForDesc, "MinLineWidth must be greater!");
+
+ llvm::formatted_raw_ostream FOut(out);
+
+ for (const auto &Pair : PrintableOptions) {
+ FOut.PadToColumn(PadForOpt) << Pair.first;
+
+ // If the buffer's length is greater then PadForDesc, print a newline.
+ if (FOut.getColumn() > PadForDesc)
+ FOut << '\n';
+
+ FOut.PadToColumn(PadForDesc);
+
+ for (char C : Pair.second) {
+ if (FOut.getColumn() > MinLineWidth && C == ' ') {
+ FOut << '\n';
+ FOut.PadToColumn(PadForDesc);
+ continue;
+ }
+ FOut << C;
+ }
+ FOut << "\n\n";
+ }
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
new file mode 100644
index 000000000000..620c0e588906
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp
@@ -0,0 +1,247 @@
+//===- CheckerRegistry.cpp - Maintains all available checkers -------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LLVM.h"
+#include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/StringMap.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/DynamicLibrary.h"
+#include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
+#include <algorithm>
+
+using namespace clang;
+using namespace ento;
+using llvm::sys::DynamicLibrary;
+
+using RegisterCheckersFn = void (*)(CheckerRegistry &);
+
+static bool isCompatibleAPIVersion(const char *versionString) {
+ // If the version string is null, it's not an analyzer plugin.
+ if (!versionString)
+ return false;
+
+ // For now, none of the static analyzer API is considered stable.
+ // Versions must match exactly.
+ return strcmp(versionString, CLANG_ANALYZER_API_VERSION_STRING) == 0;
+}
+
+CheckerRegistry::CheckerRegistry(ArrayRef<std::string> plugins,
+ DiagnosticsEngine &diags) : Diags(diags) {
+#define GET_CHECKERS
+#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \
+ addChecker(register##CLASS, FULLNAME, HELPTEXT, DOC_URI);
+#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
+#undef CHECKER
+#undef GET_CHECKERS
+
+ for (ArrayRef<std::string>::iterator i = plugins.begin(), e = plugins.end();
+ i != e; ++i) {
+ // Get access to the plugin.
+ std::string err;
+ DynamicLibrary lib = DynamicLibrary::getPermanentLibrary(i->c_str(), &err);
+ if (!lib.isValid()) {
+ diags.Report(diag::err_fe_unable_to_load_plugin) << *i << err;
+ continue;
+ }
+
+ // See if it's compatible with this build of clang.
+ const char *pluginAPIVersion =
+ (const char *) lib.getAddressOfSymbol("clang_analyzerAPIVersionString");
+ if (!isCompatibleAPIVersion(pluginAPIVersion)) {
+ Diags.Report(diag::warn_incompatible_analyzer_plugin_api)
+ << llvm::sys::path::filename(*i);
+ Diags.Report(diag::note_incompatible_analyzer_plugin_api)
+ << CLANG_ANALYZER_API_VERSION_STRING
+ << pluginAPIVersion;
+ continue;
+ }
+
+ // Register its checkers.
+ RegisterCheckersFn registerPluginCheckers =
+ (RegisterCheckersFn) (intptr_t) lib.getAddressOfSymbol(
+ "clang_registerCheckers");
+ if (registerPluginCheckers)
+ registerPluginCheckers(*this);
+ }
+}
+
+static constexpr char PackageSeparator = '.';
+
+static bool checkerNameLT(const CheckerRegistry::CheckerInfo &a,
+ const CheckerRegistry::CheckerInfo &b) {
+ return a.FullName < b.FullName;
+}
+
+static bool isInPackage(const CheckerRegistry::CheckerInfo &checker,
+ StringRef packageName) {
+ // Does the checker's full name have the package as a prefix?
+ if (!checker.FullName.startswith(packageName))
+ return false;
+
+ // Is the package actually just the name of a specific checker?
+ if (checker.FullName.size() == packageName.size())
+ return true;
+
+ // Is the checker in the package (or a subpackage)?
+ if (checker.FullName[packageName.size()] == PackageSeparator)
+ return true;
+
+ return false;
+}
+
+CheckerRegistry::CheckerInfoSet CheckerRegistry::getEnabledCheckers(
+ const AnalyzerOptions &Opts) const {
+
+ assert(std::is_sorted(Checkers.begin(), Checkers.end(), checkerNameLT) &&
+ "In order to efficiently gather checkers, this function expects them "
+ "to be already sorted!");
+
+ CheckerInfoSet enabledCheckers;
+ const auto end = Checkers.cend();
+
+ for (const std::pair<std::string, bool> &opt : Opts.CheckersControlList) {
+ // Use a binary search to find the possible start of the package.
+ CheckerRegistry::CheckerInfo packageInfo(nullptr, opt.first, "", "");
+ auto firstRelatedChecker =
+ std::lower_bound(Checkers.cbegin(), end, packageInfo, checkerNameLT);
+
+ if (firstRelatedChecker == end ||
+ !isInPackage(*firstRelatedChecker, opt.first)) {
+ Diags.Report(diag::err_unknown_analyzer_checker) << opt.first;
+ Diags.Report(diag::note_suggest_disabling_all_checkers);
+ return {};
+ }
+
+ // See how large the package is.
+ // If the package doesn't exist, assume the option refers to a single
+ // checker.
+ size_t size = 1;
+ llvm::StringMap<size_t>::const_iterator packageSize =
+ Packages.find(opt.first);
+ if (packageSize != Packages.end())
+ size = packageSize->getValue();
+
+ // Step through all the checkers in the package.
+ for (auto lastRelatedChecker = firstRelatedChecker+size;
+ firstRelatedChecker != lastRelatedChecker; ++firstRelatedChecker)
+ if (opt.second)
+ enabledCheckers.insert(&*firstRelatedChecker);
+ else
+ enabledCheckers.remove(&*firstRelatedChecker);
+ }
+
+ return enabledCheckers;
+}
+
+void CheckerRegistry::addChecker(InitializationFunction Fn, StringRef Name,
+ StringRef Desc, StringRef DocsUri) {
+ Checkers.emplace_back(Fn, Name, Desc, DocsUri);
+
+ // Record the presence of the checker in its packages.
+ StringRef packageName, leafName;
+ std::tie(packageName, leafName) = Name.rsplit(PackageSeparator);
+ while (!leafName.empty()) {
+ Packages[packageName] += 1;
+ std::tie(packageName, leafName) = packageName.rsplit(PackageSeparator);
+ }
+}
+
+void CheckerRegistry::initializeManager(CheckerManager &checkerMgr,
+ const AnalyzerOptions &Opts) const {
+ // Sort checkers for efficient collection.
+ llvm::sort(Checkers, checkerNameLT);
+
+ // Collect checkers enabled by the options.
+ CheckerInfoSet enabledCheckers = getEnabledCheckers(Opts);
+
+ // Initialize the CheckerManager with all enabled checkers.
+ for (const auto *i : enabledCheckers) {
+ checkerMgr.setCurrentCheckName(CheckName(i->FullName));
+ i->Initialize(checkerMgr);
+ }
+}
+
+void CheckerRegistry::validateCheckerOptions(
+ const AnalyzerOptions &opts) const {
+ for (const auto &config : opts.Config) {
+ size_t pos = config.getKey().find(':');
+ if (pos == StringRef::npos)
+ continue;
+
+ bool hasChecker = false;
+ StringRef checkerName = config.getKey().substr(0, pos);
+ for (const auto &checker : Checkers) {
+ if (checker.FullName.startswith(checkerName) &&
+ (checker.FullName.size() == pos || checker.FullName[pos] == '.')) {
+ hasChecker = true;
+ break;
+ }
+ }
+ if (!hasChecker)
+ Diags.Report(diag::err_unknown_analyzer_checker) << checkerName;
+ }
+}
+
+void CheckerRegistry::printHelp(raw_ostream &out,
+ size_t maxNameChars) const {
+ // FIXME: Alphabetical sort puts 'experimental' in the middle.
+ // Would it be better to name it '~experimental' or something else
+ // that's ASCIIbetically last?
+ llvm::sort(Checkers, checkerNameLT);
+
+ // FIXME: Print available packages.
+
+ out << "CHECKERS:\n";
+
+ // Find the maximum option length.
+ size_t optionFieldWidth = 0;
+ for (const auto &i : Checkers) {
+ // Limit the amount of padding we are willing to give up for alignment.
+ // Package.Name Description [Hidden]
+ size_t nameLength = i.FullName.size();
+ if (nameLength <= maxNameChars)
+ optionFieldWidth = std::max(optionFieldWidth, nameLength);
+ }
+
+ const size_t initialPad = 2;
+ for (const auto &i : Checkers) {
+ out.indent(initialPad) << i.FullName;
+
+ int pad = optionFieldWidth - i.FullName.size();
+
+ // Break on long option names.
+ if (pad < 0) {
+ out << '\n';
+ pad = optionFieldWidth + initialPad;
+ }
+ out.indent(pad + 2) << i.Desc;
+
+ out << '\n';
+ }
+}
+
+void CheckerRegistry::printList(raw_ostream &out,
+ const AnalyzerOptions &opts) const {
+ // Sort checkers for efficient collection.
+ llvm::sort(Checkers, checkerNameLT);
+
+ // Collect checkers enabled by the options.
+ CheckerInfoSet enabledCheckers = getEnabledCheckers(opts);
+
+ for (const auto *i : enabledCheckers)
+ out << i->FullName << '\n';
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp
new file mode 100644
index 000000000000..b33608042ce3
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/FrontendActions.cpp
@@ -0,0 +1,28 @@
+//===--- FrontendActions.cpp ----------------------------------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
+#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
+#include "clang/StaticAnalyzer/Frontend/ModelConsumer.h"
+using namespace clang;
+using namespace ento;
+
+std::unique_ptr<ASTConsumer>
+AnalysisAction::CreateASTConsumer(CompilerInstance &CI, StringRef InFile) {
+ return CreateAnalysisConsumer(CI);
+}
+
+ParseModelFileAction::ParseModelFileAction(llvm::StringMap<Stmt *> &Bodies)
+ : Bodies(Bodies) {}
+
+std::unique_ptr<ASTConsumer>
+ParseModelFileAction::CreateASTConsumer(CompilerInstance &CI,
+ StringRef InFile) {
+ return llvm::make_unique<ModelConsumer>(Bodies);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp
new file mode 100644
index 000000000000..60825ef7411d
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelConsumer.cpp
@@ -0,0 +1,42 @@
+//===--- ModelConsumer.cpp - ASTConsumer for consuming model files --------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file implements an ASTConsumer for consuming model files.
+///
+/// This ASTConsumer handles the AST of a parsed model file. All top level
+/// function definitions will be collected from that model file for later
+/// retrieval during the static analysis. The body of these functions will not
+/// be injected into the ASTUnit of the analyzed translation unit. It will be
+/// available through the BodyFarm which is utilized by the AnalysisDeclContext
+/// class.
+///
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Frontend/ModelConsumer.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/DeclGroup.h"
+
+using namespace clang;
+using namespace ento;
+
+ModelConsumer::ModelConsumer(llvm::StringMap<Stmt *> &Bodies)
+ : Bodies(Bodies) {}
+
+bool ModelConsumer::HandleTopLevelDecl(DeclGroupRef D) {
+ for (DeclGroupRef::iterator I = D.begin(), E = D.end(); I != E; ++I) {
+
+ // Only interested in definitions.
+ const FunctionDecl *func = llvm::dyn_cast<FunctionDecl>(*I);
+ if (func && func->hasBody()) {
+ Bodies.insert(std::make_pair(func->getName(), func->getBody()));
+ }
+ }
+ return true;
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
new file mode 100644
index 000000000000..b1927c8401d6
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.cpp
@@ -0,0 +1,115 @@
+//===-- ModelInjector.cpp ---------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ModelInjector.h"
+#include "clang/AST/Decl.h"
+#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/Stack.h"
+#include "clang/Frontend/ASTUnit.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendAction.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Serialization/ASTReader.h"
+#include "clang/StaticAnalyzer/Frontend/FrontendActions.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/CrashRecoveryContext.h"
+#include "llvm/Support/FileSystem.h"
+#include <utility>
+
+using namespace clang;
+using namespace ento;
+
+ModelInjector::ModelInjector(CompilerInstance &CI) : CI(CI) {}
+
+Stmt *ModelInjector::getBody(const FunctionDecl *D) {
+ onBodySynthesis(D);
+ return Bodies[D->getName()];
+}
+
+Stmt *ModelInjector::getBody(const ObjCMethodDecl *D) {
+ onBodySynthesis(D);
+ return Bodies[D->getName()];
+}
+
+void ModelInjector::onBodySynthesis(const NamedDecl *D) {
+
+ // FIXME: what about overloads? Declarations can be used as keys but what
+ // about file name index? Mangled names may not be suitable for that either.
+ if (Bodies.count(D->getName()) != 0)
+ return;
+
+ SourceManager &SM = CI.getSourceManager();
+ FileID mainFileID = SM.getMainFileID();
+
+ AnalyzerOptionsRef analyzerOpts = CI.getAnalyzerOpts();
+ llvm::StringRef modelPath = analyzerOpts->ModelPath;
+
+ llvm::SmallString<128> fileName;
+
+ if (!modelPath.empty())
+ fileName =
+ llvm::StringRef(modelPath.str() + "/" + D->getName().str() + ".model");
+ else
+ fileName = llvm::StringRef(D->getName().str() + ".model");
+
+ if (!llvm::sys::fs::exists(fileName.str())) {
+ Bodies[D->getName()] = nullptr;
+ return;
+ }
+
+ auto Invocation = std::make_shared<CompilerInvocation>(CI.getInvocation());
+
+ FrontendOptions &FrontendOpts = Invocation->getFrontendOpts();
+ InputKind IK = InputKind::CXX; // FIXME
+ FrontendOpts.Inputs.clear();
+ FrontendOpts.Inputs.emplace_back(fileName, IK);
+ FrontendOpts.DisableFree = true;
+
+ Invocation->getDiagnosticOpts().VerifyDiagnostics = 0;
+
+ // Modules are parsed by a separate CompilerInstance, so this code mimics that
+ // behavior for models
+ CompilerInstance Instance(CI.getPCHContainerOperations());
+ Instance.setInvocation(std::move(Invocation));
+ Instance.createDiagnostics(
+ new ForwardingDiagnosticConsumer(CI.getDiagnosticClient()),
+ /*ShouldOwnClient=*/true);
+
+ Instance.getDiagnostics().setSourceManager(&SM);
+
+ Instance.setVirtualFileSystem(&CI.getVirtualFileSystem());
+
+ // The instance wants to take ownership, however DisableFree frontend option
+ // is set to true to avoid double free issues
+ Instance.setFileManager(&CI.getFileManager());
+ Instance.setSourceManager(&SM);
+ Instance.setPreprocessor(CI.getPreprocessorPtr());
+ Instance.setASTContext(&CI.getASTContext());
+
+ Instance.getPreprocessor().InitializeForModelFile();
+
+ ParseModelFileAction parseModelFile(Bodies);
+
+ llvm::CrashRecoveryContext CRC;
+
+ CRC.RunSafelyOnThread([&]() { Instance.ExecuteAction(parseModelFile); },
+ DesiredStackSize);
+
+ Instance.getPreprocessor().FinalizeForModelFile();
+
+ Instance.resetAndLeakSourceManager();
+ Instance.resetAndLeakFileManager();
+ Instance.resetAndLeakPreprocessor();
+
+ // The preprocessor enters to the main file id when parsing is started, so
+ // the main file id is changed to the model file during parsing and it needs
+ // to be reset to the former main file id after parsing of the model file
+ // is done.
+ SM.setMainFileID(mainFileID);
+}
diff --git a/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h
new file mode 100644
index 000000000000..b1b6de9ef9d9
--- /dev/null
+++ b/contrib/llvm/tools/clang/lib/StaticAnalyzer/Frontend/ModelInjector.h
@@ -0,0 +1,70 @@
+//===-- ModelInjector.h -----------------------------------------*- C++ -*-===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// This file defines the clang::ento::ModelInjector class which implements the
+/// clang::CodeInjector interface. This class is responsible for injecting
+/// function definitions that were synthesized from model files.
+///
+/// Model files allow definitions of functions to be lazily constituted for functions
+/// which lack bodies in the original source code. This allows the analyzer
+/// to more precisely analyze code that calls such functions, analyzing the
+/// artificial definitions (which typically approximate the semantics of the
+/// called function) when called by client code. These definitions are
+/// reconstituted lazily, on-demand, by the static analyzer engine.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_SA_FRONTEND_MODELINJECTOR_H
+#define LLVM_CLANG_SA_FRONTEND_MODELINJECTOR_H
+
+#include "clang/Analysis/CodeInjector.h"
+#include "llvm/ADT/StringMap.h"
+
+namespace clang {
+
+class CompilerInstance;
+class ASTUnit;
+class ASTReader;
+class NamedDecl;
+class Module;
+
+namespace ento {
+class ModelInjector : public CodeInjector {
+public:
+ ModelInjector(CompilerInstance &CI);
+ Stmt *getBody(const FunctionDecl *D) override;
+ Stmt *getBody(const ObjCMethodDecl *D) override;
+
+private:
+ /// Synthesize a body for a declaration
+ ///
+ /// This method first looks up the appropriate model file based on the
+ /// model-path configuration option and the name of the declaration that is
+ /// looked up. If no model were synthesized yet for a function with that name
+ /// it will create a new compiler instance to parse the model file using the
+ /// ASTContext, Preprocessor, SourceManager of the original compiler instance.
+ /// The former resources are shared between the two compiler instance, so the
+ /// newly created instance have to "leak" these objects, since they are owned
+ /// by the original instance.
+ ///
+ /// The model-path should be either an absolute path or relative to the
+ /// working directory of the compiler.
+ void onBodySynthesis(const NamedDecl *D);
+
+ CompilerInstance &CI;
+
+ // FIXME: double memoization is redundant, with memoization both here and in
+ // BodyFarm.
+ llvm::StringMap<Stmt *> Bodies;
+};
+}
+}
+
+#endif