aboutsummaryrefslogtreecommitdiffstats
path: root/lib/StaticAnalyzer
diff options
context:
space:
mode:
Diffstat (limited to 'lib/StaticAnalyzer')
-rw-r--r--lib/StaticAnalyzer/Checkers/AllocationState.h7
-rw-r--r--lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp15
-rw-r--r--lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp15
-rw-r--r--lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp38
-rw-r--r--lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp53
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringChecker.cpp237
-rw-r--r--lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp21
-rw-r--r--lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp31
-rw-r--r--lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp45
-rw-r--r--lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp14
-rw-r--r--lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/CastValueChecker.cpp190
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp17
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp13
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp124
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp9
-rw-r--r--lib/StaticAnalyzer/Checkers/ChrootChecker.cpp70
-rw-r--r--lib/StaticAnalyzer/Checkers/CloneChecker.cpp49
-rw-r--r--lib/StaticAnalyzer/Checkers/ConversionChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp34
-rw-r--r--lib/StaticAnalyzer/Checkers/DebugCheckers.cpp129
-rw-r--r--lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp12
-rw-r--r--lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp13
-rw-r--r--lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp30
-rw-r--r--lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp17
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp28
-rw-r--r--lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp20
-rw-r--r--lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp15
-rw-r--r--lib/StaticAnalyzer/Checkers/GTestChecker.cpp17
-rw-r--r--lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp403
-rw-r--r--lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp13
-rw-r--r--lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/InterCheckerAPI.h10
-rw-r--r--lib/StaticAnalyzer/Checkers/IteratorChecker.cpp609
-rw-r--r--lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp21
-rw-r--r--lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp29
-rw-r--r--lib/StaticAnalyzer/Checkers/MIGChecker.cpp295
-rw-r--r--lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp7
-rw-r--r--lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h7
-rw-r--r--lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h7
-rw-r--r--lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp7
-rw-r--r--lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h7
-rw-r--r--lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocChecker.cpp134
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp14
-rw-r--r--lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp15
-rw-r--r--lib/StaticAnalyzer/Checkers/Move.h30
-rw-r--r--lib/StaticAnalyzer/Checkers/MoveChecker.cpp40
-rw-r--r--lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp14
-rw-r--r--lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp29
-rw-r--r--lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp34
-rw-r--r--lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp29
-rw-r--r--lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp13
-rw-r--r--lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp90
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp14
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp13
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp10
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp14
-rw-r--r--lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/PaddingChecker.cpp32
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp100
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp113
-rw-r--r--lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp445
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h63
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp186
-rw-r--r--lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h52
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp170
-rw-r--r--lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp13
-rw-r--r--lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp18
-rw-r--r--lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp72
-rw-r--r--lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp29
-rw-r--r--lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp21
-rw-r--r--lib/StaticAnalyzer/Checkers/StreamChecker.cpp22
-rw-r--r--lib/StaticAnalyzer/Checkers/Taint.cpp227
-rw-r--r--lib/StaticAnalyzer/Checkers/Taint.h102
-rw-r--r--lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp16
-rw-r--r--lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/TraversalChecker.cpp15
-rw-r--r--lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp16
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h37
-rw-r--r--lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp129
-rw-r--r--lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp7
-rw-r--r--lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp238
-rw-r--r--lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp15
-rw-r--r--lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp15
-rw-r--r--lib/StaticAnalyzer/Checkers/ValistChecker.cpp25
-rw-r--r--lib/StaticAnalyzer/Checkers/VforkChecker.cpp11
-rw-r--r--lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp14
-rw-r--r--lib/StaticAnalyzer/Core/APSIntType.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/AnalysisManager.cpp13
-rw-r--r--lib/StaticAnalyzer/Core/AnalyzerOptions.cpp119
-rw-r--r--lib/StaticAnalyzer/Core/BasicValueFactory.cpp17
-rw-r--r--lib/StaticAnalyzer/Core/BlockCounter.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/BugReporter.cpp47
-rw-r--r--lib/StaticAnalyzer/Core/BugReporterVisitors.cpp1365
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp37
-rw-r--r--lib/StaticAnalyzer/Core/Checker.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/CheckerContext.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/CheckerHelpers.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/CheckerManager.cpp104
-rw-r--r--lib/StaticAnalyzer/Core/CommonBugCategories.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/ConstraintManager.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/CoreEngine.cpp63
-rw-r--r--lib/StaticAnalyzer/Core/DynamicTypeMap.cpp53
-rw-r--r--lib/StaticAnalyzer/Core/Environment.cpp87
-rw-r--r--lib/StaticAnalyzer/Core/ExplodedGraph.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngine.cpp282
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineC.cpp54
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCXX.cpp55
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp78
-rw-r--r--lib/StaticAnalyzer/Core/ExprEngineObjC.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/FunctionSummary.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp133
-rw-r--r--lib/StaticAnalyzer/Core/IssueHash.cpp11
-rw-r--r--lib/StaticAnalyzer/Core/LoopUnrolling.cpp9
-rw-r--r--lib/StaticAnalyzer/Core/LoopWidening.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/MemRegion.cpp10
-rw-r--r--lib/StaticAnalyzer/Core/PathDiagnostic.cpp70
-rw-r--r--lib/StaticAnalyzer/Core/PlistDiagnostics.cpp136
-rw-r--r--lib/StaticAnalyzer/Core/PrettyStackTraceLocationContext.h13
-rw-r--r--lib/StaticAnalyzer/Core/ProgramState.cpp229
-rw-r--r--lib/StaticAnalyzer/Core/RangeConstraintManager.cpp86
-rw-r--r--lib/StaticAnalyzer/Core/RangedConstraintManager.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/RegionStore.cpp227
-rw-r--r--lib/StaticAnalyzer/Core/RetainSummaryManager.cpp1229
-rw-r--r--lib/StaticAnalyzer/Core/SMTConstraintManager.cpp18
-rw-r--r--lib/StaticAnalyzer/Core/SValBuilder.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/SVals.cpp19
-rw-r--r--lib/StaticAnalyzer/Core/SarifDiagnostics.cpp24
-rw-r--r--lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp19
-rw-r--r--lib/StaticAnalyzer/Core/Store.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/SubEngine.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/SymbolManager.cpp17
-rw-r--r--lib/StaticAnalyzer/Core/TaintManager.cpp23
-rw-r--r--lib/StaticAnalyzer/Core/WorkList.cpp7
-rw-r--r--lib/StaticAnalyzer/Core/Z3ConstraintManager.cpp841
-rw-r--r--lib/StaticAnalyzer/Frontend/AnalysisConsumer.cpp94
-rw-r--r--lib/StaticAnalyzer/Frontend/CheckerRegistration.cpp83
-rw-r--r--lib/StaticAnalyzer/Frontend/CheckerRegistry.cpp659
-rw-r--r--lib/StaticAnalyzer/Frontend/FrontendActions.cpp7
-rw-r--r--lib/StaticAnalyzer/Frontend/ModelConsumer.cpp7
-rw-r--r--lib/StaticAnalyzer/Frontend/ModelInjector.cpp9
-rw-r--r--lib/StaticAnalyzer/Frontend/ModelInjector.h7
172 files changed, 6768 insertions, 5453 deletions
diff --git a/lib/StaticAnalyzer/Checkers/AllocationState.h b/lib/StaticAnalyzer/Checkers/AllocationState.h
index c8193f77f928..25de37003319 100644
--- a/lib/StaticAnalyzer/Checkers/AllocationState.h
+++ b/lib/StaticAnalyzer/Checkers/AllocationState.h
@@ -1,9 +1,8 @@
//===--- AllocationState.h ------------------------------------- *- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
index b5d0f6620a1d..d0def6918932 100644
--- a/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/AnalysisOrderChecker.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -45,8 +44,8 @@ class AnalysisOrderChecker
check::LiveSymbols> {
bool isCallbackEnabled(AnalyzerOptions &Opts, StringRef CallbackName) const {
- return Opts.getCheckerBooleanOption("*", false, this) ||
- Opts.getCheckerBooleanOption(CallbackName, false, this);
+ return Opts.getCheckerBooleanOption(this, "*") ||
+ Opts.getCheckerBooleanOption(this, CallbackName);
}
bool isCallbackEnabled(CheckerContext &C, StringRef CallbackName) const {
@@ -176,3 +175,7 @@ public:
void ento::registerAnalysisOrderChecker(CheckerManager &mgr) {
mgr.registerChecker<AnalysisOrderChecker>();
}
+
+bool ento::shouldRegisterAnalysisOrderChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
index 5e01012401b2..20f3008b4a4b 100644
--- a/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
@@ -1,9 +1,8 @@
//==--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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This file reports various statistics about analyzer visitation.
@@ -140,3 +139,7 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
void ento::registerAnalyzerStatsChecker(CheckerManager &mgr) {
mgr.registerChecker<AnalyzerStatsChecker>();
}
+
+bool ento::shouldRegisterAnalyzerStatsChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
index 20f3092fdba4..58017acb4a24 100644
--- a/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ArrayBoundChecker.cpp
@@ -1,9 +1,8 @@
//== ArrayBoundChecker.cpp ------------------------------*- C++ -*--==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -91,3 +90,7 @@ void ArrayBoundChecker::checkLocation(SVal l, bool isLoad, const Stmt* LoadS,
void ento::registerArrayBoundChecker(CheckerManager &mgr) {
mgr.registerChecker<ArrayBoundChecker>();
}
+
+bool ento::shouldRegisterArrayBoundChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
index 26887be9f258..3bf8a1836b19 100644
--- a/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
+++ b/lib/StaticAnalyzer/Checkers/ArrayBoundCheckerV2.cpp
@@ -1,9 +1,8 @@
//== ArrayBoundCheckerV2.cpp ------------------------------------*- C++ -*--==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -12,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "Taint.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/CharUnits.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
@@ -25,6 +25,7 @@
using namespace clang;
using namespace ento;
+using namespace taint;
namespace {
class ArrayBoundCheckerV2 :
@@ -205,7 +206,7 @@ void ArrayBoundCheckerV2::checkLocation(SVal location, bool isLoad,
// 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)) {
+ if (isTainted(state, ByteOffset)) {
reportOOB(checkerContext, state_exceedsUpperBound, OOB_Tainted,
llvm::make_unique<TaintBugVisitor>(ByteOffset));
return;
@@ -354,3 +355,7 @@ RegionRawOffsetV2 RegionRawOffsetV2::computeOffset(ProgramStateRef state,
void ento::registerArrayBoundCheckerV2(CheckerManager &mgr) {
mgr.registerChecker<ArrayBoundCheckerV2>();
}
+
+bool ento::shouldRegisterArrayBoundCheckerV2(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index 577b5349f62e..e3fb4c3eb523 100644
--- a/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -1243,27 +1242,54 @@ void ento::registerNilArgChecker(CheckerManager &mgr) {
mgr.registerChecker<NilArgChecker>();
}
+bool ento::shouldRegisterNilArgChecker(const LangOptions &LO) {
+ return true;
+}
+
void ento::registerCFNumberChecker(CheckerManager &mgr) {
mgr.registerChecker<CFNumberChecker>();
}
+bool ento::shouldRegisterCFNumberChecker(const LangOptions &LO) {
+ return true;
+}
+
void ento::registerCFRetainReleaseChecker(CheckerManager &mgr) {
mgr.registerChecker<CFRetainReleaseChecker>();
}
+bool ento::shouldRegisterCFRetainReleaseChecker(const LangOptions &LO) {
+ return true;
+}
+
void ento::registerClassReleaseChecker(CheckerManager &mgr) {
mgr.registerChecker<ClassReleaseChecker>();
}
+bool ento::shouldRegisterClassReleaseChecker(const LangOptions &LO) {
+ return true;
+}
+
void ento::registerVariadicMethodTypeChecker(CheckerManager &mgr) {
mgr.registerChecker<VariadicMethodTypeChecker>();
}
+bool ento::shouldRegisterVariadicMethodTypeChecker(const LangOptions &LO) {
+ return true;
+}
+
void ento::registerObjCLoopChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCLoopChecker>();
}
-void
-ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
+bool ento::shouldRegisterObjCLoopChecker(const LangOptions &LO) {
+ return true;
+}
+
+void ento::registerObjCNonNilReturnValueChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCNonNilReturnValueChecker>();
}
+
+bool ento::shouldRegisterObjCNonNilReturnValueChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 00d08b371f37..009160fc9815 100644
--- a/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -1,9 +1,8 @@
//===-- BlockInCriticalSectionChecker.cpp -----------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -183,3 +182,7 @@ void BlockInCriticalSectionChecker::reportBlockInCritSection(
void ento::registerBlockInCriticalSectionChecker(CheckerManager &mgr) {
mgr.registerChecker<BlockInCriticalSectionChecker>();
}
+
+bool ento::shouldRegisterBlockInCriticalSectionChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
index 3008eddd397e..de8763c1b7b5 100644
--- a/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/BoolAssignmentChecker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -155,3 +154,7 @@ void BoolAssignmentChecker::checkBind(SVal loc, SVal val, const Stmt *S,
void ento::registerBoolAssignmentChecker(CheckerManager &mgr) {
mgr.registerChecker<BoolAssignmentChecker>();
}
+
+bool ento::shouldRegisterBoolAssignmentChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
index f98027942e18..10594e331cbe 100644
--- a/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/BuiltinFunctionChecker.cpp
@@ -1,9 +1,8 @@
//=== BuiltinFunctionChecker.cpp --------------------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -15,6 +14,7 @@
#include "clang/Basic/Builtins.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;
@@ -24,30 +24,32 @@ namespace {
class BuiltinFunctionChecker : public Checker<eval::Call> {
public:
- bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
};
}
-bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
+bool BuiltinFunctionChecker::evalCall(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef state = C.getState();
- const FunctionDecl *FD = C.getCalleeDecl(CE);
- const LocationContext *LCtx = C.getLocationContext();
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!FD)
return false;
+ const LocationContext *LCtx = C.getLocationContext();
+ const Expr *CE = Call.getOriginExpr();
+
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())
+ assert (Call.getNumArgs() > 0);
+ SVal Arg = Call.getArgSVal(0);
+ if (Arg.isUndef())
return true; // Return true to model purity.
- state = state->assume(ArgSVal.castAs<DefinedOrUnknownSVal>(), true);
+ state = state->assume(Arg.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) {
@@ -67,9 +69,9 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
// __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));
+ assert (Call.getNumArgs() > 0);
+ SVal Arg = Call.getArgSVal(0);
+ C.addTransition(state->BindExpr(CE, LCtx, Arg));
return true;
}
@@ -83,12 +85,14 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
// 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>();
+ auto Size = Call.getArgSVal(0);
+ if (Size.isUndef())
+ return true; // Return true to model purity.
SValBuilder& svalBuilder = C.getSValBuilder();
DefinedOrUnknownSVal Extent = R->getExtent(svalBuilder);
DefinedOrUnknownSVal extentMatchesSizeArg =
- svalBuilder.evalEQ(state, Extent, Size);
+ svalBuilder.evalEQ(state, Extent, Size.castAs<DefinedOrUnknownSVal>());
state = state->assume(extentMatchesSizeArg, true);
assert(state && "The region should not have any previous constraints");
@@ -96,21 +100,30 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
return true;
}
+ case Builtin::BI__builtin_dynamic_object_size:
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.
+ SValBuilder &SVB = C.getSValBuilder();
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);
}
+ if (FD->getBuiltinID() == Builtin::BI__builtin_constant_p) {
+ // If we didn't manage to figure out if the value is constant or not,
+ // it is safe to assume that it's not constant and unsafe to assume
+ // that it's constant.
+ if (V.isUnknown())
+ V = SVB.makeIntVal(0, CE->getType());
+ }
+
C.addTransition(state->BindExpr(CE, LCtx, V));
return true;
}
@@ -120,3 +133,7 @@ bool BuiltinFunctionChecker::evalCall(const CallExpr *CE,
void ento::registerBuiltinFunctionChecker(CheckerManager &mgr) {
mgr.registerChecker<BuiltinFunctionChecker>();
}
+
+bool ento::shouldRegisterBuiltinFunctionChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 8bffada69b9b..44f4530781a8 100644
--- a/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -1,9 +1,8 @@
//= 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -18,6 +17,7 @@
#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/ADT/STLExtras.h"
@@ -58,7 +58,7 @@ public:
static void *getTag() { static int tag; return &tag; }
- bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ bool evalCall(const CallEvent &Call, 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;
@@ -73,7 +73,38 @@ public:
typedef void (CStringChecker::*FnCheck)(CheckerContext &,
const CallExpr *) const;
+ CallDescriptionMap<FnCheck> Callbacks = {
+ {{CDF_MaybeBuiltin, "memcpy", 3}, &CStringChecker::evalMemcpy},
+ {{CDF_MaybeBuiltin, "mempcpy", 3}, &CStringChecker::evalMempcpy},
+ {{CDF_MaybeBuiltin, "memcmp", 3}, &CStringChecker::evalMemcmp},
+ {{CDF_MaybeBuiltin, "memmove", 3}, &CStringChecker::evalMemmove},
+ {{CDF_MaybeBuiltin, "memset", 3}, &CStringChecker::evalMemset},
+ {{CDF_MaybeBuiltin, "explicit_memset", 3}, &CStringChecker::evalMemset},
+ {{CDF_MaybeBuiltin, "strcpy", 2}, &CStringChecker::evalStrcpy},
+ {{CDF_MaybeBuiltin, "strncpy", 3}, &CStringChecker::evalStrncpy},
+ {{CDF_MaybeBuiltin, "stpcpy", 2}, &CStringChecker::evalStpcpy},
+ {{CDF_MaybeBuiltin, "strlcpy", 3}, &CStringChecker::evalStrlcpy},
+ {{CDF_MaybeBuiltin, "strcat", 2}, &CStringChecker::evalStrcat},
+ {{CDF_MaybeBuiltin, "strncat", 3}, &CStringChecker::evalStrncat},
+ {{CDF_MaybeBuiltin, "strlcat", 3}, &CStringChecker::evalStrlcat},
+ {{CDF_MaybeBuiltin, "strlen", 1}, &CStringChecker::evalstrLength},
+ {{CDF_MaybeBuiltin, "strnlen", 2}, &CStringChecker::evalstrnLength},
+ {{CDF_MaybeBuiltin, "strcmp", 2}, &CStringChecker::evalStrcmp},
+ {{CDF_MaybeBuiltin, "strncmp", 3}, &CStringChecker::evalStrncmp},
+ {{CDF_MaybeBuiltin, "strcasecmp", 2}, &CStringChecker::evalStrcasecmp},
+ {{CDF_MaybeBuiltin, "strncasecmp", 3}, &CStringChecker::evalStrncasecmp},
+ {{CDF_MaybeBuiltin, "strsep", 2}, &CStringChecker::evalStrsep},
+ {{CDF_MaybeBuiltin, "bcopy", 3}, &CStringChecker::evalBcopy},
+ {{CDF_MaybeBuiltin, "bcmp", 3}, &CStringChecker::evalMemcmp},
+ {{CDF_MaybeBuiltin, "bzero", 2}, &CStringChecker::evalBzero},
+ {{CDF_MaybeBuiltin, "explicit_bzero", 2}, &CStringChecker::evalBzero},
+ };
+
+ // These require a bit of special handling.
+ CallDescription StdCopy{{"std", "copy"}, 3},
+ StdCopyBackward{{"std", "copy_backward"}, 3};
+ FnCheck identifyCall(const CallEvent &Call, CheckerContext &C) 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;
@@ -1201,9 +1232,6 @@ void CStringChecker::evalCopyCommon(CheckerContext &C,
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);
@@ -1213,9 +1241,6 @@ void CStringChecker::evalMemcpy(CheckerContext &C, const CallExpr *CE) const {
}
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);
@@ -1225,9 +1250,6 @@ void CStringChecker::evalMempcpy(CheckerContext &C, const CallExpr *CE) const {
}
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);
@@ -1237,18 +1259,12 @@ void CStringChecker::evalMemmove(CheckerContext &C, const CallExpr *CE) const {
}
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";
@@ -1323,18 +1339,12 @@ void CStringChecker::evalMemcmp(CheckerContext &C, const CallExpr *CE) const {
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);
}
@@ -1459,9 +1469,6 @@ void CStringChecker::evalstrLengthCommon(CheckerContext &C, const CallExpr *CE,
}
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,
@@ -1470,9 +1477,6 @@ void CStringChecker::evalStrcpy(CheckerContext &C, const CallExpr *CE) const {
}
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,
@@ -1481,9 +1485,6 @@ void CStringChecker::evalStrncpy(CheckerContext &C, const CallExpr *CE) const {
}
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,
@@ -1492,9 +1493,6 @@ void CStringChecker::evalStpcpy(CheckerContext &C, const CallExpr *CE) const {
}
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,
@@ -1504,9 +1502,6 @@ void CStringChecker::evalStrlcpy(CheckerContext &C, const CallExpr *CE) const {
}
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,
@@ -1515,9 +1510,6 @@ void CStringChecker::evalStrcat(CheckerContext &C, const CallExpr *CE) const {
}
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,
@@ -1526,8 +1518,9 @@ void CStringChecker::evalStrncat(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalStrlcat(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
- return;
+ // FIXME: strlcat() uses a different rule for bound checking, i.e. 'n' means
+ // a different thing as compared to strncat(). This currently causes
+ // false positives in the alpha string bound checker.
//char *strlcat(char *s1, const char *s2, size_t n);
evalStrcpyCommon(C, CE,
@@ -1881,35 +1874,23 @@ void CStringChecker::evalStrcpyCommon(CheckerContext &C, const CallExpr *CE,
}
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);
}
@@ -2043,9 +2024,6 @@ void CStringChecker::evalStrcmpCommon(CheckerContext &C, const CallExpr *CE,
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();
@@ -2114,7 +2092,7 @@ void CStringChecker::evalStdCopyBackward(CheckerContext &C,
void CStringChecker::evalStdCopyCommon(CheckerContext &C,
const CallExpr *CE) const {
- if (CE->getNumArgs() < 3)
+ if (!CE->getArg(2)->getType()->isPointerType())
return;
ProgramStateRef State = C.getState();
@@ -2141,9 +2119,6 @@ void CStringChecker::evalStdCopyCommon(CheckerContext &C,
}
void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() != 3)
- return;
-
CurrentFunctionDescription = "memory set function";
const Expr *Mem = CE->getArg(0);
@@ -2192,9 +2167,6 @@ void CStringChecker::evalMemset(CheckerContext &C, const CallExpr *CE) const {
}
void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
- if (CE->getNumArgs() != 2)
- return;
-
CurrentFunctionDescription = "memory clearance function";
const Expr *Mem = CE->getArg(0);
@@ -2237,110 +2209,53 @@ void CStringChecker::evalBzero(CheckerContext &C, const CallExpr *CE) const {
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)
+CStringChecker::FnCheck CStringChecker::identifyCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return nullptr;
+
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!FD)
return nullptr;
+ if (Call.isCalled(StdCopy)) {
+ return &CStringChecker::evalStdCopy;
+ } else if (Call.isCalled(StdCopyBackward)) {
+ return &CStringChecker::evalStdCopyBackward;
+ }
+
// 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())
+ // into, say, a C++ overload of any of these functions. We could not check
+ // that for std::copy because they may have arguments of other types.
+ for (auto I : CE->arguments()) {
+ QualType T = I->getType();
+ if (!T->isIntegralOrEnumerationType() && !T->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;
+ const FnCheck *Callback = Callbacks.lookup(Call);
+ if (Callback)
+ return *Callback;
return nullptr;
}
-bool CStringChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
-
- FnCheck evalFunction = identifyCall(CE, C);
+bool CStringChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
+ FnCheck Callback = identifyCall(Call, C);
// If the callee isn't a string function, let another checker handle it.
- if (!evalFunction)
+ if (!Callback)
return false;
// Check and evaluate the call.
- (this->*evalFunction)(C, CE);
+ const auto *CE = cast<CallExpr>(Call.getOriginExpr());
+ (this->*Callback)(C, CE);
// If the evaluate call resulted in no change, chain to the next eval call
// handler.
@@ -2476,18 +2391,26 @@ void CStringChecker::checkDeadSymbols(SymbolReaper &SR,
C.addTransition(state);
}
+void ento::registerCStringModeling(CheckerManager &Mgr) {
+ Mgr.registerChecker<CStringChecker>();
+}
+
+bool ento::shouldRegisterCStringModeling(const LangOptions &LO) {
+ return true;
+}
+
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &mgr) { \
- CStringChecker *checker = mgr.registerChecker<CStringChecker>(); \
+ CStringChecker *checker = mgr.getChecker<CStringChecker>(); \
checker->Filter.Check##name = true; \
checker->Filter.CheckName##name = mgr.getCurrentCheckName(); \
+ } \
+ \
+ bool ento::shouldRegister##name(const LangOptions &LO) { \
+ return true; \
}
REGISTER_CHECKER(CStringNullArg)
REGISTER_CHECKER(CStringOutOfBounds)
REGISTER_CHECKER(CStringBufferOverlap)
REGISTER_CHECKER(CStringNotNullTerm)
-
- void ento::registerCStringCheckerBasic(CheckerManager &Mgr) {
- Mgr.registerChecker<CStringChecker>();
- }
diff --git a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
index bbeb41c5f3cf..b828ac059236 100644
--- a/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CStringSyntaxChecker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -154,8 +153,6 @@ bool WalkAST::containsBadStrncatPattern(const CallExpr *CE) {
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);
@@ -195,13 +192,8 @@ bool WalkAST::containsBadStrlcpyStrlcatPattern(const CallExpr *CE) {
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;
- }
+ if (RemainingBufferLen < ILRawVal)
+ return true;
}
}
}
@@ -290,3 +282,6 @@ void ento::registerCStringSyntaxChecker(CheckerManager &mgr) {
mgr.registerChecker<CStringSyntaxChecker>();
}
+bool ento::shouldRegisterCStringSyntaxChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
index 0b539e1188eb..01f5b9c889e3 100644
--- a/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CXXSelfAssignmentChecker.cpp
@@ -1,9 +1,8 @@
//=== CXXSelfAssignmentChecker.cpp -----------------------------*- C++ -*--===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -51,12 +50,32 @@ void CXXSelfAssignmentChecker::checkBeginFunction(CheckerContext &C) const {
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);
+ const NoteTag *SelfAssignTag =
+ C.getNoteTag([MD](BugReport &BR) -> std::string {
+ SmallString<256> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+ Out << "Assuming " << MD->getParamDecl(0)->getName() << " == *this";
+ return Out.str();
+ });
+ C.addTransition(SelfAssignState, SelfAssignTag);
+
ProgramStateRef NonSelfAssignState = State->bindLoc(Param, ParamVal, LCtx);
- C.addTransition(NonSelfAssignState);
+ const NoteTag *NonSelfAssignTag =
+ C.getNoteTag([MD](BugReport &BR) -> std::string {
+ SmallString<256> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+ Out << "Assuming " << MD->getParamDecl(0)->getName() << " != *this";
+ return Out.str();
+ });
+ C.addTransition(NonSelfAssignState, NonSelfAssignTag);
}
void ento::registerCXXSelfAssignmentChecker(CheckerManager &Mgr) {
Mgr.registerChecker<CXXSelfAssignmentChecker>();
}
+
+bool ento::shouldRegisterCXXSelfAssignmentChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
index ef30dc74c39d..5a7eba0760fe 100644
--- a/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CallAndMessageChecker.cpp
@@ -1,9 +1,8 @@
//===--- CallAndMessageChecker.cpp ------------------------------*- C++ -*--==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -29,14 +28,6 @@ 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>,
@@ -57,7 +48,8 @@ class CallAndMessageChecker
mutable std::unique_ptr<BugType> BT_call_few_args;
public:
- ChecksFilter Filter;
+ DefaultBool Check_CallAndMessageUnInitRefArg;
+ CheckName CheckName_CallAndMessageUnInitRefArg;
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
void checkPreStmt(const CXXDeleteExpr *DE, CheckerContext &C) const;
@@ -152,7 +144,7 @@ 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)
+ if (!Check_CallAndMessageUnInitRefArg)
return false;
// No parameter declaration available, i.e. variadic function argument.
@@ -608,13 +600,20 @@ void CallAndMessageChecker::HandleNilReceiver(CheckerContext &C,
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(); \
- }
+void ento::registerCallAndMessageChecker(CheckerManager &mgr) {
+ mgr.registerChecker<CallAndMessageChecker>();
+}
+
+bool ento::shouldRegisterCallAndMessageChecker(const LangOptions &LO) {
+ return true;
+}
-REGISTER_CHECKER(CallAndMessageUnInitRefArg)
-REGISTER_CHECKER(CallAndMessageChecker)
+void ento::registerCallAndMessageUnInitRefArg(CheckerManager &mgr) {
+ CallAndMessageChecker *Checker = mgr.getChecker<CallAndMessageChecker>();
+ Checker->Check_CallAndMessageUnInitRefArg = true;
+ Checker->CheckName_CallAndMessageUnInitRefArg = mgr.getCurrentCheckName();
+}
+
+bool ento::shouldRegisterCallAndMessageUnInitRefArg(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
index 5deb62d32311..05ece961467f 100644
--- a/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CastSizeChecker.cpp
@@ -1,9 +1,8 @@
//=== CastSizeChecker.cpp ---------------------------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -140,10 +139,13 @@ void CastSizeChecker::checkPreStmt(const CastExpr *CE,CheckerContext &C) const {
}
void ento::registerCastSizeChecker(CheckerManager &mgr) {
+ mgr.registerChecker<CastSizeChecker>();
+}
+
+bool ento::shouldRegisterCastSizeChecker(const LangOptions &LO) {
// 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>();
+ return !LO.CPlusPlus;
}
diff --git a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
index 2bd3879627cb..93665596be29 100644
--- a/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CastToStructChecker.cpp
@@ -1,9 +1,8 @@
//=== CastToStructChecker.cpp ----------------------------------*- C++ -*--===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -120,3 +119,7 @@ public:
void ento::registerCastToStructChecker(CheckerManager &mgr) {
mgr.registerChecker<CastToStructChecker>();
}
+
+bool ento::shouldRegisterCastToStructChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
new file mode 100644
index 000000000000..ff5d12c27c69
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -0,0 +1,190 @@
+//===- CastValueChecker - Model implementation of custom RTTIs --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines CastValueChecker which models casts of custom RTTIs.
+//
+//===----------------------------------------------------------------------===//
+
+#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"
+#include "llvm/ADT/Optional.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class CastValueChecker : public Checker<eval::Call> {
+ using CastCheck =
+ std::function<void(const CastValueChecker *, const CallExpr *,
+ DefinedOrUnknownSVal, CheckerContext &)>;
+
+public:
+ // We have three cases to evaluate a cast:
+ // 1) The parameter is non-null, the return value is non-null
+ // 2) The parameter is non-null, the return value is null
+ // 3) The parameter is null, the return value is null
+ //
+ // cast: 1; dyn_cast: 1, 2; cast_or_null: 1, 3; dyn_cast_or_null: 1, 2, 3.
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+
+private:
+ // These are known in the LLVM project.
+ const CallDescriptionMap<CastCheck> CDM = {
+ {{{"llvm", "cast"}, 1}, &CastValueChecker::evalCast},
+ {{{"llvm", "dyn_cast"}, 1}, &CastValueChecker::evalDynCast},
+ {{{"llvm", "cast_or_null"}, 1}, &CastValueChecker::evalCastOrNull},
+ {{{"llvm", "dyn_cast_or_null"}, 1},
+ &CastValueChecker::evalDynCastOrNull}};
+
+ void evalCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+ CheckerContext &C) const;
+ void evalDynCast(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+ CheckerContext &C) const;
+ void evalCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+ CheckerContext &C) const;
+ void evalDynCastOrNull(const CallExpr *CE, DefinedOrUnknownSVal ParamDV,
+ CheckerContext &C) const;
+};
+} // namespace
+
+static std::string getCastName(const Expr *Cast) {
+ return Cast->getType()->getPointeeCXXRecordDecl()->getNameAsString();
+}
+
+static void evalNonNullParamNonNullReturn(const CallExpr *CE,
+ DefinedOrUnknownSVal ParamDV,
+ CheckerContext &C) {
+ ProgramStateRef State = C.getState()->assume(ParamDV, true);
+ if (!State)
+ return;
+
+ State = State->BindExpr(CE, C.getLocationContext(), ParamDV, false);
+
+ std::string CastFromName = getCastName(CE->getArg(0));
+ std::string CastToName = getCastName(CE);
+
+ const NoteTag *CastTag = C.getNoteTag(
+ [CastFromName, CastToName](BugReport &) -> std::string {
+ SmallString<128> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+
+ Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
+ << CastToName << "' succeeds";
+ return Out.str();
+ },
+ /*IsPrunable=*/true);
+
+ C.addTransition(State, CastTag);
+}
+
+static void evalNonNullParamNullReturn(const CallExpr *CE,
+ DefinedOrUnknownSVal ParamDV,
+ CheckerContext &C) {
+ ProgramStateRef State = C.getState()->assume(ParamDV, true);
+ if (!State)
+ return;
+
+ State = State->BindExpr(CE, C.getLocationContext(),
+ C.getSValBuilder().makeNull(), false);
+
+ std::string CastFromName = getCastName(CE->getArg(0));
+ std::string CastToName = getCastName(CE);
+
+ const NoteTag *CastTag = C.getNoteTag(
+ [CastFromName, CastToName](BugReport &) -> std::string {
+ SmallString<128> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+
+ Out << "Assuming dynamic cast from '" << CastFromName << "' to '"
+ << CastToName << "' fails";
+ return Out.str();
+ },
+ /*IsPrunable=*/true);
+
+ C.addTransition(State, CastTag);
+}
+
+static void evalNullParamNullReturn(const CallExpr *CE,
+ DefinedOrUnknownSVal ParamDV,
+ CheckerContext &C) {
+ ProgramStateRef State = C.getState()->assume(ParamDV, false);
+ if (!State)
+ return;
+
+ State = State->BindExpr(CE, C.getLocationContext(),
+ C.getSValBuilder().makeNull(), false);
+
+ const NoteTag *CastTag =
+ C.getNoteTag("Assuming null pointer is passed into cast",
+ /*IsPrunable=*/true);
+
+ C.addTransition(State, CastTag);
+}
+
+void CastValueChecker::evalCast(const CallExpr *CE,
+ DefinedOrUnknownSVal ParamDV,
+ CheckerContext &C) const {
+ evalNonNullParamNonNullReturn(CE, ParamDV, C);
+}
+
+void CastValueChecker::evalDynCast(const CallExpr *CE,
+ DefinedOrUnknownSVal ParamDV,
+ CheckerContext &C) const {
+ evalNonNullParamNonNullReturn(CE, ParamDV, C);
+ evalNonNullParamNullReturn(CE, ParamDV, C);
+}
+
+void CastValueChecker::evalCastOrNull(const CallExpr *CE,
+ DefinedOrUnknownSVal ParamDV,
+ CheckerContext &C) const {
+ evalNonNullParamNonNullReturn(CE, ParamDV, C);
+ evalNullParamNullReturn(CE, ParamDV, C);
+}
+
+void CastValueChecker::evalDynCastOrNull(const CallExpr *CE,
+ DefinedOrUnknownSVal ParamDV,
+ CheckerContext &C) const {
+ evalNonNullParamNonNullReturn(CE, ParamDV, C);
+ evalNonNullParamNullReturn(CE, ParamDV, C);
+ evalNullParamNullReturn(CE, ParamDV, C);
+}
+
+bool CastValueChecker::evalCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const CastCheck *Check = CDM.lookup(Call);
+ if (!Check)
+ return false;
+
+ const auto *CE = cast<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return false;
+
+ // If we cannot obtain both of the classes we cannot be sure how to model it.
+ if (!CE->getType()->getPointeeCXXRecordDecl() ||
+ !CE->getArg(0)->getType()->getPointeeCXXRecordDecl())
+ return false;
+
+ SVal ParamV = Call.getArgSVal(0);
+ auto ParamDV = ParamV.getAs<DefinedOrUnknownSVal>();
+ if (!ParamDV)
+ return false;
+
+ (*Check)(this, CE, *ParamDV, C);
+ return true;
+}
+
+void ento::registerCastValueChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<CastValueChecker>();
+}
+
+bool ento::shouldRegisterCastValueChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
index 00a912f27a8d..a7ca814c8f96 100644
--- a/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckObjCDealloc.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -1087,10 +1086,10 @@ bool ObjCDeallocChecker::isNibLoadedIvarWithoutRetain(
}
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>();
}
+
+bool ento::shouldRegisterObjCDeallocChecker(const LangOptions &LO) {
+ // These checker only makes sense under MRR.
+ return LO.getGC() != LangOptions::GCOnly && !LO.ObjCAutoRefCount;
+}
diff --git a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
index fe6715595e6f..a020d33bfd95 100644
--- a/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
@@ -1,9 +1,8 @@
-//=- CheckObjCInstMethodRetTy.cpp - Check ObjC method signatures -*- C++ -*-==//
+//===-- CheckObjCInstMethSignature.cpp - Check ObjC method signatures -----===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -138,3 +137,7 @@ public:
void ento::registerObjCMethSigsChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCMethSigsChecker>();
}
+
+bool ento::shouldRegisterObjCMethSigsChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index 163ca9d8556f..3f1c213a5647 100644
--- a/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -45,6 +44,7 @@ struct ChecksFilter {
DefaultBool check_mktemp;
DefaultBool check_mkstemp;
DefaultBool check_strcpy;
+ DefaultBool check_DeprecatedOrUnsafeBufferHandling;
DefaultBool check_rand;
DefaultBool check_vfork;
DefaultBool check_FloatLoopCounter;
@@ -58,6 +58,7 @@ struct ChecksFilter {
CheckName checkName_mktemp;
CheckName checkName_mkstemp;
CheckName checkName_strcpy;
+ CheckName checkName_DeprecatedOrUnsafeBufferHandling;
CheckName checkName_rand;
CheckName checkName_vfork;
CheckName checkName_FloatLoopCounter;
@@ -104,6 +105,8 @@ public:
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 checkDeprecatedOrUnsafeBufferHandling(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);
@@ -149,6 +152,14 @@ void WalkAST::VisitCallExpr(CallExpr *CE) {
.Case("mkstemps", &WalkAST::checkCall_mkstemp)
.Cases("strcpy", "__strcpy_chk", &WalkAST::checkCall_strcpy)
.Cases("strcat", "__strcat_chk", &WalkAST::checkCall_strcat)
+ .Cases("sprintf", "vsprintf", "scanf", "wscanf", "fscanf", "fwscanf",
+ "vscanf", "vwscanf", "vfscanf", "vfwscanf",
+ &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
+ .Cases("sscanf", "swscanf", "vsscanf", "vswscanf", "swprintf",
+ "snprintf", "vswprintf", "vsnprintf", "memcpy", "memmove",
+ &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
+ .Cases("strncpy", "strncat", "memset",
+ &WalkAST::checkDeprecatedOrUnsafeBufferHandling)
.Case("drand48", &WalkAST::checkCall_rand)
.Case("erand48", &WalkAST::checkCall_rand)
.Case("jrand48", &WalkAST::checkCall_rand)
@@ -553,7 +564,6 @@ void WalkAST::checkCall_mktemp(const CallExpr *CE, const FunctionDecl *FD) {
CELoc, CE->getCallee()->getSourceRange());
}
-
//===----------------------------------------------------------------------===//
// Check: Use of 'mkstemp', 'mktemp', 'mkdtemp' should contain at least 6 X's.
//===----------------------------------------------------------------------===//
@@ -642,6 +652,7 @@ void WalkAST::checkCall_mkstemp(const CallExpr *CE, const FunctionDecl *FD) {
// 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;
@@ -680,6 +691,7 @@ void WalkAST::checkCall_strcpy(const CallExpr *CE, const FunctionDecl *FD) {
// 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;
@@ -702,8 +714,92 @@ void WalkAST::checkCall_strcat(const CallExpr *CE, const FunctionDecl *FD) {
}
//===----------------------------------------------------------------------===//
+// Check: Any use of 'sprintf', 'vsprintf', 'scanf', 'wscanf', 'fscanf',
+// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
+// 'swscanf', 'vsscanf', 'vswscanf', 'swprintf', 'snprintf', 'vswprintf',
+// 'vsnprintf', 'memcpy', 'memmove', 'strncpy', 'strncat', 'memset'
+// is deprecated since C11.
+//
+// Use of 'sprintf', 'vsprintf', 'scanf', 'wscanf','fscanf',
+// 'fwscanf', 'vscanf', 'vwscanf', 'vfscanf', 'vfwscanf', 'sscanf',
+// 'swscanf', 'vsscanf', 'vswscanf' without buffer limitations
+// is insecure.
+//
+// CWE-119: Improper Restriction of Operations within
+// the Bounds of a Memory Buffer
+//===----------------------------------------------------------------------===//
+
+void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
+ const FunctionDecl *FD) {
+ if (!filter.check_DeprecatedOrUnsafeBufferHandling)
+ return;
+
+ if (!BR.getContext().getLangOpts().C11)
+ return;
+
+ // Issue a warning. ArgIndex == -1: Deprecated but not unsafe (has size
+ // restrictions).
+ enum { DEPR_ONLY = -1, UNKNOWN_CALL = -2 };
+
+ StringRef Name = FD->getIdentifier()->getName();
+ if (Name.startswith("__builtin_"))
+ Name = Name.substr(10);
+
+ int ArgIndex =
+ llvm::StringSwitch<int>(Name)
+ .Cases("scanf", "wscanf", "vscanf", "vwscanf", 0)
+ .Cases("sprintf", "vsprintf", "fscanf", "fwscanf", "vfscanf",
+ "vfwscanf", "sscanf", "swscanf", "vsscanf", "vswscanf", 1)
+ .Cases("swprintf", "snprintf", "vswprintf", "vsnprintf", "memcpy",
+ "memmove", "memset", "strncpy", "strncat", DEPR_ONLY)
+ .Default(UNKNOWN_CALL);
+
+ assert(ArgIndex != UNKNOWN_CALL && "Unsupported function");
+ bool BoundsProvided = ArgIndex == DEPR_ONLY;
+
+ if (!BoundsProvided) {
+ // Currently we only handle (not wide) string literals. It is possible to do
+ // better, either by looking at references to const variables, or by doing
+ // real flow analysis.
+ auto FormatString =
+ dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());
+ if (FormatString &&
+ FormatString->getString().find("%s") == StringRef::npos &&
+ FormatString->getString().find("%[") == StringRef::npos)
+ BoundsProvided = true;
+ }
+
+ SmallString<128> Buf1;
+ SmallString<512> Buf2;
+ llvm::raw_svector_ostream Out1(Buf1);
+ llvm::raw_svector_ostream Out2(Buf2);
+
+ Out1 << "Potential insecure memory buffer bounds restriction in call '"
+ << Name << "'";
+ Out2 << "Call to function '" << Name
+ << "' is insecure as it does not provide ";
+
+ if (!BoundsProvided) {
+ Out2 << "bounding of the memory buffer or ";
+ }
+
+ Out2 << "security checks introduced "
+ "in the C11 standard. Replace with analogous functions that "
+ "support length arguments or provides boundary checks such as '"
+ << Name << "_s' in case of C11";
+
+ PathDiagnosticLocation CELoc =
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), AC);
+ BR.EmitBasicReport(AC->getDecl(),
+ filter.checkName_DeprecatedOrUnsafeBufferHandling,
+ Out1.str(), "Security", Out2.str(), 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)
@@ -906,12 +1002,23 @@ public:
};
}
+void ento::registerSecuritySyntaxChecker(CheckerManager &mgr) {
+ mgr.registerChecker<SecuritySyntaxChecker>();
+}
+
+bool ento::shouldRegisterSecuritySyntaxChecker(const LangOptions &LO) {
+ return true;
+}
+
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &mgr) { \
- SecuritySyntaxChecker *checker = \
- mgr.registerChecker<SecuritySyntaxChecker>(); \
+ SecuritySyntaxChecker *checker = mgr.getChecker<SecuritySyntaxChecker>(); \
checker->filter.check_##name = true; \
checker->filter.checkName_##name = mgr.getCurrentCheckName(); \
+ } \
+ \
+ bool ento::shouldRegister##name(const LangOptions &LO) { \
+ return true; \
}
REGISTER_CHECKER(bcmp)
@@ -926,5 +1033,4 @@ REGISTER_CHECKER(rand)
REGISTER_CHECKER(vfork)
REGISTER_CHECKER(FloatLoopCounter)
REGISTER_CHECKER(UncheckedReturn)
-
-
+REGISTER_CHECKER(DeprecatedOrUnsafeBufferHandling)
diff --git a/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
index 7688b713b06b..ec401cfa8985 100644
--- a/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckSizeofPointer.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -91,3 +90,7 @@ public:
void ento::registerSizeofPointerChecker(CheckerManager &mgr) {
mgr.registerChecker<SizeofPointerChecker>();
}
+
+bool ento::shouldRegisterSizeofPointerChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
index 44fac0278bdd..3e5e2b913914 100644
--- a/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
+++ b/lib/StaticAnalyzer/Checkers/CheckerDocumentation.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -216,7 +215,7 @@ public:
/// Evaluates function call.
///
- /// The analysis core threats all function calls in the same way. However, some
+ /// The analysis core treats 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.
diff --git a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
index 673608db1a1d..9fffedfccd87 100644
--- a/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
@@ -1,9 +1,8 @@
-//===- Chrootchecker.cpp -------- Basic security checks ---------*- C++ -*-===//
+//===-- ChrootChecker.cpp - chroot usage checks ---------------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -15,6 +14,7 @@
#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"
@@ -38,53 +38,44 @@ bool isRootChanged(intptr_t k) { return k == ROOT_CHANGED; }
// 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;
+class ChrootChecker : public Checker<eval::Call, check::PreCall> {
// This bug refers to possibly break out of a chroot() jail.
mutable std::unique_ptr<BuiltinBug> BT_BreakJail;
+ const CallDescription Chroot{"chroot", 1}, Chdir{"chdir", 1};
+
public:
- ChrootChecker() : II_chroot(nullptr), II_chdir(nullptr) {}
+ ChrootChecker() {}
static void *getTag() {
static int x;
return &x;
}
- bool evalCall(const CallExpr *CE, CheckerContext &C) const;
- void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
private:
- void Chroot(CheckerContext &C, const CallExpr *CE) const;
- void Chdir(CheckerContext &C, const CallExpr *CE) const;
+ void evalChroot(const CallEvent &Call, CheckerContext &C) const;
+ void evalChdir(const CallEvent &Call, CheckerContext &C) 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);
+bool ChrootChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
+ if (Call.isCalled(Chroot)) {
+ evalChroot(Call, C);
return true;
}
- if (FD->getIdentifier() == II_chdir) {
- Chdir(C, CE);
+ if (Call.isCalled(Chdir)) {
+ evalChdir(Call, C);
return true;
}
return false;
}
-void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const {
+void ChrootChecker::evalChroot(const CallEvent &Call, CheckerContext &C) const {
ProgramStateRef state = C.getState();
ProgramStateManager &Mgr = state->getStateManager();
@@ -94,7 +85,7 @@ void ChrootChecker::Chroot(CheckerContext &C, const CallExpr *CE) const {
C.addTransition(state);
}
-void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const {
+void ChrootChecker::evalChdir(const CallEvent &Call, CheckerContext &C) const {
ProgramStateRef state = C.getState();
ProgramStateManager &Mgr = state->getStateManager();
@@ -104,7 +95,7 @@ void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const {
return;
// After chdir("/"), enter the jail, set the enum value JAIL_ENTERED.
- const Expr *ArgExpr = CE->getArg(0);
+ const Expr *ArgExpr = Call.getArgExpr(0);
SVal ArgVal = C.getSVal(ArgExpr);
if (const MemRegion *R = ArgVal.getAsRegion()) {
@@ -121,19 +112,10 @@ void ChrootChecker::Chdir(CheckerContext &C, const CallExpr *CE) const {
}
// 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");
-
+void ChrootChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
// Ignore chroot and chdir.
- if (FD->getIdentifier() == II_chroot || FD->getIdentifier() == II_chdir)
+ if (Call.isCalled(Chroot) || Call.isCalled(Chdir))
return;
// If jail state is ROOT_CHANGED, generate BugReport.
@@ -153,3 +135,7 @@ void ChrootChecker::checkPreStmt(const CallExpr *CE, CheckerContext &C) const {
void ento::registerChrootChecker(CheckerManager &mgr) {
mgr.registerChecker<ChrootChecker>();
}
+
+bool ento::shouldRegisterChrootChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/CloneChecker.cpp b/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
index 89354b866004..4fc225056d4c 100644
--- a/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/CloneChecker.cpp
@@ -1,9 +1,8 @@
//===--- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
@@ -28,6 +27,13 @@ using namespace ento;
namespace {
class CloneChecker
: public Checker<check::ASTCodeBody, check::EndOfTranslationUnit> {
+public:
+ // Checker options.
+ int MinComplexity;
+ bool ReportNormalClones;
+ StringRef IgnoredFilesPattern;
+
+private:
mutable CloneDetector Detector;
mutable std::unique_ptr<BugType> BT_Exact, BT_Suspicious;
@@ -63,19 +69,6 @@ void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
// 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.
@@ -87,8 +80,7 @@ void CloneChecker::checkEndOfTranslationUnit(const TranslationUnitDecl *TU,
MinComplexityConstraint(MinComplexity),
RecursiveCloneTypeIIVerifyConstraint(), OnlyLargestCloneConstraint());
- if (ReportSuspiciousClones)
- reportSuspiciousClones(BR, Mgr, AllCloneGroups);
+ reportSuspiciousClones(BR, Mgr, AllCloneGroups);
// We are done for this translation unit unless we also need to report normal
// clones.
@@ -200,5 +192,22 @@ void CloneChecker::reportSuspiciousClones(
//===----------------------------------------------------------------------===//
void ento::registerCloneChecker(CheckerManager &Mgr) {
- Mgr.registerChecker<CloneChecker>();
+ auto *Checker = Mgr.registerChecker<CloneChecker>();
+
+ Checker->MinComplexity = Mgr.getAnalyzerOptions().getCheckerIntegerOption(
+ Checker, "MinimumCloneComplexity");
+
+ if (Checker->MinComplexity < 0)
+ Mgr.reportInvalidCheckerOptionValue(
+ Checker, "MinimumCloneComplexity", "a non-negative value");
+
+ Checker->ReportNormalClones = Mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ Checker, "ReportNormalClones");
+
+ Checker->IgnoredFilesPattern = Mgr.getAnalyzerOptions()
+ .getCheckerStringOption(Checker, "IgnoredFilesPattern");
+}
+
+bool ento::shouldRegisterCloneChecker(const LangOptions &LO) {
+ return true;
}
diff --git a/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
index a5c67c2a5b45..5058d101b8e5 100644
--- a/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
@@ -1,9 +1,8 @@
//=== ConversionChecker.cpp -------------------------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -196,3 +195,7 @@ bool ConversionChecker::isLossOfSign(const ImplicitCastExpr *Cast,
void ento::registerConversionChecker(CheckerManager &mgr) {
mgr.registerChecker<ConversionChecker>();
}
+
+bool ento::shouldRegisterConversionChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
index 4e0f6d3bedfd..d5baa2bcba6f 100644
--- a/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DeadStoresChecker.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -161,6 +160,26 @@ public:
return InEH->count(D);
}
+ bool isSuppressed(SourceRange R) {
+ SourceManager &SMgr = Ctx.getSourceManager();
+ SourceLocation Loc = R.getBegin();
+ if (!Loc.isValid())
+ return false;
+
+ FileID FID = SMgr.getFileID(Loc);
+ bool Invalid = false;
+ StringRef Data = SMgr.getBufferData(FID, &Invalid);
+ if (Invalid)
+ return false;
+
+ // Files autogenerated by DriverKit IIG contain some dead stores that
+ // we don't want to report.
+ if (Data.startswith("/* iig"))
+ return true;
+
+ return false;
+ }
+
void Report(const VarDecl *V, DeadStoreKind dsk,
PathDiagnosticLocation L, SourceRange R) {
if (Escaped.count(V))
@@ -176,6 +195,9 @@ public:
if (!reachableCode->isReachable(currentBlock))
return;
+ if (isSuppressed(R))
+ return;
+
SmallString<64> buf;
llvm::raw_svector_ostream os(buf);
const char *BugType = nullptr;
@@ -479,3 +501,7 @@ public:
void ento::registerDeadStoresChecker(CheckerManager &mgr) {
mgr.registerChecker<DeadStoresChecker>();
}
+
+bool ento::shouldRegisterDeadStoresChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
index 90b1111aff0f..bb9e8110b647 100644
--- a/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
+++ b/lib/StaticAnalyzer/Checkers/DebugCheckers.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -36,9 +35,9 @@ public:
void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
BugReporter &BR) const {
if (AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D)) {
- DominatorTree dom;
- dom.buildDominatorTree(*AC);
- dom.dump();
+ CFGDomTree Dom;
+ Dom.buildDominatorTree(AC->getCFG());
+ Dom.dump();
}
}
};
@@ -48,6 +47,61 @@ void ento::registerDominatorsTreeDumper(CheckerManager &mgr) {
mgr.registerChecker<DominatorsTreeDumper>();
}
+bool ento::shouldRegisterDominatorsTreeDumper(const LangOptions &LO) {
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// PostDominatorsTreeDumper
+//===----------------------------------------------------------------------===//
+
+namespace {
+class PostDominatorsTreeDumper : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ if (AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D)) {
+ CFGPostDomTree Dom;
+ Dom.buildDominatorTree(AC->getCFG());
+ Dom.dump();
+ }
+ }
+};
+}
+
+void ento::registerPostDominatorsTreeDumper(CheckerManager &mgr) {
+ mgr.registerChecker<PostDominatorsTreeDumper>();
+}
+
+bool ento::shouldRegisterPostDominatorsTreeDumper(const LangOptions &LO) {
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// ControlDependencyTreeDumper
+//===----------------------------------------------------------------------===//
+
+namespace {
+class ControlDependencyTreeDumper : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D, AnalysisManager& mgr,
+ BugReporter &BR) const {
+ if (AnalysisDeclContext *AC = mgr.getAnalysisDeclContext(D)) {
+ ControlDependencyCalculator Dom(AC->getCFG());
+ Dom.dump();
+ }
+ }
+};
+}
+
+void ento::registerControlDependencyTreeDumper(CheckerManager &mgr) {
+ mgr.registerChecker<ControlDependencyTreeDumper>();
+}
+
+bool ento::shouldRegisterControlDependencyTreeDumper(const LangOptions &LO) {
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// LiveVariablesDumper
//===----------------------------------------------------------------------===//
@@ -68,6 +122,10 @@ void ento::registerLiveVariablesDumper(CheckerManager &mgr) {
mgr.registerChecker<LiveVariablesDumper>();
}
+bool ento::shouldRegisterLiveVariablesDumper(const LangOptions &LO) {
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// LiveStatementsDumper
//===----------------------------------------------------------------------===//
@@ -87,6 +145,10 @@ void ento::registerLiveStatementsDumper(CheckerManager &mgr) {
mgr.registerChecker<LiveStatementsDumper>();
}
+bool ento::shouldRegisterLiveStatementsDumper(const LangOptions &LO) {
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// CFGViewer
//===----------------------------------------------------------------------===//
@@ -107,6 +169,10 @@ void ento::registerCFGViewer(CheckerManager &mgr) {
mgr.registerChecker<CFGViewer>();
}
+bool ento::shouldRegisterCFGViewer(const LangOptions &LO) {
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// CFGDumper
//===----------------------------------------------------------------------===//
@@ -133,6 +199,10 @@ void ento::registerCFGDumper(CheckerManager &mgr) {
mgr.registerChecker<CFGDumper>();
}
+bool ento::shouldRegisterCFGDumper(const LangOptions &LO) {
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// CallGraphViewer
//===----------------------------------------------------------------------===//
@@ -153,6 +223,10 @@ void ento::registerCallGraphViewer(CheckerManager &mgr) {
mgr.registerChecker<CallGraphViewer>();
}
+bool ento::shouldRegisterCallGraphViewer(const LangOptions &LO) {
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// CallGraphDumper
//===----------------------------------------------------------------------===//
@@ -173,6 +247,9 @@ void ento::registerCallGraphDumper(CheckerManager &mgr) {
mgr.registerChecker<CallGraphDumper>();
}
+bool ento::shouldRegisterCallGraphDumper(const LangOptions &LO) {
+ return true;
+}
//===----------------------------------------------------------------------===//
// ConfigDumper
@@ -214,6 +291,10 @@ void ento::registerConfigDumper(CheckerManager &mgr) {
mgr.registerChecker<ConfigDumper>();
}
+bool ento::shouldRegisterConfigDumper(const LangOptions &LO) {
+ return true;
+}
+
//===----------------------------------------------------------------------===//
// ExplodedGraph Viewer
//===----------------------------------------------------------------------===//
@@ -233,3 +314,37 @@ void ento::registerExplodedGraphViewer(CheckerManager &mgr) {
mgr.registerChecker<ExplodedGraphViewer>();
}
+bool ento::shouldRegisterExplodedGraphViewer(const LangOptions &LO) {
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// Emits a report for every Stmt that the analyzer visits.
+//===----------------------------------------------------------------------===//
+
+namespace {
+
+class ReportStmts : public Checker<check::PreStmt<Stmt>> {
+ BuiltinBug BT_stmtLoc{this, "Statement"};
+
+public:
+ void checkPreStmt(const Stmt *S, CheckerContext &C) const {
+ ExplodedNode *Node = C.generateNonFatalErrorNode();
+ if (!Node)
+ return;
+
+ auto Report = llvm::make_unique<BugReport>(BT_stmtLoc, "Statement", Node);
+
+ C.emitReport(std::move(Report));
+ }
+};
+
+} // end of anonymous namespace
+
+void ento::registerReportStmts(CheckerManager &mgr) {
+ mgr.registerChecker<ReportStmts>();
+}
+
+bool ento::shouldRegisterReportStmts(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp b/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
index adf5a8e77a74..8bf77c109f8a 100644
--- a/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DeleteWithNonVirtualDtorChecker.cpp
@@ -1,9 +1,8 @@
//===-- DeleteWithNonVirtualDtorChecker.cpp -----------------------*- C++ -*--//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -148,3 +147,8 @@ DeleteWithNonVirtualDtorChecker::DeleteBugVisitor::VisitNode(
void ento::registerDeleteWithNonVirtualDtorChecker(CheckerManager &mgr) {
mgr.registerChecker<DeleteWithNonVirtualDtorChecker>();
}
+
+bool ento::shouldRegisterDeleteWithNonVirtualDtorChecker(
+ const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
index d01a889d256a..2c264833f2a9 100644
--- a/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DereferenceChecker.cpp
@@ -1,9 +1,8 @@
-//== NullDerefChecker.cpp - Null dereference checker ------------*- C++ -*--==//
+//===-- DereferenceChecker.cpp - Null dereference checker -----------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -304,3 +303,7 @@ void DereferenceChecker::checkBind(SVal L, SVal V, const Stmt *S,
void ento::registerDereferenceChecker(CheckerManager &mgr) {
mgr.registerChecker<DereferenceChecker>();
}
+
+bool ento::shouldRegisterDereferenceChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
index 2a559422df34..0058f3d3881f 100644
--- a/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
+++ b/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
@@ -1,9 +1,8 @@
//=- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -206,12 +205,6 @@ void DirectIvarAssignment::MethodCrawler::VisitBinaryOperator(
}
}
-// 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) {
@@ -221,7 +214,22 @@ static bool AttrFilter(const ObjCMethodDecl *M) {
return true;
}
+// 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>();
+}
+
+bool ento::shouldRegisterDirectIvarAssignment(const LangOptions &LO) {
+ return true;
+}
+
void ento::registerDirectIvarAssignmentForAnnotatedFunctions(
CheckerManager &mgr) {
- mgr.registerChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter;
+ mgr.getChecker<DirectIvarAssignment>()->ShouldSkipMethod = &AttrFilter;
+}
+
+bool ento::shouldRegisterDirectIvarAssignmentForAnnotatedFunctions(
+ const LangOptions &LO) {
+ return true;
}
diff --git a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
index a220a0513e28..33e8fcd8af7b 100644
--- a/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DivZeroChecker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -12,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "Taint.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
@@ -20,6 +20,7 @@
using namespace clang;
using namespace ento;
+using namespace taint;
namespace {
class DivZeroChecker : public Checker< check::PreStmt<BinaryOperator> > {
@@ -84,10 +85,10 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
return;
}
- bool TaintedD = C.getState()->isTainted(*DV);
+ bool TaintedD = isTainted(C.getState(), *DV);
if ((stateNotZero && stateZero && TaintedD)) {
reportBug("Division by a tainted value, possibly zero", stateZero, C,
- llvm::make_unique<TaintBugVisitor>(*DV));
+ llvm::make_unique<taint::TaintBugVisitor>(*DV));
return;
}
@@ -99,3 +100,7 @@ void DivZeroChecker::checkPreStmt(const BinaryOperator *B,
void ento::registerDivZeroChecker(CheckerManager &mgr) {
mgr.registerChecker<DivZeroChecker>();
}
+
+bool ento::shouldRegisterDivZeroChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
index 803d7ae22a71..4d979dc9f240 100644
--- a/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypeChecker.cpp
@@ -1,9 +1,8 @@
//== DynamicTypeChecker.cpp ------------------------------------ -*- C++ -*--=//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -206,3 +205,7 @@ void DynamicTypeChecker::checkPostStmt(const ImplicitCastExpr *CE,
void ento::registerDynamicTypeChecker(CheckerManager &mgr) {
mgr.registerChecker<DynamicTypeChecker>();
}
+
+bool ento::shouldRegisterDynamicTypeChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index 31d4eebe8968..3cfe4dc82a10 100644
--- a/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -1,9 +1,8 @@
//===- DynamicTypePropagation.cpp ------------------------------*- C++ -*--===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -115,8 +114,8 @@ public:
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();
+ DynamicTypeMapTy TypeMap = State->get<DynamicTypeMap>();
+ for (DynamicTypeMapTy::iterator I = TypeMap.begin(), E = TypeMap.end();
I != E; ++I) {
if (!SR.isLiveRegion(I->first)) {
State = State->remove<DynamicTypeMap>(I->first);
@@ -145,7 +144,7 @@ static void recordFixedType(const MemRegion *Region, const CXXMethodDecl *MD,
QualType Ty = Ctx.getPointerType(Ctx.getRecordType(MD->getParent()));
ProgramStateRef State = C.getState();
- State = setDynamicTypeInfo(State, Region, Ty, /*CanBeSubclass=*/false);
+ State = setDynamicTypeInfo(State, Region, Ty, /*CanBeSubClassed=*/false);
C.addTransition(State);
}
@@ -308,7 +307,7 @@ void DynamicTypePropagation::checkPostStmt(const CXXNewExpr *NewE,
return;
C.addTransition(setDynamicTypeInfo(C.getState(), MR, NewE->getType(),
- /*CanBeSubclass=*/false));
+ /*CanBeSubClassed=*/false));
}
const ObjCObjectType *
@@ -888,7 +887,7 @@ void DynamicTypePropagation::checkPostObjCMessage(const ObjCMethodCall &M,
// 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);
+ /*CanBeSubClassed=*/true);
Pred = C.addTransition(State);
}
@@ -988,11 +987,18 @@ DynamicTypePropagation::GenericsBugVisitor::VisitNode(const ExplodedNode *N,
/// Register checkers.
void ento::registerObjCGenericsChecker(CheckerManager &mgr) {
- DynamicTypePropagation *checker =
- mgr.registerChecker<DynamicTypePropagation>();
+ DynamicTypePropagation *checker = mgr.getChecker<DynamicTypePropagation>();
checker->CheckGenerics = true;
}
+bool ento::shouldRegisterObjCGenericsChecker(const LangOptions &LO) {
+ return true;
+}
+
void ento::registerDynamicTypePropagation(CheckerManager &mgr) {
mgr.registerChecker<DynamicTypePropagation>();
}
+
+bool ento::shouldRegisterDynamicTypePropagation(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
index 4e51cffaa744..736d80ef9ec7 100644
--- a/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
@@ -1,9 +1,8 @@
//===- EnumCastOutOfRangeChecker.cpp ---------------------------*- C++ -*--===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -126,3 +125,7 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
void ento::registerEnumCastOutOfRangeChecker(CheckerManager &mgr) {
mgr.registerChecker<EnumCastOutOfRangeChecker>();
}
+
+bool ento::shouldRegisterEnumCastOutOfRangeChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
index 2553f54bbcac..f23f784016d8 100644
--- a/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -12,6 +11,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/IssueHash.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Support/ScopedPrinter.h"
@@ -54,7 +54,7 @@ class ExprInspectionChecker : public Checker<eval::Call, check::DeadSymbols,
ExplodedNode *N) const;
public:
- bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
void checkEndAnalysis(ExplodedGraph &G, BugReporter &BR,
ExprEngine &Eng) const;
@@ -64,8 +64,12 @@ public:
REGISTER_SET_WITH_PROGRAMSTATE(MarkedSymbols, SymbolRef)
REGISTER_MAP_WITH_PROGRAMSTATE(DenotedSymbols, SymbolRef, const StringLiteral *)
-bool ExprInspectionChecker::evalCall(const CallExpr *CE,
+bool ExprInspectionChecker::evalCall(const CallEvent &Call,
CheckerContext &C) const {
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return false;
+
// 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))
@@ -409,3 +413,7 @@ void ExprInspectionChecker::analyzerExpress(const CallExpr *CE,
void ento::registerExprInspectionChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ExprInspectionChecker>();
}
+
+bool ento::shouldRegisterExprInspectionChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
index 165a4e4490eb..94542be7dd7c 100644
--- a/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/FixedAddressChecker.cpp
@@ -1,9 +1,8 @@
//=== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -65,3 +64,7 @@ void FixedAddressChecker::checkPreStmt(const BinaryOperator *B,
void ento::registerFixedAddressChecker(CheckerManager &mgr) {
mgr.registerChecker<FixedAddressChecker>();
}
+
+bool ento::shouldRegisterFixedAddressChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp b/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
index 248b9c3f7693..d471c23b83bf 100644
--- a/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/GCDAntipatternChecker.cpp
@@ -1,9 +1,8 @@
//===- GCDAntipatternChecker.cpp ---------------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -197,7 +196,7 @@ static void emitDiagnostics(const BoundNodes &Nodes,
ADC->getDecl(),
Checker,
/*Name=*/"GCD performance anti-pattern",
- /*Category=*/"Performance",
+ /*BugCategory=*/"Performance",
OS.str(),
PathDiagnosticLocation::createBegin(SW, BR.getSourceManager(), ADC),
SW->getSourceRange());
@@ -222,8 +221,12 @@ void GCDAntipatternChecker::checkASTCodeBody(const Decl *D,
emitDiagnostics(Match, "group", BR, ADC, this);
}
-}
+} // end of anonymous namespace
void ento::registerGCDAntipattern(CheckerManager &Mgr) {
Mgr.registerChecker<GCDAntipatternChecker>();
}
+
+bool ento::shouldRegisterGCDAntipattern(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/GTestChecker.cpp b/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
index 818716dd6070..f4308f510f0b 100644
--- a/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/GTestChecker.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -289,11 +288,11 @@ ProgramStateRef GTestChecker::assumeValuesEqual(SVal Val1, SVal Val2,
}
void ento::registerGTestChecker(CheckerManager &Mgr) {
- const LangOptions &LangOpts = Mgr.getLangOpts();
+ Mgr.registerChecker<GTestChecker>();
+}
+
+bool ento::shouldRegisterGTestChecker(const LangOptions &LO) {
// 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>();
+ return LO.CPlusPlus;
}
diff --git a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index 32fed202d3ab..d3ab98033236 100644
--- a/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -1,9 +1,8 @@
//== GenericTaintChecker.cpp ----------------------------------- -*- C++ -*--=//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -14,6 +13,8 @@
// aggressively, even if the involved symbols are under constrained.
//
//===----------------------------------------------------------------------===//
+
+#include "Taint.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/Attr.h"
#include "clang/Basic/Builtins.h"
@@ -23,9 +24,12 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include <climits>
+#include <initializer_list>
+#include <utility>
using namespace clang;
using namespace ento;
+using namespace taint;
namespace {
class GenericTaintChecker
@@ -40,13 +44,16 @@ public:
void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+ void printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const override;
+
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 {
+ void initBugType() const {
if (!BT)
BT.reset(new BugType(this, "Use of Untrusted Data", "Untrusted Data"));
}
@@ -61,9 +68,6 @@ private:
/// 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);
@@ -71,16 +75,6 @@ private:
/// 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,
@@ -103,7 +97,7 @@ private:
bool generateReportIfTainted(const Expr *E, const char Msg[],
CheckerContext &C) const;
- typedef SmallVector<unsigned, 2> ArgVector;
+ using ArgVector = SmallVector<unsigned, 2>;
/// A struct used to specify taint propagation rules for a function.
///
@@ -115,61 +109,72 @@ private:
/// ReturnValueIndex is added to the dst list, the return value will be
/// tainted.
struct TaintPropagationRule {
+ enum class VariadicType { None, Src, Dst };
+
+ using PropagationFuncType = bool (*)(bool IsTainted, const CallExpr *,
+ CheckerContext &C);
+
/// 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);
- }
+ /// Index for the first variadic parameter if exist.
+ unsigned VariadicIndex;
+ /// Show when a function has variadic parameters. If it has, it marks all
+ /// of them as source or destination.
+ VariadicType VarType;
+ /// Special function for tainted source determination. If defined, it can
+ /// override the default behavior.
+ PropagationFuncType PropagationFunc;
+
+ TaintPropagationRule()
+ : VariadicIndex(InvalidArgIndex), VarType(VariadicType::None),
+ PropagationFunc(nullptr) {}
+
+ TaintPropagationRule(std::initializer_list<unsigned> &&Src,
+ std::initializer_list<unsigned> &&Dst,
+ VariadicType Var = VariadicType::None,
+ unsigned VarIndex = InvalidArgIndex,
+ PropagationFuncType Func = nullptr)
+ : SrcArgs(std::move(Src)), DstArgs(std::move(Dst)),
+ VariadicIndex(VarIndex), VarType(Var), PropagationFunc(Func) {}
/// 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); }
+ void addSrcArg(unsigned A) { SrcArgs.push_back(A); }
+ void addDstArg(unsigned A) { DstArgs.push_back(A); }
- inline bool isNull() const { return SrcArgs.empty(); }
+ bool isNull() const {
+ return SrcArgs.empty() && DstArgs.empty() &&
+ VariadicType::None == VarType;
+ }
- inline bool isDestinationArgument(unsigned ArgNum) const {
- return (std::find(DstArgs.begin(), DstArgs.end(), ArgNum) !=
- DstArgs.end());
+ bool isDestinationArgument(unsigned ArgNum) const {
+ return (llvm::find(DstArgs, ArgNum) != DstArgs.end());
}
- static inline bool isTaintedOrPointsToTainted(const Expr *E,
- ProgramStateRef State,
- CheckerContext &C) {
- if (State->isTainted(E, C.getLocationContext()) || isStdin(E, C))
+ static bool isTaintedOrPointsToTainted(const Expr *E, ProgramStateRef State,
+ CheckerContext &C) {
+ if (isTainted(State, 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));
+ return (V && isTainted(State, *V));
}
/// Pre-process a function which propagates taint according to the
/// taint rule.
ProgramStateRef process(const CallExpr *CE, CheckerContext &C) const;
+
+ // Functions for custom taintedness propagation.
+ static bool postSocket(bool IsTainted, const CallExpr *CE,
+ CheckerContext &C);
};
};
@@ -187,8 +192,7 @@ const char GenericTaintChecker::MsgSanitizeSystemArgs[] =
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)";
+ "for character data and the null terminator)";
} // end of anonymous namespace
@@ -207,24 +211,42 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
// 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))
+ // Source functions
+ // TODO: Add support for vfscanf & family.
+ .Case("fdopen", TaintPropagationRule({}, {ReturnValueIndex}))
+ .Case("fopen", TaintPropagationRule({}, {ReturnValueIndex}))
+ .Case("freopen", TaintPropagationRule({}, {ReturnValueIndex}))
+ .Case("getch", TaintPropagationRule({}, {ReturnValueIndex}))
+ .Case("getchar", TaintPropagationRule({}, {ReturnValueIndex}))
+ .Case("getchar_unlocked", TaintPropagationRule({}, {ReturnValueIndex}))
+ .Case("getenv", TaintPropagationRule({}, {ReturnValueIndex}))
+ .Case("gets", TaintPropagationRule({}, {0, ReturnValueIndex}))
+ .Case("scanf", TaintPropagationRule({}, {}, VariadicType::Dst, 1))
+ .Case("socket",
+ TaintPropagationRule({}, {ReturnValueIndex}, VariadicType::None,
+ InvalidArgIndex,
+ &TaintPropagationRule::postSocket))
+ .Case("wgetch", TaintPropagationRule({}, {ReturnValueIndex}))
+ // Propagating functions
+ .Case("atoi", TaintPropagationRule({0}, {ReturnValueIndex}))
+ .Case("atol", TaintPropagationRule({0}, {ReturnValueIndex}))
+ .Case("atoll", TaintPropagationRule({0}, {ReturnValueIndex}))
+ .Case("fgetc", TaintPropagationRule({0}, {ReturnValueIndex}))
+ .Case("fgetln", TaintPropagationRule({0}, {ReturnValueIndex}))
+ .Case("fgets", TaintPropagationRule({2}, {0, ReturnValueIndex}))
+ .Case("fscanf", TaintPropagationRule({0}, {}, VariadicType::Dst, 2))
+ .Case("getc", TaintPropagationRule({0}, {ReturnValueIndex}))
+ .Case("getc_unlocked", TaintPropagationRule({0}, {ReturnValueIndex}))
+ .Case("getdelim", TaintPropagationRule({3}, {0}))
+ .Case("getline", TaintPropagationRule({2}, {0}))
+ .Case("getw", TaintPropagationRule({0}, {ReturnValueIndex}))
+ .Case("pread",
+ TaintPropagationRule({0, 1, 2, 3}, {1, ReturnValueIndex}))
+ .Case("read", TaintPropagationRule({0, 2}, {1, ReturnValueIndex}))
+ .Case("strchr", TaintPropagationRule({0}, {ReturnValueIndex}))
+ .Case("strrchr", TaintPropagationRule({0}, {ReturnValueIndex}))
+ .Case("tolower", TaintPropagationRule({0}, {ReturnValueIndex}))
+ .Case("toupper", TaintPropagationRule({0}, {ReturnValueIndex}))
.Default(TaintPropagationRule());
if (!Rule.isNull())
@@ -239,12 +261,12 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
case Builtin::BImemmove:
case Builtin::BIstrncpy:
case Builtin::BIstrncat:
- return TaintPropagationRule(1, 2, 0, true);
+ return TaintPropagationRule({1, 2}, {0, ReturnValueIndex});
case Builtin::BIstrlcpy:
case Builtin::BIstrlcat:
- return TaintPropagationRule(1, 2, 0, false);
+ return TaintPropagationRule({1, 2}, {0});
case Builtin::BIstrndup:
- return TaintPropagationRule(0, 1, ReturnValueIndex);
+ return TaintPropagationRule({0, 1}, {ReturnValueIndex});
default:
break;
@@ -252,20 +274,23 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
// 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);
+ if (C.isCLibraryFunction(FDecl, "snprintf"))
+ return TaintPropagationRule({1}, {0, ReturnValueIndex}, VariadicType::Src,
+ 3);
+ else if (C.isCLibraryFunction(FDecl, "sprintf"))
+ return TaintPropagationRule({}, {0, ReturnValueIndex}, VariadicType::Src,
+ 2);
else if (C.isCLibraryFunction(FDecl, "strcpy") ||
C.isCLibraryFunction(FDecl, "stpcpy") ||
C.isCLibraryFunction(FDecl, "strcat"))
- return TaintPropagationRule(1, 0, true);
+ return TaintPropagationRule({1}, {0, ReturnValueIndex});
else if (C.isCLibraryFunction(FDecl, "bcopy"))
- return TaintPropagationRule(0, 2, 1, false);
+ return TaintPropagationRule({0, 2}, {1});
else if (C.isCLibraryFunction(FDecl, "strdup") ||
C.isCLibraryFunction(FDecl, "strdupa"))
- return TaintPropagationRule(0, ReturnValueIndex);
+ return TaintPropagationRule({0}, {ReturnValueIndex});
else if (C.isCLibraryFunction(FDecl, "wcsdup"))
- return TaintPropagationRule(0, ReturnValueIndex);
+ return TaintPropagationRule({0}, {ReturnValueIndex});
}
// Skipping the following functions, since they might be used for cleansing
@@ -277,19 +302,26 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
void GenericTaintChecker::checkPreStmt(const CallExpr *CE,
CheckerContext &C) const {
- // Check for errors first.
+ // Check for taintedness related errors first: system call, uncontrolled
+ // format string, tainted buffer size.
if (checkPre(CE, C))
return;
- // Add taint second.
+ // Marks the function's arguments and/or return value tainted if it present in
+ // the list.
addSourcesPre(CE, C);
}
void GenericTaintChecker::checkPostStmt(const CallExpr *CE,
CheckerContext &C) const {
- if (propagateFromPre(CE, C))
- return;
- addSourcesPost(CE, C);
+ // Set the marked values as tainted. The return value only accessible from
+ // checkPostStmt.
+ propagateFromPre(CE, C);
+}
+
+void GenericTaintChecker::printState(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const char *Sep) const {
+ printTaint(State, Out, NL, Sep);
}
void GenericTaintChecker::addSourcesPre(const CallExpr *CE,
@@ -314,13 +346,6 @@ void GenericTaintChecker::addSourcesPre(const CallExpr *CE,
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);
@@ -337,14 +362,10 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE,
if (TaintArgs.isEmpty())
return false;
- for (llvm::ImmutableSet<unsigned>::iterator I = TaintArgs.begin(),
- E = TaintArgs.end();
- I != E; ++I) {
- unsigned ArgNum = *I;
-
+ for (unsigned ArgNum : TaintArgs) {
// Special handling for the tainted return value.
if (ArgNum == ReturnValueIndex) {
- State = State->addTaint(CE, C.getLocationContext());
+ State = addTaint(State, CE, C.getLocationContext());
continue;
}
@@ -355,7 +376,7 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE,
const Expr *Arg = CE->getArg(ArgNum);
Optional<SVal> V = getPointedToSVal(C, Arg);
if (V)
- State = State->addTaint(*V);
+ State = addTaint(State, *V);
}
// Clear up the taint info from the state.
@@ -368,43 +389,6 @@ bool GenericTaintChecker::propagateFromPre(const CallExpr *CE,
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 {
@@ -459,54 +443,31 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
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))
+ bool IsTainted = true;
+ for (unsigned ArgNum : SrcArgs) {
+ if (ArgNum >= CE->getNumArgs())
return State;
if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(ArgNum), State, C)))
break;
}
+
+ // Check for taint in variadic arguments.
+ if (!IsTainted && VariadicType::Src == VarType) {
+ // Check if any of the arguments is tainted
+ for (unsigned int i = VariadicIndex; i < CE->getNumArgs(); ++i) {
+ if ((IsTainted = isTaintedOrPointsToTainted(CE->getArg(i), State, C)))
+ break;
+ }
+ }
+
+ if (PropagationFunc)
+ IsTainted = PropagationFunc(IsTainted, CE, C);
+
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;
- }
-
+ for (unsigned ArgNum : DstArgs) {
// Should mark the return value?
if (ArgNum == ReturnValueIndex) {
State = State->add<TaintArgsOnPostVisit>(ReturnValueIndex);
@@ -518,66 +479,38 @@ GenericTaintChecker::TaintPropagationRule::process(const CallExpr *CE,
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;
+ // Mark all variadic arguments tainted if present.
+ if (VariadicType::Dst == VarType) {
+ // 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 = VariadicIndex; 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);
+ }
}
- return nullptr;
+ return State;
}
// 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;
-
+bool GenericTaintChecker::TaintPropagationRule::postSocket(bool /*IsTainted*/,
+ const CallExpr *CE,
+ CheckerContext &C) {
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;
-}
+ return false;
-ProgramStateRef GenericTaintChecker::postRetTaint(const CallExpr *CE,
- CheckerContext &C) const {
- return C.getState()->addTaint(CE, C.getLocationContext());
+ return true;
}
bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) {
@@ -603,14 +536,14 @@ bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) {
// 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())) {
+ if (const auto *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;
+ if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC()) {
+ const auto *PtrTy = dyn_cast<PointerType>(D->getType().getTypePtr());
+ if (PtrTy && PtrTy->getPointeeType().getCanonicalType() ==
+ C.getASTContext().getFILEType().getCanonicalType())
+ return true;
+ }
}
return false;
}
@@ -648,9 +581,9 @@ bool GenericTaintChecker::generateReportIfTainted(const Expr *E,
ProgramStateRef State = C.getState();
Optional<SVal> PointedToSVal = getPointedToSVal(C, E);
SVal TaintedSVal;
- if (PointedToSVal && State->isTainted(*PointedToSVal))
+ if (PointedToSVal && isTainted(State, *PointedToSVal))
TaintedSVal = *PointedToSVal;
- else if (State->isTainted(E, C.getLocationContext()))
+ else if (isTainted(State, E, C.getLocationContext()))
TaintedSVal = C.getSVal(E);
else
return false;
@@ -746,3 +679,7 @@ bool GenericTaintChecker::checkTaintedBufferSize(const CallExpr *CE,
void ento::registerGenericTaintChecker(CheckerManager &mgr) {
mgr.registerChecker<GenericTaintChecker>();
}
+
+bool ento::shouldRegisterGenericTaintChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
index 4c2a229428d9..cc2cfb774227 100644
--- a/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IdenticalExprChecker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
@@ -116,7 +115,7 @@ bool FindIdenticalExprVisitor::VisitIfStmt(const IfStmt *I) {
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)) {
+ 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,
@@ -513,3 +512,7 @@ public:
void ento::registerIdenticalExprChecker(CheckerManager &Mgr) {
Mgr.registerChecker<FindIdenticalExprChecker>();
}
+
+bool ento::shouldRegisterIdenticalExprChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
index a4f47d727a8f..e3270f1f7be2 100644
--- a/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -1,9 +1,8 @@
//=== InnerPointerChecker.cpp -------------------------------------*- C++ -*--//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -310,3 +309,7 @@ void ento::registerInnerPointerChecker(CheckerManager &Mgr) {
registerInnerPointerCheckerAux(Mgr);
Mgr.registerChecker<InnerPointerChecker>();
}
+
+bool ento::shouldRegisterInnerPointerChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h b/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
index 81c95a4813a6..9642588d6a41 100644
--- a/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
+++ b/lib/StaticAnalyzer/Checkers/InterCheckerAPI.h
@@ -1,9 +1,8 @@
//==--- InterCheckerAPI.h ---------------------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This file allows introduction of checker dependencies. It contains APIs for
@@ -17,9 +16,6 @@ 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);
diff --git a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
index e719e19d68e9..6f1060b5f26d 100644
--- a/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IteratorChecker.cpp
@@ -1,9 +1,8 @@
//===-- IteratorChecker.cpp ---------------------------------------*- C++ -*--//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -134,8 +133,6 @@ public:
}
};
-typedef llvm::PointerUnion<const MemRegion *, SymbolRef> RegionOrSymbol;
-
// Structure to record the symbolic begin and end position of a container
struct ContainerData {
private:
@@ -173,41 +170,21 @@ public:
}
};
-// 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> {
+ check::LiveSymbols, check::DeadSymbols> {
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 handleComparison(CheckerContext &C, const Expr *CE, const SVal &RetVal,
+ const SVal &LVal, const SVal &RVal,
+ OverloadedOperatorKind Op) const;
+ void processComparison(CheckerContext &C, ProgramStateRef State,
+ SymbolRef Sym1, SymbolRef Sym2, const SVal &RetVal,
+ 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,
@@ -282,8 +259,6 @@ public:
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
@@ -293,9 +268,6 @@ REGISTER_MAP_WITH_PROGRAMSTATE(IteratorRegionMap, const MemRegion *,
REGISTER_MAP_WITH_PROGRAMSTATE(ContainerMap, const MemRegion *, ContainerData)
-REGISTER_MAP_WITH_PROGRAMSTATE(IteratorComparisonMap, const SymExpr *,
- IteratorComparison)
-
namespace {
bool isIteratorType(const QualType &Type);
@@ -325,40 +297,23 @@ 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);
+ const MemRegion *Cont, const Expr *E,
+ QualType T, const LocationContext *LCtx,
+ unsigned BlockCount);
ProgramStateRef createContainerEnd(ProgramStateRef State, const MemRegion *Cont,
- const SymbolRef Sym);
+ const Expr *E, QualType T,
+ const LocationContext *LCtx,
+ unsigned BlockCount);
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 assumeNoOverflow(ProgramStateRef State, SymbolRef Sym,
+ long Scale);
ProgramStateRef invalidateAllIteratorPositions(ProgramStateRef State,
const MemRegion *Cont);
ProgramStateRef
@@ -384,6 +339,8 @@ ProgramStateRef reassignAllIteratorPositionsUnless(ProgramStateRef State,
ProgramStateRef rebaseSymbolInIteratorPositionsIf(
ProgramStateRef State, SValBuilder &SVB, SymbolRef OldSym,
SymbolRef NewSym, SymbolRef CondSym, BinaryOperator::Opcode Opc);
+ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
+ SymbolRef Sym2, bool Equal);
const ContainerData *getContainerData(ProgramStateRef State,
const MemRegion *Cont);
ProgramStateRef setContainerData(ProgramStateRef State, const MemRegion *Cont,
@@ -399,14 +356,14 @@ bool isZero(ProgramStateRef State, const NonLoc &Val);
IteratorChecker::IteratorChecker() {
OutOfRangeBugType.reset(
- new BugType(this, "Iterator out of range", "Misuse of STL APIs"));
- OutOfRangeBugType->setSuppressOnSink(true);
+ new BugType(this, "Iterator out of range", "Misuse of STL APIs",
+ /*SuppressOnSink=*/true));
MismatchedBugType.reset(
- new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs"));
- MismatchedBugType->setSuppressOnSink(true);
+ new BugType(this, "Iterator(s) mismatched", "Misuse of STL APIs",
+ /*SuppressOnSink=*/true));
InvalidatedBugType.reset(
- new BugType(this, "Iterator invalidated", "Misuse of STL APIs"));
- InvalidatedBugType->setSuppressOnSink(true);
+ new BugType(this, "Iterator invalidated", "Misuse of STL APIs",
+ /*SuppressOnSink=*/true));
}
void IteratorChecker::checkPreCall(const CallEvent &Call,
@@ -609,78 +566,123 @@ void IteratorChecker::checkPostCall(const CallEvent &Call,
const auto Op = Func->getOverloadedOperator();
if (isAssignmentOperator(Op)) {
const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call);
- if (Func->getParamDecl(0)->getType()->isRValueReferenceType()) {
+ if (cast<CXXMethodDecl>(Func)->isMoveAssignmentOperator()) {
handleAssign(C, InstCall->getCXXThisVal(), Call.getOriginExpr(),
Call.getArgSVal(0));
- } else {
- handleAssign(C, InstCall->getCXXThisVal());
+ return;
}
+
+ handleAssign(C, InstCall->getCXXThisVal());
+ return;
} else if (isSimpleComparisonOperator(Op)) {
+ const auto *OrigExpr = Call.getOriginExpr();
+ if (!OrigExpr)
+ return;
+
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);
+ handleComparison(C, OrigExpr, Call.getReturnValue(),
+ InstCall->getCXXThisVal(), Call.getArgSVal(0), Op);
+ return;
}
+
+ handleComparison(C, OrigExpr, Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getArgSVal(1), Op);
+ return;
} 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));
+ return;
}
} else {
if (Call.getNumArgs() >= 2) {
handleRandomIncrOrDecr(C, Func->getOverloadedOperator(),
Call.getReturnValue(), Call.getArgSVal(0),
Call.getArgSVal(1));
+ return;
}
}
} 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());
+ return;
}
+
+ handleIncrement(C, Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getNumArgs());
+ return;
} 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());
+ return;
}
+
+ handleDecrement(C, Call.getReturnValue(), Call.getArgSVal(0),
+ Call.getNumArgs());
+ return;
}
} else {
if (const auto *InstCall = dyn_cast<CXXInstanceCall>(&Call)) {
if (isAssignCall(Func)) {
handleAssign(C, InstCall->getCXXThisVal());
- } else if (isClearCall(Func)) {
+ return;
+ }
+
+ if (isClearCall(Func)) {
handleClear(C, InstCall->getCXXThisVal());
- } else if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
+ return;
+ }
+
+ if (isPushBackCall(Func) || isEmplaceBackCall(Func)) {
handlePushBack(C, InstCall->getCXXThisVal());
- } else if (isPopBackCall(Func)) {
+ return;
+ }
+
+ if (isPopBackCall(Func)) {
handlePopBack(C, InstCall->getCXXThisVal());
- } else if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
+ return;
+ }
+
+ if (isPushFrontCall(Func) || isEmplaceFrontCall(Func)) {
handlePushFront(C, InstCall->getCXXThisVal());
- } else if (isPopFrontCall(Func)) {
+ return;
+ }
+
+ if (isPopFrontCall(Func)) {
handlePopFront(C, InstCall->getCXXThisVal());
- } else if (isInsertCall(Func) || isEmplaceCall(Func)) {
+ return;
+ }
+
+ if (isInsertCall(Func) || isEmplaceCall(Func)) {
handleInsert(C, Call.getArgSVal(0));
- } else if (isEraseCall(Func)) {
+ return;
+ }
+
+ if (isEraseCall(Func)) {
if (Call.getNumArgs() == 1) {
handleErase(C, Call.getArgSVal(0));
- } else if (Call.getNumArgs() == 2) {
+ return;
+ }
+
+ if (Call.getNumArgs() == 2) {
handleErase(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ return;
}
- } else if (isEraseAfterCall(Func)) {
+ }
+
+ if (isEraseAfterCall(Func)) {
if (Call.getNumArgs() == 1) {
handleEraseAfter(C, Call.getArgSVal(0));
- } else if (Call.getNumArgs() == 2) {
+ return;
+ }
+
+ if (Call.getNumArgs() == 2) {
handleEraseAfter(C, Call.getArgSVal(0), Call.getArgSVal(1));
+ return;
}
}
}
@@ -700,6 +702,7 @@ void IteratorChecker::checkPostCall(const CallEvent &Call,
InstCall->getCXXThisVal());
return;
}
+
if (isEndCall(Func)) {
handleEnd(C, OrigExpr, Call.getReturnValue(),
InstCall->getCXXThisVal());
@@ -839,77 +842,79 @@ void IteratorChecker::checkDeadSymbols(SymbolReaper &SR,
}
}
- 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;
+void IteratorChecker::handleComparison(CheckerContext &C, const Expr *CE,
+ 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();
+ const auto *LPos = getIteratorPosition(State, LVal);
+ const auto *RPos = getIteratorPosition(State, RVal);
+ const MemRegion *Cont = nullptr;
+ if (LPos) {
+ Cont = LPos->getContainer();
+ } else if (RPos) {
+ Cont = RPos->getContainer();
+ }
+ if (!Cont)
+ return;
- 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;
+ // At least one of the iterators have recorded positions. If one of them has
+ // not then create a new symbol for the offset.
+ SymbolRef Sym;
+ if (!LPos || !RPos) {
+ auto &SymMgr = C.getSymbolManager();
+ Sym = SymMgr.conjureSymbol(CE, C.getLocationContext(),
+ C.getASTContext().LongTy, C.blockCount());
+ State = assumeNoOverflow(State, Sym, 4);
+ }
- Comp = loadComparison(State, SE);
- if (!Comp)
- return State;
+ if (!LPos) {
+ State = setIteratorPosition(State, LVal,
+ IteratorPosition::getPosition(Cont, Sym));
+ LPos = getIteratorPosition(State, LVal);
+ } else if (!RPos) {
+ State = setIteratorPosition(State, RVal,
+ IteratorPosition::getPosition(Cont, Sym));
+ RPos = getIteratorPosition(State, RVal);
}
- return processComparison(State, Comp->getLeft(), Comp->getRight(),
- (Comp->isEquality() == Assumption) != Negated);
+ processComparison(C, State, LPos->getOffset(), RPos->getOffset(), RetVal, Op);
}
-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)))) {
+void IteratorChecker::processComparison(CheckerContext &C,
+ ProgramStateRef State, SymbolRef Sym1,
+ SymbolRef Sym2, const SVal &RetVal,
+ OverloadedOperatorKind Op) const {
+ if (const auto TruthVal = RetVal.getAs<nonloc::ConcreteInt>()) {
+ if ((State = relateSymbols(State, Sym1, Sym2,
+ (Op == OO_EqualEqual) ==
+ (TruthVal->getValue() != 0)))) {
C.addTransition(State);
} else {
C.generateSink(State, C.getPredecessor());
}
+ return;
+ }
+
+ const auto ConditionVal = RetVal.getAs<DefinedSVal>();
+ if (!ConditionVal)
+ return;
+
+ if (auto StateTrue = relateSymbols(State, Sym1, Sym2, Op == OO_EqualEqual)) {
+ StateTrue = StateTrue->assume(*ConditionVal, true);
+ C.addTransition(StateTrue);
+ }
+
+ if (auto StateFalse = relateSymbols(State, Sym1, Sym2, Op != OO_EqualEqual)) {
+ StateFalse = StateFalse->assume(*ConditionVal, false);
+ C.addTransition(StateFalse);
}
}
@@ -974,47 +979,6 @@ void IteratorChecker::handleDecrement(CheckerContext &C, const SVal &RetVal,
}
}
-// 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,
@@ -1099,14 +1063,34 @@ void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter,
// Verify match between a container and the container of an iterator
Cont = Cont->getMostDerivedObjectRegion();
+ if (const auto *ContSym = Cont->getSymbolicBase()) {
+ if (isa<SymbolConjured>(ContSym->getSymbol()))
+ return;
+ }
+
auto State = C.getState();
const auto *Pos = getIteratorPosition(State, Iter);
- if (Pos && Pos->getContainer() != Cont) {
+ if (!Pos)
+ return;
+
+ const auto *IterCont = Pos->getContainer();
+
+ // Skip symbolic regions based on conjured symbols. Two conjured symbols
+ // may or may not be the same. For example, the same function can return
+ // the same or a different container but we get different conjured symbols
+ // for each call. This may cause false positives so omit them from the check.
+ if (const auto *ContSym = IterCont->getSymbolicBase()) {
+ if (isa<SymbolConjured>(ContSym->getSymbol()))
+ return;
+ }
+
+ if (IterCont != Cont) {
auto *N = C.generateNonFatalErrorNode(State);
if (!N) {
return;
}
- reportMismatchedBug("Container accessed using foreign iterator argument.", Iter, Cont, C, N);
+ reportMismatchedBug("Container accessed using foreign iterator argument.",
+ Iter, Cont, C, N);
}
}
@@ -1115,8 +1099,31 @@ void IteratorChecker::verifyMatch(CheckerContext &C, const SVal &Iter1,
// Verify match between the containers of two iterators
auto State = C.getState();
const auto *Pos1 = getIteratorPosition(State, Iter1);
+ if (!Pos1)
+ return;
+
+ const auto *IterCont1 = Pos1->getContainer();
+
+ // Skip symbolic regions based on conjured symbols. Two conjured symbols
+ // may or may not be the same. For example, the same function can return
+ // the same or a different container but we get different conjured symbols
+ // for each call. This may cause false positives so omit them from the check.
+ if (const auto *ContSym = IterCont1->getSymbolicBase()) {
+ if (isa<SymbolConjured>(ContSym->getSymbol()))
+ return;
+ }
+
const auto *Pos2 = getIteratorPosition(State, Iter2);
- if (Pos1 && Pos2 && Pos1->getContainer() != Pos2->getContainer()) {
+ if (!Pos2)
+ return;
+
+ const auto *IterCont2 = Pos2->getContainer();
+ if (const auto *ContSym = IterCont2->getSymbolicBase()) {
+ if (isa<SymbolConjured>(ContSym->getSymbol()))
+ return;
+ }
+
+ if (IterCont1 != IterCont2) {
auto *N = C.generateNonFatalErrorNode(State);
if (!N)
return;
@@ -1138,11 +1145,9 @@ void IteratorChecker::handleBegin(CheckerContext &C, const Expr *CE,
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 = createContainerBegin(State, ContReg, CE, C.getASTContext().LongTy,
+ C.getLocationContext(), C.blockCount());
+ BeginSym = getContainerBegin(State, ContReg);
}
State = setIteratorPosition(State, RetVal,
IteratorPosition::getPosition(ContReg, BeginSym));
@@ -1162,11 +1167,9 @@ void IteratorChecker::handleEnd(CheckerContext &C, const Expr *CE,
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 = createContainerEnd(State, ContReg, CE, C.getASTContext().LongTy,
+ C.getLocationContext(), C.blockCount());
+ EndSym = getContainerEnd(State, ContReg);
}
State = setIteratorPosition(State, RetVal,
IteratorPosition::getPosition(ContReg, EndSym));
@@ -1853,23 +1856,6 @@ bool isRandomIncrOrDecrOperator(OverloadedOperatorKind OK) {
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)
@@ -1930,48 +1916,6 @@ const CXXRecordDecl *getCXXRecordDecl(ProgramStateRef State,
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)
@@ -1989,32 +1933,47 @@ SymbolRef getContainerEnd(ProgramStateRef State, const MemRegion *Cont) {
}
ProgramStateRef createContainerBegin(ProgramStateRef State,
- const MemRegion *Cont,
- const SymbolRef Sym) {
+ const MemRegion *Cont, const Expr *E,
+ QualType T, const LocationContext *LCtx,
+ unsigned BlockCount) {
// Only create if it does not exist
const auto *CDataPtr = getContainerData(State, Cont);
+ if (CDataPtr && CDataPtr->getBegin())
+ return State;
+
+ auto &SymMgr = State->getSymbolManager();
+ const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
+ "begin");
+ State = assumeNoOverflow(State, Sym, 4);
+
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) {
+ const Expr *E, QualType T,
+ const LocationContext *LCtx,
+ unsigned BlockCount) {
// Only create if it does not exist
const auto *CDataPtr = getContainerData(State, Cont);
+ if (CDataPtr && CDataPtr->getEnd())
+ return State;
+
+ auto &SymMgr = State->getSymbolManager();
+ const SymbolConjured *Sym = SymMgr.conjureSymbol(E, LCtx, T, BlockCount,
+ "end");
+ State = assumeNoOverflow(State, Sym, 4);
+
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);
}
@@ -2042,17 +2001,6 @@ const IteratorPosition *getIteratorPosition(ProgramStateRef State,
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()) {
@@ -2066,18 +2014,6 @@ ProgramStateRef setIteratorPosition(ProgramStateRef State, const SVal &Val,
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();
@@ -2090,21 +2026,8 @@ ProgramStateRef removeIteratorPosition(ProgramStateRef State, const SVal &Val) {
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) {
+ProgramStateRef relateSymbols(ProgramStateRef State, SymbolRef Sym1,
+ SymbolRef Sym2, bool Equal) {
auto &SVB = State->getStateManager().getSValBuilder();
// FIXME: This code should be reworked as follows:
@@ -2113,14 +2036,16 @@ ProgramStateRef relateIteratorPositions(ProgramStateRef State,
// 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());
+ SVB.evalBinOp(State, BO_EQ, nonloc::SymbolVal(Sym1),
+ nonloc::SymbolVal(Sym2), SVB.getConditionType());
assert(comparison.getAs<DefinedSVal>() &&
"Symbol comparison must be a `DefinedSVal`");
auto NewState = State->assume(comparison.castAs<DefinedSVal>(), Equal);
+ if (!NewState)
+ return nullptr;
+
if (const auto CompSym = comparison.getAsSymbol()) {
assert(isa<SymIntExpr>(CompSym) &&
"Symbol comparison must be a `SymIntExpr`");
@@ -2161,6 +2086,47 @@ bool isBoundThroughLazyCompoundVal(const Environment &Env,
return false;
}
+// 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?
+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;
+}
+
template <typename Condition, typename Process>
ProgramStateRef processIteratorPositions(ProgramStateRef State, Condition Cond,
Process Proc) {
@@ -2379,7 +2345,6 @@ bool compare(ProgramStateRef State, SymbolRef Sym1, SymbolRef Sym2,
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();
@@ -2395,12 +2360,24 @@ bool compare(ProgramStateRef State, NonLoc NL1, NonLoc NL2,
} // namespace
+void ento::registerIteratorModeling(CheckerManager &mgr) {
+ mgr.registerChecker<IteratorChecker>();
+}
+
+bool ento::shouldRegisterIteratorModeling(const LangOptions &LO) {
+ return true;
+}
+
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &Mgr) { \
- auto *checker = Mgr.registerChecker<IteratorChecker>(); \
+ auto *checker = Mgr.getChecker<IteratorChecker>(); \
checker->ChecksEnabled[IteratorChecker::CK_##name] = true; \
checker->CheckNames[IteratorChecker::CK_##name] = \
Mgr.getCurrentCheckName(); \
+ } \
+ \
+ bool ento::shouldRegister##name(const LangOptions &LO) { \
+ return true; \
}
REGISTER_CHECKER(IteratorRangeChecker)
diff --git a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
index aade62fd7491..2b75f3acc922 100644
--- a/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/IvarInvalidationChecker.cpp
@@ -1,9 +1,8 @@
//===- IvarInvalidationChecker.cpp ------------------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -736,12 +735,24 @@ public:
};
} // end anonymous namespace
+void ento::registerIvarInvalidationModeling(CheckerManager &mgr) {
+ mgr.registerChecker<IvarInvalidationChecker>();
+}
+
+bool ento::shouldRegisterIvarInvalidationModeling(const LangOptions &LO) {
+ return true;
+}
+
#define REGISTER_CHECKER(name) \
void ento::register##name(CheckerManager &mgr) { \
IvarInvalidationChecker *checker = \
- mgr.registerChecker<IvarInvalidationChecker>(); \
+ mgr.getChecker<IvarInvalidationChecker>(); \
checker->Filter.check_##name = true; \
checker->Filter.checkName_##name = mgr.getCurrentCheckName(); \
+ } \
+ \
+ bool ento::shouldRegister##name(const LangOptions &LO) { \
+ return true; \
}
REGISTER_CHECKER(InstanceVariableInvalidation)
diff --git a/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
index df238f2b2e45..7522fdd0a99b 100644
--- a/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/LLVMConventionsChecker.cpp
@@ -1,9 +1,8 @@
//=== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -314,3 +313,7 @@ public:
void ento::registerLLVMConventionsChecker(CheckerManager &mgr) {
mgr.registerChecker<LLVMConventionsChecker>();
}
+
+bool ento::shouldRegisterLLVMConventionsChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
index eda39efeca17..46067ecbca99 100644
--- a/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
@@ -1,9 +1,8 @@
//=- LocalizationChecker.cpp -------------------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -754,10 +753,9 @@ void NonLocalizedStringChecker::reportLocalizationError(
if (isDebuggingContext(C))
return;
- ExplodedNode *ErrNode = C.getPredecessor();
static CheckerProgramPointTag Tag("NonLocalizedStringChecker",
"UnlocalizedString");
- ErrNode = C.addTransition(C.getState(), C.getPredecessor(), &Tag);
+ ExplodedNode *ErrNode = C.addTransition(C.getState(), C.getPredecessor(), &Tag);
if (!ErrNode)
return;
@@ -1141,7 +1139,7 @@ void EmptyLocalizationContextChecker::MethodCrawler::VisitObjCMessageExpr(
}
bool Invalid = false;
- llvm::MemoryBuffer *BF =
+ const llvm::MemoryBuffer *BF =
Mgr.getSourceManager().getBuffer(SLInfo.first, SL, &Invalid);
if (Invalid)
return;
@@ -1398,14 +1396,27 @@ void ento::registerNonLocalizedStringChecker(CheckerManager &mgr) {
NonLocalizedStringChecker *checker =
mgr.registerChecker<NonLocalizedStringChecker>();
checker->IsAggressive =
- mgr.getAnalyzerOptions().getCheckerBooleanOption("AggressiveReport",
- false, checker);
+ mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ checker, "AggressiveReport");
+}
+
+bool ento::shouldRegisterNonLocalizedStringChecker(const LangOptions &LO) {
+ return true;
}
void ento::registerEmptyLocalizationContextChecker(CheckerManager &mgr) {
mgr.registerChecker<EmptyLocalizationContextChecker>();
}
+bool ento::shouldRegisterEmptyLocalizationContextChecker(
+ const LangOptions &LO) {
+ return true;
+}
+
void ento::registerPluralMisuseChecker(CheckerManager &mgr) {
mgr.registerChecker<PluralMisuseChecker>();
}
+
+bool ento::shouldRegisterPluralMisuseChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
new file mode 100644
index 000000000000..6e7776bb484e
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
@@ -0,0 +1,295 @@
+//== MIGChecker.cpp - MIG calling convention checker ------------*- C++ -*--==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines MIGChecker, a Mach Interface Generator calling convention
+// checker. Namely, in MIG callback implementation the following rules apply:
+// - When a server routine returns an error code that represents success, it
+// must take ownership of resources passed to it (and eventually release
+// them).
+// - Additionally, when returning success, all out-parameters must be
+// initialized.
+// - When it returns any other error code, it must not take ownership,
+// because the message and its out-of-line parameters will be destroyed
+// by the client that called the function.
+// For now we only check the last rule, as its violations lead to dangerous
+// use-after-free exploits.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/Analysis/AnyCall.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"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class MIGChecker : public Checker<check::PostCall, check::PreStmt<ReturnStmt>,
+ check::EndFunction> {
+ BugType BT{this, "Use-after-free (MIG calling convention violation)",
+ categories::MemoryError};
+
+ // The checker knows that an out-of-line object is deallocated if it is
+ // passed as an argument to one of these functions. If this object is
+ // additionally an argument of a MIG routine, the checker keeps track of that
+ // information and issues a warning when an error is returned from the
+ // respective routine.
+ std::vector<std::pair<CallDescription, unsigned>> Deallocators = {
+#define CALL(required_args, deallocated_arg, ...) \
+ {{{__VA_ARGS__}, required_args}, deallocated_arg}
+ // E.g., if the checker sees a C function 'vm_deallocate' that is
+ // defined on class 'IOUserClient' that has exactly 3 parameters, it knows
+ // that argument #1 (starting from 0, i.e. the second argument) is going
+ // to be consumed in the sense of the MIG consume-on-success convention.
+ CALL(3, 1, "vm_deallocate"),
+ CALL(3, 1, "mach_vm_deallocate"),
+ CALL(2, 0, "mig_deallocate"),
+ CALL(2, 1, "mach_port_deallocate"),
+ CALL(1, 0, "device_deallocate"),
+ CALL(1, 0, "iokit_remove_connect_reference"),
+ CALL(1, 0, "iokit_remove_reference"),
+ CALL(1, 0, "iokit_release_port"),
+ CALL(1, 0, "ipc_port_release"),
+ CALL(1, 0, "ipc_port_release_sonce"),
+ CALL(1, 0, "ipc_voucher_attr_control_release"),
+ CALL(1, 0, "ipc_voucher_release"),
+ CALL(1, 0, "lock_set_dereference"),
+ CALL(1, 0, "memory_object_control_deallocate"),
+ CALL(1, 0, "pset_deallocate"),
+ CALL(1, 0, "semaphore_dereference"),
+ CALL(1, 0, "space_deallocate"),
+ CALL(1, 0, "space_inspect_deallocate"),
+ CALL(1, 0, "task_deallocate"),
+ CALL(1, 0, "task_inspect_deallocate"),
+ CALL(1, 0, "task_name_deallocate"),
+ CALL(1, 0, "thread_deallocate"),
+ CALL(1, 0, "thread_inspect_deallocate"),
+ CALL(1, 0, "upl_deallocate"),
+ CALL(1, 0, "vm_map_deallocate"),
+ // E.g., if the checker sees a method 'releaseAsyncReference64()' that is
+ // defined on class 'IOUserClient' that takes exactly 1 argument, it knows
+ // that the argument is going to be consumed in the sense of the MIG
+ // consume-on-success convention.
+ CALL(1, 0, "IOUserClient", "releaseAsyncReference64"),
+ CALL(1, 0, "IOUserClient", "releaseNotificationPort"),
+#undef CALL
+ };
+
+ CallDescription OsRefRetain{"os_ref_retain", 1};
+
+ void checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const;
+
+public:
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+ // HACK: We're making two attempts to find the bug: checkEndFunction
+ // should normally be enough but it fails when the return value is a literal
+ // that never gets put into the Environment and ends of function with multiple
+ // returns get agglutinated across returns, preventing us from obtaining
+ // the return value. The problem is similar to https://reviews.llvm.org/D25326
+ // but now we step into it in the top-level function.
+ void checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const {
+ checkReturnAux(RS, C);
+ }
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const {
+ checkReturnAux(RS, C);
+ }
+
+};
+} // end anonymous namespace
+
+// A flag that says that the programmer has called a MIG destructor
+// for at least one parameter.
+REGISTER_TRAIT_WITH_PROGRAMSTATE(ReleasedParameter, bool)
+// A set of parameters for which the check is suppressed because
+// reference counting is being performed.
+REGISTER_SET_WITH_PROGRAMSTATE(RefCountedParameters, const ParmVarDecl *)
+
+static const ParmVarDecl *getOriginParam(SVal V, CheckerContext &C,
+ bool IncludeBaseRegions = false) {
+ // TODO: We should most likely always include base regions here.
+ SymbolRef Sym = V.getAsSymbol(IncludeBaseRegions);
+ if (!Sym)
+ return nullptr;
+
+ // If we optimistically assume that the MIG routine never re-uses the storage
+ // that was passed to it as arguments when it invalidates it (but at most when
+ // it assigns to parameter variables directly), this procedure correctly
+ // determines if the value was loaded from the transitive closure of MIG
+ // routine arguments in the heap.
+ while (const MemRegion *MR = Sym->getOriginRegion()) {
+ const auto *VR = dyn_cast<VarRegion>(MR);
+ if (VR && VR->hasStackParametersStorage() &&
+ VR->getStackFrame()->inTopFrame())
+ return cast<ParmVarDecl>(VR->getDecl());
+
+ const SymbolicRegion *SR = MR->getSymbolicBase();
+ if (!SR)
+ return nullptr;
+
+ Sym = SR->getSymbol();
+ }
+
+ return nullptr;
+}
+
+static bool isInMIGCall(CheckerContext &C) {
+ const LocationContext *LC = C.getLocationContext();
+ assert(LC && "Unknown location context");
+
+ const StackFrameContext *SFC;
+ // Find the top frame.
+ while (LC) {
+ SFC = LC->getStackFrame();
+ LC = SFC->getParent();
+ }
+
+ const Decl *D = SFC->getDecl();
+
+ if (Optional<AnyCall> AC = AnyCall::forDecl(D)) {
+ // Even though there's a Sema warning when the return type of an annotated
+ // function is not a kern_return_t, this warning isn't an error, so we need
+ // an extra sanity check here.
+ // FIXME: AnyCall doesn't support blocks yet, so they remain unchecked
+ // for now.
+ if (!AC->getReturnType(C.getASTContext())
+ .getCanonicalType()->isSignedIntegerType())
+ return false;
+ }
+
+ if (D->hasAttr<MIGServerRoutineAttr>())
+ return true;
+
+ // See if there's an annotated method in the superclass.
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(D))
+ for (const auto *OMD: MD->overridden_methods())
+ if (OMD->hasAttr<MIGServerRoutineAttr>())
+ return true;
+
+ return false;
+}
+
+void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const {
+ if (Call.isCalled(OsRefRetain)) {
+ // If the code is doing reference counting over the parameter,
+ // it opens up an opportunity for safely calling a destructor function.
+ // TODO: We should still check for over-releases.
+ if (const ParmVarDecl *PVD =
+ getOriginParam(Call.getArgSVal(0), C, /*IncludeBaseRegions=*/true)) {
+ // We never need to clean up the program state because these are
+ // top-level parameters anyway, so they're always live.
+ C.addTransition(C.getState()->add<RefCountedParameters>(PVD));
+ }
+ return;
+ }
+
+ if (!isInMIGCall(C))
+ return;
+
+ auto I = llvm::find_if(Deallocators,
+ [&](const std::pair<CallDescription, unsigned> &Item) {
+ return Call.isCalled(Item.first);
+ });
+ if (I == Deallocators.end())
+ return;
+
+ ProgramStateRef State = C.getState();
+ unsigned ArgIdx = I->second;
+ SVal Arg = Call.getArgSVal(ArgIdx);
+ const ParmVarDecl *PVD = getOriginParam(Arg, C);
+ if (!PVD || State->contains<RefCountedParameters>(PVD))
+ return;
+
+ const NoteTag *T = C.getNoteTag([this, PVD](BugReport &BR) -> std::string {
+ if (&BR.getBugType() != &BT)
+ return "";
+ SmallString<64> Str;
+ llvm::raw_svector_ostream OS(Str);
+ OS << "Value passed through parameter '" << PVD->getName()
+ << "\' is deallocated";
+ return OS.str();
+ });
+ C.addTransition(State->set<ReleasedParameter>(true), T);
+}
+
+// Returns true if V can potentially represent a "successful" kern_return_t.
+static bool mayBeSuccess(SVal V, CheckerContext &C) {
+ ProgramStateRef State = C.getState();
+
+ // Can V represent KERN_SUCCESS?
+ if (!State->isNull(V).isConstrainedFalse())
+ return true;
+
+ SValBuilder &SVB = C.getSValBuilder();
+ ASTContext &ACtx = C.getASTContext();
+
+ // Can V represent MIG_NO_REPLY?
+ static const int MigNoReply = -305;
+ V = SVB.evalEQ(C.getState(), V, SVB.makeIntVal(MigNoReply, ACtx.IntTy));
+ if (!State->isNull(V).isConstrainedTrue())
+ return true;
+
+ // If none of the above, it's definitely an error.
+ return false;
+}
+
+void MIGChecker::checkReturnAux(const ReturnStmt *RS, CheckerContext &C) const {
+ // It is very unlikely that a MIG callback will be called from anywhere
+ // within the project under analysis and the caller isn't itself a routine
+ // that follows the MIG calling convention. Therefore we're safe to believe
+ // that it's always the top frame that is of interest. There's a slight chance
+ // that the user would want to enforce the MIG calling convention upon
+ // a random routine in the middle of nowhere, but given that the convention is
+ // fairly weird and hard to follow in the first place, there's relatively
+ // little motivation to spread it this way.
+ if (!C.inTopFrame())
+ return;
+
+ if (!isInMIGCall(C))
+ return;
+
+ // We know that the function is non-void, but what if the return statement
+ // is not there in the code? It's not a compile error, we should not crash.
+ if (!RS)
+ return;
+
+ ProgramStateRef State = C.getState();
+ if (!State->get<ReleasedParameter>())
+ return;
+
+ SVal V = C.getSVal(RS);
+ if (mayBeSuccess(V, C))
+ return;
+
+ ExplodedNode *N = C.generateErrorNode();
+ if (!N)
+ return;
+
+ auto R = llvm::make_unique<BugReport>(
+ BT,
+ "MIG callback fails with error after deallocating argument value. "
+ "This is a use-after-free vulnerability because the caller will try to "
+ "deallocate it again",
+ N);
+
+ R->addRange(RS->getSourceRange());
+ bugreporter::trackExpressionValue(N, RS->getRetValue(), *R, false);
+ C.emitReport(std::move(R));
+}
+
+void ento::registerMIGChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<MIGChecker>();
+}
+
+bool ento::shouldRegisterMIGChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
index fb9bccebe465..b250d3f8795e 100644
--- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
+++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.cpp
@@ -1,9 +1,8 @@
//===-- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
index 32fcb07e3371..6fbc30288655 100644
--- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
+++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIBugReporter.h
@@ -1,9 +1,8 @@
//===-- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp
index 28c6898f7947..7f9ba0de1dc2 100644
--- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.cpp
@@ -1,9 +1,8 @@
//===-- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
@@ -188,3 +187,7 @@ void MPIChecker::allRegionsUsedByWait(
void clang::ento::registerMPIChecker(CheckerManager &MGR) {
MGR.registerChecker<clang::ento::mpi::MPIChecker>();
}
+
+bool clang::ento::shouldRegisterMPIChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h
index 6b1c062ef3d5..ce9f1afac209 100644
--- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h
+++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIChecker.h
@@ -1,9 +1,8 @@
//===-- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp
index 12760abaeeff..277b3ed2e105 100644
--- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp
+++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPIFunctionClassifier.cpp
@@ -1,9 +1,8 @@
//===-- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
diff --git a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h
index 2e7140cd771e..fe0fb2a4d0e7 100644
--- a/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h
+++ b/lib/StaticAnalyzer/Checkers/MPI-Checker/MPITypes.h
@@ -1,9 +1,8 @@
//===-- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
diff --git a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index 06e27fc5718d..32ba9bc8e2ef 100644
--- a/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -1,9 +1,8 @@
//==--- MacOSKeychainAPIChecker.cpp ------------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This checker flags misuses of KeyChainAPI. In particular, the password data
@@ -662,3 +661,7 @@ void MacOSKeychainAPIChecker::printState(raw_ostream &Out,
void ento::registerMacOSKeychainAPIChecker(CheckerManager &mgr) {
mgr.registerChecker<MacOSKeychainAPIChecker>();
}
+
+bool ento::shouldRegisterMacOSKeychainAPIChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
index f5976d7da4c1..1c52d20d0991 100644
--- a/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MacOSXAPIChecker.cpp
@@ -1,9 +1,8 @@
// 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -174,3 +173,7 @@ void MacOSXAPIChecker::checkPreStmt(const CallExpr *CE,
void ento::registerMacOSXAPIChecker(CheckerManager &mgr) {
mgr.registerChecker<MacOSXAPIChecker>();
}
+
+bool ento::shouldRegisterMacOSXAPIChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index ae1b1fc837be..a79b34189065 100644
--- a/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -1,9 +1,8 @@
//=== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -18,6 +17,7 @@
#include "clang/AST/ParentMap.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
+#include "clang/Lex/Lexer.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
@@ -178,9 +178,10 @@ public:
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_kfree(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),
@@ -250,13 +251,13 @@ private:
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;
+ *II_kfree, *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;
@@ -359,6 +360,11 @@ private:
/// Check if the memory associated with this symbol was released.
bool isReleased(SymbolRef Sym, CheckerContext &C) const;
+ /// See if deallocation happens in a suspicious context. If so, escape the
+ /// pointers that otherwise would have been deallocated and return true.
+ bool suppressDeallocationsInSuspiciousContexts(const CallExpr *CE,
+ CheckerContext &C) const;
+
bool checkUseAfterFree(SymbolRef Sym, CheckerContext &C, const Stmt *S) const;
void checkUseZeroAllocated(SymbolRef Sym, CheckerContext &C,
@@ -599,6 +605,7 @@ void MallocChecker::initIdentifierInfo(ASTContext &Ctx) const {
II_strndup = &Ctx.Idents.get("strndup");
II_wcsdup = &Ctx.Idents.get("wcsdup");
II_kmalloc = &Ctx.Idents.get("kmalloc");
+ II_kfree = &Ctx.Idents.get("kfree");
II_if_nameindex = &Ctx.Idents.get("if_nameindex");
II_if_freenameindex = &Ctx.Idents.get("if_freenameindex");
@@ -658,7 +665,7 @@ bool MallocChecker::isCMemFunction(const FunctionDecl *FD,
if (Family == AF_Malloc && CheckFree) {
if (FunI == II_free || FunI == II_realloc || FunI == II_reallocf ||
- FunI == II_g_free)
+ FunI == II_g_free || FunI == II_kfree)
return true;
}
@@ -875,7 +882,10 @@ void MallocChecker::checkPostStmt(const CallExpr *CE, CheckerContext &C) const {
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) {
+ } else if (FunI == II_free || FunI == II_g_free || FunI == II_kfree) {
+ if (suppressDeallocationsInSuspiciousContexts(CE, C))
+ return;
+
State = FreeMemAux(C, CE, State, 0, false, ReleasedAllocatedMemory);
} else if (FunI == II_strdup || FunI == II_win_strdup ||
FunI == II_wcsdup || FunI == II_win_wcsdup) {
@@ -985,7 +995,7 @@ ProgramStateRef MallocChecker::ProcessZeroAllocation(
}
else if (const CXXNewExpr *NE = dyn_cast<CXXNewExpr>(E)) {
if (NE->isArray())
- Arg = NE->getArraySize();
+ Arg = *NE->getArraySize();
else
return State;
}
@@ -1117,7 +1127,7 @@ ProgramStateRef MallocChecker::addExtentSize(CheckerContext &C,
SVal ElementCount;
const SubRegion *Region;
if (NE->isArray()) {
- const Expr *SizeExpr = NE->getArraySize();
+ const Expr *SizeExpr = *NE->getArraySize();
ElementCount = C.getSVal(SizeExpr);
// Store the extent size for the (symbolic)region
// containing the elements.
@@ -1207,7 +1217,7 @@ void MallocChecker::checkPostObjCMessage(const ObjCMethodCall &Call,
ProgramStateRef State = FreeMemAux(C, Call.getArgExpr(0),
Call.getOriginExpr(), C.getState(),
/*Hold=*/true, ReleasedAllocatedMemory,
- /*RetNullOnFailure=*/true);
+ /*ReturnsNullOnFailure=*/true);
C.addTransition(State);
}
@@ -2301,14 +2311,14 @@ void MallocChecker::reportLeak(SymbolRef Sym, ExplodedNode *N,
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);
+ BT_Leak[*CheckKind].reset(new BugType(CheckNames[*CheckKind], "Memory leak",
+ categories::MemoryError,
+ /*SuppressOnSink=*/true));
}
// Most bug reports are cached at the location where they occurred.
@@ -2531,6 +2541,35 @@ bool MallocChecker::isReleased(SymbolRef Sym, CheckerContext &C) const {
return (RS && RS->isReleased());
}
+bool MallocChecker::suppressDeallocationsInSuspiciousContexts(
+ const CallExpr *CE, CheckerContext &C) const {
+ if (CE->getNumArgs() == 0)
+ return false;
+
+ StringRef FunctionStr = "";
+ if (const auto *FD = dyn_cast<FunctionDecl>(C.getStackFrame()->getDecl()))
+ if (const Stmt *Body = FD->getBody())
+ if (Body->getBeginLoc().isValid())
+ FunctionStr =
+ Lexer::getSourceText(CharSourceRange::getTokenRange(
+ {FD->getBeginLoc(), Body->getBeginLoc()}),
+ C.getSourceManager(), C.getLangOpts());
+
+ // We do not model the Integer Set Library's retain-count based allocation.
+ if (!FunctionStr.contains("__isl_"))
+ return false;
+
+ ProgramStateRef State = C.getState();
+
+ for (const Expr *Arg : CE->arguments())
+ if (SymbolRef Sym = C.getSVal(Arg).getAsSymbol())
+ if (const RefState *RS = State->get<RegionState>(Sym))
+ State = State->set<RegionState>(Sym, RefState::getEscaped(RS));
+
+ C.addTransition(State);
+ return true;
+}
+
bool MallocChecker::checkUseAfterFree(SymbolRef Sym, CheckerContext &C,
const Stmt *S) const {
@@ -2832,7 +2871,6 @@ ProgramStateRef MallocChecker::checkPointerEscapeAux(ProgramStateRef State,
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));
}
}
@@ -3087,47 +3125,37 @@ markReleased(ProgramStateRef State, SymbolRef Sym, const Expr *Origin) {
} // 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();
+ MallocChecker *checker = mgr.getChecker<MallocChecker>();
+ checker->ChecksEnabled[MallocChecker::CK_InnerPointerChecker] = true;
+ checker->CheckNames[MallocChecker::CK_InnerPointerChecker] =
+ mgr.getCurrentCheckName();
+}
+
+void ento::registerDynamicMemoryModeling(CheckerManager &mgr) {
+ auto *checker = mgr.registerChecker<MallocChecker>();
+ checker->IsOptimistic = mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ checker, "Optimistic");
+}
+
+bool ento::shouldRegisterDynamicMemoryModeling(const LangOptions &LO) {
+ return true;
}
#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); \
+ MallocChecker *checker = mgr.getChecker<MallocChecker>(); \
checker->ChecksEnabled[MallocChecker::CK_##name] = true; \
checker->CheckNames[MallocChecker::CK_##name] = mgr.getCurrentCheckName(); \
+ } \
+ \
+ bool ento::shouldRegister##name(const LangOptions &LO) { \
+ return true; \
}
REGISTER_CHECKER(MallocChecker)
REGISTER_CHECKER(NewDeleteChecker)
+REGISTER_CHECKER(NewDeleteLeaksChecker)
REGISTER_CHECKER(MismatchedDeallocatorChecker)
diff --git a/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
index d02ed48bceaa..4fd06f24c5bc 100644
--- a/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
@@ -1,9 +1,8 @@
// 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -334,7 +333,10 @@ void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D,
OutputPossibleOverflows(PossibleMallocOverflows, D, BR, mgr);
}
-void
-ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) {
+void ento::registerMallocOverflowSecurityChecker(CheckerManager &mgr) {
mgr.registerChecker<MallocOverflowSecurityChecker>();
}
+
+bool ento::shouldRegisterMallocOverflowSecurityChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
index bb245d82bc2b..2eb4d7141e28 100644
--- a/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MallocSizeofChecker.cpp
@@ -1,9 +1,8 @@
// 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -250,3 +249,7 @@ public:
void ento::registerMallocSizeofChecker(CheckerManager &mgr) {
mgr.registerChecker<MallocSizeofChecker>();
}
+
+bool ento::shouldRegisterMallocSizeofChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
index e3b24f20b0f0..270efede8385 100644
--- a/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
@@ -1,9 +1,8 @@
// 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -83,8 +82,12 @@ void ento::registerMmapWriteExecChecker(CheckerManager &mgr) {
mgr.registerChecker<MmapWriteExecChecker>();
Mwec->ProtExecOv =
mgr.getAnalyzerOptions()
- .getCheckerIntegerOption("MmapProtExec", 0x04, Mwec);
+ .getCheckerIntegerOption(Mwec, "MmapProtExec");
Mwec->ProtReadOv =
mgr.getAnalyzerOptions()
- .getCheckerIntegerOption("MmapProtRead", 0x01, Mwec);
+ .getCheckerIntegerOption(Mwec, "MmapProtRead");
+}
+
+bool ento::shouldRegisterMmapWriteExecChecker(const LangOptions &LO) {
+ return true;
}
diff --git a/lib/StaticAnalyzer/Checkers/Move.h b/lib/StaticAnalyzer/Checkers/Move.h
new file mode 100644
index 000000000000..10644a8fcb37
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/Move.h
@@ -0,0 +1,30 @@
+//=== Move.h - Tracking moved-from objects. ------------------------*- C++ -*-//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines inter-checker API for the use-after-move checker. It allows
+// dependent checkers to figure out if an object is in a moved-from state.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MOVE_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MOVE_H
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+
+namespace clang {
+namespace ento {
+namespace move {
+
+/// Returns true if the object is known to have been recently std::moved.
+bool isMovedFrom(ProgramStateRef State, const MemRegion *Region);
+
+} // namespace move
+} // namespace ento
+} // namespace clang
+
+#endif // LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_MOVE_H
diff --git a/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
index 6efa2dfbe5b4..d8a9af78536a 100644
--- a/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
@@ -1,9 +1,8 @@
// 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -14,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/AST/ExprCXX.h"
+#include "clang/Driver/DriverDiagnostic.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
@@ -187,13 +187,17 @@ private:
AggressivenessKind Aggressiveness;
public:
- void setAggressiveness(StringRef Str) {
+ void setAggressiveness(StringRef Str, CheckerManager &Mgr) {
Aggressiveness =
llvm::StringSwitch<AggressivenessKind>(Str)
.Case("KnownsOnly", AK_KnownsOnly)
.Case("KnownsAndLocals", AK_KnownsAndLocals)
.Case("All", AK_All)
- .Default(AK_KnownsAndLocals); // A sane default.
+ .Default(AK_Invalid);
+
+ if (Aggressiveness == AK_Invalid)
+ Mgr.reportInvalidCheckerOptionValue(this, "WarnOn",
+ "either \"KnownsOnly\", \"KnownsAndLocals\" or \"All\" string value");
};
private:
@@ -223,6 +227,18 @@ private:
REGISTER_MAP_WITH_PROGRAMSTATE(TrackedRegionMap, const MemRegion *, RegionState)
+// Define the inter-checker API.
+namespace clang {
+namespace ento {
+namespace move {
+bool isMovedFrom(ProgramStateRef State, const MemRegion *Region) {
+ const RegionState *RS = State->get<TrackedRegionMap>(Region);
+ return RS && (RS->isMoved() || RS->isReported());
+}
+} // namespace move
+} // namespace ento
+} // namespace clang
+
// If a region is removed all of the subregions needs to be removed too.
static ProgramStateRef removeFromState(ProgramStateRef State,
const MemRegion *Region) {
@@ -502,9 +518,9 @@ bool MoveChecker::isStateResetMethod(const CXXMethodDecl *MethodDec) const {
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")
+ if (MethodName == "assign" || MethodName == "clear" ||
+ MethodName == "destroy" || MethodName == "reset" ||
+ MethodName == "resize" || MethodName == "shrink")
return true;
}
return false;
@@ -736,5 +752,9 @@ void MoveChecker::printState(raw_ostream &Out, ProgramStateRef State,
void ento::registerMoveChecker(CheckerManager &mgr) {
MoveChecker *chk = mgr.registerChecker<MoveChecker>();
chk->setAggressiveness(
- mgr.getAnalyzerOptions().getCheckerStringOption("WarnOn", "", chk));
+ mgr.getAnalyzerOptions().getCheckerStringOption(chk, "WarnOn"), mgr);
+}
+
+bool ento::shouldRegisterMoveChecker(const LangOptions &LO) {
+ return true;
}
diff --git a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
index 4ed1b25cb09e..6fc7c17bc42f 100644
--- a/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NSAutoreleasePoolChecker.cpp
@@ -1,9 +1,8 @@
//=- NSAutoreleasePoolChecker.cpp --------------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -76,6 +75,9 @@ void NSAutoreleasePoolChecker::checkPreObjCMessage(const ObjCMethodCall &msg,
}
void ento::registerNSAutoreleasePoolChecker(CheckerManager &mgr) {
- if (mgr.getLangOpts().getGC() != LangOptions::NonGC)
- mgr.registerChecker<NSAutoreleasePoolChecker>();
+ mgr.registerChecker<NSAutoreleasePoolChecker>();
+}
+
+bool ento::shouldRegisterNSAutoreleasePoolChecker(const LangOptions &LO) {
+ return LO.getGC() != LangOptions::NonGC;
}
diff --git a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
index 06c43c6b9470..5cec012258c1 100644
--- a/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -1,9 +1,8 @@
//=- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -308,16 +307,30 @@ static bool IsCFError(QualType T, IdentifierInfo *II) {
return TT->getDecl()->getIdentifier() == II;
}
+void ento::registerNSOrCFErrorDerefChecker(CheckerManager &mgr) {
+ mgr.registerChecker<NSOrCFErrorDerefChecker>();
+}
+
+bool ento::shouldRegisterNSOrCFErrorDerefChecker(const LangOptions &LO) {
+ return true;
+}
+
void ento::registerNSErrorChecker(CheckerManager &mgr) {
mgr.registerChecker<NSErrorMethodChecker>();
- NSOrCFErrorDerefChecker *checker =
- mgr.registerChecker<NSOrCFErrorDerefChecker>();
+ NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
checker->ShouldCheckNSError = true;
}
+bool ento::shouldRegisterNSErrorChecker(const LangOptions &LO) {
+ return true;
+}
+
void ento::registerCFErrorChecker(CheckerManager &mgr) {
mgr.registerChecker<CFErrorFunctionChecker>();
- NSOrCFErrorDerefChecker *checker =
- mgr.registerChecker<NSOrCFErrorDerefChecker>();
+ NSOrCFErrorDerefChecker *checker = mgr.getChecker<NSOrCFErrorDerefChecker>();
checker->ShouldCheckCFError = true;
}
+
+bool ento::shouldRegisterCFErrorChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
index 83d4b5b0758b..fc34255bf6c9 100644
--- a/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NoReturnFunctionChecker.cpp
@@ -1,9 +1,8 @@
//=== NoReturnFunctionChecker.cpp -------------------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -143,3 +142,7 @@ void NoReturnFunctionChecker::checkPostObjCMessage(const ObjCMethodCall &Msg,
void ento::registerNoReturnFunctionChecker(CheckerManager &mgr) {
mgr.registerChecker<NoReturnFunctionChecker>();
}
+
+bool ento::shouldRegisterNoReturnFunctionChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
index 3c4363b6850e..bf6b3e3e87cf 100644
--- a/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NonNullParamChecker.cpp
@@ -1,9 +1,8 @@
//===--- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -217,3 +216,7 @@ std::unique_ptr<BugReport> NonNullParamChecker::genReportReferenceToNullPointer(
void ento::registerNonNullParamChecker(CheckerManager &mgr) {
mgr.registerChecker<NonNullParamChecker>();
}
+
+bool ento::shouldRegisterNonNullParamChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp b/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
index ce9e950aa9ba..43dbe57b8432 100644
--- a/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NonnullGlobalConstantsChecker.cpp
@@ -1,9 +1,8 @@
//==- NonnullGlobalConstantsChecker.cpp ---------------------------*- C++ -*--//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -107,14 +106,21 @@ bool NonnullGlobalConstantsChecker::isGlobalConstString(SVal V) const {
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;
+ while (const Type *T = Ty.getTypePtr()) {
+ if (const auto *TT = dyn_cast<TypedefType>(T)) {
+ Ty = TT->getDecl()->getUnderlyingType();
+ // It is sufficient for any intermediate typedef
+ // to be classified const.
+ HasConst = HasConst || Ty.isConstQualified();
+ if (isNonnullType(Ty) && HasConst)
+ return true;
+ } else if (const auto *AT = dyn_cast<AttributedType>(T)) {
+ if (AT->getAttrKind() == attr::TypeNonNull)
+ return true;
+ Ty = AT->getModifiedType();
+ } else {
+ return false;
+ }
}
return false;
}
@@ -138,3 +144,7 @@ bool NonnullGlobalConstantsChecker::isNonnullType(QualType Ty) const {
void ento::registerNonnullGlobalConstantsChecker(CheckerManager &Mgr) {
Mgr.registerChecker<NonnullGlobalConstantsChecker>();
}
+
+bool ento::shouldRegisterNonnullGlobalConstantsChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
index e535d1ae27ac..af21c84b995b 100644
--- a/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NullabilityChecker.cpp
@@ -1,9 +1,8 @@
-//== Nullabilityhecker.cpp - Nullability checker ----------------*- C++ -*--==//
+//===-- NullabilityChecker.cpp - Nullability checker ----------------------===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -479,7 +478,7 @@ void NullabilityChecker::checkEvent(ImplicitNullDerefEvent Event) const {
return;
const MemRegion *Region =
- getTrackRegion(Event.Location, /*CheckSuperregion=*/true);
+ getTrackRegion(Event.Location, /*CheckSuperRegion=*/true);
if (!Region)
return;
@@ -1192,16 +1191,28 @@ void NullabilityChecker::printState(raw_ostream &Out, ProgramStateRef State,
}
}
+void ento::registerNullabilityBase(CheckerManager &mgr) {
+ mgr.registerChecker<NullabilityChecker>();
+}
+
+bool ento::shouldRegisterNullabilityBase(const LangOptions &LO) {
+ return true;
+}
+
#define REGISTER_CHECKER(name, trackingRequired) \
void ento::register##name##Checker(CheckerManager &mgr) { \
- NullabilityChecker *checker = mgr.registerChecker<NullabilityChecker>(); \
+ NullabilityChecker *checker = mgr.getChecker<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); \
+ mgr.getAnalyzerOptions().getCheckerBooleanOption( \
+ checker, "NoDiagnoseCallsToSystemHeaders", true); \
+ } \
+ \
+ bool ento::shouldRegister##name##Checker(const LangOptions &LO) { \
+ return true; \
}
// The checks are likely to be turned on by default and it is possible to do
diff --git a/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp b/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
index 4e3a7205f1f4..1053424ae6fa 100644
--- a/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/NumberObjectConversionChecker.cpp
@@ -1,9 +1,8 @@
//===- NumberObjectConversionChecker.cpp -------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -347,5 +346,9 @@ void ento::registerNumberObjectConversionChecker(CheckerManager &Mgr) {
NumberObjectConversionChecker *Chk =
Mgr.registerChecker<NumberObjectConversionChecker>();
Chk->Pedantic =
- Mgr.getAnalyzerOptions().getCheckerBooleanOption("Pedantic", false, Chk);
+ Mgr.getAnalyzerOptions().getCheckerBooleanOption(Chk, "Pedantic");
+}
+
+bool ento::shouldRegisterNumberObjectConversionChecker(const LangOptions &LO) {
+ return true;
}
diff --git a/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp b/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
new file mode 100644
index 000000000000..5b9895c338d8
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/OSObjectCStyleCast.cpp
@@ -0,0 +1,90 @@
+//===- OSObjectCStyleCast.cpp ------------------------------------*- C++ -*-==//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines OSObjectCStyleCast checker, which checks for C-style casts
+// of OSObjects. Such casts almost always indicate a code smell,
+// as an explicit static or dynamic cast should be used instead.
+//===----------------------------------------------------------------------===//
+
+#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 {
+
+const char *WarnAtNode = "OSObjCast";
+
+class OSObjectCStyleCastChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const;
+};
+
+static void emitDiagnostics(const BoundNodes &Nodes,
+ BugReporter &BR,
+ AnalysisDeclContext *ADC,
+ const OSObjectCStyleCastChecker *Checker) {
+ const auto *CE = Nodes.getNodeAs<CastExpr>(WarnAtNode);
+ assert(CE);
+
+ std::string Diagnostics;
+ llvm::raw_string_ostream OS(Diagnostics);
+ OS << "C-style cast of OSObject. Use OSDynamicCast instead.";
+
+ BR.EmitBasicReport(
+ ADC->getDecl(),
+ Checker,
+ /*Name=*/"OSObject C-Style Cast",
+ /*BugCategory=*/"Security",
+ OS.str(),
+ PathDiagnosticLocation::createBegin(CE, BR.getSourceManager(), ADC),
+ CE->getSourceRange());
+}
+
+static auto hasTypePointingTo(DeclarationMatcher DeclM)
+ -> decltype(hasType(pointerType())) {
+ return hasType(pointerType(pointee(hasDeclaration(DeclM))));
+}
+
+void OSObjectCStyleCastChecker::checkASTCodeBody(const Decl *D, AnalysisManager &AM,
+ BugReporter &BR) const {
+
+ AnalysisDeclContext *ADC = AM.getAnalysisDeclContext(D);
+
+ auto DynamicCastM = callExpr(callee(functionDecl(hasName("safeMetaCast"))));
+
+ auto OSObjTypeM = hasTypePointingTo(cxxRecordDecl(isDerivedFrom("OSMetaClassBase")));
+ auto OSObjSubclassM = hasTypePointingTo(
+ cxxRecordDecl(isDerivedFrom("OSObject")));
+
+ auto CastM = cStyleCastExpr(
+ allOf(hasSourceExpression(allOf(OSObjTypeM, unless(DynamicCastM))),
+ OSObjSubclassM)).bind(WarnAtNode);
+
+ auto Matches = match(stmt(forEachDescendant(CastM)), *D->getBody(), AM.getASTContext());
+ for (BoundNodes Match : Matches)
+ emitDiagnostics(Match, BR, ADC, this);
+}
+}
+
+void ento::registerOSObjectCStyleCast(CheckerManager &Mgr) {
+ Mgr.registerChecker<OSObjectCStyleCastChecker>();
+}
+
+bool ento::shouldRegisterOSObjectCStyleCast(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
index 185b57575cb0..bd8cfb14680f 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCAtSyncChecker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -89,6 +88,9 @@ void ObjCAtSyncChecker::checkPreStmt(const ObjCAtSynchronizedStmt *S,
}
void ento::registerObjCAtSyncChecker(CheckerManager &mgr) {
- if (mgr.getLangOpts().ObjC)
- mgr.registerChecker<ObjCAtSyncChecker>();
+ mgr.registerChecker<ObjCAtSyncChecker>();
+}
+
+bool ento::shouldRegisterObjCAtSyncChecker(const LangOptions &LO) {
+ return LO.ObjC;
}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
index 0424958f8e65..d2371fe60d21 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCAutoreleaseWriteChecker.cpp
@@ -1,9 +1,8 @@
//===- ObjCAutoreleaseWriteChecker.cpp ----------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -137,7 +136,7 @@ static void emitDiagnostics(BoundNodes &Match, const Decl *D, BugReporter &BR,
ADC->getDecl(), Checker,
/*Name=*/(llvm::Twine(ActionMsg)
+ " autoreleasing out parameter inside autorelease pool").str(),
- /*Category=*/"Memory",
+ /*BugCategory=*/"Memory",
(llvm::Twine(ActionMsg) + " autoreleasing out parameter " +
(IsCapture ? "'" + PVD->getName() + "'" + " " : "") + "inside " +
"autorelease pool that may exit before " + Name + " returns; consider "
@@ -207,3 +206,7 @@ void ObjCAutoreleaseWriteChecker::checkASTCodeBody(const Decl *D,
void ento::registerAutoreleaseWriteChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ObjCAutoreleaseWriteChecker>();
}
+
+bool ento::shouldRegisterAutoreleaseWriteChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
index 34ce47823d51..4450c464f89d 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCContainersASTChecker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -172,3 +171,7 @@ public:
void ento::registerObjCContainersASTChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCContainersASTChecker>();
}
+
+bool ento::shouldRegisterObjCContainersASTChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
index 1c8c0d8dedda..f69a3944a56c 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCContainersChecker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -187,3 +186,7 @@ void ObjCContainersChecker::printState(raw_ostream &OS, ProgramStateRef State,
void ento::registerObjCContainersChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCContainersChecker>();
}
+
+bool ento::shouldRegisterObjCContainersChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
index d383302b2790..33e4d2af000d 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCMissingSuperCallChecker.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -222,6 +221,9 @@ void ento::registerObjCSuperCallChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ObjCSuperCallChecker>();
}
+bool ento::shouldRegisterObjCSuperCallChecker(const LangOptions &LO) {
+ return true;
+}
/*
ToDo list for expanding this check in the future, the list is not exhaustive.
diff --git a/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp
index 018d3fcfceb9..9a49200545e3 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCPropertyChecker.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -79,3 +78,7 @@ void ObjCPropertyChecker::checkCopyMutable(const ObjCPropertyDecl *D,
void ento::registerObjCPropertyChecker(CheckerManager &Mgr) {
Mgr.registerChecker<ObjCPropertyChecker>();
}
+
+bool ento::shouldRegisterObjCPropertyChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
index efa804220765..767b7bf4063c 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCSelfInitChecker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -437,3 +436,7 @@ static bool isInitMessage(const ObjCMethodCall &Call) {
void ento::registerObjCSelfInitChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCSelfInitChecker>();
}
+
+bool ento::shouldRegisterObjCSelfInitChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
index 9058784dd345..f435f00c08e7 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCSuperDeallocChecker.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -282,8 +281,9 @@ SuperDeallocBRVisitor::VisitNode(const ExplodedNode *Succ,
//===----------------------------------------------------------------------===//
void ento::registerObjCSuperDeallocChecker(CheckerManager &Mgr) {
- const LangOptions &LangOpts = Mgr.getLangOpts();
- if (LangOpts.getGC() == LangOptions::GCOnly || LangOpts.ObjCAutoRefCount)
- return;
Mgr.registerChecker<ObjCSuperDeallocChecker>();
}
+
+bool ento::shouldRegisterObjCSuperDeallocChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
index 7f7b45316087..4b39a97c7e8d 100644
--- a/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ObjCUnusedIVarsChecker.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -186,3 +185,7 @@ public:
void ento::registerObjCUnusedIvarsChecker(CheckerManager &mgr) {
mgr.registerChecker<ObjCUnusedIvarsChecker>();
}
+
+bool ento::shouldRegisterObjCUnusedIvarsChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp b/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
index 211db392bf71..0aa410de15ff 100644
--- a/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PaddingChecker.cpp
@@ -1,9 +1,8 @@
//=======- PaddingChecker.cpp ------------------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -17,6 +16,7 @@
#include "clang/AST/DeclTemplate.h"
#include "clang/AST/RecordLayout.h"
#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/Driver/DriverDiagnostic.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
@@ -33,17 +33,14 @@ namespace {
class PaddingChecker : public Checker<check::ASTDecl<TranslationUnitDecl>> {
private:
mutable std::unique_ptr<BugType> PaddingBug;
- mutable int64_t AllowedPad;
mutable BugReporter *BR;
public:
+ int64_t AllowedPad;
+
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
@@ -277,15 +274,13 @@ public:
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) {
+ auto Iter = llvm::upper_bound(Fields, InsertPoint);
+ if (Iter != Fields.begin()) {
// We found a field that we can layout with the current alignment.
--Iter;
NewOffset += Iter->Size;
@@ -349,5 +344,14 @@ public:
} // namespace
void ento::registerPaddingChecker(CheckerManager &Mgr) {
- Mgr.registerChecker<PaddingChecker>();
+ auto *Checker = Mgr.registerChecker<PaddingChecker>();
+ Checker->AllowedPad = Mgr.getAnalyzerOptions()
+ .getCheckerIntegerOption(Checker, "AllowedPad");
+ if (Checker->AllowedPad < 0)
+ Mgr.reportInvalidCheckerOptionValue(
+ Checker, "AllowedPad", "a non-negative value");
+}
+
+bool ento::shouldRegisterPaddingChecker(const LangOptions &LO) {
+ return true;
}
diff --git a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
index de3a16ebc729..03c3f4dd2357 100644
--- a/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PointerArithChecker.cpp
@@ -1,9 +1,8 @@
//=== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -343,3 +342,7 @@ void PointerArithChecker::checkPreStmt(const BinaryOperator *BOp,
void ento::registerPointerArithChecker(CheckerManager &mgr) {
mgr.registerChecker<PointerArithChecker>();
}
+
+bool ento::shouldRegisterPointerArithChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp
new file mode 100644
index 000000000000..307e59b8eebc
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/PointerIterationChecker.cpp
@@ -0,0 +1,100 @@
+//== PointerIterationChecker.cpp ------------------------------- -*- C++ -*--=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines PointerIterationChecker which checks for non-determinism
+// caused due to iteration of unordered containers of pointer elements.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#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;
+using namespace ast_matchers;
+
+namespace {
+
+// ID of a node at which the diagnostic would be emitted.
+constexpr llvm::StringLiteral WarnAtNode = "iter";
+
+class PointerIterationChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const;
+};
+
+static void emitDiagnostics(const BoundNodes &Match, const Decl *D,
+ BugReporter &BR, AnalysisManager &AM,
+ const PointerIterationChecker *Checker) {
+ auto *ADC = AM.getAnalysisDeclContext(D);
+
+ const auto *MarkedStmt = Match.getNodeAs<Stmt>(WarnAtNode);
+ assert(MarkedStmt);
+
+ auto Range = MarkedStmt->getSourceRange();
+ auto Location = PathDiagnosticLocation::createBegin(MarkedStmt,
+ BR.getSourceManager(),
+ ADC);
+ std::string Diagnostics;
+ llvm::raw_string_ostream OS(Diagnostics);
+ OS << "Iteration of pointer-like elements "
+ << "can result in non-deterministic ordering";
+
+ BR.EmitBasicReport(ADC->getDecl(), Checker,
+ "Iteration of pointer-like elements", "Non-determinism",
+ OS.str(), Location, Range);
+}
+
+// Assumption: Iteration of ordered containers of pointers is deterministic.
+
+// TODO: Currently, we only check for std::unordered_set. Other unordered
+// containers like std::unordered_map also need to be handled.
+
+// TODO: Currently, we do not check what the for loop does with the iterated
+// pointer values. Not all iterations may cause non-determinism. For example,
+// counting or summing up the elements should not be non-deterministic.
+
+auto matchUnorderedIterWithPointers() -> decltype(decl()) {
+
+ auto UnorderedContainerM = declRefExpr(to(varDecl(hasType(
+ recordDecl(hasName("std::unordered_set")
+ )))));
+
+ auto PointerTypeM = varDecl(hasType(hasCanonicalType(pointerType())));
+
+ auto PointerIterM = stmt(cxxForRangeStmt(
+ hasLoopVariable(PointerTypeM),
+ hasRangeInit(UnorderedContainerM)
+ )).bind(WarnAtNode);
+
+ return decl(forEachDescendant(PointerIterM));
+}
+
+void PointerIterationChecker::checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const {
+ auto MatcherM = matchUnorderedIterWithPointers();
+
+ auto Matches = match(MatcherM, *D, AM.getASTContext());
+ for (const auto &Match : Matches)
+ emitDiagnostics(Match, D, BR, AM, this);
+}
+
+} // end of anonymous namespace
+
+void ento::registerPointerIterationChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<PointerIterationChecker>();
+}
+
+bool ento::shouldRegisterPointerIterationChecker(const LangOptions &LO) {
+ return LO.CPlusPlus;
+}
diff --git a/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp
new file mode 100644
index 000000000000..586d9d3af2a6
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/PointerSortingChecker.cpp
@@ -0,0 +1,113 @@
+//== PointerSortingChecker.cpp --------------------------------- -*- C++ -*--=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines PointerSortingChecker which checks for non-determinism
+// caused due to sorting containers with pointer-like elements.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#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;
+using namespace ast_matchers;
+
+namespace {
+
+// ID of a node at which the diagnostic would be emitted.
+constexpr llvm::StringLiteral WarnAtNode = "sort";
+
+class PointerSortingChecker : public Checker<check::ASTCodeBody> {
+public:
+ void checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const;
+};
+
+static void emitDiagnostics(const BoundNodes &Match, const Decl *D,
+ BugReporter &BR, AnalysisManager &AM,
+ const PointerSortingChecker *Checker) {
+ auto *ADC = AM.getAnalysisDeclContext(D);
+
+ const auto *MarkedStmt = Match.getNodeAs<CallExpr>(WarnAtNode);
+ assert(MarkedStmt);
+
+ auto Range = MarkedStmt->getSourceRange();
+ auto Location = PathDiagnosticLocation::createBegin(MarkedStmt,
+ BR.getSourceManager(),
+ ADC);
+ std::string Diagnostics;
+ llvm::raw_string_ostream OS(Diagnostics);
+ OS << "Sorting pointer-like elements "
+ << "can result in non-deterministic ordering";
+
+ BR.EmitBasicReport(ADC->getDecl(), Checker,
+ "Sorting of pointer-like elements", "Non-determinism",
+ OS.str(), Location, Range);
+}
+
+auto callsName(const char *FunctionName) -> decltype(callee(functionDecl())) {
+ return callee(functionDecl(hasName(FunctionName)));
+}
+
+// FIXME: Currently we simply check if std::sort is used with pointer-like
+// elements. This approach can have a big false positive rate. Using std::sort,
+// std::unique and then erase is common technique for deduplicating a container
+// (which in some cases might even be quicker than using, let's say std::set).
+// In case a container contains arbitrary memory addresses (e.g. multiple
+// things give different stuff but might give the same thing multiple times)
+// which we don't want to do things with more than once, we might use
+// sort-unique-erase and the sort call will emit a report.
+auto matchSortWithPointers() -> decltype(decl()) {
+ // Match any of these function calls.
+ auto SortFuncM = anyOf(
+ callsName("std::is_sorted"),
+ callsName("std::nth_element"),
+ callsName("std::partial_sort"),
+ callsName("std::partition"),
+ callsName("std::sort"),
+ callsName("std::stable_partition"),
+ callsName("std::stable_sort")
+ );
+
+ // Match only if the container has pointer-type elements.
+ auto IteratesPointerEltsM = hasArgument(0,
+ hasType(cxxRecordDecl(has(
+ fieldDecl(hasType(hasCanonicalType(
+ pointsTo(hasCanonicalType(pointerType()))
+ )))
+ ))));
+
+ auto PointerSortM = stmt(callExpr(allOf(SortFuncM, IteratesPointerEltsM))
+ ).bind(WarnAtNode);
+
+ return decl(forEachDescendant(PointerSortM));
+}
+
+void PointerSortingChecker::checkASTCodeBody(const Decl *D,
+ AnalysisManager &AM,
+ BugReporter &BR) const {
+ auto MatcherM = matchSortWithPointers();
+
+ auto Matches = match(MatcherM, *D, AM.getASTContext());
+ for (const auto &Match : Matches)
+ emitDiagnostics(Match, D, BR, AM, this);
+}
+
+} // end of anonymous namespace
+
+void ento::registerPointerSortingChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<PointerSortingChecker>();
+}
+
+bool ento::shouldRegisterPointerSortingChecker(const LangOptions &LO) {
+ return LO.CPlusPlus;
+}
diff --git a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
index 41490e45f241..c9512f4fc42f 100644
--- a/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PointerSubChecker.cpp
@@ -1,9 +1,8 @@
//=== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -73,3 +72,7 @@ void PointerSubChecker::checkPreStmt(const BinaryOperator *B,
void ento::registerPointerSubChecker(CheckerManager &mgr) {
mgr.registerChecker<PointerSubChecker>();
}
+
+bool ento::shouldRegisterPointerSubChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index 66cc37278809..33f677e1c258 100644
--- a/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -1,9 +1,8 @@
//===--- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -481,3 +480,7 @@ void PthreadLockChecker::checkDeadSymbols(SymbolReaper &SymReaper,
void ento::registerPthreadLockChecker(CheckerManager &mgr) {
mgr.registerChecker<PthreadLockChecker>();
}
+
+bool ento::shouldRegisterPthreadLockChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
index 0652af856643..4a3a8dae23a7 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
@@ -1,9 +1,8 @@
//==-- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -13,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "RetainCountChecker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
using namespace clang;
using namespace ento;
@@ -29,83 +29,20 @@ const RefVal *getRefBinding(ProgramStateRef State, SymbolRef Sym) {
return State->get<RefBindings>(Sym);
}
-ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym,
+} // end namespace retaincountchecker
+} // end namespace ento
+} // end namespace clang
+
+static ProgramStateRef setRefBinding(ProgramStateRef State, SymbolRef Sym,
RefVal Val) {
assert(Sym != nullptr);
return State->set<RefBindings>(Sym, Val);
}
-ProgramStateRef removeRefBinding(ProgramStateRef State, SymbolRef Sym) {
+static 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() << " | ";
@@ -196,7 +133,7 @@ public:
ProgramStateRef getState() const { return state; }
bool VisitSymbol(SymbolRef sym) override {
- state = state->remove<RefBindings>(sym);
+ state = removeRefBinding(state, sym);
return true;
}
};
@@ -248,7 +185,15 @@ void RetainCountChecker::checkPostStmt(const CastExpr *CE,
if (!BE)
return;
- ArgEffect AE = ArgEffect(IncRef, ObjKind::ObjC);
+ QualType QT = CE->getType();
+ ObjKind K;
+ if (QT->isObjCObjectPointerType()) {
+ K = ObjKind::ObjC;
+ } else {
+ K = ObjKind::CF;
+ }
+
+ ArgEffect AE = ArgEffect(IncRef, K);
switch (BE->getBridgeKind()) {
case OBC_Bridge:
@@ -390,6 +335,31 @@ void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
C.addTransition(State);
}
+static bool isReceiverUnconsumedSelf(const CallEvent &Call) {
+ if (const auto *MC = dyn_cast<ObjCMethodCall>(&Call)) {
+
+ // Check if the message is not consumed, we know it will not be used in
+ // an assignment, ex: "self = [super init]".
+ return MC->getMethodFamily() == OMF_init && MC->isReceiverSelfOrSuper() &&
+ !Call.getLocationContext()
+ ->getAnalysisDeclContext()
+ ->getParentMap()
+ .isConsumedExpr(Call.getOriginExpr());
+ }
+ return false;
+}
+
+const static RetainSummary *getSummary(RetainSummaryManager &Summaries,
+ const CallEvent &Call,
+ QualType ReceiverType) {
+ const Expr *CE = Call.getOriginExpr();
+ AnyCall C =
+ CE ? *AnyCall::forExpr(CE)
+ : AnyCall(cast<CXXDestructorDecl>(Call.getDecl()));
+ return Summaries.getSummary(C, Call.hasNonZeroCallbackArg(),
+ isReceiverUnconsumedSelf(Call), ReceiverType);
+}
+
void RetainCountChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
RetainSummaryManager &Summaries = getSummaryManager(C);
@@ -405,7 +375,7 @@ void RetainCountChecker::checkPostCall(const CallEvent &Call,
}
}
- const RetainSummary *Summ = Summaries.getSummary(Call, ReceiverType);
+ const RetainSummary *Summ = getSummary(Summaries, Call, ReceiverType);
if (C.wasInlined) {
processSummaryOfInlined(*Summ, Call, C);
@@ -414,20 +384,6 @@ void RetainCountChecker::checkPostCall(const CallEvent &Call,
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
@@ -529,12 +485,36 @@ void RetainCountChecker::processSummaryOfInlined(const RetainSummary &Summ,
C.addTransition(state);
}
+static bool isSmartPtrField(const MemRegion *MR) {
+ const auto *TR = dyn_cast<TypedValueRegion>(
+ cast<SubRegion>(MR)->getSuperRegion());
+ return TR && RetainSummaryManager::isKnownSmartPointer(TR->getValueType());
+}
+
+
+/// A value escapes in these possible cases:
+///
+/// - binding to something that is not a memory region.
+/// - binding to a memregion that does not have stack storage
+/// - binding to a variable that has a destructor attached using CleanupAttr
+///
+/// We do not currently model what happens when a symbol is
+/// assigned to a struct field, unless it is a known smart pointer
+/// implementation, about which we know that it is inlined.
+/// FIXME: This could definitely be improved upon.
static bool shouldEscapeRegion(const MemRegion *R) {
+ if (isSmartPtrField(R))
+ return false;
+
+ const auto *VR = dyn_cast<VarRegion>(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);
+ if (!R->hasStackStorage() || !VR)
+ return true;
+
+ const VarDecl *VD = VR->getDecl();
+ if (!VD->hasAttr<CleanupAttr>())
+ return false; // CleanupAttr attaches destructors, which cause escaping.
+ return true;
}
static SmallVector<ProgramStateRef, 2>
@@ -557,6 +537,11 @@ updateOutParameters(ProgramStateRef State, const RetainSummary &Summ,
ProgramStateRef AssumeZeroReturn = State;
if (SplitNecessary) {
+ if (!CE.getResultType()->isScalarType()) {
+ // Structures cannot be assumed. This probably deserves
+ // a compiler warning for invalid annotations.
+ return {State};
+ }
if (auto DL = L.getAs<DefinedOrUnknownSVal>()) {
AssumeNonZeroReturn = AssumeNonZeroReturn->assume(*DL, true);
AssumeZeroReturn = AssumeZeroReturn->assume(*DL, false);
@@ -629,7 +614,6 @@ void RetainCountChecker::checkSummary(const RetainSummary &Summ,
// 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) {
@@ -855,6 +839,23 @@ ProgramStateRef RetainCountChecker::updateSymbol(ProgramStateRef state,
return setRefBinding(state, sym, V);
}
+const RefCountBug &
+RetainCountChecker::errorKindToBugKind(RefVal::Kind ErrorKind,
+ SymbolRef Sym) const {
+ switch (ErrorKind) {
+ case RefVal::ErrorUseAfterRelease:
+ return useAfterRelease;
+ case RefVal::ErrorReleaseNotOwned:
+ return releaseNotOwned;
+ case RefVal::ErrorDeallocNotOwned:
+ if (Sym->getType()->getPointeeCXXRecordDecl())
+ return freeNotOwned;
+ return deallocNotOwned;
+ default:
+ llvm_unreachable("Unhandled error.");
+ }
+}
+
void RetainCountChecker::processNonLeakError(ProgramStateRef St,
SourceRange ErrorRange,
RefVal::Kind ErrorKind,
@@ -874,30 +875,9 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef 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);
+ errorKindToBugKind(ErrorKind, Sym),
+ C.getASTContext().getLangOpts(), N, Sym);
report->addRange(ErrorRange);
C.emitReport(std::move(report));
}
@@ -906,15 +886,19 @@ void RetainCountChecker::processNonLeakError(ProgramStateRef St,
// 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.
+bool RetainCountChecker::evalCall(const CallEvent &Call,
+ CheckerContext &C) const {
ProgramStateRef state = C.getState();
- const FunctionDecl *FD = C.getCalleeDecl(CE);
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!FD)
return false;
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return false;
+
RetainSummaryManager &SmrMgr = getSummaryManager(C);
- QualType ResultTy = CE->getCallReturnType(C.getASTContext());
+ QualType ResultTy = Call.getResultType();
// See if the function has 'rc_ownership_trusted_implementation'
// annotate attribute. If it does, we will not inline it.
@@ -932,18 +916,27 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
// Bind the return value.
if (BSmr == BehaviorSummary::Identity ||
- BSmr == BehaviorSummary::IdentityOrZero) {
- SVal RetVal = state->getSVal(CE->getArg(0), LCtx);
+ BSmr == BehaviorSummary::IdentityOrZero ||
+ BSmr == BehaviorSummary::IdentityThis) {
+
+ const Expr *BindReturnTo =
+ (BSmr == BehaviorSummary::IdentityThis)
+ ? cast<CXXMemberCallExpr>(CE)->getImplicitObjectArgument()
+ : CE->getArg(0);
+ SVal RetVal = state->getSVal(BindReturnTo, LCtx);
// If the receiver is unknown or the function has
// 'rc_ownership_trusted_implementation' annotate attribute, conjure a
// return value.
+ // FIXME: this branch is very strange.
if (RetVal.isUnknown() ||
(hasTrustedImplementationAnnotation && !ResultTy.isNull())) {
SValBuilder &SVB = C.getSValBuilder();
RetVal =
SVB.conjureSymbolVal(nullptr, CE, LCtx, ResultTy, C.blockCount());
}
+
+ // Bind the value.
state = state->BindExpr(CE, LCtx, RetVal, /*Invalidate=*/false);
if (BSmr == BehaviorSummary::IdentityOrZero) {
@@ -953,13 +946,12 @@ bool RetainCountChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
// Assume that output is zero on the other branch.
NullOutputState = NullOutputState->BindExpr(
CE, LCtx, C.getSValBuilder().makeNull(), /*Invalidate=*/false);
-
- C.addTransition(NullOutputState);
+ C.addTransition(NullOutputState, &CastFailTag);
// 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);
+ state = state->assume(*L, /*assumption=*/true);
}
}
@@ -988,8 +980,10 @@ ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S,
return Pred;
ProgramStateRef state = C.getState();
- SymbolRef Sym =
- state->getSValAsScalarOrLoc(RetE, C.getLocationContext()).getAsLocSymbol();
+ // We need to dig down to the symbolic base here because various
+ // custom allocators do sometimes return the symbol with an offset.
+ SymbolRef Sym = state->getSValAsScalarOrLoc(RetE, C.getLocationContext())
+ .getAsLocSymbol(/*IncludeBaseRegions=*/true);
if (!Sym)
return Pred;
@@ -1057,11 +1051,11 @@ ExplodedNode * RetainCountChecker::processReturn(const ReturnStmt *S,
// FIXME: What is the convention for blocks? Is there one?
if (const ObjCMethodDecl *MD = dyn_cast<ObjCMethodDecl>(CD)) {
- const RetainSummary *Summ = Summaries.getMethodSummary(MD);
+ const RetainSummary *Summ = Summaries.getSummary(AnyCall(MD));
RE = Summ->getRetEffect();
} else if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(CD)) {
if (!isa<CXXMethodDecl>(FD)) {
- const RetainSummary *Summ = Summaries.getFunctionSummary(FD);
+ const RetainSummary *Summ = Summaries.getSummary(AnyCall(FD));
RE = Summ->getRetEffect();
}
}
@@ -1100,8 +1094,8 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
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);
+ auto R =
+ llvm::make_unique<RefLeakReport>(leakAtReturn, LOpts, N, Sym, C);
C.emitReport(std::move(R));
}
return N;
@@ -1125,11 +1119,8 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
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);
+ returnNotOwnedForOwned, C.getASTContext().getLangOpts(), N, Sym);
C.emitReport(std::move(R));
}
return N;
@@ -1145,39 +1136,15 @@ ExplodedNode * RetainCountChecker::checkReturnWithRetEffect(const ReturnStmt *S,
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();
+ const MemRegion *MR = loc.getAsRegion();
- 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
+ // Find all symbols referenced by 'val' that we are tracking
// and stop tracking them.
- state = state->scanReachableSymbols<StopTrackingCallback>(val).getState();
- C.addTransition(state);
+ if (MR && shouldEscapeRegion(MR)) {
+ state = state->scanReachableSymbols<StopTrackingCallback>(val).getState();
+ C.addTransition(state);
+ }
}
ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state,
@@ -1196,14 +1163,14 @@ ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state,
bool changed = false;
RefBindingsTy::Factory &RefBFactory = state->get_context<RefBindings>();
+ ConstraintManager &CMgr = state->getConstraintManager();
- for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ for (auto &I : B) {
// Check if the symbol is null stop tracking the symbol.
- ConstraintManager &CMgr = state->getConstraintManager();
- ConditionTruthVal AllocFailed = CMgr.isNull(state, I.getKey());
+ ConditionTruthVal AllocFailed = CMgr.isNull(state, I.first);
if (AllocFailed.isConstrainedTrue()) {
changed = true;
- B = RefBFactory.remove(B, I.getKey());
+ B = RefBFactory.remove(B, I.first);
}
}
@@ -1213,25 +1180,21 @@ ProgramStateRef RetainCountChecker::evalAssume(ProgramStateRef state,
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 {
+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>())
+
+ for (const MemRegion *I : ExplicitRegions)
+ if (const SymbolicRegion *SR = I->StripCasts()->getAs<SymbolicRegion>())
WhitelistedSymbols.insert(SR->getSymbol());
- }
- for (SymbolRef sym :
- llvm::make_range(invalidated->begin(), invalidated->end())) {
+ for (SymbolRef sym : *invalidated) {
if (WhitelistedSymbols.count(sym))
continue;
// Remove any existing reference-count binding.
@@ -1309,12 +1272,9 @@ RetainCountChecker::handleAutoreleaseCounts(ProgramStateRef state,
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());
+ auto R = llvm::make_unique<RefCountReport>(overAutorelease, LOpts, N, Sym,
+ os.str());
Ctx.emitReport(std::move(R));
}
@@ -1356,56 +1316,48 @@ RetainCountChecker::processLeaks(ProgramStateRef state,
ExplodedNode *Pred) const {
// Generate an intermediate node representing the leak point.
ExplodedNode *N = Ctx.addTransition(state, Pred);
+ const LangOptions &LOpts = Ctx.getASTContext().getLangOpts();
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));
+ for (SymbolRef L : Leaked) {
+ const RefCountBug &BT = Pred ? leakWithinFunction : leakAtReturn;
+ Ctx.emitReport(llvm::make_unique<RefLeakReport>(BT, LOpts, N, L, 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());
+ const Decl *D = LCtx->getDecl();
+ Optional<AnyCall> C = AnyCall::forDecl(D);
- if (!FD || SmrMgr.isTrustedReferenceCountImplementation(FD))
+ if (!C || SmrMgr.isTrustedReferenceCountImplementation(D))
return;
ProgramStateRef state = Ctx.getState();
- const RetainSummary *FunctionSummary = SmrMgr.getFunctionSummary(FD);
+ const RetainSummary *FunctionSummary = SmrMgr.getSummary(*C);
ArgEffects CalleeSideArgEffects = FunctionSummary->getArgEffects();
- for (unsigned idx = 0, e = FD->getNumParams(); idx != e; ++idx) {
- const ParmVarDecl *Param = FD->getParamDecl(idx);
+ for (unsigned idx = 0, e = C->param_size(); idx != e; ++idx) {
+ const ParmVarDecl *Param = C->parameters()[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));
+ if (AE) {
+ ObjKind K = AE->getObjKind();
+ if (K == ObjKind::Generalized || K == ObjKind::OS ||
+ (TrackNSCFStartParam && (K == ObjKind::ObjC || K == ObjKind::CF))) {
+ RefVal NewVal = AE->getKind() == DecRef ? RefVal::makeOwned(K, Ty)
+ : RefVal::makeNotOwned(K, Ty);
+ state = setRefBinding(state, Sym, NewVal);
+ }
}
}
@@ -1431,9 +1383,9 @@ void RetainCountChecker::checkEndFunction(const ReturnStmt *RS,
return;
}
- for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
+ for (auto &I : B) {
state = handleAutoreleaseCounts(state, Pred, /*Tag=*/nullptr, Ctx,
- I->first, I->second);
+ I.first, I.second);
if (!state)
return;
}
@@ -1448,8 +1400,8 @@ void RetainCountChecker::checkEndFunction(const ReturnStmt *RS,
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);
+ for (auto &I : B)
+ state = handleSymbolDeath(state, I.first, I.second, Leaked);
processLeaks(state, Leaked, Ctx, Pred);
}
@@ -1459,7 +1411,6 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
ExplodedNode *Pred = C.getPredecessor();
ProgramStateRef state = C.getState();
- RefBindingsTy B = state->get<RefBindings>();
SmallVector<SymbolRef, 10> Leaked;
// Update counts from autorelease pools
@@ -1492,12 +1443,10 @@ void RetainCountChecker::checkDeadSymbols(SymbolReaper &SymReaper,
// 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>();
+ RefBindingsTy B = state->get<RefBindings>();
- for (SmallVectorImpl<SymbolRef>::iterator I = Leaked.begin(),
- E = Leaked.end();
- I != E; ++I)
- B = F.remove(B, *I);
+ for (SymbolRef L : Leaked)
+ B = F.remove(B, L);
state = state->set<RefBindings>(B);
C.addTransition(state, Pred);
@@ -1513,9 +1462,9 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
Out << Sep << NL;
- for (RefBindingsTy::iterator I = B.begin(), E = B.end(); I != E; ++I) {
- Out << I->first << " : ";
- I->second.print(Out);
+ for (auto &I : B) {
+ Out << I.first << " : ";
+ I.second.print(Out);
Out << NL;
}
}
@@ -1524,24 +1473,48 @@ void RetainCountChecker::printState(raw_ostream &Out, ProgramStateRef State,
// Checker registration.
//===----------------------------------------------------------------------===//
-void ento::registerRetainCountChecker(CheckerManager &Mgr) {
- auto *Chk = Mgr.registerChecker<RetainCountChecker>();
- Chk->TrackObjCAndCFObjects = true;
+void ento::registerRetainCountBase(CheckerManager &Mgr) {
+ Mgr.registerChecker<RetainCountChecker>();
+}
+
+bool ento::shouldRegisterRetainCountBase(const LangOptions &LO) {
+ return 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");
+static bool getOption(AnalyzerOptions &Options,
+ StringRef Postfix,
+ StringRef Value) {
+ auto I = Options.Config.find(
+ (StringRef("osx.cocoa.RetainCount:") + Postfix).str());
if (I != Options.Config.end())
- return I->getValue() == "false";
+ return I->getValue() == Value;
return false;
}
+void ento::registerRetainCountChecker(CheckerManager &Mgr) {
+ auto *Chk = Mgr.getChecker<RetainCountChecker>();
+ Chk->TrackObjCAndCFObjects = true;
+ Chk->TrackNSCFStartParam = getOption(Mgr.getAnalyzerOptions(),
+ "TrackNSCFStartParam",
+ "true");
+}
+
+bool ento::shouldRegisterRetainCountChecker(const LangOptions &LO) {
+ return true;
+}
+
void ento::registerOSObjectRetainCountChecker(CheckerManager &Mgr) {
- auto *Chk = Mgr.registerChecker<RetainCountChecker>();
- if (!hasPrevCheckOSObjectOptionDisabled(Mgr.getAnalyzerOptions()))
+ auto *Chk = Mgr.getChecker<RetainCountChecker>();
+ if (!getOption(Mgr.getAnalyzerOptions(),
+ "CheckOSObject",
+ "false"))
Chk->TrackOSObjects = true;
}
+
+bool ento::shouldRegisterOSObjectRetainCountChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
index 31e2d9ae4932..124c0e5040b9 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.h
@@ -1,9 +1,8 @@
//==--- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -22,6 +21,7 @@
#include "clang/AST/DeclObjC.h"
#include "clang/AST/ParentMap.h"
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
+#include "clang/Analysis/RetainSummaryManager.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Analysis/SelectorExtras.h"
@@ -33,7 +33,6 @@
#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"
@@ -251,14 +250,21 @@ class RetainCountChecker
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;
+
+ RefCountBug useAfterRelease{this, RefCountBug::UseAfterRelease};
+ RefCountBug releaseNotOwned{this, RefCountBug::ReleaseNotOwned};
+ RefCountBug deallocNotOwned{this, RefCountBug::DeallocNotOwned};
+ RefCountBug freeNotOwned{this, RefCountBug::FreeNotOwned};
+ RefCountBug overAutorelease{this, RefCountBug::OverAutorelease};
+ RefCountBug returnNotOwnedForOwned{this, RefCountBug::ReturnNotOwnedForOwned};
+ RefCountBug leakWithinFunction{this, RefCountBug::LeakWithinFunction};
+ RefCountBug leakAtReturn{this, RefCountBug::LeakAtReturn};
+
+ CheckerProgramPointTag DeallocSentTag{this, "DeallocSent"};
+ CheckerProgramPointTag CastFailTag{this, "DynamicCastFail"};
mutable std::unique_ptr<RetainSummaryManager> Summaries;
public:
- static constexpr const char *DeallocTagDescription = "DeallocSent";
/// Track Objective-C and CoreFoundation objects.
bool TrackObjCAndCFObjects = false;
@@ -266,22 +272,15 @@ public:
/// Track sublcasses of OSObject.
bool TrackOSObjects = false;
- RetainCountChecker() {}
+ /// Track initial parameters (for the entry point) for NS/CF objects.
+ bool TrackNSCFStartParam = false;
- RefCountBug *getLeakWithinFunctionBug(const LangOptions &LOpts) const;
-
- RefCountBug *getLeakAtReturnBug(const LangOptions &LOpts) const;
+ RetainCountChecker() {};
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);
- }
+ if (!Summaries)
+ Summaries.reset(
+ new RetainSummaryManager(Ctx, TrackObjCAndCFObjects, TrackOSObjects));
return *Summaries;
}
@@ -311,7 +310,7 @@ public:
const CallEvent &Call,
CheckerContext &C) const;
- bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
ProgramStateRef evalAssume(ProgramStateRef state, SVal Cond,
bool Assumption) const;
@@ -336,6 +335,9 @@ public:
RefVal V, ArgEffect E, RefVal::Kind &hasErr,
CheckerContext &C) const;
+ const RefCountBug &errorKindToBugKind(RefVal::Kind ErrorKind,
+ SymbolRef Sym) const;
+
void processNonLeakError(ProgramStateRef St, SourceRange ErrorRange,
RefVal::Kind ErrorKind, SymbolRef Sym,
CheckerContext &C) const;
@@ -358,6 +360,14 @@ public:
CheckerContext &Ctx,
ExplodedNode *Pred = nullptr) const;
+ const CheckerProgramPointTag &getDeallocSentTag() const {
+ return DeallocSentTag;
+ }
+
+ const CheckerProgramPointTag &getCastFailTag() const {
+ return CastFailTag;
+ }
+
private:
/// Perform the necessary checks and state adjustments at the end of the
/// function.
@@ -371,11 +381,6 @@ private:
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) {
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
index cda1a928de13..796fd882ffd5 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
@@ -1,9 +1,8 @@
// 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -19,6 +18,56 @@ using namespace clang;
using namespace ento;
using namespace retaincountchecker;
+StringRef RefCountBug::bugTypeToName(RefCountBug::RefCountBugType BT) {
+ switch (BT) {
+ case UseAfterRelease:
+ return "Use-after-release";
+ case ReleaseNotOwned:
+ return "Bad release";
+ case DeallocNotOwned:
+ return "-dealloc sent to non-exclusively owned object";
+ case FreeNotOwned:
+ return "freeing non-exclusively owned object";
+ case OverAutorelease:
+ return "Object autoreleased too many times";
+ case ReturnNotOwnedForOwned:
+ return "Method should return an owned object";
+ case LeakWithinFunction:
+ return "Leak";
+ case LeakAtReturn:
+ return "Leak of returned object";
+ }
+ llvm_unreachable("Unknown RefCountBugType");
+}
+
+StringRef RefCountBug::getDescription() const {
+ switch (BT) {
+ case UseAfterRelease:
+ return "Reference-counted object is used after it is released";
+ case ReleaseNotOwned:
+ return "Incorrect decrement of the reference count of an object that is "
+ "not owned at this point by the caller";
+ case DeallocNotOwned:
+ return "-dealloc sent to object that may be referenced elsewhere";
+ case FreeNotOwned:
+ return "'free' called on an object that may be referenced elsewhere";
+ case OverAutorelease:
+ return "Object autoreleased too many times";
+ case ReturnNotOwnedForOwned:
+ return "Object with a +0 retain count returned to caller where a +1 "
+ "(owning) retain count is expected";
+ case LeakWithinFunction:
+ case LeakAtReturn:
+ return "";
+ }
+ llvm_unreachable("Unknown RefCountBugType");
+}
+
+RefCountBug::RefCountBug(const CheckerBase *Checker, RefCountBugType BT)
+ : BugType(Checker, bugTypeToName(BT), categories::MemoryRefCount,
+ /*SuppressOnSink=*/BT == LeakWithinFunction || BT == LeakAtReturn),
+ BT(BT), Checker(Checker) {}
+
static bool isNumericLiteralExpression(const Expr *E) {
// FIXME: This set of cases was copied from SemaExprObjC.
return isa<IntegerLiteral>(E) ||
@@ -42,7 +91,8 @@ static std::string getPrettyTypeName(QualType QT) {
/// 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,
+ const RefVal *PrevT,
+ const RefVal &CurrV,
bool DeallocSent) {
// Get the previous type state.
RefVal PrevV = *PrevT;
@@ -132,6 +182,31 @@ static Optional<unsigned> findArgIdxOfSymbol(ProgramStateRef CurrSt,
return None;
}
+static Optional<std::string> findMetaClassAlloc(const Expr *Callee) {
+ if (const auto *ME = dyn_cast<MemberExpr>(Callee)) {
+ if (ME->getMemberDecl()->getNameAsString() != "alloc")
+ return None;
+ const Expr *This = ME->getBase()->IgnoreParenImpCasts();
+ if (const auto *DRE = dyn_cast<DeclRefExpr>(This)) {
+ const ValueDecl *VD = DRE->getDecl();
+ if (VD->getNameAsString() != "metaClass")
+ return None;
+
+ if (const auto *RD = dyn_cast<CXXRecordDecl>(VD->getDeclContext()))
+ return RD->getNameAsString();
+
+ }
+ }
+ return None;
+}
+
+static std::string findAllocatedObjectName(const Stmt *S, QualType QT) {
+ if (const auto *CE = dyn_cast<CallExpr>(S))
+ if (auto Out = findMetaClassAlloc(CE->getCallee()))
+ return *Out;
+ return getPrettyTypeName(QT);
+}
+
static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
const LocationContext *LCtx,
const RefVal &CurrV, SymbolRef &Sym,
@@ -189,7 +264,7 @@ static void generateDiagnosticsForCallLike(ProgramStateRef CurrSt,
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())
+ os << "an OSObject of type '" << findAllocatedObjectName(S, Sym->getType())
<< "' with a ";
} else if (CurrV.getObjKind() == ObjKind::Generalized) {
os << "an object of type '" << Sym->getType().getAsString()
@@ -338,15 +413,38 @@ annotateConsumedSummaryMismatch(const ExplodedNode *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());
+ PathDiagnosticLocation L = PathDiagnosticLocation::create(CallExitLoc, SM);
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+}
+
+/// Annotate the parameter at the analysis entry point.
+static std::shared_ptr<PathDiagnosticEventPiece>
+annotateStartParameter(const ExplodedNode *N, SymbolRef Sym,
+ const SourceManager &SM) {
+ auto PP = N->getLocationAs<BlockEdge>();
+ if (!PP)
+ return nullptr;
+
+ const CFGBlock *Src = PP->getSrc();
+ const RefVal *CurrT = getRefBinding(N->getState(), Sym);
+
+ if (&Src->getParent()->getEntry() != Src || !CurrT ||
+ getRefBinding(N->getFirstPred()->getState(), Sym))
+ return nullptr;
+
+ const auto *VR = cast<VarRegion>(cast<SymbolRegionValue>(Sym)->getRegion());
+ const auto *PVD = cast<ParmVarDecl>(VR->getDecl());
+ PathDiagnosticLocation L = PathDiagnosticLocation(PVD, SM);
+
+ std::string s;
+ llvm::raw_string_ostream os(s);
+ os << "Parameter '" << PVD->getNameAsString() << "' starts at +";
+ if (CurrT->getCount() == 1) {
+ os << "1, as it is marked as consuming";
} else {
- L = PathDiagnosticLocation(
- Call->getRuntimeDefinition().getDecl()->getSourceRange().getEnd(), SM);
+ assert(CurrT->getCount() == 0);
+ os << "0";
}
-
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
@@ -354,12 +452,22 @@ std::shared_ptr<PathDiagnosticPiece>
RefCountReportVisitor::VisitNode(const ExplodedNode *N,
BugReporterContext &BRC, BugReport &BR) {
+ const auto &BT = static_cast<const RefCountBug&>(BR.getBugType());
+ const auto *Checker =
+ static_cast<const RetainCountChecker *>(BT.getChecker());
+
+ bool IsFreeUnowned = BT.getBugType() == RefCountBug::FreeNotOwned ||
+ BT.getBugType() == RefCountBug::DeallocNotOwned;
+
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;
+ if (auto PD = annotateStartParameter(N, Sym, SM))
+ return PD;
+
// FIXME: We will eventually need to handle non-statement-based events
// (__attribute__((cleanup))).
if (!N->getLocation().getAs<StmtPoint>())
@@ -372,7 +480,8 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N,
const LocationContext *LCtx = N->getLocationContext();
const RefVal* CurrT = getRefBinding(CurrSt, Sym);
- if (!CurrT) return nullptr;
+ if (!CurrT)
+ return nullptr;
const RefVal &CurrV = *CurrT;
const RefVal *PrevT = getRefBinding(PrevSt, Sym);
@@ -382,6 +491,12 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N,
std::string sbuf;
llvm::raw_string_ostream os(sbuf);
+ if (PrevT && IsFreeUnowned && CurrV.isNotOwned() && PrevT->isOwned()) {
+ os << "Object is now not exclusively owned";
+ auto Pos = PathDiagnosticLocation::create(N->getLocation(), SM);
+ return std::make_shared<PathDiagnosticEventPiece>(Pos, os.str());
+ }
+
// This is the allocation site since the previous node had no bindings
// for this symbol.
if (!PrevT) {
@@ -428,9 +543,13 @@ RefCountReportVisitor::VisitNode(const ExplodedNode *N,
// program point
bool DeallocSent = false;
- if (N->getLocation().getTag() &&
- N->getLocation().getTag()->getTagDescription().contains(
- RetainCountChecker::DeallocTagDescription)) {
+ const ProgramPointTag *Tag = N->getLocation().getTag();
+
+ if (Tag == &Checker->getCastFailTag()) {
+ os << "Assuming dynamic cast returns null due to type mismatch";
+ }
+
+ if (Tag == &Checker->getDeallocSentTag()) {
// We only have summaries attached to nodes after evaluating CallExpr and
// ObjCMessageExprs.
const Stmt *S = N->getLocation().castAs<StmtPoint>().getStmt();
@@ -587,7 +706,7 @@ static AllocationInfo GetAllocationSite(ProgramStateManager &StateMgr,
if (AllocationNodeInCurrentOrParentContext &&
AllocationNodeInCurrentOrParentContext->getLocationContext() !=
- LeakContext)
+ LeakContext)
FirstBinding = nullptr;
return AllocationInfo(AllocationNodeInCurrentOrParentContext,
@@ -671,10 +790,19 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
}
} 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";
+ ObjKind K = RV->getObjKind();
+ if (K == ObjKind::ObjC || K == ObjKind::CF) {
+ 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 if (RV->getObjKind() == ObjKind::OS) {
+ std::string FuncName = FD->getNameAsString();
+ os << "whose name ('" << FuncName
+ << "') starts with '" << StringRef(FuncName).substr(0, 3) << "'";
+ }
}
}
} else {
@@ -685,15 +813,15 @@ RefLeakReportVisitor::getEndPath(BugReporterContext &BRC,
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
-RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
+RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
ExplodedNode *n, SymbolRef sym,
- bool registerVisitor)
- : BugReport(D, D.getDescription(), n), Sym(sym) {
- if (registerVisitor)
+ bool isLeak)
+ : BugReport(D, D.getDescription(), n), Sym(sym), isLeak(isLeak) {
+ if (!isLeak)
addVisitor(llvm::make_unique<RefCountReportVisitor>(sym));
}
-RefCountReport::RefCountReport(RefCountBug &D, const LangOptions &LOpts,
+RefCountReport::RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
ExplodedNode *n, SymbolRef sym,
StringRef endText)
: BugReport(D, D.getDescription(), endText, n) {
@@ -779,10 +907,10 @@ void RefLeakReport::createDescription(CheckerContext &Ctx) {
}
}
-RefLeakReport::RefLeakReport(RefCountBug &D, const LangOptions &LOpts,
+RefLeakReport::RefLeakReport(const RefCountBug &D, const LangOptions &LOpts,
ExplodedNode *n, SymbolRef sym,
CheckerContext &Ctx)
- : RefCountReport(D, LOpts, n, sym, false) {
+ : RefCountReport(D, LOpts, n, sym, /*isLeak=*/true) {
deriveAllocLocation(Ctx, sym);
if (!AllocBinding)
diff --git a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h
index 9f796abe8eae..ef3c75f87af5 100644
--- a/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h
+++ b/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.h
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -15,42 +14,61 @@
#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H
#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_RETAINCOUNTCHECKER_DIAGNOSTICS_H
+#include "clang/Analysis/RetainSummaryManager.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"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.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;
+ enum RefCountBugType {
+ UseAfterRelease,
+ ReleaseNotOwned,
+ DeallocNotOwned,
+ FreeNotOwned,
+ OverAutorelease,
+ ReturnNotOwnedForOwned,
+ LeakWithinFunction,
+ LeakAtReturn,
+ };
+ RefCountBug(const CheckerBase *checker, RefCountBugType BT);
+ StringRef getDescription() const;
+
+ RefCountBugType getBugType() const {
+ return BT;
+ }
+
+ const CheckerBase *getChecker() const {
+ return Checker;
+ }
- virtual bool isLeak() const { return false; }
+private:
+ RefCountBugType BT;
+ const CheckerBase *Checker;
+ static StringRef bugTypeToName(RefCountBugType BT);
};
class RefCountReport : public BugReport {
protected:
SymbolRef Sym;
+ bool isLeak = false;
public:
- RefCountReport(RefCountBug &D, const LangOptions &LOpts,
+ RefCountReport(const RefCountBug &D, const LangOptions &LOpts,
ExplodedNode *n, SymbolRef sym,
- bool registerVisitor = true);
+ bool isLeak=false);
- RefCountReport(RefCountBug &D, const LangOptions &LOpts,
+ RefCountReport(const 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())
+ if (!isLeak)
return BugReport::getRanges();
return llvm::make_range(ranges_iterator(), ranges_iterator());
}
@@ -69,7 +87,7 @@ class RefLeakReport : public RefCountReport {
void createDescription(CheckerContext &Ctx);
public:
- RefLeakReport(RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n,
+ RefLeakReport(const RefCountBug &D, const LangOptions &LOpts, ExplodedNode *n,
SymbolRef sym, CheckerContext &Ctx);
PathDiagnosticLocation getLocation(const SourceManager &SM) const override {
diff --git a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
index 17ef39531628..9eb47e0526dc 100644
--- a/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -1,9 +1,8 @@
//== ReturnPointerRangeChecker.cpp ------------------------------*- C++ -*--==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -90,3 +89,7 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
void ento::registerReturnPointerRangeChecker(CheckerManager &mgr) {
mgr.registerChecker<ReturnPointerRangeChecker>();
}
+
+bool ento::shouldRegisterReturnPointerRangeChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
index 3e0613e8ba68..f55c369da67e 100644
--- a/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ReturnUndefChecker.cpp
@@ -1,9 +1,8 @@
//== ReturnUndefChecker.cpp -------------------------------------*- C++ -*--==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -121,3 +120,7 @@ void ReturnUndefChecker::checkReference(CheckerContext &C, const Expr *RetE,
void ento::registerReturnUndefChecker(CheckerManager &mgr) {
mgr.registerChecker<ReturnUndefChecker>();
}
+
+bool ento::shouldRegisterReturnUndefChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp b/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
new file mode 100644
index 000000000000..103208d8b5a5
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
@@ -0,0 +1,170 @@
+//===- ReturnValueChecker - Applies guaranteed return values ----*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This defines ReturnValueChecker, which checks for calls with guaranteed
+// boolean return value. It ensures the return value of each function call.
+//
+//===----------------------------------------------------------------------===//
+
+#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"
+#include "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class ReturnValueChecker : public Checker<check::PostCall, check::EndFunction> {
+public:
+ // It sets the predefined invariant ('CDM') if the current call not break it.
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+ // It reports whether a predefined invariant ('CDM') is broken.
+ void checkEndFunction(const ReturnStmt *RS, CheckerContext &C) const;
+
+private:
+ // The pairs are in the following form: {{{class, call}}, return value}
+ const CallDescriptionMap<bool> CDM = {
+ // These are known in the LLVM project: 'Error()'
+ {{{"ARMAsmParser", "Error"}}, true},
+ {{{"HexagonAsmParser", "Error"}}, true},
+ {{{"LLLexer", "Error"}}, true},
+ {{{"LLParser", "Error"}}, true},
+ {{{"MCAsmParser", "Error"}}, true},
+ {{{"MCAsmParserExtension", "Error"}}, true},
+ {{{"TGParser", "Error"}}, true},
+ {{{"X86AsmParser", "Error"}}, true},
+ // 'TokError()'
+ {{{"LLParser", "TokError"}}, true},
+ {{{"MCAsmParser", "TokError"}}, true},
+ {{{"MCAsmParserExtension", "TokError"}}, true},
+ {{{"TGParser", "TokError"}}, true},
+ // 'error()'
+ {{{"MIParser", "error"}}, true},
+ {{{"WasmAsmParser", "error"}}, true},
+ {{{"WebAssemblyAsmParser", "error"}}, true},
+ // Other
+ {{{"AsmParser", "printError"}}, true}};
+};
+} // namespace
+
+static std::string getName(const CallEvent &Call) {
+ std::string Name = "";
+ if (const auto *MD = dyn_cast<CXXMethodDecl>(Call.getDecl()))
+ if (const CXXRecordDecl *RD = MD->getParent())
+ Name += RD->getNameAsString() + "::";
+
+ Name += Call.getCalleeIdentifier()->getName();
+ return Name;
+}
+
+// The predefinitions ('CDM') could break due to the ever growing code base.
+// Check for the expected invariants and see whether they apply.
+static Optional<bool> isInvariantBreak(bool ExpectedValue, SVal ReturnV,
+ CheckerContext &C) {
+ auto ReturnDV = ReturnV.getAs<DefinedOrUnknownSVal>();
+ if (!ReturnDV)
+ return None;
+
+ if (ExpectedValue)
+ return C.getState()->isNull(*ReturnDV).isConstrainedTrue();
+
+ return C.getState()->isNull(*ReturnDV).isConstrainedFalse();
+}
+
+void ReturnValueChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ const bool *RawExpectedValue = CDM.lookup(Call);
+ if (!RawExpectedValue)
+ return;
+
+ SVal ReturnV = Call.getReturnValue();
+ bool ExpectedValue = *RawExpectedValue;
+ Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
+ if (!IsInvariantBreak)
+ return;
+
+ // If the invariant is broken it is reported by 'checkEndFunction()'.
+ if (*IsInvariantBreak)
+ return;
+
+ std::string Name = getName(Call);
+ const NoteTag *CallTag = C.getNoteTag(
+ [Name, ExpectedValue](BugReport &) -> std::string {
+ SmallString<128> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+
+ Out << '\'' << Name << "' returns "
+ << (ExpectedValue ? "true" : "false");
+ return Out.str();
+ },
+ /*IsPrunable=*/true);
+
+ ProgramStateRef State = C.getState();
+ State = State->assume(ReturnV.castAs<DefinedOrUnknownSVal>(), ExpectedValue);
+ C.addTransition(State, CallTag);
+}
+
+void ReturnValueChecker::checkEndFunction(const ReturnStmt *RS,
+ CheckerContext &C) const {
+ if (!RS || !RS->getRetValue())
+ return;
+
+ // We cannot get the caller in the top-frame.
+ const StackFrameContext *SFC = C.getStackFrame();
+ if (C.getStackFrame()->inTopFrame())
+ return;
+
+ ProgramStateRef State = C.getState();
+ CallEventManager &CMgr = C.getStateManager().getCallEventManager();
+ CallEventRef<> Call = CMgr.getCaller(SFC, State);
+ if (!Call)
+ return;
+
+ const bool *RawExpectedValue = CDM.lookup(*Call);
+ if (!RawExpectedValue)
+ return;
+
+ SVal ReturnV = State->getSVal(RS->getRetValue(), C.getLocationContext());
+ bool ExpectedValue = *RawExpectedValue;
+ Optional<bool> IsInvariantBreak = isInvariantBreak(ExpectedValue, ReturnV, C);
+ if (!IsInvariantBreak)
+ return;
+
+ // If the invariant is appropriate it is reported by 'checkPostCall()'.
+ if (!*IsInvariantBreak)
+ return;
+
+ std::string Name = getName(*Call);
+ const NoteTag *CallTag = C.getNoteTag(
+ [Name, ExpectedValue](BugReport &BR) -> std::string {
+ SmallString<128> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+
+ // The following is swapped because the invariant is broken.
+ Out << '\'' << Name << "' returns "
+ << (ExpectedValue ? "false" : "true");
+
+ return Out.str();
+ },
+ /*IsPrunable=*/false);
+
+ C.addTransition(State, CallTag);
+}
+
+void ento::registerReturnValueChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<ReturnValueChecker>();
+}
+
+bool ento::shouldRegisterReturnValueChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp b/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
index cf03b3c21132..5e305aa709b6 100644
--- a/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/RunLoopAutoreleaseLeakChecker.cpp
@@ -1,9 +1,8 @@
//=- RunLoopAutoreleaseLeakChecker.cpp --------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//
//===----------------------------------------------------------------------===//
@@ -116,7 +115,7 @@ static void emitDiagnostics(BoundNodes &Match,
BR.EmitBasicReport(ADC->getDecl(), Checker,
/*Name=*/"Memory leak inside autorelease pool",
- /*Category=*/"Memory",
+ /*BugCategory=*/"Memory",
/*Name=*/
(Twine("Temporary objects allocated in the") +
" autorelease pool " +
@@ -203,3 +202,7 @@ void RunLoopAutoreleaseLeakChecker::checkASTCodeBody(const Decl *D,
void ento::registerRunLoopAutoreleaseLeakChecker(CheckerManager &mgr) {
mgr.registerChecker<RunLoopAutoreleaseLeakChecker>();
}
+
+bool ento::shouldRegisterRunLoopAutoreleaseLeakChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
index 819d437e6883..ec5e9622c236 100644
--- a/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
@@ -1,9 +1,8 @@
//===-- SimpleStreamChecker.cpp -----------------------------------------*- C++ -*--//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -109,10 +108,10 @@ SimpleStreamChecker::SimpleStreamChecker()
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);
+ LeakBugType.reset(
+ new BugType(this, "Resource Leak", "Unix Stream API Error",
+ /*SuppressOnSink=*/true));
}
void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
@@ -269,3 +268,8 @@ SimpleStreamChecker::checkPointerEscape(ProgramStateRef State,
void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
mgr.registerChecker<SimpleStreamChecker>();
}
+
+// This checker should be enabled regardless of how language options are set.
+bool ento::shouldRegisterSimpleStreamChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
new file mode 100644
index 000000000000..fd372aafa50d
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
@@ -0,0 +1,72 @@
+// SmartPtrModeling.cpp - Model behavior of C++ smart pointers - C++ ------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines a checker that models various aspects of
+// C++ smart pointer behavior.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Move.h"
+
+#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"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class SmartPtrModeling : public Checker<eval::Call> {
+ bool isNullAfterMoveMethod(const CallEvent &Call) const;
+
+public:
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
+};
+} // end of anonymous namespace
+
+bool SmartPtrModeling::isNullAfterMoveMethod(const CallEvent &Call) const {
+ // TODO: Update CallDescription to support anonymous calls?
+ // TODO: Handle other methods, such as .get() or .release().
+ // But once we do, we'd need a visitor to explain null dereferences
+ // that are found via such modeling.
+ const auto *CD = dyn_cast_or_null<CXXConversionDecl>(Call.getDecl());
+ return CD && CD->getConversionType()->isBooleanType();
+}
+
+bool SmartPtrModeling::evalCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!isNullAfterMoveMethod(Call))
+ return false;
+
+ ProgramStateRef State = C.getState();
+ const MemRegion *ThisR =
+ cast<CXXInstanceCall>(&Call)->getCXXThisVal().getAsRegion();
+
+ if (!move::isMovedFrom(State, ThisR)) {
+ // TODO: Model this case as well. At least, avoid invalidation of globals.
+ return false;
+ }
+
+ // TODO: Add a note to bug reports describing this decision.
+ C.addTransition(
+ State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
+ C.getSValBuilder().makeZeroVal(Call.getResultType())));
+ return true;
+}
+
+void ento::registerSmartPtrModeling(CheckerManager &Mgr) {
+ Mgr.registerChecker<SmartPtrModeling>();
+}
+
+bool ento::shouldRegisterSmartPtrModeling(const LangOptions &LO) {
+ return LO.CPlusPlus;
+}
diff --git a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index 0f53d826a5f6..b93bed5c3097 100644
--- a/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -1,9 +1,8 @@
//=== StackAddrEscapeChecker.cpp ----------------------------------*- C++ -*--//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -360,11 +359,23 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
}
}
-#define REGISTER_CHECKER(name) \
- void ento::register##name(CheckerManager &Mgr) { \
- StackAddrEscapeChecker *Chk = \
- Mgr.registerChecker<StackAddrEscapeChecker>(); \
- Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
+void ento::registerStackAddrEscapeBase(CheckerManager &mgr) {
+ mgr.registerChecker<StackAddrEscapeChecker>();
+}
+
+bool ento::shouldRegisterStackAddrEscapeBase(const LangOptions &LO) {
+ return true;
+}
+
+#define REGISTER_CHECKER(name) \
+ void ento::register##name(CheckerManager &Mgr) { \
+ StackAddrEscapeChecker *Chk = \
+ Mgr.getChecker<StackAddrEscapeChecker>(); \
+ Chk->ChecksEnabled[StackAddrEscapeChecker::CK_##name] = true; \
+ } \
+ \
+ bool ento::shouldRegister##name(const LangOptions &LO) { \
+ return true; \
}
REGISTER_CHECKER(StackAddrEscapeChecker)
diff --git a/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index 6478128ce954..2cdee8da375e 100644
--- a/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -1,9 +1,8 @@
//=== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -225,7 +224,7 @@ class StdLibraryFunctionsChecker : public Checker<check::PostCall, eval::Call> {
public:
void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
- bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
private:
Optional<FunctionSummaryTy> findFunctionSummary(const FunctionDecl *FD,
@@ -368,12 +367,16 @@ void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call,
}
}
-bool StdLibraryFunctionsChecker::evalCall(const CallExpr *CE,
+bool StdLibraryFunctionsChecker::evalCall(const CallEvent &Call,
CheckerContext &C) const {
- const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(CE->getCalleeDecl());
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!FD)
return false;
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return false;
+
Optional<FunctionSummaryTy> FoundSummary = findFunctionSummary(FD, CE, C);
if (!FoundSummary)
return false;
@@ -1056,3 +1059,7 @@ void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
// class, turning on different function summaries.
mgr.registerChecker<StdLibraryFunctionsChecker>();
}
+
+bool ento::shouldRegisterStdCLibraryFunctionsChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index 92647f032730..1ea5e0769513 100644
--- a/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -1,9 +1,8 @@
//===-- StreamChecker.cpp -----------------------------------------*- C++ -*--//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -15,6 +14,7 @@
#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"
@@ -72,7 +72,7 @@ public:
II_fsetpos(nullptr), II_clearerr(nullptr), II_feof(nullptr),
II_ferror(nullptr), II_fileno(nullptr) {}
- bool evalCall(const CallExpr *CE, CheckerContext &C) const;
+ bool evalCall(const CallEvent &Call, CheckerContext &C) const;
void checkDeadSymbols(SymbolReaper &SymReaper, CheckerContext &C) const;
private:
@@ -104,11 +104,15 @@ private:
REGISTER_MAP_WITH_PROGRAMSTATE(StreamMap, SymbolRef, StreamState)
-bool StreamChecker::evalCall(const CallExpr *CE, CheckerContext &C) const {
- const FunctionDecl *FD = C.getCalleeDecl(CE);
+bool StreamChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
if (!FD || FD->getKind() != Decl::Function)
return false;
+ const auto *CE = dyn_cast_or_null<CallExpr>(Call.getOriginExpr());
+ if (!CE)
+ return false;
+
ASTContext &Ctx = C.getASTContext();
if (!II_fopen)
II_fopen = &Ctx.Idents.get("fopen");
@@ -409,3 +413,7 @@ void StreamChecker::checkDeadSymbols(SymbolReaper &SymReaper,
void ento::registerStreamChecker(CheckerManager &mgr) {
mgr.registerChecker<StreamChecker>();
}
+
+bool ento::shouldRegisterStreamChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/Taint.cpp b/lib/StaticAnalyzer/Checkers/Taint.cpp
new file mode 100644
index 000000000000..bc120949ee4f
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/Taint.cpp
@@ -0,0 +1,227 @@
+//=== Taint.cpp - Taint tracking and basic propagation rules. ------*- C++ -*-//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines basic, non-domain-specific mechanisms for tracking tainted values.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Taint.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
+
+using namespace clang;
+using namespace ento;
+using namespace taint;
+
+// Fully tainted symbols.
+REGISTER_MAP_WITH_PROGRAMSTATE(TaintMap, SymbolRef, TaintTagType)
+
+// Partially tainted symbols.
+REGISTER_MAP_FACTORY_WITH_PROGRAMSTATE(TaintedSubRegions, const SubRegion *,
+ TaintTagType)
+REGISTER_MAP_WITH_PROGRAMSTATE(DerivedSymTaint, SymbolRef, TaintedSubRegions)
+
+void taint::printTaint(ProgramStateRef State, raw_ostream &Out, const char *NL,
+ const char *Sep) {
+ TaintMapTy TM = State->get<TaintMap>();
+
+ if (!TM.isEmpty())
+ Out << "Tainted symbols:" << NL;
+
+ for (const auto &I : TM)
+ Out << I.first << " : " << I.second << NL;
+}
+
+void dumpTaint(ProgramStateRef State) {
+ printTaint(State, llvm::errs());
+}
+
+ProgramStateRef taint::addTaint(ProgramStateRef State, const Stmt *S,
+ const LocationContext *LCtx,
+ TaintTagType Kind) {
+ return addTaint(State, State->getSVal(S, LCtx), Kind);
+}
+
+ProgramStateRef taint::addTaint(ProgramStateRef State, SVal V,
+ TaintTagType Kind) {
+ SymbolRef Sym = V.getAsSymbol();
+ if (Sym)
+ return addTaint(State, 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 =
+ State->getStateManager().getStoreManager()
+ .getDefaultBinding(*LCV)) {
+ if (SymbolRef Sym = binding->getAsSymbol())
+ return addPartialTaint(State, Sym, LCV->getRegion(), Kind);
+ }
+ }
+
+ const MemRegion *R = V.getAsRegion();
+ return addTaint(State, R, Kind);
+}
+
+ProgramStateRef taint::addTaint(ProgramStateRef State, const MemRegion *R,
+ TaintTagType Kind) {
+ if (const SymbolicRegion *SR = dyn_cast_or_null<SymbolicRegion>(R))
+ return addTaint(State, SR->getSymbol(), Kind);
+ return State;
+}
+
+ProgramStateRef taint::addTaint(ProgramStateRef State, SymbolRef Sym,
+ TaintTagType Kind) {
+ // 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 = State->set<TaintMap>(Sym, Kind);
+ assert(NewState);
+ return NewState;
+}
+
+ProgramStateRef taint::addPartialTaint(ProgramStateRef State,
+ SymbolRef ParentSym,
+ const SubRegion *SubRegion,
+ TaintTagType Kind) {
+ // Ignore partial taint if the entire parent symbol is already tainted.
+ if (const TaintTagType *T = State->get<TaintMap>(ParentSym))
+ if (*T == Kind)
+ return State;
+
+ // Partial taint applies if only a portion of the symbol is tainted.
+ if (SubRegion == SubRegion->getBaseRegion())
+ return addTaint(State, ParentSym, Kind);
+
+ const TaintedSubRegions *SavedRegs = State->get<DerivedSymTaint>(ParentSym);
+ TaintedSubRegions::Factory &F = State->get_context<TaintedSubRegions>();
+ TaintedSubRegions Regs = SavedRegs ? *SavedRegs : F.getEmptyMap();
+
+ Regs = F.add(Regs, SubRegion, Kind);
+ ProgramStateRef NewState = State->set<DerivedSymTaint>(ParentSym, Regs);
+ assert(NewState);
+ return NewState;
+}
+
+bool taint::isTainted(ProgramStateRef State, const Stmt *S,
+ const LocationContext *LCtx, TaintTagType Kind) {
+ SVal val = State->getSVal(S, LCtx);
+ return isTainted(State, val, Kind);
+}
+
+bool taint::isTainted(ProgramStateRef State, SVal V, TaintTagType Kind) {
+ if (const SymExpr *Sym = V.getAsSymExpr())
+ return isTainted(State, Sym, Kind);
+ if (const MemRegion *Reg = V.getAsRegion())
+ return isTainted(State, Reg, Kind);
+ return false;
+}
+
+bool taint::isTainted(ProgramStateRef State, const MemRegion *Reg,
+ TaintTagType K) {
+ 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(State, ER->getSuperRegion(), K) ||
+ isTainted(State, ER->getIndex(), K);
+
+ if (const SymbolicRegion *SR = dyn_cast<SymbolicRegion>(Reg))
+ return isTainted(State, SR->getSymbol(), K);
+
+ if (const SubRegion *ER = dyn_cast<SubRegion>(Reg))
+ return isTainted(State, ER->getSuperRegion(), K);
+
+ return false;
+}
+
+bool taint::isTainted(ProgramStateRef State, SymbolRef Sym, TaintTagType Kind) {
+ 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 = State->get<TaintMap>(*SI)) {
+ if (*Tag == Kind)
+ return true;
+ }
+
+ if (const auto *SD = dyn_cast<SymbolDerived>(*SI)) {
+ // If this is a SymbolDerived with a tainted parent, it's also tainted.
+ if (isTainted(State, 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 =
+ State->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 auto *SRV = dyn_cast<SymbolRegionValue>(*SI)) {
+ if (isTainted(State, SRV->getRegion(), Kind))
+ return true;
+ }
+
+ // If this is a SymbolCast from a tainted value, it's also tainted.
+ if (const auto *SC = dyn_cast<SymbolCast>(*SI)) {
+ if (isTainted(State, SC->getOperand(), Kind))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+TaintBugVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
+ BugReport &BR) {
+
+ // Find the ExplodedNode where the taint was first introduced
+ if (!isTainted(N->getState(), V) ||
+ isTainted(N->getFirstPred()->getState(), 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");
+}
diff --git a/lib/StaticAnalyzer/Checkers/Taint.h b/lib/StaticAnalyzer/Checkers/Taint.h
new file mode 100644
index 000000000000..72cf6a79d52c
--- /dev/null
+++ b/lib/StaticAnalyzer/Checkers/Taint.h
@@ -0,0 +1,102 @@
+//=== Taint.h - Taint tracking and basic propagation rules. --------*- C++ -*-//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// Defines basic, non-domain-specific mechanisms for tracking tainted values.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAINT_H
+#define LLVM_CLANG_LIB_STATICANALYZER_CHECKERS_TAINT_H
+
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
+
+namespace clang {
+namespace ento {
+namespace taint {
+
+/// The type of taint, which helps to differentiate between different types of
+/// taint.
+using TaintTagType = unsigned;
+
+static constexpr TaintTagType TaintTagGeneric = 0;
+
+/// Create a new state in which the value of the statement is marked as tainted.
+LLVM_NODISCARD ProgramStateRef
+addTaint(ProgramStateRef State, const Stmt *S, const LocationContext *LCtx,
+ TaintTagType Kind = TaintTagGeneric);
+
+/// Create a new state in which the value is marked as tainted.
+LLVM_NODISCARD ProgramStateRef
+addTaint(ProgramStateRef State, SVal V,
+ TaintTagType Kind = TaintTagGeneric);
+
+/// Create a new state in which the symbol is marked as tainted.
+LLVM_NODISCARD ProgramStateRef
+addTaint(ProgramStateRef State, SymbolRef Sym,
+ TaintTagType Kind = TaintTagGeneric);
+
+/// Create a new state in which the pointer represented by the region
+/// is marked as tainted.
+LLVM_NODISCARD ProgramStateRef
+addTaint(ProgramStateRef State, const MemRegion *R,
+ TaintTagType Kind = TaintTagGeneric);
+
+/// Create a new state in a which a sub-region of a given symbol is tainted.
+/// This might be necessary when referring to regions that can not have an
+/// individual symbol, e.g. if they are represented by the default binding of
+/// a LazyCompoundVal.
+LLVM_NODISCARD ProgramStateRef
+addPartialTaint(ProgramStateRef State,
+ SymbolRef ParentSym, const SubRegion *SubRegion,
+ TaintTagType Kind = TaintTagGeneric);
+
+/// Check if the statement has a tainted value in the given state.
+bool isTainted(ProgramStateRef State, const Stmt *S,
+ const LocationContext *LCtx,
+ TaintTagType Kind = TaintTagGeneric);
+
+/// Check if the value is tainted in the given state.
+bool isTainted(ProgramStateRef State, SVal V,
+ TaintTagType Kind = TaintTagGeneric);
+
+/// Check if the symbol is tainted in the given state.
+bool isTainted(ProgramStateRef State, SymbolRef Sym,
+ TaintTagType Kind = TaintTagGeneric);
+
+/// Check if the pointer represented by the region is tainted in the given
+/// state.
+bool isTainted(ProgramStateRef State, const MemRegion *Reg,
+ TaintTagType Kind = TaintTagGeneric);
+
+void printTaint(ProgramStateRef State, raw_ostream &Out, const char *nl = "\n",
+ const char *sep = "");
+
+LLVM_DUMP_METHOD void dumpTaint(ProgramStateRef State);
+
+/// The bug visitor prints a diagnostic message at the location where a given
+/// variable was tainted.
+class TaintBugVisitor final : public BugReporterVisitor {
+private:
+ const SVal V;
+
+public:
+ TaintBugVisitor(const SVal V) : V(V) {}
+ void Profile(llvm::FoldingSetNodeID &ID) const override { ID.Add(V); }
+
+ std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) override;
+};
+
+} // namespace taint
+} // namespace ento
+} // namespace clang
+
+#endif
+
diff --git a/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp b/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
index 3aa8e95d0ad0..094762e2faf8 100644
--- a/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/TaintTesterChecker.cpp
@@ -1,15 +1,16 @@
//== TaintTesterChecker.cpp ----------------------------------- -*- C++ -*--=//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This checker can be used for testing how taint data is propagated.
//
//===----------------------------------------------------------------------===//
+
+#include "Taint.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
@@ -18,6 +19,7 @@
using namespace clang;
using namespace ento;
+using namespace taint;
namespace {
class TaintTesterChecker : public Checker< check::PostStmt<Expr> > {
@@ -47,7 +49,7 @@ void TaintTesterChecker::checkPostStmt(const Expr *E,
if (!State)
return;
- if (State->isTainted(E, C.getLocationContext())) {
+ if (isTainted(State, E, C.getLocationContext())) {
if (ExplodedNode *N = C.generateNonFatalErrorNode()) {
initBugType();
auto report = llvm::make_unique<BugReport>(*BT, "tainted",N);
@@ -60,3 +62,7 @@ void TaintTesterChecker::checkPostStmt(const Expr *E,
void ento::registerTaintTesterChecker(CheckerManager &mgr) {
mgr.registerChecker<TaintTesterChecker>();
}
+
+bool ento::shouldRegisterTaintTesterChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp b/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
index 527e371571f1..7a33845a6a26 100644
--- a/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/TestAfterDivZeroChecker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -261,3 +260,7 @@ void TestAfterDivZeroChecker::checkBranchCondition(const Stmt *Condition,
void ento::registerTestAfterDivZeroChecker(CheckerManager &mgr) {
mgr.registerChecker<TestAfterDivZeroChecker>();
}
+
+bool ento::shouldRegisterTestAfterDivZeroChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
index 2f06469bb209..73183aa468f6 100644
--- a/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/TraversalChecker.cpp
@@ -1,9 +1,8 @@
//== TraversalChecker.cpp -------------------------------------- -*- C++ -*--=//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -65,6 +64,10 @@ void ento::registerTraversalDumper(CheckerManager &mgr) {
mgr.registerChecker<TraversalDumper>();
}
+bool ento::shouldRegisterTraversalDumper(const LangOptions &LO) {
+ return true;
+}
+
//------------------------------------------------------------------------------
namespace {
@@ -112,3 +115,7 @@ void CallDumper::checkPostCall(const CallEvent &Call, CheckerContext &C) const {
void ento::registerCallDumper(CheckerManager &mgr) {
mgr.registerChecker<CallDumper>();
}
+
+bool ento::shouldRegisterCallDumper(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp b/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
index 5e777803af00..62a4c2ab0209 100644
--- a/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/TrustNonnullChecker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -88,7 +87,7 @@ public:
if (isNonNullPtr(Call, C))
if (auto L = Call.getReturnValue().getAs<Loc>())
- State = State->assume(*L, /*Assumption=*/true);
+ State = State->assume(*L, /*assumption=*/true);
C.addTransition(State);
}
@@ -107,7 +106,7 @@ public:
(Msg.getSelector() == SetObjectForKeyedSubscriptSel ||
Msg.getSelector() == SetObjectForKeySel)) {
if (auto L = Msg.getArgSVal(1).getAs<Loc>())
- State = State->assume(*L, /*Assumption=*/true);
+ State = State->assume(*L, /*assumption=*/true);
}
// Record an implication: index is non-null if the output is non-null.
@@ -249,7 +248,10 @@ private:
} // end empty namespace
-
void ento::registerTrustNonnullChecker(CheckerManager &Mgr) {
Mgr.registerChecker<TrustNonnullChecker>(Mgr.getASTContext());
}
+
+bool ento::shouldRegisterTrustNonnullChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
index d7fad4e475ab..3a4a1dbf641b 100644
--- a/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefBranchChecker.cpp
@@ -1,9 +1,8 @@
//=== UndefBranchChecker.cpp -----------------------------------*- C++ -*--===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -109,3 +108,7 @@ void UndefBranchChecker::checkBranchCondition(const Stmt *Condition,
void ento::registerUndefBranchChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefBranchChecker>();
}
+
+bool ento::shouldRegisterUndefBranchChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
index 8a625227b81e..c787ef58660a 100644
--- a/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefCapturedBlockVarChecker.cpp
@@ -1,9 +1,8 @@
// 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -100,3 +99,7 @@ UndefCapturedBlockVarChecker::checkPostStmt(const BlockExpr *BE,
void ento::registerUndefCapturedBlockVarChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefCapturedBlockVarChecker>();
}
+
+bool ento::shouldRegisterUndefCapturedBlockVarChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
index 624cff6048fd..1ae287d39f11 100644
--- a/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefResultChecker.cpp
@@ -1,9 +1,8 @@
//=== UndefResultChecker.cpp ------------------------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -186,3 +185,7 @@ void UndefResultChecker::checkPostStmt(const BinaryOperator *B,
void ento::registerUndefResultChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefResultChecker>();
}
+
+bool ento::shouldRegisterUndefResultChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
index 1d78d7cebd67..4c517d6f0562 100644
--- a/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefinedArraySubscriptChecker.cpp
@@ -1,9 +1,8 @@
//===--- UndefinedArraySubscriptChecker.h ----------------------*- C++ -*--===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -62,3 +61,7 @@ UndefinedArraySubscriptChecker::checkPreStmt(const ArraySubscriptExpr *A,
void ento::registerUndefinedArraySubscriptChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefinedArraySubscriptChecker>();
}
+
+bool ento::shouldRegisterUndefinedArraySubscriptChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
index 8e10bfdd2f3c..d32d2a4042de 100644
--- a/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UndefinedAssignmentChecker.cpp
@@ -1,9 +1,8 @@
//===--- UndefinedAssignmentChecker.h ---------------------------*- C++ -*--==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -120,3 +119,7 @@ void UndefinedAssignmentChecker::checkBind(SVal location, SVal val,
void ento::registerUndefinedAssignmentChecker(CheckerManager &mgr) {
mgr.registerChecker<UndefinedAssignmentChecker>();
}
+
+bool ento::shouldRegisterUndefinedAssignmentChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
index c3291a21c164..2fcdd6086309 100644
--- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
+++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObject.h
@@ -1,9 +1,8 @@
//===----- UninitializedObject.h ---------------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -18,7 +17,7 @@
// 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`.
+// `-analyzer-config optin.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
@@ -26,27 +25,34 @@
// to it in notes. Defaults to false.
//
// `-analyzer-config \
-// alpha.cplusplus.UninitializedObject:NotesAsWarnings=true`.
+// optin.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`.
+// optin.cplusplus.UninitializedObject:CheckPointeeInitialization=true`.
+//
+// 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.
//
// - "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"`.
+// optin.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.
+// - "IgnoreGuardedFields" (boolean). If set to true, the checker will analyze
+// _syntactically_ whether the found uninitialized object is used without a
+// preceding assert call. Defaults to false.
+//
+// `-analyzer-config \
+// optin.cplusplus.UninitializedObject:IgnoreGuardedFields=true`.
//
// Most of the following methods as well as the checker itself is defined in
// UninitializedObjectChecker.cpp.
@@ -69,6 +75,7 @@ struct UninitObjCheckerOptions {
bool ShouldConvertNotesToWarnings = false;
bool CheckPointeeInitialization = false;
std::string IgnoredRecordsWithFieldPattern;
+ bool IgnoreGuardedFields = false;
};
/// A lightweight polymorphic wrapper around FieldRegion *. We'll use this
@@ -316,8 +323,8 @@ private:
/// 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();
+ T->isFunctionType() || T->isAtomicType() ||
+ T->isVectorType() || T->isScalarType();
}
inline bool isDereferencableType(const QualType &T) {
diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
index 208e303e8295..9d608c12d19b 100644
--- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
@@ -1,9 +1,8 @@
//===----- UninitializedObjectChecker.cpp ------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -20,6 +19,8 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "UninitializedObject.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Driver/DriverDiagnostic.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -27,6 +28,7 @@
using namespace clang;
using namespace clang::ento;
+using namespace clang::ast_matchers;
/// We'll mark fields (and pointee of fields) that are confirmed to be
/// uninitialized as already analyzed.
@@ -119,6 +121,16 @@ static bool willObjectBeAnalyzedLater(const CXXConstructorDecl *Ctor,
/// \p Pattern.
static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern);
+/// Checks _syntactically_ whether it is possible to access FD from the record
+/// that contains it without a preceding assert (even if that access happens
+/// inside a method). This is mainly used for records that act like unions, like
+/// having multiple bit fields, with only a fraction being properly initialized.
+/// If these fields are properly guarded with asserts, this method returns
+/// false.
+///
+/// Since this check is done syntactically, this method could be inaccurate.
+static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State);
+
//===----------------------------------------------------------------------===//
// Methods for UninitializedObjectChecker.
//===----------------------------------------------------------------------===//
@@ -235,6 +247,13 @@ bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,
"One must also pass the pointee region as a parameter for "
"dereferenceable fields!");
+ if (State->getStateManager().getContext().getSourceManager().isInSystemHeader(
+ FR->getDecl()->getLocation()))
+ return false;
+
+ if (Opts.IgnoreGuardedFields && !hasUnguardedAccess(FR->getDecl(), State))
+ return false;
+
if (State->contains<AnalyzedRegions>(FR))
return false;
@@ -247,13 +266,10 @@ bool FindUninitializedFields::addFieldToUninits(FieldChainInfo Chain,
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;
}
@@ -442,8 +458,8 @@ static const TypedValueRegion *
getConstructedRegion(const CXXConstructorDecl *CtorDecl,
CheckerContext &Context) {
- Loc ThisLoc = Context.getSValBuilder().getCXXThis(CtorDecl,
- Context.getStackFrame());
+ Loc ThisLoc =
+ Context.getSValBuilder().getCXXThis(CtorDecl, Context.getStackFrame());
SVal ObjectV = Context.getState()->getSVal(ThisLoc);
@@ -496,6 +512,75 @@ static bool shouldIgnoreRecord(const RecordDecl *RD, StringRef Pattern) {
return false;
}
+static const Stmt *getMethodBody(const CXXMethodDecl *M) {
+ if (isa<CXXConstructorDecl>(M))
+ return nullptr;
+
+ if (!M->isDefined())
+ return nullptr;
+
+ return M->getDefinition()->getBody();
+}
+
+static bool hasUnguardedAccess(const FieldDecl *FD, ProgramStateRef State) {
+
+ if (FD->getAccess() == AccessSpecifier::AS_public)
+ return true;
+
+ const auto *Parent = dyn_cast<CXXRecordDecl>(FD->getParent());
+
+ if (!Parent)
+ return true;
+
+ Parent = Parent->getDefinition();
+ assert(Parent && "The record's definition must be avaible if an uninitialized"
+ " field of it was found!");
+
+ ASTContext &AC = State->getStateManager().getContext();
+
+ auto FieldAccessM = memberExpr(hasDeclaration(equalsNode(FD))).bind("access");
+
+ auto AssertLikeM = callExpr(callee(functionDecl(
+ anyOf(hasName("exit"), hasName("panic"), hasName("error"),
+ hasName("Assert"), hasName("assert"), hasName("ziperr"),
+ hasName("assfail"), hasName("db_error"), hasName("__assert"),
+ hasName("__assert2"), hasName("_wassert"), hasName("__assert_rtn"),
+ hasName("__assert_fail"), hasName("dtrace_assfail"),
+ hasName("yy_fatal_error"), hasName("_XCAssertionFailureHandler"),
+ hasName("_DTAssertionFailureHandler"),
+ hasName("_TSAssertionFailureHandler")))));
+
+ auto NoReturnFuncM = callExpr(callee(functionDecl(isNoReturn())));
+
+ auto GuardM =
+ stmt(anyOf(ifStmt(), switchStmt(), conditionalOperator(), AssertLikeM,
+ NoReturnFuncM))
+ .bind("guard");
+
+ for (const CXXMethodDecl *M : Parent->methods()) {
+ const Stmt *MethodBody = getMethodBody(M);
+ if (!MethodBody)
+ continue;
+
+ auto Accesses = match(stmt(hasDescendant(FieldAccessM)), *MethodBody, AC);
+ if (Accesses.empty())
+ continue;
+ const auto *FirstAccess = Accesses[0].getNodeAs<MemberExpr>("access");
+ assert(FirstAccess);
+
+ auto Guards = match(stmt(hasDescendant(GuardM)), *MethodBody, AC);
+ if (Guards.empty())
+ return true;
+ const auto *FirstGuard = Guards[0].getNodeAs<Stmt>("guard");
+ assert(FirstGuard);
+
+ if (FirstAccess->getBeginLoc() < FirstGuard->getBeginLoc())
+ 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
@@ -526,13 +611,23 @@ void ento::registerUninitializedObjectChecker(CheckerManager &Mgr) {
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.IsPedantic = AnOpts.getCheckerBooleanOption(Chk, "Pedantic");
+ ChOpts.ShouldConvertNotesToWarnings = AnOpts.getCheckerBooleanOption(
+ Chk, "NotesAsWarnings");
ChOpts.CheckPointeeInitialization = AnOpts.getCheckerBooleanOption(
- "CheckPointeeInitialization", /*DefaultVal*/ false, Chk);
+ Chk, "CheckPointeeInitialization");
ChOpts.IgnoredRecordsWithFieldPattern =
- AnOpts.getCheckerStringOption("IgnoreRecordsWithField",
- /*DefaultVal*/ "", Chk);
+ AnOpts.getCheckerStringOption(Chk, "IgnoreRecordsWithField");
+ ChOpts.IgnoreGuardedFields =
+ AnOpts.getCheckerBooleanOption(Chk, "IgnoreGuardedFields");
+
+ std::string ErrorMsg;
+ if (!llvm::Regex(ChOpts.IgnoredRecordsWithFieldPattern).isValid(ErrorMsg))
+ Mgr.reportInvalidCheckerOptionValue(Chk, "IgnoreRecordsWithField",
+ "a valid regex, building failed with error message "
+ "\"" + ErrorMsg + "\"");
+}
+
+bool ento::shouldRegisterUninitializedObjectChecker(const LangOptions &LO) {
+ return true;
}
diff --git a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
index aead59c7bf87..a5dc250104f3 100644
--- a/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
+++ b/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedPointee.cpp
@@ -1,9 +1,8 @@
//===----- UninitializedPointee.cpp ------------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
index bab0c12704fa..2ccb519891f3 100644
--- a/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -1,9 +1,8 @@
//= 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -21,10 +20,7 @@
#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;
@@ -40,8 +36,9 @@ enum class OpenVariant {
};
namespace {
-class UnixAPIChecker : public Checker< check::PreStmt<CallExpr> > {
- mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce, BT_mallocZero;
+
+class UnixAPIMisuseChecker : public Checker< check::PreStmt<CallExpr> > {
+ mutable std::unique_ptr<BugType> BT_open, BT_pthreadOnce;
mutable Optional<uint64_t> Val_O_CREAT;
public:
@@ -51,8 +48,25 @@ public:
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 CheckOpenVariant(CheckerContext &C,
+ const CallExpr *CE, OpenVariant Variant) const;
+
+ void ReportOpenBug(CheckerContext &C,
+ ProgramStateRef State,
+ const char *Msg,
+ SourceRange SR) const;
+
+};
+
+class UnixAPIPortabilityChecker : public Checker< check::PreStmt<CallExpr> > {
+public:
+ void checkPreStmt(const CallExpr *CE, CheckerContext &C) const;
+
+private:
+ mutable std::unique_ptr<BugType> BT_mallocZero;
+
void CheckCallocZero(CheckerContext &C, const CallExpr *CE) const;
void CheckMallocZero(CheckerContext &C, const CallExpr *CE) const;
void CheckReallocZero(CheckerContext &C, const CallExpr *CE) const;
@@ -61,13 +75,6 @@ public:
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,
@@ -77,48 +84,75 @@ private:
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
+static void LazyInitialize(const CheckerBase *Checker,
+ std::unique_ptr<BugType> &BT,
+ const char *name) {
+ if (BT)
+ return;
+ BT.reset(new BugType(Checker, name, categories::UnixAPI));
+}
+
//===----------------------------------------------------------------------===//
// "open" (man 2 open)
-//===----------------------------------------------------------------------===//
+//===----------------------------------------------------------------------===/
-void UnixAPIChecker::ReportOpenBug(CheckerContext &C,
- ProgramStateRef State,
- const char *Msg,
- SourceRange SR) const {
+void UnixAPIMisuseChecker::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 (FName == "open")
+ CheckOpen(C, CE);
+
+ else if (FName == "openat")
+ CheckOpenAt(C, CE);
+
+ else if (FName == "pthread_once")
+ CheckPthreadOnce(C, CE);
+}
+void UnixAPIMisuseChecker::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'");
+ LazyInitialize(this, 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 {
+void UnixAPIMisuseChecker::CheckOpen(CheckerContext &C,
+ const CallExpr *CE) const {
CheckOpenVariant(C, CE, OpenVariant::Open);
}
-void UnixAPIChecker::CheckOpenAt(CheckerContext &C, const CallExpr *CE) const {
+void UnixAPIMisuseChecker::CheckOpenAt(CheckerContext &C,
+ const CallExpr *CE) const {
CheckOpenVariant(C, CE, OpenVariant::OpenAt);
}
-void UnixAPIChecker::CheckOpenVariant(CheckerContext &C,
- const CallExpr *CE,
- OpenVariant Variant) const {
+void UnixAPIMisuseChecker::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;
@@ -236,7 +270,7 @@ void UnixAPIChecker::CheckOpenVariant(CheckerContext &C,
// pthread_once
//===----------------------------------------------------------------------===//
-void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
+void UnixAPIMisuseChecker::CheckPthreadOnce(CheckerContext &C,
const CallExpr *CE) const {
// This is similar to 'CheckDispatchOnce' in the MacOSXAPIChecker.
@@ -268,7 +302,7 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
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'");
+ LazyInitialize(this, BT_pthreadOnce, "Improper use of 'pthread_once'");
auto report = llvm::make_unique<BugReport>(*BT_pthreadOnce, os.str(), N);
report->addRange(CE->getArg(0)->getSourceRange());
@@ -279,15 +313,16 @@ void UnixAPIChecker::CheckPthreadOnce(CheckerContext &C,
// "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) {
+ const SVal argVal,
+ ProgramStateRef *trueState,
+ ProgramStateRef *falseState) {
std::tie(*trueState, *falseState) =
state->assume(argVal.castAs<DefinedSVal>());
@@ -297,15 +332,16 @@ static bool IsZeroByteAllocation(ProgramStateRef state,
// 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 {
+bool UnixAPIPortabilityChecker::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,
+ LazyInitialize(this, BT_mallocZero,
"Undefined allocation of 0 bytes (CERT MEM04-C; CWE-131)");
SmallString<256> S;
@@ -322,11 +358,11 @@ bool UnixAPIChecker::ReportZeroByteAllocation(CheckerContext &C,
// 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 {
+void UnixAPIPortabilityChecker::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;
@@ -351,8 +387,8 @@ void UnixAPIChecker::BasicAllocationCheck(CheckerContext &C,
C.addTransition(trueState);
}
-void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
- const CallExpr *CE) const {
+void UnixAPIPortabilityChecker::CheckCallocZero(CheckerContext &C,
+ const CallExpr *CE) const {
unsigned int nArgs = CE->getNumArgs();
if (nArgs != 2)
return;
@@ -387,43 +423,39 @@ void UnixAPIChecker::CheckCallocZero(CheckerContext &C,
C.addTransition(trueState);
}
-void UnixAPIChecker::CheckMallocZero(CheckerContext &C,
- const CallExpr *CE) const {
+void UnixAPIPortabilityChecker::CheckMallocZero(CheckerContext &C,
+ const CallExpr *CE) const {
BasicAllocationCheck(C, CE, 1, 0, "malloc");
}
-void UnixAPIChecker::CheckReallocZero(CheckerContext &C,
- const CallExpr *CE) const {
+void UnixAPIPortabilityChecker::CheckReallocZero(CheckerContext &C,
+ const CallExpr *CE) const {
BasicAllocationCheck(C, CE, 2, 1, "realloc");
}
-void UnixAPIChecker::CheckReallocfZero(CheckerContext &C,
- const CallExpr *CE) const {
+void UnixAPIPortabilityChecker::CheckReallocfZero(CheckerContext &C,
+ const CallExpr *CE) const {
BasicAllocationCheck(C, CE, 2, 1, "reallocf");
}
-void UnixAPIChecker::CheckAllocaZero(CheckerContext &C,
- const CallExpr *CE) const {
+void UnixAPIPortabilityChecker::CheckAllocaZero(CheckerContext &C,
+ const CallExpr *CE) const {
BasicAllocationCheck(C, CE, 1, 0, "alloca");
}
-void UnixAPIChecker::CheckAllocaWithAlignZero(CheckerContext &C,
- const CallExpr *CE) const {
+void UnixAPIPortabilityChecker::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 {
+void UnixAPIPortabilityChecker::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 {
+void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
+ CheckerContext &C) const {
const FunctionDecl *FD = C.getCalleeDecl(CE);
if (!FD || FD->getKind() != Decl::Function)
return;
@@ -438,42 +470,40 @@ void UnixAPIChecker::checkPreStmt(const CallExpr *CE,
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);
- }
- }
+ if (FName == "calloc")
+ CheckCallocZero(C, CE);
+
+ else if (FName == "malloc")
+ CheckMallocZero(C, CE);
+
+ else if (FName == "realloc")
+ CheckReallocZero(C, CE);
+
+ else if (FName == "reallocf")
+ CheckReallocfZero(C, CE);
+
+ else if (FName == "alloca" || FName == "__builtin_alloca")
+ CheckAllocaZero(C, CE);
+
+ else if (FName == "__builtin_alloca_with_align")
+ CheckAllocaWithAlignZero(C, CE);
+
+ else if (FName == "valloc")
+ CheckVallocZero(C, CE);
}
//===----------------------------------------------------------------------===//
// Registration.
//===----------------------------------------------------------------------===//
-#define REGISTER_CHECKER(Name) \
- void ento::registerUnixAPI##Name##Checker(CheckerManager &mgr) { \
- mgr.registerChecker<UnixAPIChecker>()->Check##Name = true; \
+#define REGISTER_CHECKER(CHECKERNAME) \
+ void ento::register##CHECKERNAME(CheckerManager &mgr) { \
+ mgr.registerChecker<CHECKERNAME>(); \
+ } \
+ \
+ bool ento::shouldRegister##CHECKERNAME(const LangOptions &LO) { \
+ return true; \
}
-REGISTER_CHECKER(Misuse)
-REGISTER_CHECKER(Portability)
+REGISTER_CHECKER(UnixAPIMisuseChecker)
+REGISTER_CHECKER(UnixAPIPortabilityChecker)
diff --git a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
index 16b4d5e925ba..0b0bf8465c9d 100644
--- a/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// This file implements a generalized unreachable code checker using a
@@ -205,7 +204,7 @@ const Stmt *UnreachableCodeChecker::getUnreachableStmt(const CFGBlock *CB) {
return S->getStmt();
}
}
- if (const Stmt *S = CB->getTerminator())
+ if (const Stmt *S = CB->getTerminatorStmt())
return S;
else
return nullptr;
@@ -251,9 +250,13 @@ bool UnreachableCodeChecker::isInvalidPath(const CFGBlock *CB,
bool UnreachableCodeChecker::isEmptyCFGBlock(const CFGBlock *CB) {
return CB->getLabel() == nullptr // No labels
&& CB->size() == 0 // No statements
- && !CB->getTerminator(); // No terminator
+ && !CB->getTerminatorStmt(); // No terminator
}
void ento::registerUnreachableCodeChecker(CheckerManager &mgr) {
mgr.registerChecker<UnreachableCodeChecker>();
}
+
+bool ento::shouldRegisterUnreachableCodeChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
index e458e0554ee2..1630896c3b60 100644
--- a/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VLASizeChecker.cpp
@@ -1,9 +1,8 @@
//=== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -14,6 +13,7 @@
//
//===----------------------------------------------------------------------===//
+#include "Taint.h"
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/CharUnits.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
@@ -26,6 +26,7 @@
using namespace clang;
using namespace ento;
+using namespace taint;
namespace {
class VLASizeChecker : public Checker< check::PreStmt<DeclStmt> > {
@@ -107,7 +108,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
return;
// Check if the size is tainted.
- if (state->isTainted(sizeV)) {
+ if (isTainted(state, sizeV)) {
reportBug(VLA_Tainted, SE, nullptr, C,
llvm::make_unique<TaintBugVisitor>(sizeV));
return;
@@ -183,3 +184,7 @@ void VLASizeChecker::checkPreStmt(const DeclStmt *DS, CheckerContext &C) const {
void ento::registerVLASizeChecker(CheckerManager &mgr) {
mgr.registerChecker<VLASizeChecker>();
}
+
+bool ento::shouldRegisterVLASizeChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
index 748b226b7a1e..13ad3d98e8eb 100644
--- a/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -276,8 +275,8 @@ void ValistChecker::reportLeakedVALists(const RegionVector &LeakedVALists,
new BugType(CheckNames[CK_Unterminated].getName().empty()
? CheckNames[CK_Uninitialized]
: CheckNames[CK_Unterminated],
- "Leaked va_list", categories::MemoryError));
- BT_leakedvalist->setSuppressOnSink(true);
+ "Leaked va_list", categories::MemoryError,
+ /*SuppressOnSink=*/true));
}
const ExplodedNode *StartNode = getStartCallSite(N, Reg);
@@ -400,11 +399,23 @@ std::shared_ptr<PathDiagnosticPiece> ValistChecker::ValistBugVisitor::VisitNode(
return std::make_shared<PathDiagnosticEventPiece>(Pos, Msg, true);
}
+void ento::registerValistBase(CheckerManager &mgr) {
+ mgr.registerChecker<ValistChecker>();
+}
+
+bool ento::shouldRegisterValistBase(const LangOptions &LO) {
+ return true;
+}
+
#define REGISTER_CHECKER(name) \
void ento::register##name##Checker(CheckerManager &mgr) { \
- ValistChecker *checker = mgr.registerChecker<ValistChecker>(); \
+ ValistChecker *checker = mgr.getChecker<ValistChecker>(); \
checker->ChecksEnabled[ValistChecker::CK_##name] = true; \
checker->CheckNames[ValistChecker::CK_##name] = mgr.getCurrentCheckName(); \
+ } \
+ \
+ bool ento::shouldRegister##name##Checker(const LangOptions &LO) { \
+ return true; \
}
REGISTER_CHECKER(Uninitialized)
diff --git a/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
index 3ee9f1a07fa2..40d14aa5c7d4 100644
--- a/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -216,3 +215,7 @@ void VforkChecker::checkPreStmt(const ReturnStmt *RS, CheckerContext &C) const {
void ento::registerVforkChecker(CheckerManager &mgr) {
mgr.registerChecker<VforkChecker>();
}
+
+bool ento::shouldRegisterVforkChecker(const LangOptions &LO) {
+ return true;
+}
diff --git a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
index 567063197405..762c9c1c8d7a 100644
--- a/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
+++ b/lib/StaticAnalyzer/Checkers/VirtualCallChecker.cpp
@@ -1,9 +1,8 @@
//=======- VirtualCallChecker.cpp --------------------------------*- C++ -*-==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -280,6 +279,9 @@ void ento::registerVirtualCallChecker(CheckerManager &mgr) {
VirtualCallChecker *checker = mgr.registerChecker<VirtualCallChecker>();
checker->IsPureOnly =
- mgr.getAnalyzerOptions().getCheckerBooleanOption("PureOnly", false,
- checker);
+ mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "PureOnly");
+}
+
+bool ento::shouldRegisterVirtualCallChecker(const LangOptions &LO) {
+ return true;
}
diff --git a/lib/StaticAnalyzer/Core/APSIntType.cpp b/lib/StaticAnalyzer/Core/APSIntType.cpp
index c7e95268213e..a1de10c89ed9 100644
--- a/lib/StaticAnalyzer/Core/APSIntType.cpp
+++ b/lib/StaticAnalyzer/Core/APSIntType.cpp
@@ -1,9 +1,8 @@
//===--- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Core/AnalysisManager.cpp b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
index 7fb1c09ca049..1b1ffff5ade8 100644
--- a/lib/StaticAnalyzer/Core/AnalysisManager.cpp
+++ b/lib/StaticAnalyzer/Core/AnalysisManager.cpp
@@ -1,9 +1,8 @@
//===-- AnalysisManager.cpp -------------------------------------*- C++ -*-===//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
@@ -24,7 +23,7 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags,
: AnaCtxMgr(
ASTCtx, Options.UnoptimizedCFG,
Options.ShouldIncludeImplicitDtorsInCFG,
- /*AddInitializers=*/true,
+ /*addInitializers=*/true,
Options.ShouldIncludeTemporaryDtorsInCFG,
Options.ShouldIncludeLifetimeInCFG,
// Adding LoopExit elements to the CFG is a requirement for loop
@@ -36,7 +35,9 @@ AnalysisManager::AnalysisManager(ASTContext &ASTCtx, DiagnosticsEngine &diags,
Options.ShouldConditionalizeStaticInitializers,
/*addCXXNewAllocator=*/true,
Options.ShouldIncludeRichConstructorsInCFG,
- Options.ShouldElideConstructors, injector),
+ Options.ShouldElideConstructors,
+ /*addVirtualBaseBranches=*/true,
+ injector),
Ctx(ASTCtx), Diags(diags), LangOpts(ASTCtx.getLangOpts()),
PathConsumers(PDC), CreateStoreMgr(storemgr),
CreateConstraintMgr(constraintmgr), CheckerMgr(checkerMgr),
diff --git a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index 0588c2bd3d35..71abe2ae6c0e 100644
--- a/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -20,6 +19,7 @@
#include "llvm/ADT/Twine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/FormattedStream.h"
#include "llvm/Support/raw_ostream.h"
#include <cassert>
#include <cstddef>
@@ -34,7 +34,7 @@ std::vector<StringRef>
AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) {
static const StringRef StaticAnalyzerChecks[] = {
#define GET_CHECKERS
-#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI) \
+#define CHECKER(FULLNAME, CLASS, HELPTEXT, DOC_URI, IS_HIDDEN) \
FULLNAME,
#include "clang/StaticAnalyzer/Checkers/Checkers.inc"
#undef CHECKER
@@ -49,6 +49,37 @@ AnalyzerOptions::getRegisteredCheckers(bool IncludeExperimental /* = false */) {
return Result;
}
+void AnalyzerOptions::printFormattedEntry(
+ llvm::raw_ostream &Out,
+ std::pair<StringRef, StringRef> EntryDescPair,
+ size_t InitialPad, size_t EntryWidth, size_t MinLineWidth) {
+
+ llvm::formatted_raw_ostream FOut(Out);
+
+ const size_t PadForDesc = InitialPad + EntryWidth;
+
+ FOut.PadToColumn(InitialPad) << EntryDescPair.first;
+ // If the buffer's length is greater then PadForDesc, print a newline.
+ if (FOut.getColumn() > PadForDesc)
+ FOut << '\n';
+
+ FOut.PadToColumn(PadForDesc);
+
+ if (MinLineWidth == 0) {
+ FOut << EntryDescPair.second;
+ return;
+ }
+
+ for (char C : EntryDescPair.second) {
+ if (FOut.getColumn() > MinLineWidth && C == ' ') {
+ FOut << '\n';
+ FOut.PadToColumn(PadForDesc);
+ continue;
+ }
+ FOut << C;
+ }
+}
+
ExplorationStrategyKind
AnalyzerOptions::getExplorationStrategy() const {
auto K =
@@ -102,18 +133,13 @@ AnalyzerOptions::mayInlineCXXMemberFunction(
return *K >= Param;
}
-StringRef AnalyzerOptions::getCheckerStringOption(StringRef OptionName,
- StringRef DefaultVal,
- const CheckerBase *C,
+StringRef AnalyzerOptions::getCheckerStringOption(StringRef CheckerName,
+ StringRef OptionName,
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 =
@@ -122,35 +148,66 @@ StringRef AnalyzerOptions::getCheckerStringOption(StringRef OptionName,
return StringRef(I->getValue());
size_t Pos = CheckerName.rfind('.');
if (Pos == StringRef::npos)
- return DefaultVal;
+ break;
+
CheckerName = CheckerName.substr(0, Pos);
} while (!CheckerName.empty() && SearchInParents);
- return DefaultVal;
+
+ llvm_unreachable("Unknown checker option! Did you call getChecker*Option "
+ "with incorrect parameters? User input must've been "
+ "verified by CheckerRegistry.");
+
+ return "";
}
-bool AnalyzerOptions::getCheckerBooleanOption(StringRef Name, bool DefaultVal,
- const CheckerBase *C,
+StringRef AnalyzerOptions::getCheckerStringOption(const ento::CheckerBase *C,
+ StringRef OptionName,
+ bool SearchInParents) const {
+ return getCheckerStringOption(
+ C->getTagDescription(), OptionName, SearchInParents);
+}
+
+bool AnalyzerOptions::getCheckerBooleanOption(StringRef CheckerName,
+ StringRef OptionName,
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,
+ auto Ret = llvm::StringSwitch<llvm::Optional<bool>>(
+ getCheckerStringOption(CheckerName, OptionName,
SearchInParents))
.Case("true", true)
.Case("false", false)
- .Default(DefaultVal);
+ .Default(None);
+
+ assert(Ret &&
+ "This option should be either 'true' or 'false', and should've been "
+ "validated by CheckerRegistry!");
+
+ return *Ret;
+}
+
+bool AnalyzerOptions::getCheckerBooleanOption(const ento::CheckerBase *C,
+ StringRef OptionName,
+ bool SearchInParents) const {
+ return getCheckerBooleanOption(
+ C->getTagDescription(), OptionName, SearchInParents);
}
-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,
+int AnalyzerOptions::getCheckerIntegerOption(StringRef CheckerName,
+ StringRef OptionName,
+ bool SearchInParents) const {
+ int Ret = 0;
+ bool HasFailed = getCheckerStringOption(CheckerName, OptionName,
SearchInParents)
- .getAsInteger(10, Ret);
- assert(!HasFailed && "analyzer-config option should be numeric");
+ .getAsInteger(0, Ret);
+ assert(!HasFailed &&
+ "This option should be numeric, and should've been validated by "
+ "CheckerRegistry!");
(void)HasFailed;
return Ret;
}
+
+int AnalyzerOptions::getCheckerIntegerOption(const ento::CheckerBase *C,
+ StringRef OptionName,
+ bool SearchInParents) const {
+ return getCheckerIntegerOption(
+ C->getTagDescription(), OptionName, SearchInParents);
+}
diff --git a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
index d8ed6942de81..7cd48bf44374 100644
--- a/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ b/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -232,9 +231,6 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
// 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;
@@ -243,8 +239,13 @@ BasicValueFactory::evalAPSInt(BinaryOperator::Opcode Op,
if (Amt >= V1.getBitWidth())
return nullptr;
- if (V1.isSigned() && Amt > V1.countLeadingZeros())
+ if (!Ctx.getLangOpts().CPlusPlus2a) {
+ if (V1.isSigned() && V1.isNegative())
+ return nullptr;
+
+ if (V1.isSigned() && Amt > V1.countLeadingZeros())
return nullptr;
+ }
return &getValue( V1.operator<<( (unsigned) Amt ));
}
diff --git a/lib/StaticAnalyzer/Core/BlockCounter.cpp b/lib/StaticAnalyzer/Core/BlockCounter.cpp
index 8c99bd808494..e7ac6f1cfa00 100644
--- a/lib/StaticAnalyzer/Core/BlockCounter.cpp
+++ b/lib/StaticAnalyzer/Core/BlockCounter.cpp
@@ -1,9 +1,8 @@
//==- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/StaticAnalyzer/Core/BugReporter.cpp b/lib/StaticAnalyzer/Core/BugReporter.cpp
index fd7f53210490..e5a0794f10e2 100644
--- a/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -155,8 +154,6 @@ static void removeRedundantMsgs(PathPieces &path) {
case PathDiagnosticPiece::Macro:
removeRedundantMsgs(cast<PathDiagnosticMacroPiece>(*piece).subPieces);
break;
- case PathDiagnosticPiece::ControlFlow:
- break;
case PathDiagnosticPiece::Event: {
if (i == N-1)
break;
@@ -176,7 +173,9 @@ static void removeRedundantMsgs(PathPieces &path) {
}
break;
}
+ case PathDiagnosticPiece::ControlFlow:
case PathDiagnosticPiece::Note:
+ case PathDiagnosticPiece::PopUp:
break;
}
path.push_back(std::move(piece));
@@ -231,9 +230,8 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
break;
}
case PathDiagnosticPiece::ControlFlow:
- break;
-
case PathDiagnosticPiece::Note:
+ case PathDiagnosticPiece::PopUp:
break;
}
@@ -243,6 +241,16 @@ static bool removeUnneededCalls(PathPieces &pieces, BugReport *R,
return containsSomethingInteresting;
}
+/// Same logic as above to remove extra pieces.
+static void removePopUpNotes(PathPieces &Path) {
+ for (unsigned int i = 0; i < Path.size(); ++i) {
+ auto Piece = std::move(Path.front());
+ Path.pop_front();
+ if (!isa<PathDiagnosticPopUpPiece>(*Piece))
+ Path.push_back(std::move(Piece));
+ }
+}
+
/// 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) {
@@ -679,7 +687,7 @@ void generateMinimalDiagForBlockEdge(const ExplodedNode *N, BlockEdge BE,
const LocationContext *LC = N->getLocationContext();
const CFGBlock *Src = BE.getSrc();
const CFGBlock *Dst = BE.getDst();
- const Stmt *T = Src->getTerminator();
+ const Stmt *T = Src->getTerminatorStmt();
if (!T)
return;
@@ -1204,7 +1212,7 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N,
const CFGBlock *BSrc = BE->getSrc();
ParentMap &PM = PDB.getParentMap();
- if (const Stmt *Term = BSrc->getTerminator()) {
+ if (const Stmt *Term = BSrc->getTerminatorStmt()) {
// Are we jumping past the loop body without ever executing the
// loop (because the condition was false)?
if (isLoop(Term)) {
@@ -1247,11 +1255,11 @@ static void generatePathDiagnosticsForNode(const ExplodedNode *N,
static std::unique_ptr<PathDiagnostic>
generateEmptyDiagnosticForReport(BugReport *R, SourceManager &SM) {
- BugType &BT = R->getBugType();
+ const 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->getShortDescription(/*UseFallback=*/false), BT.getCategory(),
R->getUniqueingLocation(), R->getUniqueingDecl(),
findExecutedLines(SM, R->getErrorNode()));
}
@@ -1370,8 +1378,7 @@ static void addContextEdges(PathPieces &pieces, SourceManager &SM,
break;
// If the source is in the same context, we're already good.
- if (std::find(SrcContexts.begin(), SrcContexts.end(), DstContext) !=
- SrcContexts.end())
+ if (llvm::find(SrcContexts, DstContext) != SrcContexts.end())
break;
// Update the subexpression node to point to the context edge.
@@ -1983,6 +1990,10 @@ static std::unique_ptr<PathDiagnostic> generatePathDiagnosticForConsumer(
(void)stillHasNotes;
}
+ // Remove pop-up notes if needed.
+ if (!Opts.ShouldAddPopUpNotes)
+ removePopUpNotes(PD->getMutablePieces());
+
// Redirect all call pieces to have valid locations.
adjustCallLocations(PD->getMutablePieces());
removePiecesWithInvalidLocations(PD->getMutablePieces());
@@ -2612,7 +2623,7 @@ std::pair<BugReport*, std::unique_ptr<VisitorsDiagnosticsTy>> findValidReport(
// Register additional node visitors.
R->addVisitor(llvm::make_unique<NilReceiverBRVisitor>());
R->addVisitor(llvm::make_unique<ConditionBRVisitor>());
- R->addVisitor(llvm::make_unique<CXXSelfAssignmentBRVisitor>());
+ R->addVisitor(llvm::make_unique<TagVisitor>());
BugReporterContext BRC(Reporter, ErrorGraph.BackMap);
@@ -2684,7 +2695,7 @@ GRBugReporter::generatePathDiagnostics(
return Out;
}
-void BugReporter::Register(BugType *BT) {
+void BugReporter::Register(const BugType *BT) {
BugTypes = F.add(BugTypes, BT);
}
@@ -2718,7 +2729,7 @@ void BugReporter::emitReport(std::unique_ptr<BugReport> R) {
R->Profile(ID);
// Lookup the equivance class. If there isn't one, create it.
- BugType& BT = R->getBugType();
+ const BugType& BT = R->getBugType();
Register(&BT);
void *InsertPos;
BugReportEquivClass* EQ = EQClasses.FindNodeOrInsertPos(ID, InsertPos);
@@ -2836,7 +2847,7 @@ FindReportInEquivalenceClass(BugReportEquivClass& EQ,
SmallVectorImpl<BugReport*> &bugReports) {
BugReportEquivClass::iterator I = EQ.begin(), E = EQ.end();
assert(I != E);
- BugType& BT = I->getBugType();
+ const 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
diff --git a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index da94b6eb21e9..250793c4baff 100644
--- a/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -23,6 +22,7 @@
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Analysis/Analyses/Dominators.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/CFGStmtMap.h"
@@ -154,35 +154,47 @@ const Expr *bugreporter::getDerefExpr(const Stmt *S) {
return E;
}
-//===----------------------------------------------------------------------===//
-// Definitions for bug reporter visitors.
-//===----------------------------------------------------------------------===//
+/// Comparing internal representations of symbolic values (via
+/// SVal::operator==()) is a valid way to check if the value was updated,
+/// unless it's a LazyCompoundVal that may have a different internal
+/// representation every time it is loaded from the state. In this function we
+/// do an approximate comparison for lazy compound values, checking that they
+/// are the immediate snapshots of the tracked region's bindings within the
+/// node's respective states but not really checking that these snapshots
+/// actually contain the same set of bindings.
+static bool hasVisibleUpdate(const ExplodedNode *LeftNode, SVal LeftVal,
+ const ExplodedNode *RightNode, SVal RightVal) {
+ if (LeftVal == RightVal)
+ return true;
-std::shared_ptr<PathDiagnosticPiece>
-BugReporterVisitor::getEndPath(BugReporterContext &,
- const ExplodedNode *, BugReport &) {
- return nullptr;
-}
+ const auto LLCV = LeftVal.getAs<nonloc::LazyCompoundVal>();
+ if (!LLCV)
+ return false;
-void
-BugReporterVisitor::finalizeVisitor(BugReporterContext &,
- const ExplodedNode *, BugReport &) {}
+ const auto RLCV = RightVal.getAs<nonloc::LazyCompoundVal>();
+ if (!RLCV)
+ return false;
-std::shared_ptr<PathDiagnosticPiece> BugReporterVisitor::getDefaultEndPath(
- BugReporterContext &BRC, const ExplodedNode *EndPathNode, BugReport &BR) {
- PathDiagnosticLocation L =
- PathDiagnosticLocation::createEndOfPath(EndPathNode,BRC.getSourceManager());
+ return LLCV->getRegion() == RLCV->getRegion() &&
+ LLCV->getStore() == LeftNode->getState()->getStore() &&
+ RLCV->getStore() == RightNode->getState()->getStore();
+}
- const auto &Ranges = BR.getRanges();
+static Optional<const llvm::APSInt *>
+getConcreteIntegerValue(const Expr *CondVarExpr, const ExplodedNode *N) {
+ ProgramStateRef State = N->getState();
+ const LocationContext *LCtx = N->getLocationContext();
- // 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);
+ // The declaration of the value may rely on a pointer so take its l-value.
+ if (const auto *DRE = dyn_cast_or_null<DeclRefExpr>(CondVarExpr)) {
+ if (const auto *VD = dyn_cast_or_null<VarDecl>(DRE->getDecl())) {
+ SVal DeclSVal = State->getSVal(State->getLValue(VD, LCtx));
+ if (auto DeclCI = DeclSVal.getAs<nonloc::ConcreteInt>())
+ return &DeclCI->getValue();
+ }
+ }
- return P;
+ return {};
}
/// \return name of the macro inside the location \p Loc.
@@ -209,36 +221,70 @@ static bool isFunctionMacroExpansion(SourceLocation Loc,
}
/// \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) {
+/// where \p ValueAfter is \c RegionOfInterest's value at the end of the
+/// stack 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>())
+ 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()))
+ 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() &&
+ if (!Mgr.getSValBuilder()
+ .areEqual(State, ValueAtN, ValueAfter)
+ .isConstrainedTrue() &&
(!ValueAtN.isUndef() || !ValueAfter.isUndef()))
return true;
return false;
}
+//===----------------------------------------------------------------------===//
+// Implementation of BugReporterVisitor.
+//===----------------------------------------------------------------------===//
+
+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;
+}
+
+//===----------------------------------------------------------------------===//
+// Implementation of NoStoreFuncVisitor.
+//===----------------------------------------------------------------------===//
namespace {
@@ -269,6 +315,7 @@ class NoStoreFuncVisitor final : public BugReporterVisitor {
llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated;
using RegionVector = SmallVector<const MemRegion *, 5>;
+
public:
NoStoreFuncVisitor(const SubRegion *R)
: RegionOfInterest(R), MmrMgr(*R->getMemRegionManager()),
@@ -281,354 +328,418 @@ public:
ID.AddPointer(RegionOfInterest);
}
+ void *getTag() const {
+ static int Tag = 0;
+ return static_cast<void *>(&Tag);
+ }
+
std::shared_ptr<PathDiagnosticPiece> VisitNode(const ExplodedNode *N,
BugReporterContext &BR,
- BugReport &) override {
+ BugReport &R) override;
+private:
+ /// Attempts to find the region of interest in a given record 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);
+
+ /// 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();
- ProgramStateRef State = N->getState();
- auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+ if (!FramesModifyingCalculated.count(SCtx))
+ findModifyingFrames(N);
+ return FramesModifyingRegion.count(SCtx);
+ }
- // No diagnostic if region was modified inside the frame.
- if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N))
- return nullptr;
+ /// Write to \c FramesModifyingRegion all stack frames along
+ /// the path in the current stack frame which modify \c RegionOfInterest.
+ void findModifyingFrames(const ExplodedNode *N);
- CallEventRef<> Call =
- BR.getStateManager().getCallEventManager().getCaller(SCtx, State);
+ /// Consume the information on the no-store stack frame in order to
+ /// either emit a note or suppress the report enirely.
+ /// \return Diagnostics piece for region not modified in the current function,
+ /// if it decides to emit one.
+ std::shared_ptr<PathDiagnosticPiece>
+ maybeEmitNote(BugReport &R, const CallEvent &Call, const ExplodedNode *N,
+ const RegionVector &FieldChain, const MemRegion *MatchedRegion,
+ StringRef FirstElement, bool FirstIsReferenceType,
+ unsigned IndirectionLevel);
- if (SM.isInSystemHeader(Call->getDecl()->getSourceRange().getBegin()))
- return nullptr;
+ /// 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);
- // 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);
- }
- }
+ /// Print first item in the chain, return new separator.
+ static StringRef prettyPrintFirstElement(StringRef FirstElement,
+ bool MoreItemsExpected,
+ int IndirectionLevel,
+ llvm::raw_svector_ostream &os);
+};
- 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);
+} // end of anonymous namespace
- // Do not generate diagnostics for not modified parameters in
- // constructors.
- return nullptr;
- }
+/// \return Whether the method declaration \p Parent
+/// syntactically has a binary operation writing into the ivar \p Ivar.
+static 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;
- 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++;
- }
- }
+ const Expr *Base = IvarRef->getBase();
+ if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base))
+ Base = ICE->getSubExpr();
- return nullptr;
+ 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;
+}
-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) {
+/// Get parameters associated with runtime definition in order
+/// to get the correct parameter name.
+static 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();
+}
- if (depth == DEREFERENCE_LIMIT) // Limit the recursion depth.
+/// \return whether \p Ty points to a const type, or is a const reference.
+static bool isPointerToConst(QualType Ty) {
+ return !Ty->getPointeeType().isNull() &&
+ Ty->getPointeeType().getCanonicalType().isConstQualified();
+}
+
+/// 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<NoStoreFuncVisitor::RegionVector>
+NoStoreFuncVisitor::findRegionOfInterestInRecord(
+ const RecordDecl *RD, ProgramStateRef State, const MemRegion *R,
+ const NoStoreFuncVisitor::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;
- 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))
+ // 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 (Optional<RegionVector> Out =
+ findRegionOfInterestInRecord(RRD, State, R, Vec, depth))
return Out;
- QualType PT = FT->getPointeeType();
- if (PT.isNull() || PT->isVoidType() || !VR) continue;
+ 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();
- if (const RecordDecl *RRD = PT->getAsRecordDecl())
- if (auto Out =
- findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1))
- return Out;
+ RegionVector VecF = Vec;
+ VecF.push_back(FR);
- }
+ if (RegionOfInterest == VR)
+ return VecF;
- return None;
+ 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 (Optional<RegionVector> Out =
+ findRegionOfInterestInRecord(RRD, State, VR, VecF, depth + 1))
+ return Out;
}
- /// \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;
+ return None;
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+NoStoreFuncVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BR,
+ BugReport &R) {
- const Expr *Base = IvarRef->getBase();
- if (const auto *ICE = dyn_cast<ImplicitCastExpr>(Base))
- Base = ICE->getSubExpr();
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *SCtx = Ctx->getStackFrame();
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
- if (const auto *DRE = dyn_cast<DeclRefExpr>(Base))
- if (const auto *ID = dyn_cast<ImplicitParamDecl>(DRE->getDecl()))
- if (ID->getParameterKind() == ImplicitParamDecl::ObjCSelf)
- return true;
+ // No diagnostic if region was modified inside the frame.
+ if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N))
+ return nullptr;
- return false;
+ CallEventRef<> Call =
+ BR.getStateManager().getCallEventManager().getCaller(SCtx, State);
+
+ // 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 maybeEmitNote(R, *Call, N, {}, SelfRegion, "self",
+ /*FirstIsReferenceType=*/false, 1);
}
- 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);
+ if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
+ const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(ThisR) &&
+ !CCall->getDecl()->isImplicit())
+ return maybeEmitNote(R, *Call, N, {}, 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 V = Call->getArgSVal(I);
+ bool ParamIsReferenceType = PVD->getType()->isReferenceType();
+ std::string ParamName = PVD->getNameAsString();
- /// 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();
+ int IndirectionLevel = 1;
+ QualType T = PVD->getType();
+ while (const MemRegion *MR = V.getAsRegion()) {
+ if (RegionOfInterest->isSubRegionOf(MR) && !isPointerToConst(T))
+ return maybeEmitNote(R, *Call, N, {}, MR, ParamName,
+ ParamIsReferenceType, IndirectionLevel);
- 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();
- }
- }
+ QualType PT = T->getPointeeType();
+ if (PT.isNull() || PT->isVoidType())
+ break;
- // Stop calculation at the call to the current function.
- if (auto CE = N->getLocationAs<CallEnter>())
- if (CE->getCalleeContext() == OriginalSCtx)
- break;
+ if (const RecordDecl *RD = PT->getAsRecordDecl())
+ if (Optional<RegionVector> P =
+ findRegionOfInterestInRecord(RD, State, MR))
+ return maybeEmitNote(R, *Call, N, *P, RegionOfInterest, ParamName,
+ ParamIsReferenceType, IndirectionLevel);
- N = N->getFirstPred();
- } while (N);
+ V = State->getSVal(MR, PT);
+ T = PT;
+ IndirectionLevel++;
+ }
}
- /// 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 nullptr;
+}
- return Call->parameters();
- }
+void NoStoreFuncVisitor::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();
- /// \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();
- }
+ do {
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+ if (CallExitLoc) {
+ LastReturnState = State;
+ ValueAtReturn = LastReturnState->getSVal(RegionOfInterest);
+ }
- /// \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);
+ 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();
+ }
}
- SmallString<256> sbuf;
- llvm::raw_svector_ostream os(sbuf);
- os << "Returning without writing to '";
+ // Stop calculation at the call to the current function.
+ if (auto CE = N->getLocationAs<CallEnter>())
+ if (CE->getCalleeContext() == OriginalSCtx)
+ break;
- // Do not generate the note if failed to pretty-print.
- if (!prettyPrintRegionName(FirstElement, FirstIsReferenceType,
- MatchedRegion, FieldChain, IndirectionLevel, os))
- return nullptr;
+ N = N->getFirstPred();
+ } while (N);
+}
- os << "'";
- return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+std::shared_ptr<PathDiagnosticPiece> NoStoreFuncVisitor::maybeEmitNote(
+ BugReport &R, const CallEvent &Call, const ExplodedNode *N,
+ const RegionVector &FieldChain, const MemRegion *MatchedRegion,
+ StringRef FirstElement, bool FirstIsReferenceType,
+ unsigned IndirectionLevel) {
+ // Optimistically suppress uninitialized value bugs that result
+ // from system headers having a chance to initialize the value
+ // but failing to do so. It's too unlikely a system header's fault.
+ // It's much more likely a situation in which the function has a failure
+ // mode that the user decided not to check. If we want to hunt such
+ // omitted checks, we should provide an explicit function-specific note
+ // describing the precondition under which the function isn't supposed to
+ // initialize its out-parameter, and additionally check that such
+ // precondition can actually be fulfilled on the current path.
+ if (Call.isInSystemHeader()) {
+ // We make an exception for system header functions that have no branches.
+ // Such functions unconditionally fail to initialize the variable.
+ // If they call other functions that have more paths within them,
+ // this suppression would still apply when we visit these inner functions.
+ // One common example of a standard function that doesn't ever initialize
+ // its out parameter is operator placement new; it's up to the follow-up
+ // constructor (if any) to initialize the memory.
+ if (!N->getStackFrame()->getCFG()->isLinear())
+ R.markInvalid(getTag(), nullptr);
+ return nullptr;
}
- /// 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) {
+ PathDiagnosticLocation L =
+ PathDiagnosticLocation::create(N->getLocation(), SM);
+
+ // For now this shouldn't trigger, but once it does (as we add more
+ // functions to the body farm), we'll need to decide if these reports
+ // are worth suppressing as well.
+ if (!L.hasValidLocation())
+ return nullptr;
- if (FirstIsReferenceType)
- IndirectionLevel--;
+ SmallString<256> sbuf;
+ llvm::raw_svector_ostream os(sbuf);
+ os << "Returning without writing to '";
- RegionVector RegionSequence;
+ // Do not generate the note if failed to pretty-print.
+ if (!prettyPrintRegionName(FirstElement, FirstIsReferenceType, MatchedRegion,
+ FieldChain, IndirectionLevel, os))
+ return nullptr;
- // 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());
+ os << "'";
+ return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
+}
- StringRef Sep;
- for (const MemRegion *R : RegionSequence) {
+bool NoStoreFuncVisitor::prettyPrintRegionName(StringRef FirstElement,
+ bool FirstIsReferenceType,
+ const MemRegion *MatchedRegion,
+ const RegionVector &FieldChain,
+ int IndirectionLevel,
+ llvm::raw_svector_ostream &os) {
- // Just keep going up to the base region.
- // Element regions may appear due to casts.
- if (isa<CXXBaseObjectRegion>(R) || isa<CXXTempObjectRegion>(R))
- continue;
+ if (FirstIsReferenceType)
+ IndirectionLevel--;
- if (Sep.empty())
- Sep = prettyPrintFirstElement(FirstElement,
- /*MoreItemsExpected=*/true,
- IndirectionLevel, os);
+ RegionVector RegionSequence;
- os << Sep;
+ // 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());
- // Can only reasonably pretty-print DeclRegions.
- if (!isa<DeclRegion>(R))
- return false;
+ StringRef Sep;
+ for (const MemRegion *R : RegionSequence) {
- const auto *DR = cast<DeclRegion>(R);
- Sep = DR->getValueType()->isAnyPointerType() ? "->" : ".";
- DR->getDecl()->getDeclName().print(os, PP);
- }
+ // 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())
- prettyPrintFirstElement(FirstElement,
- /*MoreItemsExpected=*/false, IndirectionLevel,
- os);
- return true;
- }
+ Sep = prettyPrintFirstElement(FirstElement,
+ /*MoreItemsExpected=*/true,
+ IndirectionLevel, os);
- /// 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 = "->";
- }
+ os << Sep;
- if (IndirectionLevel > 0 && MoreItemsExpected)
- os << "(";
+ // Can only reasonably pretty-print DeclRegions.
+ if (!isa<DeclRegion>(R))
+ return false;
- for (int i=0; i<IndirectionLevel; i++)
- os << "*";
- os << FirstElement;
+ const auto *DR = cast<DeclRegion>(R);
+ Sep = DR->getValueType()->isAnyPointerType() ? "->" : ".";
+ DR->getDecl()->getDeclName().print(os, PP);
+ }
- if (IndirectionLevel > 0 && MoreItemsExpected)
- os << ")";
+ if (Sep.empty())
+ prettyPrintFirstElement(FirstElement,
+ /*MoreItemsExpected=*/false, IndirectionLevel, os);
+ return true;
+}
+
+StringRef NoStoreFuncVisitor::prettyPrintFirstElement(
+ StringRef FirstElement, bool MoreItemsExpected, int IndirectionLevel,
+ llvm::raw_svector_ostream &os) {
+ StringRef Out = ".";
- return 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;
+}
+
+//===----------------------------------------------------------------------===//
+// Implementation of MacroNullReturnSuppressionVisitor.
+//===----------------------------------------------------------------------===//
+
+namespace {
/// Suppress null-pointer-dereference bugs where dereferenced null was returned
/// the macro.
@@ -718,6 +829,10 @@ private:
}
};
+} // end of anonymous namespace
+
+namespace {
+
/// 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,
@@ -769,16 +884,38 @@ public:
return;
// First, find when we processed the statement.
+ // If we work with a 'CXXNewExpr' that is going to be purged away before
+ // its call take place. We would catch that purge in the last condition
+ // as a 'StmtPoint' so we have to bypass it.
+ const bool BypassCXXNewExprEval = isa<CXXNewExpr>(S);
+
+ // This is moving forward when we enter into another context.
+ const StackFrameContext *CurrentSFC = Node->getStackFrame();
+
do {
- if (auto CEE = Node->getLocationAs<CallExitEnd>())
+ // If that is satisfied we found our statement as an inlined call.
+ if (Optional<CallExitEnd> CEE = Node->getLocationAs<CallExitEnd>())
if (CEE->getCalleeContext()->getCallSite() == S)
break;
- if (auto SP = Node->getLocationAs<StmtPoint>())
- if (SP->getStmt() == S)
- break;
+ // Try to move forward to the end of the call-chain.
Node = Node->getFirstPred();
- } while (Node);
+ if (!Node)
+ break;
+
+ const StackFrameContext *PredSFC = Node->getStackFrame();
+
+ // If that is satisfied we found our statement.
+ // FIXME: This code currently bypasses the call site for the
+ // conservatively evaluated allocator.
+ if (!BypassCXXNewExprEval)
+ if (Optional<StmtPoint> SP = Node->getLocationAs<StmtPoint>())
+ // See if we do not enter into another context.
+ if (SP->getStmt() == S && CurrentSFC == PredSFC)
+ break;
+
+ CurrentSFC = PredSFC;
+ } while (Node->getStackFrame() == CurrentSFC);
// Next, step over any post-statement checks.
while (Node && Node->getLocation().getAs<PostStmt>())
@@ -993,7 +1130,11 @@ public:
}
};
-} // namespace
+} // end of anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Implementation of FindLastStoreBRVisitor.
+//===----------------------------------------------------------------------===//
void FindLastStoreBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
static int tag = 0;
@@ -1188,7 +1329,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
if (Succ->getState()->getSVal(R) != V)
return nullptr;
- if (Pred->getState()->getSVal(R) == V) {
+ if (hasVisibleUpdate(Pred, Pred->getState()->getSVal(R), Succ, V)) {
Optional<PostStore> PS = Succ->getLocationAs<PostStore>();
if (!PS || PS->getLocationValue() != R)
return nullptr;
@@ -1209,6 +1350,7 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
// 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();
@@ -1302,6 +1444,10 @@ FindLastStoreBRVisitor::VisitNode(const ExplodedNode *Succ,
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
+//===----------------------------------------------------------------------===//
+// Implementation of TrackConstraintBRVisitor.
+//===----------------------------------------------------------------------===//
+
void TrackConstraintBRVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
static int tag = 0;
ID.AddPointer(&tag);
@@ -1374,6 +1520,10 @@ TrackConstraintBRVisitor::VisitNode(const ExplodedNode *N,
return nullptr;
}
+//===----------------------------------------------------------------------===//
+// Implementation of SuppressInlineDefensiveChecksVisitor.
+//===----------------------------------------------------------------------===//
+
SuppressInlineDefensiveChecksVisitor::
SuppressInlineDefensiveChecksVisitor(DefinedSVal Value, const ExplodedNode *N)
: V(Value) {
@@ -1446,7 +1596,7 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ,
return nullptr;
CFGStmtMap *Map = CurLC->getAnalysisDeclContext()->getCFGStmtMap();
- CurTerminatorStmt = Map->getBlock(CurStmt)->getTerminator();
+ CurTerminatorStmt = Map->getBlock(CurStmt)->getTerminatorStmt();
} else {
return nullptr;
}
@@ -1469,6 +1619,114 @@ SuppressInlineDefensiveChecksVisitor::VisitNode(const ExplodedNode *Succ,
return nullptr;
}
+//===----------------------------------------------------------------------===//
+// TrackControlDependencyCondBRVisitor.
+//===----------------------------------------------------------------------===//
+
+namespace {
+/// Tracks the expressions that are a control dependency of the node that was
+/// supplied to the constructor.
+/// For example:
+///
+/// cond = 1;
+/// if (cond)
+/// 10 / 0;
+///
+/// An error is emitted at line 3. This visitor realizes that the branch
+/// on line 2 is a control dependency of line 3, and tracks it's condition via
+/// trackExpressionValue().
+class TrackControlDependencyCondBRVisitor final : public BugReporterVisitor {
+ const ExplodedNode *Origin;
+ ControlDependencyCalculator ControlDeps;
+ llvm::SmallSet<const CFGBlock *, 32> VisitedBlocks;
+
+public:
+ TrackControlDependencyCondBRVisitor(const ExplodedNode *O)
+ : Origin(O), ControlDeps(&O->getCFG()) {}
+
+ 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;
+};
+} // end of anonymous namespace
+
+static CFGBlock *GetRelevantBlock(const ExplodedNode *Node) {
+ if (auto SP = Node->getLocationAs<StmtPoint>()) {
+ const Stmt *S = SP->getStmt();
+ assert(S);
+
+ return const_cast<CFGBlock *>(Node->getLocationContext()
+ ->getAnalysisDeclContext()->getCFGStmtMap()->getBlock(S));
+ }
+
+ return nullptr;
+}
+
+static std::shared_ptr<PathDiagnosticEventPiece>
+constructDebugPieceForTrackedCondition(const Expr *Cond,
+ const ExplodedNode *N,
+ BugReporterContext &BRC) {
+
+ if (BRC.getAnalyzerOptions().AnalysisDiagOpt == PD_NONE ||
+ !BRC.getAnalyzerOptions().ShouldTrackConditionsDebug)
+ return nullptr;
+
+ std::string ConditionText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Cond->getSourceRange()),
+ BRC.getSourceManager(),
+ BRC.getASTContext().getLangOpts());
+
+ return std::make_shared<PathDiagnosticEventPiece>(
+ PathDiagnosticLocation::createBegin(
+ Cond, BRC.getSourceManager(), N->getLocationContext()),
+ (Twine() + "Tracking condition '" + ConditionText + "'").str());
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+TrackControlDependencyCondBRVisitor::VisitNode(const ExplodedNode *N,
+ BugReporterContext &BRC,
+ BugReport &BR) {
+ // We can only reason about control dependencies within the same stack frame.
+ if (Origin->getStackFrame() != N->getStackFrame())
+ return nullptr;
+
+ CFGBlock *NB = GetRelevantBlock(N);
+
+ // Skip if we already inspected this block.
+ if (!VisitedBlocks.insert(NB).second)
+ return nullptr;
+
+ CFGBlock *OriginB = GetRelevantBlock(Origin);
+
+ // TODO: Cache CFGBlocks for each ExplodedNode.
+ if (!OriginB || !NB)
+ return nullptr;
+
+ if (ControlDeps.isControlDependent(OriginB, NB)) {
+ if (const Expr *Condition = NB->getLastCondition()) {
+ // Keeping track of the already tracked conditions on a visitor level
+ // isn't sufficient, because a new visitor is created for each tracked
+ // expression, hence the BugReport level set.
+ if (BR.addTrackedCondition(N)) {
+ bugreporter::trackExpressionValue(
+ N, Condition, BR, /*EnableNullFPSuppression=*/false);
+ return constructDebugPieceForTrackedCondition(Condition, N, BRC);
+ }
+ }
+ }
+
+ return nullptr;
+}
+
+//===----------------------------------------------------------------------===//
+// Implementation of trackExpressionValue.
+//===----------------------------------------------------------------------===//
+
static const MemRegion *getLocationRegionIfReference(const Expr *E,
const ExplodedNode *N) {
if (const auto *DR = dyn_cast<DeclRefExpr>(E)) {
@@ -1518,7 +1776,7 @@ static const Expr *peelOffOuterExpr(const Expr *Ex,
ProgramPoint ProgPoint = NI->getLocation();
if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
const CFGBlock *srcBlk = BE->getSrc();
- if (const Stmt *term = srcBlk->getTerminator()) {
+ if (const Stmt *term = srcBlk->getTerminatorStmt()) {
if (term == CO) {
bool TookTrueBranch = (*(srcBlk->succ_begin()) == BE->getDst());
if (TookTrueBranch)
@@ -1581,12 +1839,26 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
ProgramStateRef LVState = LVNode->getState();
+ // We only track expressions if we believe that they are important. Chances
+ // are good that control dependencies to the tracking point are also improtant
+ // because of this, let's explain why we believe control reached this point.
+ // TODO: Shouldn't we track control dependencies of every bug location, rather
+ // than only tracked expressions?
+ if (LVState->getAnalysisManager().getAnalyzerOptions().ShouldTrackConditions)
+ report.addVisitor(llvm::make_unique<TrackControlDependencyCondBRVisitor>(
+ InputNode));
+
// 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);
+ // Track the index if this is an array subscript.
+ if (const auto *Arr = dyn_cast<ArraySubscriptExpr>(Inner))
+ trackExpressionValue(
+ LVNode, Arr->getIdx(), report, /*EnableNullFPSuppression*/ false);
+
// 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)) {
@@ -1688,6 +1960,10 @@ bool bugreporter::trackExpressionValue(const ExplodedNode *InputNode,
return true;
}
+//===----------------------------------------------------------------------===//
+// Implementation of NulReceiverBRVisitor.
+//===----------------------------------------------------------------------===//
+
const Expr *NilReceiverBRVisitor::getNilReceiver(const Stmt *S,
const ExplodedNode *N) {
const auto *ME = dyn_cast<ObjCMessageExpr>(S);
@@ -1738,6 +2014,10 @@ NilReceiverBRVisitor::VisitNode(const ExplodedNode *N,
return std::make_shared<PathDiagnosticEventPiece>(L, OS.str());
}
+//===----------------------------------------------------------------------===//
+// Implementation of FindLastStoreBRVisitor.
+//===----------------------------------------------------------------------===//
+
// Registers every VarDecl inside a Stmt with a last store visitor.
void FindLastStoreBRVisitor::registerStatementVarDecls(BugReport &BR,
const Stmt *S,
@@ -1798,39 +2078,36 @@ ConditionBRVisitor::VisitNode(const ExplodedNode *N,
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;
+ ProgramPoint ProgPoint = N->getLocation();
+ const std::pair<const ProgramPointTag *, const ProgramPointTag *> &Tags =
+ ExprEngine::geteagerlyAssumeBinOpBifurcationTags();
// 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);
+ if (Optional<BlockEdge> BE = ProgPoint.getAs<BlockEdge>()) {
+ const CFGBlock *SrcBlock = BE->getSrc();
+ if (const Stmt *Term = SrcBlock->getTerminatorStmt()) {
+ // If the tag of the previous node is 'Eagerly Assume...' the current
+ // 'BlockEdge' has the same constraint information. We do not want to
+ // report the value as it is just an assumption on the predecessor node
+ // which will be caught in the next VisitNode() iteration as a 'PostStmt'.
+ const ProgramPointTag *PreviousNodeTag =
+ N->getFirstPred()->getLocation().getTag();
+ if (PreviousNodeTag == Tags.first || PreviousNodeTag == Tags.second)
+ return nullptr;
+
+ return VisitTerminator(Term, N, SrcBlock, 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);
+ if (Optional<PostStmt> PS = ProgPoint.getAs<PostStmt>()) {
+ const ProgramPointTag *CurrentNodeTag = PS->getTag();
+ if (CurrentNodeTag != Tags.first && CurrentNodeTag != Tags.second)
+ return nullptr;
- return nullptr;
+ bool TookTrue = CurrentNodeTag == Tags.first;
+ return VisitTrueTest(cast<Expr>(PS->getStmt()), BRC, BR, N, TookTrue);
}
return nullptr;
@@ -1876,6 +2153,8 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator(
break;
}
+ Cond = Cond->IgnoreParens();
+
// 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.
@@ -1887,18 +2166,30 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTerminator(
assert(Cond);
assert(srcBlk->succ_size() == 2);
- const bool tookTrue = *(srcBlk->succ_begin()) == dstBlk;
- return VisitTrueTest(Cond, tookTrue, BRC, R, N);
+ const bool TookTrue = *(srcBlk->succ_begin()) == dstBlk;
+ return VisitTrueTest(Cond, BRC, R, N, TookTrue);
}
std::shared_ptr<PathDiagnosticPiece>
-ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue,
- BugReporterContext &BRC, BugReport &R,
- const ExplodedNode *N) {
+ConditionBRVisitor::VisitTrueTest(const Expr *Cond, BugReporterContext &BRC,
+ BugReport &R, const ExplodedNode *N,
+ bool TookTrue) {
+ ProgramStateRef CurrentState = N->getState();
+ ProgramStateRef PrevState = N->getFirstPred()->getState();
+ const LocationContext *LCtx = N->getLocationContext();
+
+ // If the constraint information is changed between the current and the
+ // previous program state we assuming the newly seen constraint information.
+ // If we cannot evaluate the condition (and the constraints are the same)
+ // the analyzer has no information about the value and just assuming it.
+ bool IsAssuming =
+ !BRC.getStateManager().haveEqualConstraints(CurrentState, PrevState) ||
+ CurrentState->getSVal(Cond, LCtx).isUnknownOrUndef();
+
// 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;
+ bool TookTrueTmp = TookTrue;
while (true) {
CondTmp = CondTmp->IgnoreParenCasts();
@@ -1907,18 +2198,23 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue,
break;
case Stmt::BinaryOperatorClass:
if (auto P = VisitTrueTest(Cond, cast<BinaryOperator>(CondTmp),
- tookTrueTmp, BRC, R, N))
+ BRC, R, N, TookTrueTmp, IsAssuming))
return P;
break;
case Stmt::DeclRefExprClass:
if (auto P = VisitTrueTest(Cond, cast<DeclRefExpr>(CondTmp),
- tookTrueTmp, BRC, R, N))
+ BRC, R, N, TookTrueTmp, IsAssuming))
+ return P;
+ break;
+ case Stmt::MemberExprClass:
+ if (auto P = VisitTrueTest(Cond, cast<MemberExpr>(CondTmp),
+ BRC, R, N, TookTrueTmp, IsAssuming))
return P;
break;
case Stmt::UnaryOperatorClass: {
const auto *UO = cast<UnaryOperator>(CondTmp);
if (UO->getOpcode() == UO_LNot) {
- tookTrueTmp = !tookTrueTmp;
+ TookTrueTmp = !TookTrueTmp;
CondTmp = UO->getSubExpr();
continue;
}
@@ -1930,13 +2226,17 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue,
// 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();
+ // If it is too complex and we know the evaluation of the condition do not
+ // repeat the note from 'BugReporter.cpp'
+ if (!IsAssuming)
+ return nullptr;
+
PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
if (!Loc.isValid() || !Loc.asLocation().isValid())
return nullptr;
return std::make_shared<PathDiagnosticEventPiece>(
- Loc, tookTrue ? GenericTrueMessage : GenericFalseMessage);
+ Loc, TookTrue ? GenericTrueMessage : GenericFalseMessage);
}
bool ConditionBRVisitor::patternMatch(const Expr *Ex,
@@ -1945,47 +2245,27 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex,
BugReporterContext &BRC,
BugReport &report,
const ExplodedNode *N,
- Optional<bool> &prunable) {
+ Optional<bool> &prunable,
+ bool IsSameFieldName) {
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);
+ if (isa<GNUNullExpr>(Ex) || isa<ObjCBoolLiteralExpr>(Ex) ||
+ isa<CXXBoolLiteralExpr>(Ex) || isa<IntegerLiteral>(Ex) ||
+ isa<FloatingLiteral>(Ex)) {
+ // Use heuristics to determine if the expression is a macro
+ // expanding to a literal and if so, use the macro's name.
+ SourceLocation BeginLoc = OriginalExpr->getBeginLoc();
+ SourceLocation EndLoc = OriginalExpr->getEndLoc();
+ if (BeginLoc.isMacroID() && EndLoc.isMacroID()) {
+ SourceManager &SM = BRC.getSourceManager();
+ const LangOptions &LO = BRC.getASTContext().getLangOpts();
+ if (Lexer::isAtStartOfMacroExpansion(BeginLoc, SM, LO) &&
+ Lexer::isAtEndOfMacroExpansion(EndLoc, SM, LO)) {
+ CharSourceRange R = Lexer::getAsCharRange({BeginLoc, EndLoc}, SM, LO);
+ Out << Lexer::getSourceText(R, SM, LO);
+ return false;
}
- StringRef MacroName = Lexer::getImmediateMacroNameForDiagnostics(
- Loc, BRC.getSourceManager(), BRC.getASTContext().getLangOpts());
-
- // Return the macro name.
- Out << MacroName;
- return false;
}
}
@@ -2032,23 +2312,43 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex,
return false;
}
+ if (const auto *ME = dyn_cast<MemberExpr>(Ex)) {
+ if (!IsSameFieldName)
+ Out << "field '" << ME->getMemberDecl()->getName() << '\'';
+ else
+ Out << '\''
+ << Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Ex->getSourceRange()),
+ BRC.getSourceManager(), BRC.getASTContext().getLangOpts(), 0)
+ << '\'';
+ }
+
return false;
}
-std::shared_ptr<PathDiagnosticPiece>
-ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr,
- const bool tookTrue, BugReporterContext &BRC,
- BugReport &R, const ExplodedNode *N) {
+std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(
+ const Expr *Cond, const BinaryOperator *BExpr, BugReporterContext &BRC,
+ BugReport &R, const ExplodedNode *N, bool TookTrue, bool IsAssuming) {
bool shouldInvert = false;
Optional<bool> shouldPrune;
+ // Check if the field name of the MemberExprs is ambiguous. Example:
+ // " 'a.d' is equal to 'h.d' " in 'test/Analysis/null-deref-path-notes.cpp'.
+ bool IsSameFieldName = false;
+ if (const auto *LhsME =
+ dyn_cast<MemberExpr>(BExpr->getLHS()->IgnoreParenCasts()))
+ if (const auto *RhsME =
+ dyn_cast<MemberExpr>(BExpr->getRHS()->IgnoreParenCasts()))
+ IsSameFieldName = LhsME->getMemberDecl()->getName() ==
+ RhsME->getMemberDecl()->getName();
+
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);
+ const bool isVarLHS = patternMatch(BExpr->getLHS(), BExpr, OutLHS, BRC, R,
+ N, shouldPrune, IsSameFieldName);
+ const bool isVarRHS = patternMatch(BExpr->getRHS(), BExpr, OutRHS, BRC, R,
+ N, shouldPrune, IsSameFieldName);
shouldInvert = !isVarLHS && isVarRHS;
}
@@ -2058,8 +2358,8 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr,
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);
+ return VisitConditionVariable(LhsString, BExpr->getLHS(), BRC, R, N,
+ TookTrue);
}
// For non-assignment operations, we require that we can understand
@@ -2071,7 +2371,8 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr,
// 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 ";
+ Out << (IsAssuming ? "Assuming " : "")
+ << (shouldInvert ? RhsString : LhsString) << " is ";
// Do we need to invert the opcode?
if (shouldInvert)
@@ -2083,7 +2384,7 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr,
case BO_GE: Op = BO_LE; break;
}
- if (!tookTrue)
+ if (!TookTrue)
switch (Op) {
case BO_EQ: Op = BO_NE; break;
case BO_NE: Op = BO_EQ; break;
@@ -2110,15 +2411,24 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const BinaryOperator *BExpr,
Out << (shouldInvert ? LhsString : RhsString);
const LocationContext *LCtx = N->getLocationContext();
PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
- auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
+
+ // Convert 'field ...' to 'Field ...' if it is a MemberExpr.
+ std::string Message = Out.str();
+ Message[0] = toupper(Message[0]);
+
+ // If we know the value create a pop-up note.
+ if (!IsAssuming)
+ return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Message);
+
+ auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Message);
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) {
+ StringRef LhsString, const Expr *CondVarExpr, BugReporterContext &BRC,
+ BugReport &report, const ExplodedNode *N, bool TookTrue) {
// 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)
@@ -2126,17 +2436,7 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable(
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
+ if (!printValue(CondVarExpr, Out, N, TookTrue, /*IsAssuming=*/true))
return nullptr;
const LocationContext *LCtx = N->getLocationContext();
@@ -2156,34 +2456,29 @@ std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitConditionVariable(
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());
+std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(
+ const Expr *Cond, const DeclRefExpr *DRE, BugReporterContext &BRC,
+ BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) {
+ const auto *VD = dyn_cast<VarDecl>(DRE->getDecl());
if (!VD)
return nullptr;
SmallString<256> Buf;
llvm::raw_svector_ostream Out(Buf);
- Out << "Assuming '" << VD->getDeclName() << "' is ";
+ Out << (IsAssuming ? "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
+ if (!printValue(DRE, Out, N, TookTrue, IsAssuming))
return nullptr;
const LocationContext *LCtx = N->getLocationContext();
PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
- auto event = std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
+ // If we know the value create a pop-up note.
+ if (!IsAssuming)
+ return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str());
+
+ 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))
@@ -2197,6 +2492,67 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, const DeclRefExpr *DR,
return std::move(event);
}
+std::shared_ptr<PathDiagnosticPiece> ConditionBRVisitor::VisitTrueTest(
+ const Expr *Cond, const MemberExpr *ME, BugReporterContext &BRC,
+ BugReport &report, const ExplodedNode *N, bool TookTrue, bool IsAssuming) {
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+
+ Out << (IsAssuming ? "Assuming field '" : "Field '")
+ << ME->getMemberDecl()->getName() << "' is ";
+
+ if (!printValue(ME, Out, N, TookTrue, IsAssuming))
+ return nullptr;
+
+ const LocationContext *LCtx = N->getLocationContext();
+ PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx);
+ if (!Loc.isValid() || !Loc.asLocation().isValid())
+ return nullptr;
+
+ // If we know the value create a pop-up note.
+ if (!IsAssuming)
+ return std::make_shared<PathDiagnosticPopUpPiece>(Loc, Out.str());
+
+ return std::make_shared<PathDiagnosticEventPiece>(Loc, Out.str());
+}
+
+bool ConditionBRVisitor::printValue(const Expr *CondVarExpr, raw_ostream &Out,
+ const ExplodedNode *N, bool TookTrue,
+ bool IsAssuming) {
+ QualType Ty = CondVarExpr->getType();
+
+ if (Ty->isPointerType()) {
+ Out << (TookTrue ? "non-null" : "null");
+ return true;
+ }
+
+ if (Ty->isObjCObjectPointerType()) {
+ Out << (TookTrue ? "non-nil" : "nil");
+ return true;
+ }
+
+ if (!Ty->isIntegralOrEnumerationType())
+ return false;
+
+ Optional<const llvm::APSInt *> IntValue;
+ if (!IsAssuming)
+ IntValue = getConcreteIntegerValue(CondVarExpr, N);
+
+ if (IsAssuming || !IntValue.hasValue()) {
+ if (Ty->isBooleanType())
+ Out << (TookTrue ? "true" : "false");
+ else
+ Out << (TookTrue ? "not equal to 0" : "0");
+ } else {
+ if (Ty->isBooleanType())
+ Out << (IntValue.getValue()->getBoolValue() ? "true" : "false");
+ else
+ Out << *IntValue.getValue();
+ }
+
+ return true;
+}
+
const char *const ConditionBRVisitor::GenericTrueMessage =
"Assuming the condition is true";
const char *const ConditionBRVisitor::GenericFalseMessage =
@@ -2208,6 +2564,10 @@ bool ConditionBRVisitor::isPieceMessageGeneric(
Piece->getString() == GenericFalseMessage;
}
+//===----------------------------------------------------------------------===//
+// Implementation of LikelyFalsePositiveSuppressionBRVisitor.
+//===----------------------------------------------------------------------===//
+
void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor(
BugReporterContext &BRC, const ExplodedNode *N, BugReport &BR) {
// Here we suppress false positives coming from system headers. This list is
@@ -2290,6 +2650,10 @@ void LikelyFalsePositiveSuppressionBRVisitor::finalizeVisitor(
}
}
+//===----------------------------------------------------------------------===//
+// Implementation of UndefOrNullArgVisitor.
+//===----------------------------------------------------------------------===//
+
std::shared_ptr<PathDiagnosticPiece>
UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N,
BugReporterContext &BRC, BugReport &BR) {
@@ -2340,78 +2704,9 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N,
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");
-}
+//===----------------------------------------------------------------------===//
+// Implementation of FalsePositiveRefutationBRVisitor.
+//===----------------------------------------------------------------------===//
FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor()
: Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {}
@@ -2422,7 +2717,7 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor(
VisitNode(EndPathNode, BRC, BR);
// Create a refutation manager
- SMTSolverRef RefutationSolver = CreateZ3Solver();
+ llvm::SMTSolverRef RefutationSolver = llvm::CreateZ3Solver();
ASTContext &Ctx = BRC.getASTContext();
// Add constraints to the solver
@@ -2430,7 +2725,7 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor(
const SymbolRef Sym = I.first;
auto RangeIt = I.second.begin();
- SMTExprRef Constraints = SMTConv::getRangeExpr(
+ llvm::SMTExprRef Constraints = SMTConv::getRangeExpr(
RefutationSolver, Ctx, Sym, RangeIt->From(), RangeIt->To(),
/*InRange=*/true);
while ((++RangeIt) != I.second.end()) {
@@ -2477,3 +2772,33 @@ void FalsePositiveRefutationBRVisitor::Profile(
static int Tag = 0;
ID.AddPointer(&Tag);
}
+
+//===----------------------------------------------------------------------===//
+// Implementation of TagVisitor.
+//===----------------------------------------------------------------------===//
+
+int NoteTag::Kind = 0;
+
+void TagVisitor::Profile(llvm::FoldingSetNodeID &ID) const {
+ static int Tag = 0;
+ ID.AddPointer(&Tag);
+}
+
+std::shared_ptr<PathDiagnosticPiece>
+TagVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
+ BugReport &R) {
+ ProgramPoint PP = N->getLocation();
+ const NoteTag *T = dyn_cast_or_null<NoteTag>(PP.getTag());
+ if (!T)
+ return nullptr;
+
+ if (Optional<std::string> Msg = T->generateMessage(BRC, R)) {
+ PathDiagnosticLocation Loc =
+ PathDiagnosticLocation::create(PP, BRC.getSourceManager());
+ auto Piece = std::make_shared<PathDiagnosticEventPiece>(Loc, *Msg);
+ Piece->setPrunable(T->isPrunable());
+ return Piece;
+ }
+
+ return nullptr;
+}
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
index 0e7f31502e81..a5f7500e6307 100644
--- a/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -357,20 +356,32 @@ bool CallEvent::isCalled(const CallDescription &CD) const {
// FIXME: Add ObjC Message support.
if (getKind() == CE_ObjCMessage)
return false;
+
+ const IdentifierInfo *II = getCalleeIdentifier();
+ if (!II)
+ return false;
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(getDecl());
+ if (!FD)
+ return false;
+
+ if (CD.Flags & CDF_MaybeBuiltin) {
+ return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) &&
+ (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs());
+ }
+
if (!CD.IsLookupDone) {
CD.IsLookupDone = true;
CD.II = &getState()->getStateManager().getContext().Idents.get(
CD.getFunctionName());
}
- const IdentifierInfo *II = getCalleeIdentifier();
- if (!II || II != CD.II)
+
+ if (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();
+ if (CD.QualifiedName.size() > 1 && FD) {
+ const DeclContext *Ctx = FD->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()) {
@@ -394,8 +405,7 @@ bool CallEvent::isCalled(const CallDescription &CD) const {
return false;
}
- return (CD.RequiredArgs == CallDescription::NoArgRequirement ||
- CD.RequiredArgs == getNumArgs());
+ return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs());
}
SVal CallEvent::getArgSVal(unsigned Index) const {
@@ -756,8 +766,11 @@ RuntimeDefinition CXXInstanceCall::getRuntimeDefinition() const {
// Does the decl that we found have an implementation?
const FunctionDecl *Definition;
- if (!Result->hasBody(Definition))
+ if (!Result->hasBody(Definition)) {
+ if (!DynType.canBeASubClass())
+ return AnyFunctionCall::getRuntimeDefinition();
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
diff --git a/lib/StaticAnalyzer/Core/Checker.cpp b/lib/StaticAnalyzer/Core/Checker.cpp
index 72bfd84b40a3..f4e6f909d764 100644
--- a/lib/StaticAnalyzer/Core/Checker.cpp
+++ b/lib/StaticAnalyzer/Core/Checker.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/StaticAnalyzer/Core/CheckerContext.cpp b/lib/StaticAnalyzer/Core/CheckerContext.cpp
index 6cf931abbddd..725ff1002e29 100644
--- a/lib/StaticAnalyzer/Core/CheckerContext.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerContext.cpp
@@ -1,9 +1,8 @@
//== 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
index e73a22ae3981..34cdc9db699d 100644
--- a/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerHelpers.cpp
@@ -1,9 +1,8 @@
//===---- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/StaticAnalyzer/Core/CheckerManager.cpp b/lib/StaticAnalyzer/Core/CheckerManager.cpp
index 688c47e984cc..27d5797b4cbc 100644
--- a/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -15,7 +14,9 @@
#include "clang/AST/DeclBase.h"
#include "clang/AST/Stmt.h"
#include "clang/Analysis/ProgramPoint.h"
+#include "clang/Basic/JsonSupport.h"
#include "clang/Basic/LLVM.h"
+#include "clang/Driver/DriverDiagnostic.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -59,6 +60,15 @@ void CheckerManager::finishedCheckerRegistration() {
#endif
}
+void CheckerManager::reportInvalidCheckerOptionValue(
+ const CheckerBase *C, StringRef OptionName, StringRef ExpectedValueDesc) {
+
+ Context.getDiagnostics()
+ .Report(diag::err_analyzer_checker_option_invalid_input)
+ << (llvm::Twine() + C->getTagDescription() + ":" + OptionName).str()
+ << ExpectedValueDesc;
+}
+
//===----------------------------------------------------------------------===//
// Functions for running checkers for AST traversing..
//===----------------------------------------------------------------------===//
@@ -641,7 +651,6 @@ 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;
@@ -650,16 +659,19 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
// 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);
+ // TODO: Support the situation when the call doesn't correspond
+ // to any Expr.
+ ProgramPoint L = ProgramPoint::getProgramPoint(
+ cast<CallExpr>(Call.getOriginExpr()),
+ ProgramPoint::PostStmtKind,
+ 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);
+ evaluated = EvalCallChecker(Call, C);
}
assert(!(evaluated && anyEvaluated)
&& "There are more than one checkers evaluating the call");
@@ -689,11 +701,73 @@ void CheckerManager::runCheckersOnEndOfTranslationUnit(
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);
+void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out,
+ ProgramStateRef State,
+ const char *NL,
+ unsigned int Space,
+ bool IsDot) const {
+ Indent(Out, Space, IsDot) << "\"checker_messages\": ";
+
+ // Create a temporary stream to see whether we have any message.
+ SmallString<1024> TempBuf;
+ llvm::raw_svector_ostream TempOut(TempBuf);
+ unsigned int InnerSpace = Space + 2;
+
+ // Create the new-line in JSON with enough space.
+ SmallString<128> NewLine;
+ llvm::raw_svector_ostream NLOut(NewLine);
+ NLOut << "\", " << NL; // Inject the ending and a new line
+ Indent(NLOut, InnerSpace, IsDot) << "\""; // then begin the next message.
+
+ ++Space;
+ bool HasMessage = false;
+
+ // Store the last CheckerTag.
+ const void *LastCT = nullptr;
+ for (const auto &CT : CheckerTags) {
+ // See whether the current checker has a message.
+ CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/"");
+
+ if (TempBuf.empty())
+ continue;
+
+ if (!HasMessage) {
+ Out << '[' << NL;
+ HasMessage = true;
+ }
+
+ LastCT = &CT;
+ TempBuf.clear();
+ }
+
+ for (const auto &CT : CheckerTags) {
+ // See whether the current checker has a message.
+ CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/"");
+
+ if (TempBuf.empty())
+ continue;
+
+ Indent(Out, Space, IsDot)
+ << "{ \"checker\": \"" << CT.second->getCheckName().getName()
+ << "\", \"messages\": [" << NL;
+ Indent(Out, InnerSpace, IsDot)
+ << '\"' << TempBuf.str().trim() << '\"' << NL;
+ Indent(Out, Space, IsDot) << "]}";
+
+ if (&CT != LastCT)
+ Out << ',';
+ Out << NL;
+
+ TempBuf.clear();
+ }
+
+ // It is the last element of the 'program_state' so do not add a comma.
+ if (HasMessage)
+ Indent(Out, --Space, IsDot) << "]";
+ else
+ Out << "null";
+
+ Out << NL;
}
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
index cdae3ef0116a..54501314386a 100644
--- a/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
+++ b/lib/StaticAnalyzer/Core/CommonBugCategories.cpp
@@ -1,9 +1,8 @@
//=--- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
diff --git a/lib/StaticAnalyzer/Core/ConstraintManager.cpp b/lib/StaticAnalyzer/Core/ConstraintManager.cpp
index ef9c44c51be4..d642c3530268 100644
--- a/lib/StaticAnalyzer/Core/ConstraintManager.cpp
+++ b/lib/StaticAnalyzer/Core/ConstraintManager.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/StaticAnalyzer/Core/CoreEngine.cpp b/lib/StaticAnalyzer/Core/CoreEngine.cpp
index 196854cb09da..94cf74de8293 100644
--- a/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -217,6 +216,25 @@ void CoreEngine::HandleBlockEdge(const BlockEdge &L, ExplodedNode *Pred) {
LC->getDecl(),
LC->getCFG()->getNumBlockIDs());
+ // Display a prunable path note to the user if it's a virtual bases branch
+ // and we're taking the path that skips virtual base constructors.
+ if (L.getSrc()->getTerminator().isVirtualBaseBranch() &&
+ L.getDst() == *L.getSrc()->succ_begin()) {
+ ProgramPoint P = L.withTag(getNoteTags().makeNoteTag(
+ [](BugReporterContext &, BugReport &) -> std::string {
+ // TODO: Just call out the name of the most derived class
+ // when we know it.
+ return "Virtual base initialization skipped because "
+ "it has already been handled by the most derived class";
+ }, /*IsPrunable=*/true));
+ // Perform the transition.
+ ExplodedNodeSet Dst;
+ NodeBuilder Bldr(Pred, Dst, BuilderCtx);
+ Pred = Bldr.generateNode(P, Pred->getState(), Pred);
+ if (!Pred)
+ return;
+ }
+
// Check if we are entering the EXIT block.
if (Blk == &(L.getLocationContext()->getCFG()->getExit())) {
assert(L.getLocationContext()->getCFG()->getExit().empty() &&
@@ -276,14 +294,14 @@ void CoreEngine::HandleBlockEntrance(const BlockEntrance &L,
}
void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
- if (const Stmt *Term = B->getTerminator()) {
+ if (const Stmt *Term = B->getTerminatorStmt()) {
switch (Term->getStmtClass()) {
default:
llvm_unreachable("Analysis for this terminator not implemented.");
case Stmt::CXXBindTemporaryExprClass:
HandleCleanupTemporaryBranch(
- cast<CXXBindTemporaryExpr>(B->getTerminator().getStmt()), B, Pred);
+ cast<CXXBindTemporaryExpr>(Term), B, Pred);
return;
// Model static initializers.
@@ -378,9 +396,19 @@ void CoreEngine::HandleBlockExit(const CFGBlock * B, ExplodedNode *Pred) {
case Stmt::WhileStmtClass:
HandleBranch(cast<WhileStmt>(Term)->getCond(), Term, B, Pred);
return;
+
+ case Stmt::GCCAsmStmtClass:
+ assert(cast<GCCAsmStmt>(Term)->isAsmGoto() && "Encountered GCCAsmStmt without labels");
+ // TODO: Handle jumping to labels
+ return;
}
}
+ if (B->getTerminator().isVirtualBaseBranch()) {
+ HandleVirtualBaseBranch(B, Pred);
+ return;
+ }
+
assert(B->succ_size() == 1 &&
"Blocks with no terminator should have at most 1 successor.");
@@ -440,6 +468,29 @@ void CoreEngine::HandlePostStmt(const CFGBlock *B, unsigned StmtIdx,
}
}
+void CoreEngine::HandleVirtualBaseBranch(const CFGBlock *B,
+ ExplodedNode *Pred) {
+ const LocationContext *LCtx = Pred->getLocationContext();
+ if (const auto *CallerCtor = dyn_cast_or_null<CXXConstructExpr>(
+ LCtx->getStackFrame()->getCallSite())) {
+ switch (CallerCtor->getConstructionKind()) {
+ case CXXConstructExpr::CK_NonVirtualBase:
+ case CXXConstructExpr::CK_VirtualBase: {
+ BlockEdge Loc(B, *B->succ_begin(), LCtx);
+ HandleBlockEdge(Loc, Pred);
+ return;
+ }
+ default:
+ break;
+ }
+ }
+
+ // We either don't see a parent stack frame because we're in the top frame,
+ // or the parent stack frame doesn't initialize our virtual bases.
+ BlockEdge Loc(B, *(B->succ_begin() + 1), LCtx);
+ HandleBlockEdge(Loc, Pred);
+}
+
/// generateNode - Utility method to generate nodes, hook up successors,
/// and add nodes to the worklist.
void CoreEngine::generateNode(const ProgramPoint &Loc,
diff --git a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
index da7854df1def..79424452240d 100644
--- a/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
+++ b/lib/StaticAnalyzer/Core/DynamicTypeMap.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -14,6 +13,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeMap.h"
+#include "clang/Basic/JsonSupport.h"
#include "clang/Basic/LLVM.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -36,7 +36,7 @@ DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State,
// Otherwise, fall back to what we know about the region.
if (const auto *TR = dyn_cast<TypedRegion>(Reg))
- return DynamicTypeInfo(TR->getLocationType(), /*CanBeSubclass=*/false);
+ return DynamicTypeInfo(TR->getLocationType(), /*CanBeSub=*/false);
if (const auto *SR = dyn_cast<SymbolicRegion>(Reg)) {
SymbolRef Sym = SR->getSymbol();
@@ -54,27 +54,38 @@ ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *Reg,
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 << " : ";
+void printDynamicTypeInfoJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, unsigned int Space, bool IsDot) {
+ Indent(Out, Space, IsDot) << "\"dynamic_types\": ";
+
+ const DynamicTypeMapTy &DTM = State->get<DynamicTypeMap>();
+ if (DTM.isEmpty()) {
+ Out << "null," << NL;
+ return;
+ }
+
+ ++Space;
+ Out << '[' << NL;
+ for (DynamicTypeMapTy::iterator I = DTM.begin(); I != DTM.end(); ++I) {
+ const MemRegion *MR = I->first;
+ const DynamicTypeInfo &DTI = I->second;
+ Out << "{ \"region\": \"" << MR << "\", \"dyn_type\": ";
if (DTI.isValid()) {
- Out << DTI.getType()->getPointeeType().getAsString();
- if (DTI.canBeASubClass()) {
- Out << " (or its subclass)";
- }
+ Out << '\"' << DTI.getType()->getPointeeType().getAsString()
+ << "\", \"sub_classable\": "
+ << (DTI.canBeASubClass() ? "true" : "false");
} else {
- Out << "Invalid type info";
+ Out << "null"; // Invalid type info
}
+ Out << "}";
+
+ if (std::next(I) != DTM.end())
+ Out << ',';
Out << NL;
}
+
+ --Space;
+ Indent(Out, Space, IsDot) << "]," << NL;
}
void *ProgramStateTrait<DynamicTypeMap>::GDMIndex() {
diff --git a/lib/StaticAnalyzer/Core/Environment.cpp b/lib/StaticAnalyzer/Core/Environment.cpp
index b45f93b6dde8..551c89b04db4 100644
--- a/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/lib/StaticAnalyzer/Core/Environment.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -19,6 +18,7 @@
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
+#include "clang/Basic/JsonSupport.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
@@ -200,43 +200,86 @@ EnvironmentManager::removeDeadBindings(Environment Env,
return NewEnv;
}
-void Environment::print(raw_ostream &Out, const char *NL,
- const char *Sep,
- const ASTContext &Context,
- const LocationContext *WithLC) const {
- if (ExprBindings.isEmpty())
+void Environment::printJson(raw_ostream &Out, const ASTContext &Ctx,
+ const LocationContext *LCtx, const char *NL,
+ unsigned int Space, bool IsDot) const {
+ Indent(Out, Space, IsDot) << "\"environment\": ";
+
+ if (ExprBindings.isEmpty()) {
+ Out << "null," << NL;
return;
+ }
- if (!WithLC) {
+ ++Space;
+ if (!LCtx) {
// Find the freshest location context.
llvm::SmallPtrSet<const LocationContext *, 16> FoundContexts;
- for (auto I : *this) {
+ for (const 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;
+ LCtx = LC;
for (const LocationContext *LCI = LC; LCI; LCI = LCI->getParent())
FoundContexts.insert(LCI);
}
}
}
- assert(WithLC);
+ assert(LCtx);
+
+ Out << "{ \"pointer\": \"" << (const void *)LCtx->getStackFrame()
+ << "\", \"items\": [" << NL;
+ PrintingPolicy PP = Ctx.getPrintingPolicy();
- PrintingPolicy PP = Context.getPrintingPolicy();
+ LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) {
+ // LCtx items begin
+ bool HasItem = false;
+ unsigned int InnerSpace = Space + 1;
- Out << NL << "Expressions by stack frame:" << NL;
- WithLC->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) {
- for (auto I : ExprBindings) {
- if (I.first.getLocationContext() != LC)
+ // Store the last ExprBinding which we will print.
+ BindingsTy::iterator LastI = ExprBindings.end();
+ for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end();
+ ++I) {
+ if (I->first.getLocationContext() != LC)
continue;
- const Stmt *S = I.first.getStmt();
+ if (!HasItem) {
+ HasItem = true;
+ Out << '[' << NL;
+ }
+
+ const Stmt *S = I->first.getStmt();
+ (void)S;
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;
+ LastI = I;
+ }
+
+ for (BindingsTy::iterator I = ExprBindings.begin(); I != ExprBindings.end();
+ ++I) {
+ if (I->first.getLocationContext() != LC)
+ continue;
+
+ const Stmt *S = I->first.getStmt();
+ Indent(Out, InnerSpace, IsDot)
+ << "{ \"stmt_id\": " << S->getID(Ctx) << ", \"pretty\": ";
+ S->printJson(Out, nullptr, PP, /*AddQuotes=*/true);
+
+ Out << ", \"value\": ";
+ I->second.printJson(Out, /*AddQuotes=*/true);
+
+ Out << " }";
+
+ if (I != LastI)
+ Out << ',';
+ Out << NL;
}
+
+ if (HasItem)
+ Indent(Out, --InnerSpace, IsDot) << ']';
+ else
+ Out << "null ";
});
+
+ Indent(Out, --Space, IsDot) << "]}," << NL;
}
diff --git a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index d6bcbb96b55f..c86b1436baab 100644
--- a/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/StaticAnalyzer/Core/ExprEngine.cpp b/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 151eef56fece..1fef5b3c1edd 100644
--- a/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -34,6 +33,7 @@
#include "clang/Analysis/ConstructionContext.h"
#include "clang/Analysis/ProgramPoint.h"
#include "clang/Basic/IdentifierTable.h"
+#include "clang/Basic/JsonSupport.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
#include "clang/Basic/PrettyStackTrace.h"
@@ -142,21 +142,34 @@ public:
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());
+ void printJson(llvm::raw_ostream &Out, PrinterHelper *Helper,
+ PrintingPolicy &PP) const {
+ const Stmt *S = getItem().getStmtOrNull();
+ const CXXCtorInitializer *I = nullptr;
+ if (!S)
+ I = getItem().getCXXCtorInitializer();
+
+ if (S)
+ Out << "\"stmt_id\": " << S->getID(getASTContext());
else
- OS << 'I' << getItem().getCXXCtorInitializer()->getID(getASTContext());
- OS << ',' << getItem().getKindAsString();
+ Out << "\"init_id\": " << I->getID(getASTContext());
+
+ // Kind
+ Out << ", \"kind\": \"" << getItem().getKindAsString()
+ << "\", \"argument_index\": ";
+
if (getItem().getKind() == ConstructionContextItem::ArgumentKind)
- OS << " #" << getItem().getIndex();
- OS << ") ";
- if (const Stmt *S = getItem().getStmtOrNull()) {
- S->printPretty(OS, Helper, PP);
+ Out << getItem().getIndex();
+ else
+ Out << "null";
+
+ // Pretty-print
+ Out << ", \"pretty\": ";
+
+ if (S) {
+ S->printJson(Out, Helper, PP, /*AddQuotes=*/true);
} else {
- const CXXCtorInitializer *I = getItem().getCXXCtorInitializer();
- OS << I->getAnyMember()->getNameAsString();
+ Out << '\"' << I->getAnyMember()->getNameAsString() << '\"';
}
}
@@ -198,9 +211,13 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU,
mgr.getConstraintManagerCreator(), G.getAllocator(),
this),
SymMgr(StateMgr.getSymbolManager()),
- svalBuilder(StateMgr.getSValBuilder()), ObjCNoRet(mgr.getASTContext()),
+ MRMgr(StateMgr.getRegionManager()),
+ svalBuilder(StateMgr.getSValBuilder()),
+ ObjCNoRet(mgr.getASTContext()),
BR(mgr, *this),
- VisitedCallees(VisitedCalleesIn), HowToInline(HowToInlineIn) {
+ VisitedCallees(VisitedCalleesIn),
+ HowToInline(HowToInlineIn)
+ {
unsigned TrimInterval = mgr.options.GraphTrimInterval;
if (TrimInterval != 0) {
// Enable eager node reclamation when constructing the ExplodedGraph.
@@ -208,10 +225,6 @@ ExprEngine::ExprEngine(cross_tu::CrossTranslationUnitContext &CTU,
}
}
-ExprEngine::~ExprEngine() {
- BR.FlushReports();
-}
-
//===----------------------------------------------------------------------===//
// Utility methods.
//===----------------------------------------------------------------------===//
@@ -538,36 +551,73 @@ ExprEngine::processRegionChanges(ProgramStateRef state,
LCtx, Call);
}
-static void printObjectsUnderConstructionForContext(raw_ostream &Out,
- ProgramStateRef State,
- const char *NL,
- const LocationContext *LC) {
+static void
+printObjectsUnderConstructionJson(raw_ostream &Out, ProgramStateRef State,
+ const char *NL, const LocationContext *LCtx,
+ unsigned int Space = 0, bool IsDot = false) {
PrintingPolicy PP =
- LC->getAnalysisDeclContext()->getASTContext().getPrintingPolicy();
- for (auto I : State->get<ObjectsUnderConstruction>()) {
- ConstructedObjectKey Key = I.first;
+ LCtx->getAnalysisDeclContext()->getASTContext().getPrintingPolicy();
+
+ ++Space;
+ bool HasItem = false;
+
+ // Store the last key.
+ const ConstructedObjectKey *LastKey = nullptr;
+ for (const auto &I : State->get<ObjectsUnderConstruction>()) {
+ const ConstructedObjectKey &Key = I.first;
+ if (Key.getLocationContext() != LCtx)
+ continue;
+
+ if (!HasItem) {
+ Out << "[" << NL;
+ HasItem = true;
+ }
+
+ LastKey = &Key;
+ }
+
+ for (const auto &I : State->get<ObjectsUnderConstruction>()) {
+ const ConstructedObjectKey &Key = I.first;
SVal Value = I.second;
- if (Key.getLocationContext() != LC)
+ if (Key.getLocationContext() != LCtx)
continue;
- Key.print(Out, nullptr, PP);
- Out << " : " << Value << NL;
+
+ Indent(Out, Space, IsDot) << "{ ";
+ Key.printJson(Out, nullptr, PP);
+ Out << ", \"value\": \"" << Value << "\" }";
+
+ if (&Key != LastKey)
+ Out << ',';
+ Out << NL;
+ }
+
+ if (HasItem)
+ Indent(Out, --Space, IsDot) << ']'; // End of "location_context".
+ else {
+ Out << "null ";
}
}
-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;
+void ExprEngine::printJson(raw_ostream &Out, ProgramStateRef State,
+ const LocationContext *LCtx, const char *NL,
+ unsigned int Space, bool IsDot) const {
+ Indent(Out, Space, IsDot) << "\"constructing_objects\": ";
- LCtx->dumpStack(Out, "", NL, Sep, [&](const LocationContext *LC) {
- printObjectsUnderConstructionForContext(Out, State, NL, LC);
- });
- }
+ if (LCtx && !State->get<ObjectsUnderConstruction>().isEmpty()) {
+ ++Space;
+ Out << '[' << NL;
+ LCtx->printJson(Out, NL, Space, IsDot, [&](const LocationContext *LC) {
+ printObjectsUnderConstructionJson(Out, State, NL, LC, Space, IsDot);
+ });
+
+ --Space;
+ Indent(Out, Space, IsDot) << "]," << NL; // End of "constructing_objects".
+ } else {
+ Out << "null," << NL;
}
- getCheckerManager().runCheckersForPrintState(Out, State, NL, Sep);
+ getCheckerManager().runCheckersForPrintStateJson(Out, State, NL, Space,
+ IsDot);
}
void ExprEngine::processEndWorklist() {
@@ -1338,6 +1388,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::NoInitExprClass:
case Stmt::SizeOfPackExprClass:
case Stmt::StringLiteralClass:
+ case Stmt::SourceLocExprClass:
case Stmt::ObjCStringLiteralClass:
case Stmt::CXXPseudoDestructorExprClass:
case Stmt::SubstNonTypeTemplateParmExprClass:
@@ -1517,7 +1568,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
ProgramStateRef NewState =
createTemporaryRegionIfNeeded(State, LCtx, OCE->getArg(0));
if (NewState != State) {
- Pred = Bldr.generateNode(OCE, Pred, NewState, /*Tag=*/nullptr,
+ Pred = Bldr.generateNode(OCE, Pred, NewState, /*tag=*/nullptr,
ProgramPoint::PreStmtKind);
// Did we cache out?
if (!Pred)
@@ -1636,6 +1687,7 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::CXXReinterpretCastExprClass:
case Stmt::CXXConstCastExprClass:
case Stmt::CXXFunctionalCastExprClass:
+ case Stmt::BuiltinBitCastExprClass:
case Stmt::ObjCBridgedCastExprClass: {
Bldr.takeNodes(Pred);
const auto *C = cast<CastExpr>(S);
@@ -1858,7 +1910,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
// other constraints) then consider completely unrolling it.
if(AMgr.options.ShouldUnrollLoops) {
unsigned maxBlockVisitOnPath = AMgr.options.maxBlockVisitOnPath;
- const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator();
+ const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt();
if (Term) {
ProgramStateRef NewState = updateLoopStack(Term, AMgr.getASTContext(),
Pred, maxBlockVisitOnPath);
@@ -1879,7 +1931,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
unsigned int BlockCount = nodeBuilder.getContext().blockCount();
if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 &&
AMgr.options.ShouldWidenLoops) {
- const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminator();
+ const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt();
if (!(Term &&
(isa<ForStmt>(Term) || isa<WhileStmt>(Term) || isa<DoStmt>(Term))))
return;
@@ -2004,8 +2056,8 @@ static const Stmt *ResolveCondition(const Stmt *Condition,
if (!BO || !BO->isLogicalOp())
return Condition;
- assert(!B->getTerminator().isTemporaryDtorsBranch() &&
- "Temporary destructor branches handled by processBindTemporary.");
+ assert(B->getTerminator().isStmtBranch() &&
+ "Other kinds of branches are handled separately!");
// For logical operations, we still have the case where some branches
// use the traditional "merge" approach and others sink the branch
@@ -2258,7 +2310,6 @@ void ExprEngine::processEndOfFunction(NodeBuilderContext& BC,
Pred->getStackFrame()->getParent()));
PrettyStackTraceLocationContext CrashInfo(Pred->getLocationContext());
- StateMgr.EndPath(Pred->getState());
ExplodedNodeSet Dst;
if (Pred->getLocationContext()->inTopFrame()) {
@@ -2620,43 +2671,39 @@ void ExprEngine::VisitAtomicExpr(const AtomicExpr *AE, ExplodedNode *Pred,
getCheckerManager().runCheckersForPostStmt(Dst, AfterInvalidateSet, AE, *this);
}
-// A value escapes in three possible cases:
+// A value escapes in four 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
+// (2) We are binding to a MemRegion that does not have stack storage.
+// (3) We are binding to a top-level parameter region with a non-trivial
+// destructor. We won't see the destructor during analysis, but it's there.
+// (4) 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;
+ProgramStateRef
+ExprEngine::processPointerEscapedOnBind(ProgramStateRef State, SVal Loc,
+ SVal Val, const LocationContext *LCtx) {
+
+ // Cases (1) and (2).
+ const MemRegion *MR = Loc.getAsRegion();
+ if (!MR || !MR->hasStackStorage())
+ return escapeValue(State, Val, PSK_EscapeOnBind);
+
+ // Case (3).
+ if (const auto *VR = dyn_cast<VarRegion>(MR->getBaseRegion()))
+ if (VR->hasStackParametersStorage() && VR->getStackFrame()->inTopFrame())
+ if (const auto *RD = VR->getValueType()->getAsCXXRecordDecl())
+ if (!RD->hasTrivialDestructor())
+ return escapeValue(State, Val, PSK_EscapeOnBind);
+
+ // Case (4): in order to test that, 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(MR);
+ if (StoredVal != Val)
+ if (State == (State->bindLoc(loc::MemRegionVal(MR), Val, LCtx)))
+ return escapeValue(State, Val, PSK_EscapeOnBind);
- // Otherwise, find all symbols referenced by 'val' that we are tracking
- // and stop tracking them.
- State = escapeValue(State, Val, PSK_EscapeOnBind);
return State;
}
@@ -2959,7 +3006,8 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
for (const auto &EQ : EQClasses) {
for (const BugReport &Report : EQ) {
- if (Report.getErrorNode() == N)
+ if (Report.getErrorNode()->getState() == N->getState() &&
+ Report.getErrorNode()->getLocation() == N->getLocation())
return true;
}
}
@@ -2995,57 +3043,63 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
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);
+ std::string Buf;
+ llvm::raw_string_ostream Out(Buf);
+ const bool IsDot = true;
+ const unsigned int Space = 1;
ProgramStateRef State = N->getState();
+ auto Noop = [](const ExplodedNode*){};
+ bool HasReport = traverseHiddenNodes(
+ N, Noop, Noop, &nodeHasBugReport);
+ bool IsSink = traverseHiddenNodes(
+ N, Noop, Noop, [](const ExplodedNode *N) { return N->isSink(); });
+
+ Out << "{ \"node_id\": " << N->getID(G) << ", \"pointer\": \""
+ << (const void *)N << "\", \"state_id\": " << State->getID()
+ << ", \"has_report\": " << (HasReport ? "true" : "false")
+ << ", \"is_sink\": " << (IsSink ? "true" : "false")
+ << ",\\l";
+
+ Indent(Out, Space, IsDot) << "\"program_points\": [\\l";
+
// Dump program point for all the previously skipped nodes.
traverseHiddenNodes(
N,
[&](const ExplodedNode *OtherNode) {
- OtherNode->getLocation().print(/*CR=*/"\\l", Out);
+ Indent(Out, Space + 1, IsDot) << "{ ";
+ OtherNode->getLocation().printJson(Out, /*NL=*/"\\l");
+ Out << ", \"tag\": ";
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";
+ Out << '\"' << Tag->getTagDescription() << "\" }";
+ else
+ Out << "null }";
},
- [&](const ExplodedNode *) { Out << "\\l--------\\l"; },
+ // Adds a comma and a new-line between each program point.
+ [&](const ExplodedNode *) { Out << ",\\l"; },
[&](const ExplodedNode *) { return false; });
- Out << "\\l\\|";
-
- Out << "StateID: ST" << State->getID() << ", NodeID: N" << N->getID(G)
- << " <" << (const void *)N << ">\\|";
+ Out << "\\l"; // Adds a new-line to the last program point.
+ Indent(Out, Space, IsDot) << "],\\l";
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());
+
+ if (!SameAsAllPredecessors) {
+ State->printDOT(Out, N->getLocationContext(), Space);
+ } else {
+ Indent(Out, Space, IsDot) << "\"program_state\": null";
+ }
+
+ Out << "\\l}\\l";
return Out.str();
}
};
diff --git a/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index b980628878e9..f436650fbdd9 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -1,9 +1,8 @@
//=-- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -101,6 +100,10 @@ void ExprEngine::VisitBinaryOperator(const BinaryOperator* B,
SVal Result = evalBinOp(state, Op, LeftV, RightV, B->getType());
if (!Result.isUnknown()) {
state = state->BindExpr(B, LCtx, Result);
+ } else {
+ // If we cannot evaluate the operation escape the operands.
+ state = escapeValue(state, LeftV, PSK_EscapeOther);
+ state = escapeValue(state, RightV, PSK_EscapeOther);
}
Bldr.generateNode(B, *it, state);
@@ -377,9 +380,9 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_Dependent:
case CK_ArrayToPointerDecay:
case CK_BitCast:
+ case CK_LValueToRValueBitCast:
case CK_AddressSpaceConversion:
case CK_BooleanToSignedIntegral:
- case CK_NullToPointer:
case CK_IntegralToPointer:
case CK_PointerToIntegral: {
SVal V = state->getSVal(Ex, LCtx);
@@ -416,7 +419,9 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_IntToOCLSampler:
case CK_LValueBitCast:
case CK_FixedPointCast:
- case CK_FixedPointToBoolean: {
+ case CK_FixedPointToBoolean:
+ case CK_FixedPointToIntegral:
+ case CK_IntegralToFixedPoint: {
state =
handleLValueBitCast(state, Ex, LCtx, T, ExTy, CastE, Bldr, Pred);
continue;
@@ -502,6 +507,12 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
Bldr.generateNode(CastE, Pred, state);
continue;
}
+ case CK_NullToPointer: {
+ SVal V = svalBuilder.makeNull();
+ state = state->BindExpr(CastE, LCtx, V);
+ Bldr.generateNode(CastE, Pred, state);
+ continue;
+ }
case CK_NullToMemberPointer: {
SVal V = svalBuilder.getMemberPointer(nullptr);
state = state->BindExpr(CastE, LCtx, V);
@@ -626,6 +637,21 @@ void ExprEngine::VisitDeclStmt(const DeclStmt *DS, ExplodedNode *Pred,
void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
ExplodedNodeSet &Dst) {
+ // This method acts upon CFG elements for logical operators && and ||
+ // and attaches the value (true or false) to them as expressions.
+ // It doesn't produce any state splits.
+ // If we made it that far, we're past the point when we modeled the short
+ // circuit. It means that we should have precise knowledge about whether
+ // we've short-circuited. If we did, we already know the value we need to
+ // bind. If we didn't, the value of the RHS (casted to the boolean type)
+ // is the answer.
+ // Currently this method tries to figure out whether we've short-circuited
+ // by looking at the ExplodedGraph. This method is imperfect because there
+ // could inevitably have been merges that would have resulted in multiple
+ // potential path traversal histories. We bail out when we fail.
+ // Due to this ambiguity, a more reliable solution would have been to
+ // track the short circuit operation history path-sensitively until
+ // we evaluate the respective logical operator.
assert(B->getOpcode() == BO_LAnd ||
B->getOpcode() == BO_LOr);
@@ -647,10 +673,20 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred,
ProgramPoint P = N->getLocation();
assert(P.getAs<PreStmt>()|| P.getAs<PreStmtPurgeDeadSymbols>());
(void) P;
- assert(N->pred_size() == 1);
+ if (N->pred_size() != 1) {
+ // We failed to track back where we came from.
+ Bldr.generateNode(B, Pred, state);
+ return;
+ }
N = *N->pred_begin();
}
- assert(N->pred_size() == 1);
+
+ if (N->pred_size() != 1) {
+ // We failed to track back where we came from.
+ Bldr.generateNode(B, Pred, state);
+ return;
+ }
+
N = *N->pred_begin();
BlockEdge BE = N->getLocation().castAs<BlockEdge>();
SVal X;
@@ -703,7 +739,7 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
QualType T = getContext().getCanonicalType(IE->getType());
unsigned NumInitElements = IE->getNumInits();
- if (!IE->isGLValue() &&
+ if (!IE->isGLValue() && !IE->isTransparent() &&
(T->isArrayType() || T->isRecordType() || T->isVectorType() ||
T->isAnyComplexType())) {
llvm::ImmutableList<SVal> vals = getBasicVals().getEmptySValList();
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 6445b9df5a58..1cbd09ea5793 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -197,6 +196,12 @@ std::pair<ProgramStateRef, SVal> ExprEngine::prepareForObjectConstruction(
// able to find construction context at all.
break;
}
+ if (isa<BlockInvocationContext>(CallerLCtx)) {
+ // Unwrap block invocation contexts. They're mostly part of
+ // the current stack frame.
+ CallerLCtx = CallerLCtx->getParent();
+ assert(!isa<BlockInvocationContext>(CallerLCtx));
+ }
return prepareForObjectConstruction(
cast<Expr>(SFC->getCallSite()), State, CallerLCtx,
RTC->getConstructionContext(), CallOpts);
@@ -423,25 +428,20 @@ void ExprEngine::VisitCXXConstructExpr(const CXXConstructExpr *CE,
prepareForObjectConstruction(CE, State, LCtx, CC, CallOpts);
break;
}
- case CXXConstructExpr::CK_VirtualBase:
+ 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;
- }
- }
- }
+ const auto *OuterCtor = dyn_cast_or_null<CXXConstructExpr>(
+ LCtx->getStackFrame()->getCallSite());
+ assert(
+ (!OuterCtor ||
+ OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Complete ||
+ OuterCtor->getConstructionKind() == CXXConstructExpr::CK_Delegating) &&
+ ("This virtual base should have already been initialized by "
+ "the most derived class!"));
+ (void)OuterCtor;
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
@@ -604,6 +604,7 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
ExplodedNode *Pred,
ExplodedNodeSet &Dst,
const EvalCallOptions &CallOpts) {
+ assert(S && "A destructor without a trigger!");
const LocationContext *LCtx = Pred->getLocationContext();
ProgramStateRef State = Pred->getState();
@@ -611,6 +612,19 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
assert(RecordDecl && "Only CXXRecordDecls should have destructors");
const CXXDestructorDecl *DtorDecl = RecordDecl->getDestructor();
+ // FIXME: There should always be a Decl, otherwise the destructor call
+ // shouldn't have been added to the CFG in the first place.
+ if (!DtorDecl) {
+ // Skip the invalid destructor. We cannot simply return because
+ // it would interrupt the analysis instead.
+ static SimpleProgramPointTag T("ExprEngine", "SkipInvalidDestructor");
+ // FIXME: PostImplicitCall with a null decl may crash elsewhere anyway.
+ PostImplicitCall PP(/*Decl=*/nullptr, S->getEndLoc(), LCtx, &T);
+ NodeBuilder Bldr(Pred, Dst, *currBldrCtx);
+ Bldr.generateNode(PP, Pred->getState(), Pred);
+ return;
+ }
+
CallEventManager &CEMgr = getStateManager().getCallEventManager();
CallEventRef<CXXDestructorCall> Call =
CEMgr.getCXXDestructorCall(DtorDecl, S, Dest, IsBaseDtor, State, LCtx);
@@ -629,7 +643,6 @@ void ExprEngine::VisitCXXDestructor(QualType ObjectType,
I != E; ++I)
defaultEvalCall(Bldr, *I, *Call, CallOpts);
- ExplodedNodeSet DstPostCall;
getCheckerManager().runCheckersForPostCall(Dst, DstInvalidated,
*Call, *this);
}
diff --git a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 758195d8d911..b935e3afe34b 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -1,9 +1,8 @@
//=-- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -328,30 +327,30 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
ExplodedNodeSet DstPostPostCallCallback;
getCheckerManager().runCheckersForPostCall(DstPostPostCallCallback,
CEENode, *UpdatedCall, *this,
- /*WasInlined=*/true);
+ /*wasInlined=*/true);
for (auto I : DstPostPostCallCallback) {
getCheckerManager().runCheckersForNewAllocator(
CNE,
*getObjectUnderConstruction(I->getState(), CNE,
calleeCtx->getParent()),
DstPostCall, I, *this,
- /*WasInlined=*/true);
+ /*wasInlined=*/true);
}
} else {
getCheckerManager().runCheckersForPostCall(DstPostCall, CEENode,
*UpdatedCall, *this,
- /*WasInlined=*/true);
+ /*wasInlined=*/true);
}
ExplodedNodeSet Dst;
if (const ObjCMethodCall *Msg = dyn_cast<ObjCMethodCall>(Call)) {
getCheckerManager().runCheckersForPostObjCMessage(Dst, DstPostCall, *Msg,
*this,
- /*WasInlined=*/true);
+ /*wasInlined=*/true);
} else if (CE &&
!(isa<CXXNewExpr>(CE) && // Called when visiting CXXNewExpr.
AMgr.getAnalyzerOptions().MayInlineCXXAllocator)) {
getCheckerManager().runCheckersForPostStmt(Dst, DstPostCall, CE,
- *this, /*WasInlined=*/true);
+ *this, /*wasInlined=*/true);
} else {
Dst.insert(DstPostCall);
}
@@ -365,6 +364,26 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
}
}
+bool ExprEngine::isSmall(AnalysisDeclContext *ADC) const {
+ // When there are no branches in the function, it means that there's no
+ // exponential complexity introduced by inlining such function.
+ // Such functions also don't trigger various fundamental problems
+ // with our inlining mechanism, such as the problem of
+ // inlined defensive checks. Hence isLinear().
+ const CFG *Cfg = ADC->getCFG();
+ return Cfg->isLinear() || Cfg->size() <= AMgr.options.AlwaysInlineSize;
+}
+
+bool ExprEngine::isLarge(AnalysisDeclContext *ADC) const {
+ const CFG *Cfg = ADC->getCFG();
+ return Cfg->size() >= AMgr.options.MinCFGSizeTreatFunctionsAsLarge;
+}
+
+bool ExprEngine::isHuge(AnalysisDeclContext *ADC) const {
+ const CFG *Cfg = ADC->getCFG();
+ return Cfg->getNumBlockIDs() > AMgr.options.MaxInlinableSize;
+}
+
void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx,
bool &IsRecursive, unsigned &StackDepth) {
IsRecursive = false;
@@ -385,8 +404,7 @@ void ExprEngine::examineStackFrames(const Decl *D, const LocationContext *LCtx,
// 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)
+ if (!isSmall(CalleeADC))
++StackDepth;
}
LCtx = LCtx->getParent();
@@ -616,12 +634,19 @@ ProgramStateRef ExprEngine::bindReturnValue(const CallEvent &Call,
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);
+ const MemRegion *TargetR = Target.getAsRegion();
+ assert(TargetR);
+ // Invalidate the region so that it didn't look uninitialized. If this is
+ // a field or element constructor, we do not want to invalidate
+ // the whole structure. Pointer escape is meaningless because
+ // the structure is a product of conservative evaluation
+ // and therefore contains nothing interesting at this point.
+ RegionAndSymbolInvalidationTraits ITraits;
+ ITraits.setTrait(TargetR,
+ RegionAndSymbolInvalidationTraits::TK_DoNotInvalidateSuperRegion);
+ State = State->invalidateRegions(TargetR, E, Count, LCtx,
+ /* CausesPointerEscape=*/false, nullptr,
+ &Call, &ITraits);
R = State->getSVal(Target.castAs<Loc>(), E->getType());
} else {
@@ -833,8 +858,7 @@ static bool isCXXSharedPtrDtor(const FunctionDecl *FD) {
/// 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) {
+bool ExprEngine::mayInlineDecl(AnalysisDeclContext *CalleeADC) const {
AnalyzerOptions &Opts = AMgr.getAnalyzerOptions();
// FIXME: Do not inline variadic calls.
if (CallEvent::isVariadic(CalleeADC->getDecl()))
@@ -879,7 +903,7 @@ static bool mayInlineDecl(AnalysisManager &AMgr,
return false;
// Do not inline large functions.
- if (CalleeCFG->getNumBlockIDs() > Opts.MaxInlinableSize)
+ if (isHuge(CalleeADC))
return false;
// It is possible that the live variables analysis cannot be
@@ -919,7 +943,7 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *D,
} 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)) {
+ if (mayInlineDecl(CalleeADC)) {
Engine.FunctionSummaries->markMayInline(D);
} else {
Engine.FunctionSummaries->markShouldNotInline(D);
@@ -940,29 +964,23 @@ bool ExprEngine::shouldInlineCall(const CallEvent &Call, const Decl *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))
+ (!isSmall(CalleeADC) || IsRecursive))
return false;
// Do not inline large functions too many times.
if ((Engine.FunctionSummaries->getNumTimesInlined(D) >
Opts.MaxTimesInlineLarge) &&
- CalleeCFG->getNumBlockIDs() >=
- Opts.MinCFGSizeTreatFunctionsAsLarge) {
+ isLarge(CalleeADC)) {
NumReachedInlineCountMax++;
return false;
}
- if (HowToInline == Inline_Minimal &&
- (CalleeCFG->getNumBlockIDs() > Opts.AlwaysInlineSize
- || IsRecursive))
+ if (HowToInline == Inline_Minimal && (!isSmall(CalleeADC) || IsRecursive))
return false;
return true;
diff --git a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index 6b8402f621e0..eb9a0be2e5d6 100644
--- a/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -1,9 +1,8 @@
//=-- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/StaticAnalyzer/Core/FunctionSummary.cpp b/lib/StaticAnalyzer/Core/FunctionSummary.cpp
index 94edd84d15d2..2b9a45133bba 100644
--- a/lib/StaticAnalyzer/Core/FunctionSummary.cpp
+++ b/lib/StaticAnalyzer/Core/FunctionSummary.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
diff --git a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index fc82f1176942..64c42699fcf3 100644
--- a/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -91,8 +90,9 @@ public:
const PathDiagnosticMacroPiece& P,
unsigned num);
- void HandlePiece(Rewriter& R, FileID BugFileID,
- const PathDiagnosticPiece& P, unsigned num, unsigned max);
+ void HandlePiece(Rewriter &R, FileID BugFileID, const PathDiagnosticPiece &P,
+ const std::vector<SourceRange> &PopUpRanges, unsigned num,
+ unsigned max);
void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range,
const char *HighlightStart = "<span class=\"mrange\">",
@@ -274,7 +274,7 @@ std::string HTMLDiagnostics::GenerateHTML(const PathDiagnostic& D, Rewriter &R,
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())
+ if (llvm::is_contained(FileIDs, FID))
continue;
FileIDs.push_back(FID);
@@ -606,6 +606,53 @@ window.addEventListener("keydown", function (event) {
)<<<";
}
+static void
+HandlePopUpPieceStartTag(Rewriter &R,
+ const std::vector<SourceRange> &PopUpRanges) {
+ for (const auto &Range : PopUpRanges) {
+ html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "",
+ "<table class='variable_popup'><tbody>",
+ /*IsTokenRange=*/false);
+ }
+}
+
+static void HandlePopUpPieceEndTag(Rewriter &R,
+ const PathDiagnosticPopUpPiece &Piece,
+ std::vector<SourceRange> &PopUpRanges,
+ unsigned int LastReportedPieceIndex,
+ unsigned int PopUpPieceIndex) {
+ SmallString<256> Buf;
+ llvm::raw_svector_ostream Out(Buf);
+
+ SourceRange Range(Piece.getLocation().asRange());
+
+ // Write out the path indices with a right arrow and the message as a row.
+ Out << "<tr><td valign='top'><div class='PathIndex PathIndexPopUp'>"
+ << LastReportedPieceIndex;
+
+ // Also annotate the state transition with extra indices.
+ Out << '.' << PopUpPieceIndex;
+
+ Out << "</div></td><td>" << Piece.getString() << "</td></tr>";
+
+ // If no report made at this range mark the variable and add the end tags.
+ if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) ==
+ PopUpRanges.end()) {
+ // Store that we create a report at this range.
+ PopUpRanges.push_back(Range);
+
+ Out << "</tbody></table></span>";
+ html::HighlightRange(R, Range.getBegin(), Range.getEnd(),
+ "<span class='variable'>", Buf.c_str(),
+ /*IsTokenRange=*/false);
+
+ // Otherwise inject just the new row at the end of the range.
+ } else {
+ html::HighlightRange(R, Range.getBegin(), Range.getEnd(), "", Buf.c_str(),
+ /*IsTokenRange=*/false);
+ }
+}
+
void HTMLDiagnostics::RewriteFile(Rewriter &R,
const PathPieces& path, FileID FID) {
// Process the path.
@@ -616,39 +663,80 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R,
[](const std::shared_ptr<PathDiagnosticPiece> &p) {
return isa<PathDiagnosticNotePiece>(*p);
});
+ unsigned PopUpPieceCount =
+ std::count_if(path.begin(), path.end(),
+ [](const std::shared_ptr<PathDiagnosticPiece> &p) {
+ return isa<PathDiagnosticPopUpPiece>(*p);
+ });
- unsigned TotalRegularPieces = TotalPieces - TotalNotePieces;
+ unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;
unsigned NumRegularPieces = TotalRegularPieces;
unsigned NumNotePieces = TotalNotePieces;
+ // Stores the count of the regular piece indices.
+ std::map<int, int> IndexMap;
+ // Stores the different ranges where we have reported something.
+ std::vector<SourceRange> PopUpRanges;
for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) {
- if (isa<PathDiagnosticNotePiece>(I->get())) {
+ const auto &Piece = *I->get();
+
+ if (isa<PathDiagnosticPopUpPiece>(Piece)) {
+ ++IndexMap[NumRegularPieces];
+ } else if (isa<PathDiagnosticNotePiece>(Piece)) {
// 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);
+ HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces);
--NumNotePieces;
} else {
- HandlePiece(R, FID, **I, NumRegularPieces, TotalRegularPieces);
+ HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces,
+ TotalRegularPieces);
--NumRegularPieces;
}
}
- // Add line numbers, header, footer, etc.
+ // Secondary indexing if we are having multiple pop-ups between two notes.
+ // (e.g. [(13) 'a' is 'true']; [(13.1) 'b' is 'false']; [(13.2) 'c' is...)
+ NumRegularPieces = TotalRegularPieces;
+ for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) {
+ const auto &Piece = *I->get();
+
+ if (const auto *PopUpP = dyn_cast<PathDiagnosticPopUpPiece>(&Piece)) {
+ int PopUpPieceIndex = IndexMap[NumRegularPieces];
+
+ // Pop-up pieces needs the index of the last reported piece and its count
+ // how many times we report to handle multiple reports on the same range.
+ // This marks the variable, adds the </table> end tag and the message
+ // (list element) as a row. The <table> start tag will be added after the
+ // rows has been written out. Note: It stores every different range.
+ HandlePopUpPieceEndTag(R, *PopUpP, PopUpRanges, NumRegularPieces,
+ PopUpPieceIndex);
+
+ if (PopUpPieceIndex > 0)
+ --IndexMap[NumRegularPieces];
+
+ } else if (!isa<PathDiagnosticNotePiece>(Piece)) {
+ --NumRegularPieces;
+ }
+ }
+
+ // Add the <table> start tag of pop-up pieces based on the stored ranges.
+ HandlePopUpPieceStartTag(R, PopUpRanges);
+ // 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,
+void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
+ const PathDiagnosticPiece &P,
+ const std::vector<SourceRange> &PopUpRanges,
unsigned num, unsigned max) {
// For now, just draw a box above the line in question, and emit the
// warning.
@@ -690,9 +778,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
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::Event: Kind = "Event"; break;
case PathDiagnosticPiece::ControlFlow: Kind = "Control"; break;
// Setting Kind to "Control" is intentional.
case PathDiagnosticPiece::Macro: Kind = "Control"; break;
@@ -701,6 +787,9 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
IsNote = true;
SuppressIndex = true;
break;
+ case PathDiagnosticPiece::Call:
+ case PathDiagnosticPiece::PopUp:
+ llvm_unreachable("Calls and extra notes should already be handled");
}
std::string sbuf;
@@ -860,8 +949,14 @@ void HTMLDiagnostics::HandlePiece(Rewriter& R, FileID BugFileID,
// Now highlight the ranges.
ArrayRef<SourceRange> Ranges = P.getRanges();
- for (const auto &Range : Ranges)
+ for (const auto &Range : Ranges) {
+ // If we have already highlighted the range as a pop-up there is no work.
+ if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) !=
+ PopUpRanges.end())
+ continue;
+
HighlightRange(R, LPosInfo.first, Range);
+ }
}
static void EmitAlphaCounter(raw_ostream &os, unsigned n) {
diff --git a/lib/StaticAnalyzer/Core/IssueHash.cpp b/lib/StaticAnalyzer/Core/IssueHash.cpp
index 6c55c61dd399..e7497f3fbdaa 100644
--- a/lib/StaticAnalyzer/Core/IssueHash.cpp
+++ b/lib/StaticAnalyzer/Core/IssueHash.cpp
@@ -1,9 +1,8 @@
//===---------- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/IssueHash.h"
@@ -121,7 +120,7 @@ static std::string GetEnclosingDeclContextSignature(const Decl *D) {
return "";
}
-static StringRef GetNthLineOfFile(llvm::MemoryBuffer *Buffer, int Line) {
+static StringRef GetNthLineOfFile(const llvm::MemoryBuffer *Buffer, int Line) {
if (!Buffer)
return "";
@@ -145,7 +144,7 @@ static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L,
col++;
SourceLocation StartOfLine =
SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);
- llvm::MemoryBuffer *Buffer =
+ const llvm::MemoryBuffer *Buffer =
SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine);
if (!Buffer)
return {};
diff --git a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
index da4574c61515..9838249ae82c 100644
--- a/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
+++ b/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
@@ -1,9 +1,8 @@
//===--- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
@@ -235,7 +234,7 @@ bool madeNewBranch(ExplodedNode *N, const Stmt *LoopStmt) {
ProgramPoint P = N->getLocation();
if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>())
- S = BE->getBlock()->getTerminator();
+ S = BE->getBlock()->getTerminatorStmt();
if (S == LoopStmt)
return false;
diff --git a/lib/StaticAnalyzer/Core/LoopWidening.cpp b/lib/StaticAnalyzer/Core/LoopWidening.cpp
index 8f6cb9a6b09e..9a7b1a24b819 100644
--- a/lib/StaticAnalyzer/Core/LoopWidening.cpp
+++ b/lib/StaticAnalyzer/Core/LoopWidening.cpp
@@ -1,9 +1,8 @@
//===--- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
///
diff --git a/lib/StaticAnalyzer/Core/MemRegion.cpp b/lib/StaticAnalyzer/Core/MemRegion.cpp
index 9a1d4d73c20b..f763701af7fb 100644
--- a/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -845,6 +844,7 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
const LocationContext *LC) {
+ D = D->getCanonicalDecl();
const MemRegion *sReg = nullptr;
if (D->hasGlobalStorage() && !D->isStaticLocal()) {
@@ -931,6 +931,7 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
const MemRegion *superR) {
+ D = D->getCanonicalDecl();
return getSubRegion<VarRegion>(D, superR);
}
@@ -1008,6 +1009,7 @@ MemRegionManager::getElementRegion(QualType elementType, NonLoc Idx,
const FunctionCodeRegion *
MemRegionManager::getFunctionCodeRegion(const NamedDecl *FD) {
+ // To think: should we canonicalize the declaration here?
return getSubRegion<FunctionCodeRegion>(FD, getCodeRegion());
}
diff --git a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
index 3e93bb6a7c4f..54fbd6a5bc49 100644
--- a/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
+++ b/lib/StaticAnalyzer/Core/PathDiagnostic.cpp
@@ -1,9 +1,8 @@
//===- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -91,6 +90,8 @@ PathDiagnosticMacroPiece::~PathDiagnosticMacroPiece() = default;
PathDiagnosticNotePiece::~PathDiagnosticNotePiece() = default;
+PathDiagnosticPopUpPiece::~PathDiagnosticPopUpPiece() = default;
+
void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,
bool ShouldFlattenMacros) const {
for (auto &Piece : *this) {
@@ -120,6 +121,7 @@ void PathPieces::flattenTo(PathPieces &Primary, PathPieces &Current,
case PathDiagnosticPiece::Event:
case PathDiagnosticPiece::ControlFlow:
case PathDiagnosticPiece::Note:
+ case PathDiagnosticPiece::PopUp:
Current.push_back(Piece);
break;
}
@@ -370,15 +372,16 @@ static Optional<bool> comparePiece(const PathDiagnosticPiece &X,
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));
+ case PathDiagnosticPiece::Event:
+ case PathDiagnosticPiece::Note:
+ case PathDiagnosticPiece::PopUp:
+ return None;
}
llvm_unreachable("all cases handled");
}
@@ -572,6 +575,8 @@ static SourceLocation getValidSourceLocation(const Stmt* S,
} while (!L.isValid());
}
+ // FIXME: Ironically, this assert actually fails in some cases.
+ //assert(L.isValid());
return L;
}
@@ -672,7 +677,15 @@ PathDiagnosticLocation::createConditionalColonLoc(
PathDiagnosticLocation
PathDiagnosticLocation::createMemberLoc(const MemberExpr *ME,
const SourceManager &SM) {
- return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);
+
+ assert(ME->getMemberLoc().isValid() || ME->getBeginLoc().isValid());
+
+ // In some cases, getMemberLoc isn't valid -- in this case we'll return with
+ // some other related valid SourceLocation.
+ if (ME->getMemberLoc().isValid())
+ return PathDiagnosticLocation(ME->getMemberLoc(), SM, SingleLocK);
+
+ return PathDiagnosticLocation(ME->getBeginLoc(), SM, SingleLocK);
}
PathDiagnosticLocation
@@ -715,7 +728,24 @@ PathDiagnosticLocation::create(const ProgramPoint& P,
const Stmt* S = nullptr;
if (Optional<BlockEdge> BE = P.getAs<BlockEdge>()) {
const CFGBlock *BSrc = BE->getSrc();
- S = BSrc->getTerminatorCondition();
+ if (BSrc->getTerminator().isVirtualBaseBranch()) {
+ // TODO: VirtualBaseBranches should also appear for destructors.
+ // In this case we should put the diagnostic at the end of decl.
+ return PathDiagnosticLocation::createBegin(
+ P.getLocationContext()->getDecl(), SMng);
+
+ } else {
+ S = BSrc->getTerminatorCondition();
+ if (!S) {
+ // If the BlockEdge has no terminator condition statement but its
+ // source is the entry of the CFG (e.g. a checker crated the branch at
+ // the beginning of a function), use the function's declaration instead.
+ assert(BSrc == &BSrc->getParent()->getEntry() && "CFGBlock has no "
+ "TerminatorCondition and is not the enrty block of the CFG");
+ return PathDiagnosticLocation::createBegin(
+ P.getLocationContext()->getDecl(), SMng);
+ }
+ }
} else if (Optional<StmtPoint> SP = P.getAs<StmtPoint>()) {
S = SP->getStmt();
if (P.getAs<PostStmtPurgeDeadSymbols>())
@@ -735,6 +765,12 @@ PathDiagnosticLocation::create(const ProgramPoint& P,
return getLocationForCaller(CEE->getCalleeContext(),
CEE->getLocationContext(),
SMng);
+ } else if (auto CEB = P.getAs<CallExitBegin>()) {
+ if (const ReturnStmt *RS = CEB->getReturnStmt())
+ return PathDiagnosticLocation::createBegin(RS, SMng,
+ CEB->getLocationContext());
+ return PathDiagnosticLocation(
+ CEB->getLocationContext()->getDecl()->getSourceRange().getEnd(), SMng);
} else if (Optional<BlockEntrance> BE = P.getAs<BlockEntrance>()) {
CFGElement BlockFront = BE->getBlock()->front();
if (auto StmtElt = BlockFront.getAs<CFGStmt>()) {
@@ -744,6 +780,9 @@ PathDiagnosticLocation::create(const ProgramPoint& P,
NewAllocElt->getAllocatorExpr()->getBeginLoc(), SMng);
}
llvm_unreachable("Unexpected CFG element at front of block");
+ } else if (Optional<FunctionExitPoint> FE = P.getAs<FunctionExitPoint>()) {
+ return PathDiagnosticLocation(FE->getStmt(), SMng,
+ FE->getLocationContext());
} else {
llvm_unreachable("Unexpected ProgramPoint");
}
@@ -779,7 +818,7 @@ const Stmt *PathDiagnosticLocation::getStmt(const ExplodedNode *N) {
if (auto SP = P.getAs<StmtPoint>())
return SP->getStmt();
if (auto BE = P.getAs<BlockEdge>())
- return BE->getSrc()->getTerminator();
+ return BE->getSrc()->getTerminatorStmt();
if (auto CE = P.getAs<CallEnter>())
return CE->getCallExpr();
if (auto CEE = P.getAs<CallExitEnd>())
@@ -1255,6 +1294,10 @@ void PathDiagnosticNotePiece::Profile(llvm::FoldingSetNodeID &ID) const {
PathDiagnosticSpotPiece::Profile(ID);
}
+void PathDiagnosticPopUpPiece::Profile(llvm::FoldingSetNodeID &ID) const {
+ PathDiagnosticSpotPiece::Profile(ID);
+}
+
void PathDiagnostic::Profile(llvm::FoldingSetNodeID &ID) const {
ID.Add(getLocation());
ID.AddString(BugType);
@@ -1380,6 +1423,13 @@ LLVM_DUMP_METHOD void PathDiagnosticNotePiece::dump() const {
getLocation().dump();
}
+LLVM_DUMP_METHOD void PathDiagnosticPopUpPiece::dump() const {
+ llvm::errs() << "POP-UP\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";
diff --git a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index db4cf76578d8..838751279297 100644
--- a/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -1,9 +1,8 @@
//===--- 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.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
@@ -23,6 +22,7 @@
#include "clang/StaticAnalyzer/Core/IssueHash.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/Statistic.h"
+#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
@@ -120,6 +120,9 @@ private:
case PathDiagnosticPiece::Note:
ReportNote(o, cast<PathDiagnosticNotePiece>(P), indent);
break;
+ case PathDiagnosticPiece::PopUp:
+ ReportPopUp(o, cast<PathDiagnosticPopUpPiece>(P), indent);
+ break;
}
}
@@ -138,6 +141,9 @@ private:
unsigned indent, unsigned depth);
void ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
unsigned indent);
+
+ void ReportPopUp(raw_ostream &o, const PathDiagnosticPopUpPiece &P,
+ unsigned indent);
};
} // end of anonymous namespace
@@ -397,6 +403,34 @@ void PlistPrinter::ReportNote(raw_ostream &o, const PathDiagnosticNotePiece& P,
Indent(o, indent); o << "</dict>\n";
}
+void PlistPrinter::ReportPopUp(raw_ostream &o,
+ const PathDiagnosticPopUpPiece &P,
+ unsigned indent) {
+ const SourceManager &SM = PP.getSourceManager();
+
+ Indent(o, indent) << "<dict>\n";
+ ++indent;
+
+ Indent(o, indent) << "<key>kind</key><string>pop-up</string>\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 text.
+ EmitMessage(o, P.getString(), indent);
+
+ // Finish up.
+ --indent;
+ Indent(o, indent) << "</dict>\n";
+}
+
//===----------------------------------------------------------------------===//
// Static function definitions.
//===----------------------------------------------------------------------===//
@@ -714,7 +748,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
}
// Finish.
- o << "</dict>\n</plist>";
+ o << "</dict>\n</plist>\n";
}
//===----------------------------------------------------------------------===//
@@ -777,10 +811,20 @@ public:
/// 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);
+///
+/// To avoid infinite recursion we maintain the already processed tokens in
+/// a set. This is carried as a parameter through the recursive calls. The set
+/// is extended with the currently processed token and after processing it, the
+/// token is removed. If the token is already in the set, then recursion stops:
+///
+/// #define f(y) x
+/// #define x f(x)
+static std::string getMacroNameAndPrintExpansion(
+ TokenPrinter &Printer,
+ SourceLocation MacroLoc,
+ const Preprocessor &PP,
+ const MacroArgMap &PrevArgs,
+ llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens);
/// Retrieves the name of the macro and what it's arguments expand into
/// at \p ExpanLoc.
@@ -829,19 +873,38 @@ static ExpansionInfo getExpandedMacro(SourceLocation MacroLoc,
llvm::SmallString<200> ExpansionBuf;
llvm::raw_svector_ostream OS(ExpansionBuf);
TokenPrinter Printer(OS, PP);
+ llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens;
+
std::string MacroName =
- getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{});
+ getMacroNameAndPrintExpansion(Printer, MacroLoc, PP, MacroArgMap{},
+ AlreadyProcessedTokens);
return { MacroName, OS.str() };
}
-static std::string getMacroNameAndPrintExpansion(TokenPrinter &Printer,
- SourceLocation MacroLoc,
- const Preprocessor &PP,
- const MacroArgMap &PrevArgs) {
+static std::string getMacroNameAndPrintExpansion(
+ TokenPrinter &Printer,
+ SourceLocation MacroLoc,
+ const Preprocessor &PP,
+ const MacroArgMap &PrevArgs,
+ llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens